QProgressDialog only shows after long-running code is finished - python

I have a program designed according to the MVC model. My view has an open button where I can open files. The files are parsed and a lot of calculation is done with the file contents.
Now I want to display a loader to indicate that the user should wait. I am totally new to Python and Qt. I am using PyQt5 (Qt 5.6.2) with Python 3.6.2.
I added the showLoader() method to the openFiles() method of my view:
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, controller, parent = None):
# initializing the window
def showOpenDialog(self):
files, filters = QtWidgets.QFileDialog.getOpenFileNames(self, 'Open file(s)', '',
"Raw files (*.rw.dat);;Data files (*.dat)" +
";;Text files (*.txt);;All files (*)")
self.showLoader("Loading file(s)")
self.doSomeStuffWithTheFiles(files)
self.hideLoader()
def showLoader(self, text):
self._progress = QtWidgets.QProgressDialog(text, "Abort", 0, 0, self);
self._progress.setWindowModality(QtCore.Qt.WindowModal);
This will display the Loader but it will appear after the file is loaded. Not even immediately after the file is loaded, but it will take an additional 1-2 seconds after everything is done (including some repainting of the Window)
I read a lot about threads so I assume the file parsing is blocking the progress loader which makes sense. I read that I should add the QProgressDialog to a slot (I don't really know what that is) but this doesn't help me because I want the QProgressDialog to be displayed after the QFileDialog.
I also read something about adding QtWidgets.QApplication.processEvents() to repaint the Window, but this didn't work for me (or I used it wrong).
So my questions are:
How do I display the QProgressDialog when I call the showLoader() method?
Do I have to execute my calculations and file parsing in a different thread and if have to how do I do this?
If I wanted to display some more information in the QProgressDialog like updating the text and the progress, how do I do this?
Further Question
The solution pointed out by #ekhumoro works fine. I see the loader and the files are parsed correctly. My problem is now that updating my existing MainWindow does not work.
After executing the code I see a little window popping up but it is disappearing instantly. (I had a problem like this and it was about the C++ garbage collector in the background of Qt. But in my understanding the layout should keep a reference to the ParsedDataWidget so this doesn't make sense for me.) Also the ParsedDataWidget is a widget which should be added to the layout "inline" and not appearing as a "window".
# a class that handles the data parsing of each file and creates an
# object that contains all the data with some methods...
class DataParser
def __init__(self, data):
# handle the data
# displaying the parsed data in a fancy way
class ParsedDataWidget(QtWidgets.QWidget)
def __init__(self, data):
# create some UI
# the worker class just like #ekhumoro wrote it (stripped down to
# relevant code)
class Worker(QtCore.QObject):
def run(self):
self._stop = False
for count, file in enumerate(self._files, 1):
# parse the data in the DataParser and create an object
# of the files data
data = DataParser(file)
# works fine, the data is parsed correctly
print(data)
# does not work
window.addParsedData(data)
self.loaded.emit(count, file)
if self._stop:
break
self.finished.emit()
# the window class like mentioned before (or like #ekhumoro wrote it)
class Window(QtWidgets.QWidget):
def __init__(self):
self._data_container = QtWidgets.QWidget()
layout = QtWidgets.QVBoxLayout()
self._data_container.setLayout(layout)
def addParsedData(data):
data_widget = ParsedDataWidget(data)
layout = self._data_container.layout()
layout.addWidget(data_widget)
So what do I have to do in order to get the addParsedData method to work?
Edit
I was trying some changings of the code. If I replace the ParsedDataWidget with a QLabel I get the following result:
If i close the window python crashes.
Solution
With some further research I found my problem: You should not use threads with PyQt, you should use SIGNALS instead (written here)
So I changed the code of the worker, I added another SIGNAL called finishedParsing which is emitted if the loading is completed. This SIGNAL
holds the DataParser. The could would look like this:
class Worker(QtCore.QObject):
finishedParsing = QtCore.pyqtSignal(DataParser)
def run(self):
self._stop = False
for count, file in enumerate(self._files, 1):
# parse the data in the DataParser and create an object
# of the files data
data = DataParser(file)
# emit a signal to let the window know that this data is
# ready to use
self.finishedParsing.emit(data)
self.loaded.emit(count, file)
if self._stop:
break
self.finished.emit()
class Window(QtWidgets.QWidget):
def showOpenDialog(self):
if files and not self.thread.isRunning():
# do the opening stuff like written before
self.worker = Worker(files)
#...
self.worker.finishedParsing.connect(self.addParsedData)
This works now!

Below is an example that implements what you asked for. In real usage, the QThread.sleep line should be replaced with a function call that processes each file. This could either be defined as a method of the Worker class, or passed in as an argument to its __init__.
import sys, os
from PyQt5 import QtCore, QtWidgets
class Worker(QtCore.QObject):
loaded = QtCore.pyqtSignal(int, str)
finished = QtCore.pyqtSignal()
def __init__(self, files):
super().__init__()
self._files = files
def run(self):
self._stop = False
for count, file in enumerate(self._files, 1):
QtCore.QThread.sleep(2) # process file...
self.loaded.emit(count, file)
if self._stop:
break
self.finished.emit()
def stop(self):
self._stop = True
class Window(QtWidgets.QWidget):
def __init__(self):
super(Window, self).__init__()
self.button = QtWidgets.QPushButton('Choose Files')
self.button.clicked.connect(self.showOpenDialog)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.button)
self.thread = QtCore.QThread()
def showOpenDialog(self):
files, filters = QtWidgets.QFileDialog.getOpenFileNames(
self, 'Open file(s)', '',
'Raw files (*.rw.dat);;Data files (*.dat)'
';;Text files (*.txt);;All files (*)',
'All files (*)')
if files and not self.thread.isRunning():
self.worker = Worker(files)
self.worker.moveToThread(self.thread)
self.worker.finished.connect(self.thread.quit)
self.thread.started.connect(self.worker.run)
self.thread.finished.connect(self.worker.deleteLater)
self.showProgress(
'Loading file(s)...', len(files), self.worker.stop)
self.worker.loaded.connect(self.updateProgress)
self.thread.start()
def updateProgress(self, count, file):
if not self.progress.wasCanceled():
self.progress.setLabelText(
'Loaded: %s' % os.path.basename(file))
self.progress.setValue(count)
else:
QtWidgets.QMessageBox.warning(
self, 'Load Files', 'Loading Aborted!')
def showProgress(self, text, length, handler):
self.progress = QtWidgets.QProgressDialog(
text, "Abort", 0, length, self)
self.progress.setWindowModality(QtCore.Qt.WindowModal)
self.progress.canceled.connect(
handler, type=QtCore.Qt.DirectConnection)
self.progress.forceShow()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.setGeometry(600, 100, 100, 50)
window.show()
sys.exit(app.exec_())

Related

Access items and attributes across multiple windows

i have a main GUI-Window from which i open a new Window (FCT-popup) with a buttonclick:
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.ui = Ui_MainWindow() # sets ui = to the main window from the ui-file
self.ui.setupUi(self)
[...]
def enter_fct_results(self):
self.FCTpopup = FCT_Window()
self.FCTpopup.show()
In the Window i have a QTable to fill and a button to submit the data and close the window:
class FCT_Window(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.ui = Ui_FCT_Window()
self.ui.setupUi(self)
[...]
self.ui.pushButton_submitFCT.clicked.connect(lambda: MainWindow.store_fct_data(MainWindow, self.on_submit()[0]))
def on_submit(self): # event when user clicks
fct_nparray = np.zeros((self.ui.tableFCTinputs.rowCount(), self.ui.tableFCTinputs.columnCount()))
for j in range(self.ui.tableFCTinputs.columnCount()):
for i in range(self.ui.tableFCTinputs.rowCount()):
fct_nparray[i, j] = float(self.ui.tableFCTinputs.item(i, j).text())
return fct_nparray, lambda: self.close()
self.ui.pushButton_submitFCT.clicked.connect(lambda: MainWindow.store_fct_data(MainWindow, self.on_submit()[0]))
The receiving function iin the main window looks like ths:
def store_fct_data(self, data):
self.fct_data = data
Now i just want to understand how i can make either the mainwindow or the pushbutton which opens the 2nd window disabled. Disabling inside enter_fct_results() works, but if i want to enable it again with either store_fct_data or on_submit provides errors like this:
self.ui.pushButton_FCTresults.setEnabled(1)
self.ui.pushButton_submitFCT.clicked.connect(lambda: MainWindow.store_fct_data(MainWindow, self.on_submit()[0]))
AttributeError: type object 'MainWindow' has no attribute 'ui'
I dont think i have understood it here how to deal with multiple windows and stuff. For example how would i change a the color of a button in the main window by using a button in window2. How do i access the widgets? if i am inside the same Window i do that easily by
self.ui.pushbutton.setText("New Text")
I dont get how to access items and attributes across Windows :( Can you help me?
Access to attributes of another instance
There is a fundamental difference between disabling the button of the second window in enter_fct_results and what you tried in the lambda: in the first case, you're accessing an instance attribute (for instance, self.FCTpopup.ui.pushButton), while in the second you're trying to access a class attribute.
The self.ui object is created in the __init__ (when the class instance is created), so the instance will have an ui attribute, not the class:
class Test:
def __init__(self):
self.value = True
test = Test()
print(test.value)
>>> True
print(Test.value)
>>> AttributeError: type object 'Test' has no attribute 'value'
Provide a reference
The simple solution is to create a reference of the instance of the first window for the second:
def enter_fct_results(self):
self.FCTpopup = FCT_Window(self)
self.FCTpopup.show()
class FCT_Window(QMainWindow):
def __init__(self, mainWindow):
QMainWindow.__init__(self)
self.mainWindow = mainWindow
self.ui.pushButton_submitFCT.clicked.connect(self.doSomething)
def doSomething(self):
# ...
self.mainWindow.ui.pushButton.setEnabled(True)
Using modal windows (aka, dialogs)
Whenever a window is required for some temporary interaction (data input, document preview, etc), a dialog is preferred: the main benefit of using dialogs is that they are modal to the parent, preventing interaction on that parent until the dialog is closed; another benefit is that (at least on Qt) they also have a blocking event loop within their exec() function, which will only return as soon as the dialog is closed. Both of these aspects also make unnecessary disabling any button in the parent window. Another important reason is that QMainWindow is not intended for this kind of operation, also because it has features that generally unnecessary for that (toolbars, statusbars, menus, etc).
def enter_fct_results(self):
self.FCTpopup = FCT_Window(self)
self.FCTpopup.exec_()
class FCT_Window(QDialog):
def __init__(self, parent):
QMainWindow.__init__(self, parent)
self.ui.pushButton_submitFCT.clicked.connect(self.doSomething)
def doSomething(self):
# ...
self.accept()
The above makes mandatory to recreate the ui in designer using a QDialog (and not a QMainWindow) instead. You can just create a new one and drag&drop widgets from the original one.
i finally found my mistake: It was the place of the signal connection. It has to be right before the 2nd window is opened:
def enter_fct_results(self):
self.FCTpopup = Dialog(self.fct_data)
self.FCTpopup.submitted.connect(self.store_fct_data)
self.FCTpopup.exec_()
With this now i can send the stored data from the mainwindow to the opened window, import into the table, edit it and send it back to the main window on submit.

PySide: Emiting signal from QThread in new syntax

I'm trying (and researching) with little success to emit a signal from a working Qthread to the main window. I don't seem to understand how I should go about this in the new syntax.
Here's a simple example.
from PySide.QtCore import *
from PySide.QtGui import *
import sys
import time
class Dialog(QDialog):
def __init__(self, parent=None):
super(Dialog, self).__init__(parent)
button = QPushButton("Test me!")
layout = QVBoxLayout()
layout.addWidget(button)
self.setLayout(layout)
#self.button.clicked.connect(self.test) ----> 'Dialog' object has no attribute 'button'
self.connect(button, SIGNAL('clicked()'), self.test)
self.workerThread = WorkerThread()
def test(self):
self.workerThread.start()
QMessageBox.information(self, 'Done!', 'Done.')
class WorkerThread(QThread):
def __init__(self, parent=None):
super(WorkerThread, self).__init__(parent)
def run(self):
time.sleep(5)
print "Thread done!"
app = QApplication(sys.argv)
dialog = Dialog()
dialog.show()
app.exec_()
I understand that if I didn't have another thread I'd create the signal inside the Dialog class and connect it in the __init__ but how can I create a custom signal that can be emitted from WorkerThread and be used test()?
As a side question. You can see it commented out of the code that the new syntax for connecting the signal errors out. Is it something in my configurations?
I'm on OsX El Capitan, Python 2.7
Any help is highly appreciated! Thanks a lot
TL:DR: I'd like to emmit a signal from the WorkerThread after 5 seconds so that the test function displays the QMessageBox only after WorkingThread is done using the new syntax.
Ok, it's been a long day trying to figure this out. My main resource was this: http://www.matteomattei.com/pyside-signals-and-slots-with-qthread-example/
In the new syntax, in order to handle signals from different threads, you have to create a class for your signal like so:
class WorkerThreadSignal(QObject):
workerThreadDone = Signal()
This is how the WorkerThread end up looking like:
class WorkerThread(QThread):
def __init__(self, parent=None):
super(WorkerThread, self).__init__(parent)
self.workerThreadSignal = WorkerThreadSignal()
def run(self):
time.sleep(3)
self.workerThreadSignal.workerThreadDone.emit()
And for the connections on the Dialog class:
self.workerThread = WorkerThread()
self.buttonn.clicked.connect(self.test)
and:
self.workerThreadSignal = WorkerThreadSignal()
self.workerThread.workerThreadSignal.workerThreadDone.connect(self.success)
def success(self):
QMessageBox.warning(self, 'Warning!', 'Thread executed to completion!')
So the success method is called once the signal is emitted.
What took me the longest to figure out was this last line of code. I originally thought I could connect directly to the WorkerThreadSignal class but, at least in this case, it only worked once I backtracked it's location. From the Dialog init to WorkerThread init back to the WorkerThreadSignal. I took this hint from the website mentioned above.
I find strange that I have to create the same local variables on both classes, maybe there's a way to create one global variable I can refer to instead all the current solution but it works for now.
I hope this helps someone also stuck in this process!
PS: The syntax problem for the connection was also solved. So everything is written with the new syntax, which is great.

How to display progress without multi-threading

Let's say I have a PyQt program that goes through a given directory, looks for *JPEG images, and does some processing every time it finds one. Depending on the size of the selected directory, this may take from some seconds to minutes.
I would like to keep my user updated with the status - preferably with something like "x files processed out of y files" . If not, a simple running pulse progress bar by setting progressbar.setRange(0,0) works too.
From my understanding, in order to prevent my GUI from freezing, I will need a seperate thread that process the images, and the original thread that updates the GUI every interval.
But I am wondering if there is any possible way for me to do both in the same thread?
Yes, you can easily do this using processEvents, which is provided for this exact purpose.
I have used this technique for implementing a simple find-in-files dialog box. All you need to do is launch the function that processes the files with a single-shot timer, and then periodically call processEvents in the loop. This is should be good enough to update a counter with the number of files processed, and also allow the user to cancel the process, if necessary.
The only real issue is deciding on how frequently to call processEvents. The more often you call it, the more responsive the GUI will be - but this comes at the cost of considerably slowing the processing of the files. So you may have to experiment a little bit in order to find an acceptable compromise.
UPDATE:
Here's a simple demo that shows how the code could be structured:
import sys, time
from PyQt5 import QtWidgets, QtCore
class Window(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.button = QtWidgets.QPushButton('Start')
self.progress = QtWidgets.QLabel('0')
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.button)
layout.addWidget(self.progress)
self.button.clicked.connect(self.test)
self._stop = False
self._stopped = True
def test(self):
if self._stopped:
self._stop = False
self.progress.setText('0')
self.button.setText('Stop')
QtCore.QTimer.singleShot(1, self.process)
else:
self._stop = True
def process(self):
self._stopped = False
for index in range(1, 1000):
time.sleep(0.01)
self.progress.setText(str(index))
if not index % 20:
QtWidgets.qApp.processEvents(
QtCore.QEventLoop.AllEvents, 50)
if self._stop:
break
self._stopped = True
self.button.setText('Start')
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
I could not achieve the thing you need without multi threading and this is not possible because gui can be only updated in main thread. Below is an algorithm how I did this with multithreading.
Let's say you have your application processing images. Then there are the following threads:
Main thread (that blocks by GUI/QApplication-derived classes.exec())
Timer with, for example, 1 second interval which updates a variable and calls a slot in GUI thread which updates a variable in user interface.
A thread which is processing images on your pc.
def process(self):
self._status = "processing image 1"
....
def _update(self):
self.status_label.setText(self._status)
def start_processing(self, image_path):
# create thread for process and run it
# create thread for updating by using QtCore.QTimer()
# connect qtimer triggered signal to and `self._update()` slot
# connect image processing thread (use connect signal to any slot, in this example I'll stop timer after processing thread finishes)
#pyqtSlot()
def _stop_timer():
self._qtimer.stop()
self._qtimer = None
_update_thread.finished.connect(_stop_timer)
In pyqt5 it is possible to assign a pyqtvariable from a one nested thread(first level). So you can make your variable a pyqtvariable with setter and getter and update gui in a setter or think how you can do this by yourself.
You could just use the python threading module and emit a signal in your threaded routine.
Here's a working example
from PyQt4 import QtGui, QtCore
import threading
import time
class MyWidget(QtGui.QWidget):
valueChanged = QtCore.pyqtSignal(int)
def __init__(self, parent=None):
super(MyWidget, self).__init__(parent)
self.computeButton = QtGui.QPushButton("Compute", self)
self.progressBar = QtGui.QProgressBar()
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.computeButton)
layout.addWidget(self.progressBar)
self.computeButton.clicked.connect(self.compute)
self.valueChanged.connect(self.progressBar.setValue)
def compute(self):
nbFiles = 10
self.progressBar.setRange(0, nbFiles)
def inner():
for i in range(1, nbFiles+1):
time.sleep(0.5) # Process Image
self.valueChanged.emit(i) # Notify progress
self.thread = threading.Thread(target = inner)
self.thread.start()
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
widget = MyWidget()
widget.show()
sys.exit(app.exec_())

Tkinter update values from other class

I've got a tkinter window that have 3 features: background color,foreground color, and a text label. These features is in a text config file (properties.conf) in my home folder. I want to update window features when the config file changed. I watch the changes in config file with pyinotify and I want to update window when it changes. This is the code:
#!/usr/bin/python
import threading
from Tkinter import *
import os
import ConfigParser
import pyinotify
class WatchFile(threading.Thread):
def run(self):
def onChange(ev):
Gui().updateGUI()
print 2
wm = pyinotify.WatchManager()
wm.add_watch('/home/mnrl/window_configs', pyinotify.IN_CLOSE_WRITE, onChange)
notifier = pyinotify.Notifier(wm)
notifier.loop()
class ConfigParse():
def __init__(self):
self.confDir = os.path.join(os.getenv('HOME'), 'window_configs/')
self.confFile = os.path.join(self.confDir + "properties.conf")
self.config = ConfigParser.ConfigParser()
if os.path.isfile(self.confFile):
self.config.read(self.confFile)
else:
if not os.path.exists(self.confDir):
os.makedirs(self.confDir)
self.config.add_section('bolum1')
self.config.set('section1', 'setting1', 'green')
self.config.set('section1', 'setting2', 'red')
self.config.set('section1', 'setting3', 'sample text')
with open(self.confFile, 'wb') as self.confFile:
self.config.write(self.confFile)
class Gui(object):
def __init__(self):
self.root = Tk()
self.lbl = Label(self.root, text=ConfigParse().config.get('section1', 'setting3'), fg=ConfigParse().config.get('section1', 'setting1'), bg=ConfigParse().config.get('section1', 'setting2'))
self.lbl.pack()
def updateGUI(self):
self.lbl["text"] = ConfigParse().config.get('bolum1', 'ayar3')
self.lbl["fg"] = ConfigParse().config.get('bolum1', 'ayar1')
self.lbl["bg"] = ConfigParse().config.get('bolum1', 'ayar2')
self.root.update()
WatchFile().start()
Gui().root.mainloop()
But whenever properties.conf file changes a new window more appears near the old tkinter window. So tkinter window not updates, new windows open. How can I correct it?
The problem is that in WatchFile.run() you're doing this:
def onChange(ev):
Gui().updateGUI()
This does something different than what you're expecting. It creates a new GUI instance, and then immediately calls the updateGUI() method on it. Hence the two windows.
What you need to do instead, is something along the lines of:
#!/usr/bin/env python
gui = None
class WatchFile(threading.Thread):
def run(self):
def onChange(ev):
gui.updateGUI()
[...]
WatchFile().start()
gui = Gui()
gui.root.mainloop()
Here, a variable gui is created, and has an instance of your GUI class assigned to it. Later, the updateGUI() method is called on the same instance.
This problem is more or less repeated with your usage of your ConfigParser class, for example:
self.lbl["text"] = ConfigParse().config.get('bolum1', 'ayar3')
In this case, it 'works' because your ConfigParse() class can be executed twice without side-effects (such as opening windows), but it's not very efficient. You're reading the same file multiple times.
What would be better, is to just use a function (a class with only a __init__ defined is effectively the same), run this once, and return a dict.

PyQt - open only one child window and minimize it with parent window

The idea is to open a child window from parent window menu and when I minimize the parent window, the child window must be minimized also and only one child window can be opened.
I have the solution for minimizing the child when parent is minimized, but I can open child window multiple-times (although the child is already opened) and I would like to disable opening of multiple child windows.
The parent window is MainWindow.py:
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setWindowTitle('Parent window')
self.flags = QtCore.Qt.Window
self.ControlPanel = Control_panel_window()
self.createActions()
self.createMenus()
def createActions(self):
# window - menu
self.windowShowControlPanelAction = QtGui.QAction(self.tr("&Control panel"), self, statusTip='Control panel')
self.connect(self.windowShowControlPanelAction, QtCore.SIGNAL("triggered()"), self.ShowControlPanel)
def createMenus(self):
# window
self.windowMenu = QtGui.QMenu(self.tr("&Window"), self)
self.windowMenu.addAction(self.windowShowControlPanelAction)
self.menuBar().addMenu(self.windowMenu)
def ShowControlPanel(self):
self.ControlPanel = Control_panel_window(self)
self.ControlPanel.setWindowFlags(QtCore.Qt.Window)
self.ControlPanel.show()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
win = MainWindow()
win.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
win.show()
sys.exit(app.exec_())
The child window is ChildWindow.py:
class Control_panel_window(QWidget):
def __init__(self, parent = None):
super(Control_panel_window, self).__init__(parent)
self.setFixedSize(200, 300)
def setWindowFlags(self, flags):
print "flags value in setWindowFlags"
print flags
super(Control_panel_window, self).setWindowFlags(flags)
The problem is: how can I set that only one child window is opened?
In your ShowControlPanel function you are creating a new control panel each time the signal is triggered. Since you already have an instance available why don't you use that instead?
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setWindowTitle('Parent window')
self.flags = QtCore.Qt.Window
self.control_panel = ControlPanelWindow(self)
self.control_panel.setWindowFlags(self.flags)
#...
def create_actions(self):
self.show_control_panel_action = QtGui.QAction(
self.tr("&Control panel"),
self,
statusTip='Control panel'
)
self.show_control_panel_action.triggered.connect(self.show_control_panel)
#...
def show_control_panel(self):
self.control_panel.show()
Some stylistic notes:
Try to follow PEP8 official python coding-style guide. This include using CamelCase for classes, lowercase_with_underscore for almost everything else. In this case, since Qt uses halfCamelCase for methods etc you may use it too for consistency.
Use the new-style signal syntax:
the_object.signal_name.connect(function)
instead of:
self.connect(the_object, QtCore.SIGNAL('signal_name'), function)
not only it reads nicer, but it also provides better debugging information. Using QtCore.SIGNAL you will not receive an error if the signal doesn't exist (e.g. you wrote a typo like trigered() instead of triggered()). The new-style syntax does raise an exception in that case you will be able to correct the mistake earlier, without having to guess why something is not working right and searching the whole codebase for the typo.

Categories

Resources