PyQt5 on Fullscreen with embedded vispy canvas flickers when any widget is clicked

268 views Asked by At

I'm working on an application where I've embedded a vispy scene.SceneCanvas to pyqt5 to plot volumes. But when I put pyqt5 on full screen, it flickers whenever I click on any widget. Is there a way to fix this or is this?

I've added a small script bellow that reproduces the same issue. Clicking the load volume button or the drop down, makes the screen flicker excessively. Not sure if this happens on other OS but I'm using windows 10.

Any help will be appreciated. Thanks

from PyQt5.QtWidgets import QComboBox, QVBoxLayout, QWidget, QHBoxLayout, QApplication, QFileDialog, QPushButton, QMainWindow
from vispy import scene
from vispy.visuals.transforms import STTransform, MatrixTransform
import numpy as np
import tifffile
import sys

import skimage
import skimage.exposure

class vispyTest(QMainWindow):

    def __init__(self):
        super(vispyTest, self).__init__()

        self.setVispyCanvas = scene.SceneCanvas()
        # self.setVispyCanvas.measure_fps()
        self.vispyView = self.setVispyCanvas.central_widget.add_view()

        # Create an XYZAxis visual
        self.axis = scene.visuals.XYZAxis(parent=self.vispyView)
        s = STTransform(translate=(50, 50), scale=(50, 50, 50, 1))
        affine = s.as_matrix()
        self.axis.transform = affine

        # Create cameras
        fov = 60.
        self.imageCamera = scene.cameras.TurntableCamera(
            parent=self.vispyView.scene, fov=fov, name='Turntable')
        self.vispyView.camera = self.imageCamera

        # Set initial view angle.
        self.vispyView.camera.azimuth = 90
        self.vispyView.camera.elevation = 30

        self.loadVolume = QPushButton()
        self.loadVolume.setText("LOAD VOLUME")
        self.loadVolume.clicked.connect(self.set_volume)

        self.closeWindow = QPushButton()
        self.closeWindow.setText("CLOSE")
        self.closeWindow.clicked.connect(self.close)

        colors = ["Yellow", "Magenta", "Black", "White",
                       "Green", "Blue", "Cyan", "Red"]
        self.drop_down = QComboBox()
        for color in colors:
            self.drop_down.addItem(color)

        self.buttonLayout = QVBoxLayout()
        self.buttonLayout.addWidget(self.loadVolume)
        self.buttonLayout.addWidget(self.drop_down)
        self.buttonLayout.addWidget(self.closeWindow)

        self.mainWidget = QWidget()
        self.mainLayout = QHBoxLayout(self.mainWidget)

        self.mainLayout.setSpacing(10)
        self.mainLayout.setContentsMargins(20, 20, 20, 20)
        self.mainLayout.addLayout(self.buttonLayout)
        self.mainLayout.addWidget(self.setVispyCanvas.native, 1)

        self.setCentralWidget(self.mainWidget)

        self.showFullScreen()

    def set_volume(self):

        openImageFile, _ = QFileDialog.getOpenFileName(
            self, "open", "", "Image Files (*.tif *.tiff *.TIF ""*.TIFF);;All Files (*)")

        if not openImageFile:
            return

        with tifffile.TiffFile(openImageFile) as tif:
            vol1 = tif.asarray()

        self.imageVolume = self.prepare_for_rendering(vol1)

        self.volume1 = scene.visuals.Volume(
            vol=self.imageVolume,
            parent=self.vispyView.scene,
            threshold=0.225,
            relative_step_size=2.0,
            method="mip")

        transformVolume = scene.STTransform(
            translate=(0, 0, 0))
        self.volume1.transform = transformVolume

        self.setVispyCanvas.update()

    def prepare_for_rendering(self, vol):
        """Convert the volume to uint8 and normalize intensity."""
        # Normalize intensity for float volumes between -1.0 and 1.0
        if (vol.dtype == np.float32 or vol.dtype == np.float64) and np.min(
                vol) < -1.0 or np.max(vol) > 1.0:
            vol = skimage.exposure.rescale_intensity(vol)
        if vol.dtype != np.uint8:
            vol = skimage.img_as_ubyte(vol)
        return vol

if __name__ == '__main__':
    # print(__doc__)
    app = QApplication(sys.argv)
    main_window = vispyTest()
    sys.exit(app.exec_())
1

There are 1 answers

9
djhoese On

Here's a simplified version of your script that doesn't require any extra dependencies and displays a solid color Volume:

from PyQt5.QtWidgets import QComboBox, QVBoxLayout, QWidget, QHBoxLayout, QApplication, QFileDialog, QPushButton, QMainWindow
from vispy import scene
from vispy.visuals.transforms import STTransform, MatrixTransform
import numpy as np
import sys


class vispyTest(QMainWindow):

    def __init__(self):
        super(vispyTest, self).__init__()

        self.setVispyCanvas = scene.SceneCanvas()
        # self.setVispyCanvas.measure_fps()
        self.vispyView = self.setVispyCanvas.central_widget.add_view()

        # Create an XYZAxis visual
        self.axis = scene.visuals.XYZAxis(parent=self.vispyView)
        s = STTransform(translate=(50, 50), scale=(50, 50, 50, 1))
        affine = s.as_matrix()
        self.axis.transform = affine

        # Create cameras
        fov = 60.
        self.imageCamera = scene.cameras.TurntableCamera(
            parent=self.vispyView.scene, fov=fov, name='Turntable')
        self.vispyView.camera = self.imageCamera

        # Set initial view angle.
        self.vispyView.camera.azimuth = 90
        self.vispyView.camera.elevation = 30

        self.loadVolume = QPushButton()
        self.loadVolume.setText("LOAD VOLUME")
        self.loadVolume.clicked.connect(self.set_volume)

        self.closeWindow = QPushButton()
        self.closeWindow.setText("CLOSE")
        self.closeWindow.clicked.connect(self.close)

        colors = ["Yellow", "Magenta", "Black", "White",
                       "Green", "Blue", "Cyan", "Red"]
        self.drop_down = QComboBox()
        for color in colors:
            self.drop_down.addItem(color)

        self.buttonLayout = QVBoxLayout()
        self.buttonLayout.addWidget(self.loadVolume)
        self.buttonLayout.addWidget(self.drop_down)
        self.buttonLayout.addWidget(self.closeWindow)

        self.mainWidget = QWidget()
        self.mainLayout = QHBoxLayout(self.mainWidget)

        self.mainLayout.setSpacing(10)
        self.mainLayout.setContentsMargins(20, 20, 20, 20)
        self.mainLayout.addLayout(self.buttonLayout)
        self.mainLayout.addWidget(self.setVispyCanvas.native, 1)

        self.setCentralWidget(self.mainWidget)

        self.showFullScreen()

    def set_volume(self):
        self.imageVolume = np.ones((100, 200, 300), dtype=np.uint8)
        self.volume1 = scene.visuals.Volume(
            vol=self.imageVolume,
            parent=self.vispyView.scene,
            threshold=0.225,
            relative_step_size=2.0,
            cmap="viridis",
            method="mip")

        transformVolume = scene.STTransform(translate=(0, 0, 0))
        self.volume1.transform = transformVolume
        self.setVispyCanvas.update()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    main_window = vispyTest()
    sys.exit(app.exec_())

After zooming out for a while this shows:

enter image description here

The only time I saw "flickering" on my PopOS (Linux) system was when I initially zoomed out from the starting position. This starting position is set by your camera and the transform you put on your Volume. Once you zoom out do you still see flickering? You said "any widget", does that mean even the Qt drop down? I'm not able to reproduce that. Do you see the flickering if you use self.show() instead of self.showFullScreen()?