I want to set up a gui that runs in its own thread. Therefor I create my own class MainWindow
as a subclass of QMainWindow
and created a function run()
that is executed in another thread (see code below).
Now my problem:
When I execute run()
in a separate thread and use my own MainWindow
as a widget, I always get the debug messages when closing the window:
QObject::killTimer: Timers cannot be stopped from another thread
QObject::~QObject: Timers cannot be stopped from another thread
But when I execute run()
in a separate thread and use an empty QMainWindow
as a widget, these messages are not prompted.
And when I execute run()
in the main thread and not in a separate thread together with my MainWindow
-widget, these messages also are not prompted.
So my question is, where does this “timer” comes from? I do not use a timer inside my widget-class!?
from threading import Thread
import logging
from pathlib import Path
from PySide6.QtCore import Qt, QSize, QTimer
from PySide6.QtGui import QAction, QCloseEvent, QIcon, QScreen
from PySide6.QtWidgets import (
QApplication,
QMainWindow,
QWidget,
QToolBar,
QHBoxLayout,
QVBoxLayout,
QLabel,
QPushButton,
QSlider
)
import matplotlib
matplotlib.use('Qt5Agg')
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
from modules.control.control import Control, ControlCommand, ControlState, ControlStatus
from modules.machine.machine import Machine, MachineCommand, MachineState, MachineStatus
from modules.laserscanner.laserscanner import Laserscanner, LaserscannerCommand, LaserscannerState, LaserscannerStatus
class MplCanvas(FigureCanvasQTAgg):
def __init__(self, parent: QWidget = None, width = 5, height = 4, dpi = 150):
_fig = Figure(figsize=(width, height), dpi=dpi)
self._axes = _fig.add_subplot()
super(MplCanvas, self).__init__(_fig)
class MainWindow(QMainWindow):
def __init__(self, control: Control, machine: Machine, scanner: Laserscanner) -> None:
super(MainWindow, self).__init__()
# references
self._control = control
self._machine = machine
self._scanner = scanner
self._nc_file: Path = Path()
# title
self.setWindowTitle('label_title')
# menu
# actions
self._action_menu_file_load = QAction(QIcon.fromTheme(QIcon.ThemeIcon.DocumentOpen), 'label_menu_file_load')
self._action_menu_file_load.triggered.connect(self.dummy_callback)
self._action_menu_file_exit = QAction(QIcon.fromTheme(QIcon.ThemeIcon.ApplicationExit), 'label_menu_file_exit')
self._action_menu_file_exit.triggered.connect(self.dummy_callback)
self._menu = self.menuBar()
self._menu_file = self._menu.addMenu('&' + 'label_menu_file')
self._menu_file.addAction(self._action_menu_file_load)
self._menu_file.addSeparator()
self._menu_file.addAction(self._action_menu_file_exit)
# toolbar
self._toolbar = QToolBar(self)
self._toolbar.setIconSize(QSize(16,16))
self._toolbar.setAllowedAreas(Qt.ToolBarArea.TopToolBarArea)
self._toolbar.setMovable(False)
self._toolbar.setFloatable(False)
self._toolbar.addAction(self._action_menu_file_load)
self.addToolBar(self._toolbar)
# layout & content
# boxes
self._box_window = QHBoxLayout()
self._box_control = QVBoxLayout()
self._box_control_view = QHBoxLayout()
self._box_control_view_area = QVBoxLayout()
self._box_control_view_slider = QVBoxLayout()
self._box_control_selection = QHBoxLayout()
self._box_control_machine = QHBoxLayout()
self._box_status = QVBoxLayout()
# fill boxes
# control
# viewing area
self._sc = MplCanvas(self, width=5, height=4, dpi=150)
self._sc._axes.plot([0,1,2,3,4], [10,1,20,3,40])
self._toolbar_sc = NavigationToolbar(self._sc, self)
self._box_control_view_area.addWidget(self._toolbar_sc)
self._box_control_view_area.addWidget(self._sc)
self._label_layer_max = QLabel('10')
self._label_layer_max.setAlignment(Qt.AlignmentFlag.AlignHCenter)
self._slider_layer = QSlider()
self._label_layer_min = QLabel('0')
self._label_layer_min.setAlignment(Qt.AlignmentFlag.AlignHCenter)
self._box_control_view_slider.addWidget(self._label_layer_max)
self._box_control_view_slider.addWidget(self._slider_layer)
self._box_control_view_slider.addWidget(self._label_layer_min)
self._box_control_view.addLayout(self._box_control_view_area)
self._box_control_view.addLayout(self._box_control_view_slider)
# viewing area buttons
self._button_view_welding = QPushButton('button_welding')
self._button_view_scan = QPushButton('button_scan')
self._button_view_defects = QPushButton('button_defects')
self._button_view_milling = QPushButton('button_milling')
self._box_control_selection.addWidget(self._button_view_welding)
self._box_control_selection.addWidget(self._button_view_scan)
self._box_control_selection.addWidget(self._button_view_defects)
self._box_control_selection.addWidget(self._button_view_milling)
# machine
self._button_machine_start = QPushButton('button_start')
self._button_machine_pause = QPushButton('button_pause')
self._button_machine_stop = QPushButton('button_stop')
self._button_machine_acknowledge = QPushButton('button_acknowledge')
self._box_control_machine.addWidget(self._button_machine_start)
self._box_control_machine.addWidget(self._button_machine_pause)
self._box_control_machine.addWidget(self._button_machine_stop)
self._box_control_machine.addWidget(self._button_machine_acknowledge)
# pack boxes
self._box_control.addLayout(self._box_control_view)
self._box_control.addLayout(self._box_control_selection)
self._box_control.addLayout(self._box_control_machine)
# status
self._label_status_control = QLabel('label_status')
self._label_status_control_state = QLabel('unknown')
self._label_status_control_state.setAlignment(Qt.AlignmentFlag.AlignHCenter)
self._label_status_control_state.setStyleSheet('background-color: rgb(255,0,0); margin:5px; border:1px solid rgb(0, 0, 0);')
self._label_status_machine = QLabel('label_status_machine')
self._label_status_machine_state = QLabel('unknown')
self._label_status_machine_state.setAlignment(Qt.AlignmentFlag.AlignHCenter)
self._label_status_machine_state.setStyleSheet('background-color: rgb(255,0,0); margin:5px; border:1px solid rgb(0, 0, 0);')
self._label_status_scanner = QLabel('label_status_scanner')
self._label_status_scanner_state = QLabel('unknown')
self._label_status_scanner_state.setAlignment(Qt.AlignmentFlag.AlignHCenter)
self._label_status_scanner_state.setStyleSheet('background-color: rgb(255,0,0); margin:5px; border:1px solid rgb(0, 0, 0);')
self._box_status.addWidget(self._label_status_control)
self._box_status.addWidget(self._label_status_control_state)
self._box_status.addWidget(self._label_status_machine)
self._box_status.addWidget(self._label_status_machine_state)
self._box_status.addWidget(self._label_status_scanner)
self._box_status.addWidget(self._label_status_scanner_state)
self._box_status.addStretch()
# pack boxes
self._box_window.addLayout(self._box_control)
self._box_window.addLayout(self._box_status)
# central widget
self._widget_central = QWidget()
self._widget_central.setLayout(self._box_window)
self.setCentralWidget(self._widget_central)
def dummy_callback(self):
print('dummy callback')
def run() -> None:
_app = QApplication([])
# _window = QMainWindow()
_window = MainWindow(None, None, None)
_window.show()
_app.exec()
def main():
_t_gui = Thread(target=run, name='GUI')
_t_gui.start()
_t_gui.join()
# run()
if __name__ == '__main__':
main()