UI scrambling, painter not active using QCheckbox() in PyQT
-
I have a fairly large QT app that is having an issue where I am trying to update a QToggle() pragmatically and it periodically causes my whole UI to look scrambled.
My app is started with a QMainWindow that also creates a QWidget, in a layout, on the main window called "fluid control". This fluidic control is an attribute to the QMainWindow class, and I am familiar with how signal/slots work to update the UI. Since fluid control is an an attribute to the QMainWindow, as long as the same thread that spawned the fluidic control widget is the one updating the UI, it should be fine.
This fluid control widget updates many spin boxes, buttons, etc. However, what messes up the UI is QCheckbox(), specifically when I try to update its state (and widget UI) programmatically .
@pyqtSlot(bool) def update_vol_toggle_slot(self, toggle): self.dispense_vol_toggle.setChecked(toggle)
The code above is how I update the toggle. When it messes up I get the error:
QPainter::begin: Paint device returned engine == 0, type: 3 QPainter::setCompositionMode: Painter not active QPainter::fillRect: Painter not active QPainter::end: Painter not active, aborted
Is there a safer or better way to update a QCheckbox() programmatically
-
Hi,
Any chances you are calling that slot directly from a different thread ?
-
@SGaist Hello, I creating the QWidget() which contains this slot/signal from my UI thread.
I spawn my UI, which creates a QWidget called FluidicControl, which contains a QGridLayout PumpUiDevice. No threading or multiprocessing anywhere for any of these.
Despite what I said above, is this issue indicative of the slot being called from the wrong thread? If that was the error is from then somehow I must be mistaken. Below is a small example of how everything is called. do QWidgets() start their own thread behind the scenes for event handling?
class Ui(QtWidgets.QMainWindow): def __init__(self): self.fluidic_ui = FluidicControl()
class FluidicControl(QtWidgets.QWidget): def __init__(self): self.pump_ui = PumpUiDevice()
class PumpUiDevice(QGridLayout): update_vol_toggle_s = pyqtSignal(bool)
Edit: another important thing I just want to make sure i am right on, the signal is being fired from a separate thread, but I thought that's what signals and slots were used for, so the slot is called in the thread you want. Am I mistaken on that?
-
No, QWidgets don't have any threads.
Why did you subclass QGridLayout ? There's rarely reason to subclass layout classes.
Where exactly is that signal you are firing from a different thread ?
-
I have a fairly large QT app that is having an issue where I am trying to update a QToggle() pragmatically and it periodically causes my whole UI to look scrambled.
My app is started with a QMainWindow that also creates a QWidget, in a layout, on the main window called "fluid control". This fluidic control is an attribute to the QMainWindow class, and I am familiar with how signal/slots work to update the UI. Since fluid control is an an attribute to the QMainWindow, as long as the same thread that spawned the fluidic control widget is the one updating the UI, it should be fine.
This fluid control widget updates many spin boxes, buttons, etc. However, what messes up the UI is QCheckbox(), specifically when I try to update its state (and widget UI) programmatically .
@pyqtSlot(bool) def update_vol_toggle_slot(self, toggle): self.dispense_vol_toggle.setChecked(toggle)
The code above is how I update the toggle. When it messes up I get the error:
QPainter::begin: Paint device returned engine == 0, type: 3 QPainter::setCompositionMode: Painter not active QPainter::fillRect: Painter not active QPainter::end: Painter not active, aborted
Is there a safer or better way to update a QCheckbox() programmatically
@kgenbio said in UI scrambling, painter not active using QCheckbox() in PyQT:
The code above is how I update the toggle. When it messes up I get the error:
QPainter::begin: Paint device returned engine == 0, type: 3 QPainter::setCompositionMode: Painter not active QPainter::fillRect: Painter not active QPainter::end: Painter not active, aborted Is there a safer or better way to update a QCheckbox() programmatically
Do you paint (use a
QPainter
) outside some widget'spaintEvent
handler? -
No, QWidgets don't have any threads.
Why did you subclass QGridLayout ? There's rarely reason to subclass layout classes.
Where exactly is that signal you are firing from a different thread ?
The FluidicControl class has an event handler that is started and constantly checks for new events, which then fires off signals.
class FluidicControl(QtWidgets.QWidget): def __init__(self): main_loop_thread = Thread(target=self.main_loop) main_loop_thread.start() def main_loop(self): while self._run_flag: try: msg = self.fluidic_event_queue.get(block=False) self.handle_event(msg) except: pass
signals are fired off from the "handle_event()" function. they signal/slot connections are made in the fluidiccontrol init function.
-
The FluidicControl class has an event handler that is started and constantly checks for new events, which then fires off signals.
class FluidicControl(QtWidgets.QWidget): def __init__(self): main_loop_thread = Thread(target=self.main_loop) main_loop_thread.start() def main_loop(self): while self._run_flag: try: msg = self.fluidic_event_queue.get(block=False) self.handle_event(msg) except: pass
signals are fired off from the "handle_event()" function. they signal/slot connections are made in the fluidiccontrol init function.
-
Do you access the UI from this thread / loop? Or draw on your widget?
You must only access the UI from the main Qt UI thread.Any chance that youe own
main_loop
blocks or interrupts the Qt event loop so widgets can't get updated/re-painted?This thread fires off pyqt signals only, it does not access the UI itself. I am unsure what thread the slots are handled in, i figured they were going to run in the main GUI thread.
Is the thread that signals are fired from the same as the thread where the slot is run?
-
You are firing a signal that belongs to a widget class which shall only happen in the main thread. Use the worker object approach with a QObject based class no QWidget there.