PDA

View Full Version : QApplication Process seems to hang



SaibotMagd
7th July 2020, 17:51
Hello,

I'm a somewhat programming and python nooby and I have to implement a pipeline in my institut. A researcher create a dataviewer in the tutorial file using PyQt5 but even the test-function push the process into a hanging state (kernel is forced to restart after the window is terminated). i.e. it creates a pop-up window with the correct name and resolution but the window itself is black without any widgets and it halts immediately.

I know it's a somewhat impossible mission to find the solution but maybe someone got a climse of a idea what I could try next.


class DataViewer(pg.QtGui.QWidget):
def __init__(self, source, axis = None, scale = None, title = None, invertY = False, minMax = None, screen = None, parent = None, *args):

### Print pointer to QApplication Instance (we now know that at this time a QApp is initalized)
print(pg.QtGui.QApplication.instance())
print(pg.QtGui.QApplication.allWidgets())

### Images soures
self.initializeSources(source, axis = axis, scale = scale)
#print('init')
### Gui Construction
pg.QtGui.QWidget.__init__(self, parent, *args);
#print('gui')

if title is None:
if isinstance(source, str):
title = source;
elif isinstance(source, io.src.Source):
title = source.location;
if title is None:
title = 'DataViewer';
self.setWindowTitle(title);
self.resize(1600,1200)
#print('title')


self.layout = pg.QtGui.QGridLayout(self);
self.layout.setContentsMargins(0,0,0,0)
#print('layout')

# image pane
self.view = pg.ViewBox();
self.view.setAspectLocked(True);
self.view.invertY(invertY)

self.graphicsView = pg.GraphicsView()
self.graphicsView.setObjectName("GraphicsView")
self.graphicsView.setCentralItem(self.view)

splitter = pg.QtGui.QSplitter();
splitter.setOrientation(pg.QtCore.Qt.Horizontal)
splitter.setSizes([self.width() - 10, 10]);
self.layout.addWidget(splitter);

image_splitter = pg.QtGui.QSplitter();
image_splitter.setOrientation(pg.QtCore.Qt.Vertica l)
image_splitter.setSizePolicy(pg.QtGui.QSizePolicy. Expanding, pg.QtGui.QSizePolicy.Expanding)
splitter.addWidget(image_splitter);
#print('image')

# Image plots
image_options = dict(clipToView = True, autoDownsample = True, autoLevels = False, useOpenGL = None);
self.image_items = [pg.ImageItem(s[self.source_slice[:s.ndim]], **image_options) for s in self.sources];
for i in self.image_items:
i.setRect(pg.QtCore.QRect(0, 0, self.source_range_x, self.source_range_y))
i.setCompositionMode(pg.QtGui.QPainter.Composition Mode_Plus);
self.view.addItem(i);
self.view.setXRange(0, self.source_range_x);
self.view.setYRange(0, self.source_range_y);
#print('plots')

# Slice Selector
self.slicePlot = pg.PlotWidget()
sizePolicy = pg.QtGui.QSizePolicy(pg.QtGui.QSizePolicy.Preferre d, pg.QtGui.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.slicePlot.sizePo licy().hasHeightForWidth())
self.slicePlot.setSizePolicy(sizePolicy)
self.slicePlot.setMinimumSize(pg.QtCore.QSize(0, 40))
self.slicePlot.setObjectName("roiPlot");
#self.sliceCurve = self.slicePlot.plot()

self.sliceLine = pg.InfiniteLine(0, movable=True)
self.sliceLine.setPen((255, 255, 255, 200))
self.sliceLine.setZValue(1)
self.slicePlot.addItem(self.sliceLine)
self.slicePlot.hideAxis('left')

self.updateSlicer();

self.sliceLine.sigPositionChanged.connect(self.upd ateSlice)
#print('slice')

# Axis Tools
axis_tools_layout = pg.QtGui.QGridLayout()
self.axis_buttons = [];
axesnames = ['x', 'y', 'z'];
for d in range(3):
button = pg.QtGui.QRadioButton(axesnames[d]);
button.setMaximumWidth(50);
axis_tools_layout.addWidget(button,0,d);
button.clicked.connect(ft.partial(self.setSliceAxi s, d));
self.axis_buttons.append(button);
self.axis_buttons[self.source_axis].setChecked(True);
axis_tools_widget = pg.QtGui.QWidget();
axis_tools_widget.setLayout(axis_tools_layout);
#print('axis')

# coordinate label
self.source_pointer = [0,0,0];
self.source_label = pg.QtGui.QLabel("");
axis_tools_layout.addWidget(self.source_label,0,3) ;

self.graphicsView.scene().sigMouseMoved.connect(se lf.updateLabelFromMouseMove);
#print('coords')

#compose the image viewer
image_splitter.addWidget(self.graphicsView);
image_splitter.addWidget(self.slicePlot)
image_splitter.addWidget(axis_tools_widget);
image_splitter.setSizes([self.height()-35-20, 35, 20])
#print('viewer')

# lut widgets
if self.nsources == 1:
cols = ['flame'];
elif self.nsources == 2:
cols = ['purple', 'green'];
else:
cols = np.array(['white', 'green','red', 'blue', 'purple'] * self.nsources)[:self.nsources];

self.luts = [LUT(image = i, color = c) for i,c in zip(self.image_items, cols)];

lut_layout = pg.QtGui.QGridLayout();

lut_layout.setContentsMargins(0,0,0,0);
for d,l in enumerate(self.luts):
lut_layout.addWidget(l,0,d);
lut_widget = pg.QtGui.QWidget();
lut_widget.setLayout(lut_layout);
lut_widget.setContentsMargins(0,0,0,0);
lut_widget.setSizePolicy(pg.QtGui.QSizePolicy.Pref erred, pg.QtGui.QSizePolicy.Expanding)
splitter.addWidget(lut_widget);

splitter.setStretchFactor(0, 1);
splitter.setStretchFactor(1, 0);

# update scale
for l in self.luts:
l.range_buttons[1][2].click();
if minMax is not None:
self.setMinMax(minMax);

self.show();
print(pg.QtGui.QApplication.instance())

What I found out till now:

the "allWidget()" function print out all widgets collected by the QApp. and this works finde
the code runs till the end (I got a instance pointer at the beginning and in the end; there're the same)
I tried several PyQt examples to test for openGL or non openGL Outputs and they all worked fine
there's no out-of-memory or CPU overload
I tried to cut the code per widget to find the issue, but whatever line of code I commented out no window pops-up anymore
I found this great article: https://doc.qt.io/archives/qq/qq27-responsive-guis.html#conclusion Could it be something like that?


Like I said I'm not a programmer and I didn't write the code myself (the author don't answer right now). Its overwhelming but I have to find a solution. Any ideas what I could try next? Anything would be helpful.

d_stranz
7th July 2020, 23:32
All you are showing is a class definition without showing how it is being used. There are a number of dependencies that you are also not giving any details on (ViewBox, GraphcsView, PlotWidget, InfiniteLine). Maybe these are extensions provided by PyQt5 - they aren't part of the standard Qt widget set.

So a little context would be helpful.

SaibotMagd
8th July 2020, 08:52
The whole code came from: https://github.com/ChristophKirst/ClearMap2. He build a DataViewer to look at tif or numpy converted files.
The Testcode to check if this class is working is:
def _test():
import numpy as np
import ClearMap.Visualization.Qt.DataViewer as dv

img1 = np.random.rand(*(100,80,30));

dv.DataViewer(img1) its in ClearMap/Visualization/Qt/DataViewer.py

I think it's so basic that it isn't helpful at all. Most interesting part is that the creator and at least 2 others are able to use the Viewer. So the problem shouldn't be in the code itself more about dependencies or the OS. I now try to clone the exact OS he's using. As you see I'm just started using linux and python code, so I don't really know what to do.

d_stranz
8th July 2020, 17:22
OK, the problem with the test code is that it does not create a QApplication instance, does not call dv.show() (although the __init__ method for DataViewer -does- call it, which it shouldn't). Your test code should look more like this:




def _test() :
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import Qt

import numpy as np
import ClearMap.Visualization.Qt.DataViewer as dv

app = QApplication(sys.argv)

img1 = np.random.rand(*(100,80,30));

myViewer = dv.DataViewer(img1)

myViewer.show()

sys.exit(app.exec_())


The reason the widget works inside a normal application is because the application almost certainly creates the QApplication instance and calls its exec() method so that the normal Qt event loop is up and running and the widget is responding to events. The original _test() method did none of that. Modifying it so it behaves like a mini-standalone app fixes that.

SaibotMagd
8th July 2020, 18:12
OK, the problem with the test code is that it does not create a QApplication instance, does not call dv.show() (although the __init__ method for DataViewer -does- call it, which it shouldn't). Your test code should look more like this:

The reason the widget works inside a normal application is because the application almost certainly creates the QApplication instance and calls its exec() method so that the normal Qt event loop is up and running and the widget is responding to events. The original _test() method did none of that. Modifying it so it behaves like a mini-standalone app fixes that.

I barely know what to say, Im in tears. Thats phantastic thank you very very much. It works great and I believe I can implement the solution into the package, and I will do it.

Great work! :)