0

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_())
SorinT
  • 53
  • 1
  • 6

1 Answers1

0

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()?

djhoese
  • 3,567
  • 1
  • 27
  • 45
  • The application I'm working on needs to be in full screen, so if I just use ```self.show()```, it brings up a minimized window. The flickering doesn't occur when minimized. About the "any widget", I get a whole window flickering when I click either the drop down or the open button ass shown in the example above. I ran your simplify version but still got the flickering. This made me think probably it's a windows issue – SorinT May 08 '22 at 21:57
  • If you take out vispy completely, do you still get the flickering? – djhoese May 09 '22 at 01:05
  • No, it doesn't flicker if vispy is out. After some research, I fond that the flicker has something to do with openGL and pyqt in full screen mode. I saw some threads on the Qt forum about it but didn't find a solution to it. – SorinT May 09 '22 at 01:17
  • Ok, that was going to be my next guess. This is the first I've heard of this and wasn't able to reproduce it myself. If it is a Qt + Windows issue I'm not sure there is an easy solution on my end. Could you link to the Qt forums here? – djhoese May 09 '22 at 16:32
  • Yes. This [link](https://forum.qt.io/topic/68132/flicker-with-qopenglwidget-when-fullscreen-and-frameless-window) uses openGL widget but the overall issue seems to be related to mine. There are also some other forums similar to the same issue. – SorinT May 09 '22 at 17:39
  • Thanks. What version of PyQt5 are you using? – djhoese May 10 '22 at 15:43
  • I'm using version 5.15.4 – SorinT May 10 '22 at 19:08
  • Ok so definitely a modern version. I'm not sure where to go from here except for trying the workarounds mentioned on the various forums and bug reports you already found. – djhoese May 11 '22 at 00:27
  • Thank you for the suggestion. I ended up using the workaround of setting the geometry with a height plus 1 pixel. Ex. ```self.setGeometry(0, 0, height+1, width) ```. It works just like full screen for now – SorinT May 11 '22 at 01:57