Im trying to make function, which passes 8 values to the progress bars in window app made with QtDesigner.
That is there is a function which actually generates this values. I want to execute it every second in order to get theese values and update progress bars which displays values.
I combined tutorials about making a graphic app with Python and making an app with dynamically updating progess bars:
Python Qt Development: https://www.youtube.com/watch?v=eD91nE8q8Nk
PyQt Progress bar with Thread: https://www.youtube.com/watch?v=ivcxZSHL7jM
The problem is that values are passed correctly, but when it comes to send the signal, the whole app crashes with message:
Process finished with exit code 139 (interrupted by signal 11:
SIGSEGV)
Here is my code:
Module which generates the values (probkowanie.py):
import time
from random import randint
import threading
def getSignals():
time.sleep(1)
signals = (randint(0, 100), randint(0, 100), randint(0, 100), randint(0, 100), randint(0, 100), randint(0, 100), randint(0, 100), randint(0, 100), )
return signals
and here is main program code:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from PySide.QtCore import *
from PySide.QtGui import *
from PyQt4 import QtCore
import sys
import time
import signalPreview # window library
import probkowanie # values generating library
class MainDialog(QDialog, signalPreview.Ui_mainDialog):
def __init__(self, parent=None):
super(MainDialog, self).__init__(parent)
self.setupUi(self)
self.threadclass = ThreadClass()
self.threadclass.start()
self.connect(self.threadclass, QtCore.SIGNAL('MYO_SIGNALS'), self.updateProgressBars)
def updateProgressBars(self, signals):
self.progressBar.setValue(signals[0])
class ThreadClass(QtCore.QThread):
def __init__(self, parent = None):
super(ThreadClass, self).__init__(parent)
def run(self):
while 1:
signals = probkowanie.getSignals()
self.emit(QtCore.SIGNAL('MYO_SIGNALS'), 5) # this line causes crash
print signals # it works correctly
app = QApplication(sys.argv)
form = MainDialog()
form.show()
app.exec_()
I think it may be caused by mixing libraries. I use QtCore from PySide even though tutorial number 2 is based on PyQt4. My decision is because tutorial number 1 is based on PySide.
I tried changing:
from PySide import QtCore
to
from PyQt4 import QtCore
but then I get another bunch of errors, which I don't know what to do with:
Traceback (most recent call last):
File "/usr/lib/pycharm/helpers/pydev/pydevd.py", line 1580, in <module>
globals = debugger.run(setup['file'], None, None, is_module)
File "/usr/lib/pycharm/helpers/pydev/pydevd.py", line 964, in run
pydev_imports.execfile(file, globals, locals) # execute the script
File "/mnt/Grubas/Projekty/Biomed/MYO/myo.py", line 36, in <module>
form = MainDialog()
File "/mnt/Grubas/Projekty/Biomed/MYO/myo.py", line 20, in __init__
self.connect(self.threadclass, QtCore.SIGNAL('MYO_SIGNALS'), self.updateProgressBars)
TypeError: 'PySide.QtCore.QObject.connect' called with wrong argument types:
PySide.QtCore.QObject.connect(ThreadClass, str, instancemethod)
Supported signatures:
PySide.QtCore.QObject.connect(PySide.QtCore.QObject, str, callable, PySide.QtCore.Qt.ConnectionType = Qt.AutoConnection)
PySide.QtCore.QObject.connect(PySide.QtCore.QObject, PySide.QtCore.QMetaMethod, PySide.QtCore.QObject, PySide.QtCore.QMetaMethod, PySide.QtCore.Qt.ConnectionType = Qt.AutoConnection)
PySide.QtCore.QObject.connect(PySide.QtCore.QObject, str, PySide.QtCore.QObject, str, PySide.QtCore.Qt.ConnectionType = Qt.AutoConnection)
PySide.QtCore.QObject.connect(PySide.QtCore.QObject, str, str, PySide.QtCore.Qt.ConnectionType = Qt.AutoConnection) PySide.QtCore.QObject.connect(str, callable, PySide.QtCore.Qt.ConnectionType = Qt.AutoConnection)
PySide.QtCore.QObject.connect(str, PySide.QtCore.QObject, str, PySide.QtCore.Qt.ConnectionType = Qt.AutoConnection)
#EDIT
After providing changes proposed by #ekhumoro script doens't crash, but now I'm getting another error:
QObject::connect: Cannot queue arguments of type 'object' (Make sure
'object' is registered using qRegisterMetaType().)
I tried to look for solution on my own, but I didn't find the exact code which I need. I also tried transforming type of signal from (object) to (tuple) or (list), but it leads to another errors:
TypeError: Unknown type used to call meta function (that may be a
signal): tuple
The most solutions I found are based on PyQT. Is there an easy way to rewrite it for PySide?
Here is an example of solution, which seems to be correct but is made using PyQT:
https://stackoverflow.com/a/2595607/2550466
You are right to think that mixing PySide and PyQt will cause problems, so you will need to remove one of them. However, the crash itself is probably caused by a bug in PySide. There is an SO question with a similar problem shown here:
How to send None with Signals across threads?
So you also need to change the way you define and emit MYO_SIGNALS.
Below is a fixed version of your script (the changed lines are commented):
# from PySide.QtCore import *
# from PySide.QtGui import *
# from PyQt4 import QtCore
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys
import time
import signalPreview # window library
import probkowanie # values generating library
class MainDialog(QDialog):
def __init__(self, parent=None):
super(MainDialog, self).__init__(parent)
self.setupUi(self)
self.threadclass = ThreadClass()
self.threadclass.start()
# self.connect(self.threadclass, QtCore.SIGNAL('MYO_SIGNALS'), self.updateProgressBars)
self.connect(self.threadclass, SIGNAL('MYO_SIGNALS'), self.updateProgressBars)
def updateProgressBars(self, signals):
self.progressBar.setValue(signals[0])
# class ThreadClass(QtCore.QThread):
class ThreadClass(QThread):
def __init__(self, parent = None):
super(ThreadClass, self).__init__(parent)
def run(self):
while 1:
signals = probkowanie.getSignals()
# self.emit(QtCore.SIGNAL('MYO_SIGNALS'), 5) # this line causes crash
self.emit(SIGNAL('MYO_SIGNALS'), signals)
print signals # it works correctly
app = QApplication(sys.argv)
form = MainDialog()
form.show()
app.exec_()
Related
Trying to use the builtin 'find' function of the QTextEdit widget but when I try to pass the text from a QLineEdit widget, it gives me the following error:
Traceback (most recent call last):
File "C:\SVN\Gocator\Trunk\Gocator\GoPy\Scripts\testfind.pyw", line 52, in on_find_button_clicked
self.fileEdit.find(self.findLine.text)
TypeError: QTextEdit.find(str, QTextDocument.FindFlags options=0): argument 1 has unexpected type 'builtin_function_or_method'
I've reviewed the documentation for the QTextEdit class and there's not much to it but I can't figure out why it's giving me the error. What's interesting is if I replace the reference to the QLineEdit text property with a string literal (e.g. "What") in the find() call (line 52: self.fileEdit.find(self.findLine.text)), it will work.
My test code is pretty straight forward so I think it's just something I'm not seeing right in front of my eyes. Does anyone see where I went wrong or even get the same issue? Here's my test script (I only have Qt4 installed):
#!/usr/bin/env python
# Needs Qt5 (recommended) or Qt4
# PyQt5: run pip3 install pyqt5
# PyQt4: from http://www.riverbankcomputing.com/software/pyqt/download
import shelve
import sys
sys.path.append('../GoPy')
sys.path.append('../../GoPy')
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class ControlEngine:
def __init__(self):
self.shelf = shelve.open("ui_control.shelf")
class MainWindow(QWidget):
def __init__(self):
QWidget.__init__(self)
self.engine = ControlEngine()
self.create_ui()
def create_ui(self):
mainLayout = QVBoxLayout()
# View group
viewGroup = QGroupBox("File View")
viewLayout = QVBoxLayout()
self.fileEdit = QTextEdit("Today at Safeway, here's what we have for you.")
self.fileEdit.setFont(QFont("Courier New", 10))
viewLayout.addWidget(self.fileEdit)
self.findButton = QPushButton("Find Next Word")
self.findButton.clicked.connect(self.on_find_button_clicked)
self.findLine = QLineEdit("What")
viewLayout.addWidget(self.findLine)
viewLayout.addWidget(self.findButton)
viewGroup.setLayout(viewLayout)
mainLayout.addWidget(viewGroup)
self.setLayout(mainLayout)
self.fileEdit.moveCursor(1)
#pyqtSlot(int, int)
def on_find_button_clicked(self):
self.fileEdit.find(self.findLine.text)
class App(QApplication):
def __init__(self, argv):
QApplication.__init__(self, argv)
def start(self):
mainWindow = MainWindow()
self.mainWindow = mainWindow
mainWindow.resize(300, 300)
mainWindow.setWindowTitle("Test Find")
mainWindow.show()
return self.exec_()
def main():
app = App(sys.argv)
sys.exit(app.start())
if __name__ == '__main__':
main()
Change this
self.fileEdit.find(self.findLine.text)
to
self.fileEdit.find(self.findLine.text())
because its a function not a property :)
Being (quite) new to both Python and Qt I'm fiddling with this code.
It might be somewhat messy but I've tried to shave the code down as much as possible and still get the core-dumps.
Basically there's a button to start "something" - now it's just a for-loop -
a progress bar, and a label.
Clicking the button in the interface outputs 1 to the console and then core-dumps. No data is seen in either the progress bar or the label.
I am using Python 2.7.11, Qt-4.8.7 and PySide 1.2.2.
The threaded code is from this youtube vid:
https://www.youtube.com/watch?v=ivcxZSHL7jM
I've tried placing the emitting lines outside the loop, even into the MainDialog class, and it seems that as soon as the emitting signal comes from outside the MainDialog class, it crashes. It only works inside MainDialog (using static integers for testing purposes, tested after the progressbar reset).
showGui.py - nothing wrong here - (made with designer and converted with pyside):
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'show.ui'
#
# Created: Wed Jul 13 09:10:12 2016
# by: pyside-uic 0.2.15 running on PySide 1.2.2
#
# WARNING! All changes made in this file will be lost!
from PySide import QtCore, QtGui
class Ui_mainDialog(object):
def setupUi(self, mainDialog):
mainDialog.setObjectName("mainDialog")
mainDialog.resize(369, 171)
self.pushButton = QtGui.QPushButton(mainDialog)
self.pushButton.setGeometry(QtCore.QRect(50, 40, 84, 33))
self.pushButton.setObjectName("pushButton")
self.progressBar = QtGui.QProgressBar(mainDialog)
self.progressBar.setGeometry(QtCore.QRect(50, 110, 231, 23))
self.progressBar.setProperty("value", 0)
self.progressBar.setObjectName("progressBar")
self.label = QtGui.QLabel(mainDialog)
self.label.setGeometry(QtCore.QRect(170, 40, 81, 31))
self.label.setObjectName("label")
self.retranslateUi(mainDialog)
QtCore.QMetaObject.connectSlotsByName(mainDialog)
def retranslateUi(self, mainDialog):
mainDialog.setWindowTitle(QtGui.QApplication.translate("mainDialog", "MainWindow", None, QtGui.QApplication.UnicodeUTF8))
self.pushButton.setText(QtGui.QApplication.translate("mainDialog", "Button", None, QtGui.QApplication.UnicodeUTF8))
self.label.setText(QtGui.QApplication.translate("mainDialog", "TextLabel", None, QtGui.QApplication.UnicodeUTF8))
test.py - this fails when emitting the signals:
from __future__ import print_function
import sys
import time
from PySide import QtCore, QtGui
import showGui
from PySide.QtCore import *
from PySide.QtGui import *
class MainDialog(QDialog, showGui.Ui_mainDialog):
def __init__(self, parent=None):
super(MainDialog, self).__init__(parent)
self.setupUi(self)
self.threadclass = ThreadClass()
self.connect(self.threadclass, QtCore.SIGNAL('GFX_PROGRESS'), self.setProgress)
self.connect(self.threadclass, QtCore.SIGNAL('TEXT_PROGRESS'), self.setTextLabel)
self.connect(self.pushButton, SIGNAL("clicked()"), self.threadclass.doSomething)
self.progressBar.reset()
def setTextLabel(self, val):
self.label.setText(val)
def setProgress(self, val):
self.progressBar.setValue(val)
class ThreadClass(QtCore.QThread):
def __init__(self, parent=None):
super(ThreadClass, self).__init__(parent)
def doSomething(self):
self.runcmd()
# some more code here
def runcmd(self):
for i in range(1, 100):
print("Status at : %s " % i)
# this one crashes
self.emit(QtCore.SIGNAL('TEXT_PROGRESS'), i)
# this one crashes too
self.emit(QtCore.SIGNAL('GFX_PROGRESS'), i)
time.sleep(1)
app = QApplication(sys.argv)
form = MainDialog()
form.show()
app.exec_()
Do not use the old-style signal and slot syntax. It's bug-prone, and does not raise an exception if you get it wrong. In addition to that, it looks like the implementation is somewhat broken in PySide. I converted your code example to PyQt4, and it does not dump core.
To get your example working in PySide, you first need to switch to the new-style signal and slot syntax. Also, your current threading implementation is wrong. It doesn't actually start the worker thread, so the all the code will run in the main thread, and it will block the gui.
The following fixes should get the example working as expected:
class MainDialog(QDialog, Ui_mainDialog):
def __init__(self, parent=None):
..
# use new-style connections
self.threadclass.gfxProgress.connect(self.setProgress)
self.threadclass.textProgress.connect(self.setTextLabel)
self.pushButton.clicked.connect(self.threadclass.doSomething)
class ThreadClass(QtCore.QThread):
# define new-style signals
gfxProgress = QtCore.Signal(int)
textProgress = QtCore.Signal(str)
def __init__(self, parent=None):
super(ThreadClass, self).__init__(parent)
def doSomething(self):
# start the thread
# by default, this will automatically call run()
self.start()
def run(self):
for i in range(1, 100):
print("Status at : %s " % i)
# emit the new-style signals
self.gfxProgress.emit(i)
self.textProgress.emit(str(i))
time.sleep(1)
What is the way of getting the Sender of a right-mouse-click on a QLabel()? I want to know on which Widget the right-mouse-click happened. I have code to get the position, but how can i get the Sender?
Getting the Sender i could retrieve the accessibleName()
Here is my current minimal Code:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import * #!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
# qbtn = QtGui.QPushButton('Quit', self)
label = QLabel("BG Sessions", self)
label.setContextMenuPolicy(Qt.CustomContextMenu)
label.setObjectName("title")
label.customContextMenuRequested.connect(self.clearCache)
label.move(50, 50)
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Quit button')
self.show()
def clearCache(self, pos):
print pos
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
if you want to use sender() as suggested by Alexander Lutsenko keep this in mind:
QT-documentation
Warning: This function violates the object-oriented principle of modularity. However, getting access to the sender might be useful when many signals are connected to a single slot.
if all widgets sending the signal are from the same type,findChild() can be used instead. this is working with my tableViews:
# create signalMapper
self.signalMapper = QtCore.QSignalMapper(self)
self.signalMapper.mapped[str].connect(<slot>)
# connect the widgets with signalMapper
self.<widget>.<yourSignal>.connect(self.signalMapper.map)
self.signalMapper.setMapping(self.<widget>, self.<widget>.objectName()) # sends objectName() to slot
# in slot:
obj = self.findChild(QtWidgets.QLabel,objectname)
You could change the way you connect your signal such that you pass in a reference to the object you are connecting the signal from.
For instance:
my_object.customContextMenuRequested.connect(lambda pos, obj=my_object: self.clearCache(pos, obj))
(you will of course need to modify clearCache() so that it accepts this extra argument)
Note: the obj=my_object line is not redundant, see here for details.
self.sender() inside the slot does this job.
I am trying to create a common class which I want to use inside different applications.
The idea: a class which could be used as a base class for ui creations. This class will be based to a widget of the applications in it's specific gui system (PySide, or PyQt)
This would allow me to follow the same code flow to generate gui's. I like to have same structures inside my pipeline and it makes it much easier to work in different applications with the same commands.
The problem: PyQt and PySide are compiled in C++ and do not follow the Python class structure
I tried a lot of different things to get it to work, but every time I got stock at some point and doesn't get the result I want.
Try (rebase):
in this try I used __class__ to rebase the class itself.
from PyQt4 import QtGui, QtCore
class UiMainBase(object):
PARENT = QtGui.QMainWindow
def __init__(self, uiFile=None, parent=None):
if parent: self.PARENT = parent
self.__class__ = self.PARENT
if __name__ == "__main__":
import sys
from PySide import QtGui as PySideGui
class MyGui(UiMainBase):
def __init__(self, uiFile):
super(MyGui, self).__init__(uiFile, PySideGui.QMainWindow)
Ui = r"C:\pythonTest\ui\test.ui"
app = PySideGui.QApplication(sys.argv)
win = MyGui(Ui)
win.show()
sys.exit(app.exec_())
The errors I get are reasonable and doesn't has to be explained.
# 1st Error :
Traceback (most recent call last):
#[..]
File "C:/pythonTest/ui/ui.py", line 18, in __init__
self.__class__ = self.PARENT
TypeError: __class__ assignment: only for heap types
# 2nd Error (without parsing the parent
# argument to __init__ of super in MyGui) :
Traceback (most recent call last):
#[..]
File "C:/pythonTest/ui/uiTest.py", line 18, in __init__
self.__class__ = self.PARENT
TypeError: __class__ assignment: 'MyGui' object layout differs from 'QMainWindow'
Try (rebase):
in this try I used __bases__ to rebase the class itself.
from PyQt4 import QtGui, QtCore
class UiMainBase(object):
PARENT = QtGui.QMainWindow
def __init__(self, uiFile=None, parent=None):
if parent: self.PARENT = parent
self.__bases__= (self.PARENT,)
if __name__ == "__main__":
import sys
from PySide import QtGui as PySideGui
class MyGui(UiMainBase):
def __init__(self, uiFile):
super(MyGui, self).__init__(uiFile, PySideGui.QMainWindow)
Ui = r"C:\pythonTest\ui\test.ui"
app = PySideGui.QApplication(sys.argv)
win = MyGui(Ui)
print win.__bases__ # <- shows what base the object has
win.show()
sys.exit(app.exec_())
In this result, we can see that the object now get's the right base, but don't get it's methods and variables (not even if I call super after setting __bases__).
<type 'PySide.QtGui.QMainWindow'> # <- the base
Traceback (most recent call last):
File "C:/pythonTest/ui/uiTest.py", line 34, in <module>
win.show()
AttributeError: 'MyGui' object has no attribute 'show'
Try (decorator) :
instead of rebase an object I tried to replace the class with another one that has the right base
from PyQt4 import QtGui, QtCore
def GetUiObject(uiClass):
parentWidget = uiClass.PARENT # <- Gets the original value,
# not the latest, why?
class ParentUI(parentWidget, uiClass):
def __init__(self, *args, **kwargs):
super(ParentUI, self).__init__()
uiClass.__init__(self, *args, **kwargs)
#[..]
def __call__(self, cls):
for func in uiClass.__dict__:
setattr(cls, func, uiClass.__dict__[func])
#ParentUI = type("ParentUI", (parentWidget,),ParentUI.__dict__.copy())
return ParentUI
#GetUiObject
class UiMainBase( object ):
PARENT = QtGui.QMainWindow
def __init__(self, uiFile=None, *args, **kwargs):
"""constructor.."""
#[..]
if __name__ == "__main__":
import sys
from PySide import QtGui as PySideGui
UiMainBase.PARENT = PySideGui.QMainWindow # <- specify the application
# ui architecture as parent
class MyGui(UiMainBase):
def __init__(self, uiFile=None):
# This variant of base class definition doesn't allow parsing
# the parent argument with with it
# (__init__ will be executed after base is set)
super(MyGui, self).__init__(uiFile=None)
print self.menuBar () # <- check used ui architecture
Th e result of this variant doesn't error and prints: <PyQt4.QtGui.QMenuBar object at 0x00000000021C9D38>, but MyGui is not based to PySideGui.QMainWindow like I expected
EDIT:
Why do not defining classes with a base of PySide and one with a base of PyQt4?:
Because I want to leave it open for a genral use and later extensions. It should be free to set a parent widget, which is defined by the application (PySide or PyQt). But each application has it's own method or function to get it's MainWindow. So if you want extend some widgets of this MainWindow, or just parent a new ui to it you need to define it as the parent, or directly base from it. Some applications and it's API's do not support a free generated ui from type PySide.QtGui.QMainWindow or PyQt4.QtGui.QMainWindow. That's the main reason I was trying to do it in this way. Of course I could create a UiMainWindow class for each application and base it to it's main window (this is what I do for now), but I was trying to make it more generic and global for my pipeline API.
Example of usage:
This example is for 3DS-Max and inside a module of it's pipeline integration.
from pythonLibrary.file import Path
from qtLibrary.ui import UiMainBase
from blurdev.gui import Window
UiMainBase.PARENT = Window
class UiWidget( UiMainBase ):
"""
This class creates a widget which is parented
to the PySide implementation of blur's Python
in 3DS-Max
"""
def __init__(self, uiFile=None):
super(UiWidget, self).__init__()
if uiFile: self.loadUi(uiFile, self)
self.show()
#[..]
class PublishUi(UiWidget):
def __init__(self):
uiFile = Path.Combine(__file__,self.__class__.__name__+".ui")
super(PublishUi, self).__init__(uiFile)
#[..]
if __name__ == "__main__":
# This should be called from a menu entry inside 3DS-Max
publishWindow = PublishUi()
Does anyone has a solution for this situation?
Cheers,
Michael
Make a factory function which dynamically generates the correct base class. In real life, you'd probably memoize the factory so that it always returns the same class object for a given parent, rather than creating multiple identical classes.
from PyQt4 import QtGui, QtCore
def BaseFactory(parent):
class UiMainBase(parent):
....
return UiMainBase
if __name__ == "__main__":
import sys
from PySide import QtGui as PySideGui
# UiMainBase = BaseFactory(QtGui.QMainWindow)
UiMainBase = BaseFactory(PySideGui.QMainWindow)
class MyGui(UiMainBase):
...
Ui = r"C:\pythonTest\ui\test.ui"
app = PySideGui.QApplication(sys.argv)
win = MyGui(Ui)
I am trying to connect a push button signal to a callable I created, but for some reason this error keeps on popping up. I've checked to make sure QtCore is imported ... what else am I missing?
Sample code:
from PyQt4 import QtCore
from PyQt4 import QtGui
import sys
class guiFindFiles(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
#Create window
self.setFixedSize(400,180)
self.setWindowTitle("Choose the files to use")
#Create all layouts to be used by window
self.windowLayout = QtGui.QVBoxLayout()
self.fileLayout1 = QtGui.QHBoxLayout()
self.fileLayout2 = QtGui.QHBoxLayout()
self.fileLayout3 = QtGui.QHBoxLayout()
#Create the prompt for user to load in the q script to use
self.qFileTF = QtGui.QLineEdit("Choose the q script file to use")
self.qFileButton = QtGui.QPushButton("Open")
self.qFileButton.setFixedSize(100,27)
self.fileLayout1.addWidget(self.qFileTF)
self.fileLayout1.addWidget(self.qFileButton)
#Connect all the signals and slots
self.connect(self.qFileButton, SIGNAL("pressed()"), self.loadFile)
def loadFile():
fileName = []
selFile = QtGui.QFileDailog.getOpenFileName(self)
print selFile
SIGNAL is inside QtCore, so the line should be:
self.connect(self.qFileButton, QtCore.SIGNAL("pressed()"), self.loadFile)
but you really should use the new style connections:
self.qFileButton.pressed.connect(self.loadFile)
And, unless you meant to differentiate a click from press/release couple, you'd better use clicked signal:
self.qFileButton.clicked.connect(self.loadFile)
SIGNAL is defined inside QtCore, so you must use it within QtCore namespace if you've imported QtCore as a whole. So use:
QtCore.SIGNAL(...)
instead of:
SIGNAL(...)
Or you can import SIGNAL from QtCore explicitly:
from PyQt4.QtCore import SIGNAL