When Signal Does Not Find its Slot - python

Clicking a button crashes the process. What could be wrong?
from PySide import QtCore, QtGui
class button(QtGui.QPushButton):
def __init__(self, parent=None):
super(button, self).__init__(parent)
self.clicked.connect(self.click)
self.show()
def click(self):
self.emit(QtCore.SIGNAL('customSignal'), 'String Argument')
btn = button()
class label(QtGui.QLabel):
def __init__(self, parent=None):
super(label, self).__init__(parent)
self.connect(btn, QtCore.SIGNAL('customSignal'), self.Function)
self.show()
#QtCore.Slot(str)
def Function(self, arg=None):
print 'Function arg: %r'%arg
lbl = label()

No need for #QtCore.Slot decorator:
from PyQt4 import QtCore, QtGui
import sys
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
class button(QtGui.QPushButton):
def __init__(self, parent=None):
super(button, self).__init__(parent)
self.clicked.connect(self.click)
self.show()
def click(self):
self.emit(QtCore.SIGNAL('customSignal'), 'String Argument')
btn = button()
class label(QtGui.QLabel):
def __init__(self, parent=None):
super(label, self).__init__(parent)
self.connect(btn, QtCore.SIGNAL('customSignal'), self.method)
self.show()
def method(self, arg=None):
print 'method arg: %r'%arg
lbl = label()
sys.exit(app.exec_())
==================
from PySide import QtCore, QtGui
def function(arg=None):
print 'function arg: %r'%arg
class button(QtGui.QPushButton):
def __init__(self, parent=None):
super(button, self).__init__(parent)
self.clicked.connect(self.click)
self.show()
def click(self):
self.emit(QtCore.SIGNAL('customSignal'), 'String Argument')
btn = button()
class label(QtGui.QLabel):
def __init__(self, parent=None):
super(label, self).__init__(parent)
self.connect(btn, QtCore.SIGNAL('customSignal'), function)
self.show()
lbl = label()
====
from PyQt4 import QtCore, QtGui
import datetime
def getDatetime():
return '%s'%datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%d %H:%M:%S')
class CustomEvent(QtCore.QEvent):
_type = QtCore.QEvent.registerEventType()
def __init__(self, name=None):
QtCore.QEvent.__init__(self, CustomEvent._type)
class label(QtGui.QLabel):
def __init__(self, parent=None):
super(label, self).__init__(parent)
def customEvent(self, event):
print '....customEvent %s if event.type(): %s == CustomEvent._type: %s'%(event, event.type(), CustomEvent._type)
if event.type() == CustomEvent._type:
self.setText(getDatetime() )
class button(QtGui.QPushButton):
def __init__(self, parent=None):
super(button, self).__init__(parent)
self.clicked.connect(self.onClick)
self.show()
def onClick(self):
event = CustomEvent(name = 'Event Name')
QtCore.QCoreApplication.postEvent(lbl, event)
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
btn = button()
btn.show()
lbl = label()
lbl.show()
sys.exit(app.exec_())

Related

How can i capture a mouse clicks onto the main Window in a child class

I would like to capture mouse clicks onto the MainWindow in a child class of the application.
I've tried the following but without success:
import sys
from PyQt5 import QtWidgets, QtGui, QtCore
from abc import ABCMeta, abstractmethod
class BaseView(object):
def __init__(self,parent, page=None):
self.parent = parent
self.page = page
#abstractmethod
def preprare_view(self):
pass
#abstractmethod
def clean_up_view(self):
pass
class FooView(BaseView):
def __init__(self, parent, page):
super(FooView, self).__init__(parent, page)
self.parent = parent
def mousePressEvent(self, QMouseEvent):
print(parent.QMouseEvent.pos())
def mouseReleaseEvent(self, QMouseEvent):
cursor = QtGui.QCursor()
print(parent.cursor.pos())
class Example(QtWidgets.QMainWindow):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.foo = "fooview"
self.Foo = FooView(self,self.foo)
qbtn = QtWidgets.QPushButton('Quit', self)
qbtn.resize(qbtn.sizeHint())
qbtn.move(50, 50)
self.setGeometry(0, 0, 1024, 768)
self.setWindowTitle('Quit button')
self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint)
self.show()
def main():
app = QtWidgets.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I'm wondering how I can capture the mouse clicks in the FooView child class - is that even possible?
There is terminology that the OP uses that is confusing, for example FooView is a child class of BaseView but that has nothing to do with Qt so it is irrelevant for this case so I will omit that class and show the example of how another class can obtain information about the click event of a widget.
The logic is to create a class that inherits from QObject and apply an event filter to the other widget, then override the eventFilter method where the events of the widget are obtained.
import sys
from PyQt5 import QtWidgets, QtGui, QtCore
class MouseObserver(QtCore.QObject):
def __init__(self, widget):
super(MouseObserver, self).__init__(widget)
self._widget = widget
self.widget.installEventFilter(self)
#property
def widget(self):
return self._widget
def eventFilter(self, obj, event):
if obj is self.widget:
if event.type() == QtCore.QEvent.MouseButtonPress:
print(event.pos(), QtGui.QCursor.pos())
elif event.type() == QtCore.QEvent.MouseButtonRelease:
print(event.pos(), QtGui.QCursor.pos())
return super(MouseObserver, self).eventFilter(obj, event)
class Example(QtWidgets.QMainWindow):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.observer = MouseObserver(self)
qbtn = QtWidgets.QPushButton("Quit", self)
qbtn.resize(qbtn.sizeHint())
qbtn.move(50, 50)
self.setGeometry(0, 0, 1024, 768)
self.setWindowTitle("Quit button")
self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint)
self.show()
def main():
app = QtWidgets.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

Call function of a different class with parameter PyQt

I want to trigger a function of a different class on press of a button. Something like this example How to emit custom Events to the Event Loop in PyQt.
But I also want to pass a parameter to that function everytime the button is clicked. How do I achieve that?
If you want to add additional arguments you can use functools.partial:
main.py
from PyQt5 import QtCore, QtWidgets
from globalobject import GlobalObject
import functools
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
button = QtWidgets.QPushButton(text="Press me", clicked=self.on_clicked)
self.setCentralWidget(button)
#QtCore.pyqtSlot()
def on_clicked(self):
GlobalObject().dispatchEvent("hello")
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
wrapper = functools.partial(self.foo, "foo", bar="baz")
GlobalObject().addEventListener("hello", wrapper)
self._label = QtWidgets.QLabel()
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self._label)
def foo(self, foo, bar=None):
print(foo, bar)
self._label.setText(foo)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w1 = MainWindow()
w2 = Widget()
w1.show()
w2.show()
sys.exit(app.exec_())
That logic can also be implemented in the library:
globalobject.py
from PyQt5 import QtCore
import functools
#functools.lru_cache()
class GlobalObject(QtCore.QObject):
def __init__(self):
super().__init__()
self._events = {}
def addEventListener(self, name, func, *, args=(), kwargs=None):
kwargs = kwargs or {}
if name not in self._events:
self._events[name] = []
self._events[name].append((func, args, kwargs))
def dispatchEvent(self, name):
functions = self._events.get(name, [])
for func, args, kwargs in functions:
wrapper = func
wrapper = functools.partial(func, *args, **kwargs)
QtCore.QTimer.singleShot(0, wrapper)
main.py
from PyQt5 import QtCore, QtWidgets
from globalobject import GlobalObject
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
button = QtWidgets.QPushButton(text="Press me", clicked=self.on_clicked)
self.setCentralWidget(button)
#QtCore.pyqtSlot()
def on_clicked(self):
GlobalObject().dispatchEvent("hello")
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
GlobalObject().addEventListener(
"hello", self.foo, args=("foo",), kwargs={"bar": "baz"}
)
self._label = QtWidgets.QLabel()
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self._label)
def foo(self, foo, bar=None):
print(foo, bar)
self._label.setText(foo)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w1 = MainWindow()
w2 = Widget()
w1.show()
w2.show()
sys.exit(app.exec_())
Update:
If you want to send arguments through the dispatchEvent method then you should use the following:
globalobject.py
from PyQt5 import QtCore
import functools
#functools.lru_cache()
class GlobalObject(QtCore.QObject):
def __init__(self):
super().__init__()
self._events = {}
def addEventListener(self, name, func):
if name not in self._events:
self._events[name] = []
self._events[name].append(func)
def dispatchEvent(self, name, *, args=(), kwargs=None):
kwargs = kwargs or {}
functions = self._events.get(name, [])
for func in functions:
wrapper = func
wrapper = functools.partial(func, *args, **kwargs)
QtCore.QTimer.singleShot(0, wrapper)
main.py
from PyQt5 import QtCore, QtWidgets
from globalobject import GlobalObject
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
button = QtWidgets.QPushButton(text="Press me", clicked=self.on_clicked)
self.setCentralWidget(button)
self.counter = 0
#QtCore.pyqtSlot()
def on_clicked(self):
self.counter += 1
GlobalObject().dispatchEvent("hello", args=(self.counter,))
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
GlobalObject().addEventListener("hello", self.foo)
self._label = QtWidgets.QLabel()
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self._label)
def foo(self, x):
print(x)
self._label.setNum(x)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w1 = MainWindow()
w2 = Widget()
w1.show()
w2.show()
sys.exit(app.exec_())

PYQT: How to send a custom signal from the mainwindow(parent) to a child window?

How do I send a signal from a parent/mainwindow to a child window?
For example, how would I register and send mainSignal down to either widgetA or widgetB?
from PyQt5 import QtCore, QtGui, QtWidgets
class widgetB(QtWidgets.QWidget):
procDone = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
super(widgetB, self).__init__(parent)
self.lineEdit = QtWidgets.QLineEdit(self)
self.button = QtWidgets.QPushButton("Send Message to A", self)
self.layout = QtWidgets.QHBoxLayout(self)
self.layout.addWidget(self.lineEdit)
self.layout.addWidget(self.button)
self.button.clicked.connect(self.on_button_clicked)
#QtCore.pyqtSlot()
def on_button_clicked(self):
self.procDone.emit(self.lineEdit.text())
#QtCore.pyqtSlot(str)
def on_procStart(self, message):
self.lineEdit.setText("From A: " + message)
self.raise_()
class widgetA(QtWidgets.QWidget):
procStart = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
super(widgetA, self).__init__(parent)
self.lineEdit = QtWidgets.QLineEdit(self)
self.lineEdit.setText("Hello!")
self.button = QtWidgets.QPushButton("Send Message to B", self)
self.button.clicked.connect(self.on_button_clicked)
self.layout = QtWidgets.QHBoxLayout(self)
self.layout.addWidget(self.lineEdit)
self.layout.addWidget(self.button)
#QtCore.pyqtSlot()
def on_button_clicked(self):
self.procStart.emit(self.lineEdit.text())
#QtCore.pyqtSlot(str)
def on_widgetB_procDone(self, message):
self.lineEdit.setText("From B: " + message)
self.raise_()
class mainwindow(QtWidgets.QMainWindow):
mainSignal = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
super(mainwindow, self).__init__(parent)
self.button = QtWidgets.QPushButton("Click Me", self)
self.button.clicked.connect(self.on_button_clicked)
self.setCentralWidget(self.button)
self.widgetA = widgetA()
self.widgetB = widgetB()
self.widgetA.procStart.connect(self.widgetB.on_procStart)
self.widgetB.procDone.connect(self.widgetA.on_widgetB_procDone)
#QtCore.pyqtSlot()
def on_button_clicked(self):
self.widgetA.show()
self.widgetB.show()
self.widgetA.raise_()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
main = mainwindow()
main.show()
sys.exit(app.exec_())
The logic is the same, here it does not matter if they are parents, children, etc. It is only necessary that both objects can be accessed at the same time. The design of the signals and slots are made so that the classes are independent.
from PyQt5 import QtCore, QtGui, QtWidgets
class widgetB(QtWidgets.QWidget):
procDone = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
super(widgetB, self).__init__(parent)
self.lineEdit = QtWidgets.QLineEdit(self)
self.button = QtWidgets.QPushButton("Send Message to A", self)
self.layout = QtWidgets.QHBoxLayout(self)
self.layout.addWidget(self.lineEdit)
self.layout.addWidget(self.button)
self.button.clicked.connect(self.on_button_clicked)
#QtCore.pyqtSlot()
def on_button_clicked(self):
self.procDone.emit(self.lineEdit.text())
#QtCore.pyqtSlot(str)
def on_procStart(self, message):
self.lineEdit.setText("From A: " + message)
self.raise_()
#QtCore.pyqtSlot(str)
def on_message_from_main(self, text):
self.lineEdit.setText("From Main: " + text)
class widgetA(QtWidgets.QWidget):
procStart = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
super(widgetA, self).__init__(parent)
self.lineEdit = QtWidgets.QLineEdit(self)
self.lineEdit.setText("Hello!")
self.button = QtWidgets.QPushButton("Send Message to B", self)
self.button.clicked.connect(self.on_button_clicked)
self.layout = QtWidgets.QHBoxLayout(self)
self.layout.addWidget(self.lineEdit)
self.layout.addWidget(self.button)
#QtCore.pyqtSlot()
def on_button_clicked(self):
self.procStart.emit(self.lineEdit.text())
#QtCore.pyqtSlot(str)
def on_widgetB_procDone(self, message):
self.lineEdit.setText("From B: " + message)
self.raise_()
#QtCore.pyqtSlot(str)
def on_message_from_main(self, text):
self.lineEdit.setText("From Main: " + text)
class mainwindow(QtWidgets.QMainWindow):
mainSignal = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
super(mainwindow, self).__init__(parent)
self.button = QtWidgets.QPushButton("Click Me", self)
self.button.clicked.connect(self.on_button_clicked)
self.setCentralWidget(self.button)
self.widgetA = widgetA()
self.widgetB = widgetB()
self.widgetA.procStart.connect(self.widgetB.on_procStart)
self.widgetB.procDone.connect(self.widgetA.on_widgetB_procDone)
self.mainSignal.connect(self.widgetA.on_message_from_main)
self.mainSignal.connect(self.widgetB.on_message_from_main)
#QtCore.pyqtSlot()
def on_button_clicked(self):
self.widgetA.show()
self.widgetB.show()
self.widgetA.raise_()
self.mainSignal.emit("Hello")
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
main = mainwindow()
main.show()
sys.exit(app.exec_())

Signals And Slots: When simple example is not working

There are two widgets: the button and the label.
When the button is pressed I want to run the label's myFunction() method.
How to achieve this using signals and slots? The example below does not work.
from PyQt import QtCore, QtGui
class label(QtGui.QLabel):
def __init__(self, parent=None):
super(label, self).__init__(parent)
self.show()
def myFunction(self, arg=None):
print '...myFunction: received arg =', arg
class button(QtGui.QPushButton):
def __init__(self, parent=None):
super(button, self).__init__(parent)
self.clicked.connect(self.click)
self.show()
def click(self):
print 'emitted'
self.emit(QtCore.SIGNAL('buttonClicked'))
lbl = label()
btn = button()
Approach 1
One solution is to use a globally declared QObject variable connector. We use the connector.signal for both: to emit the signal and to connect the method to be executed on signal.emit().
To emit from first widget: connector.signal.emit()
To connect to second widget's method: connector.signal.connect(self.myFunction)
from PySide import QtCore, QtGui
class Communicate(QtCore.QObject):
signal = QtCore.Signal(str)
connector=Communicate()
class label(QtGui.QLabel):
def __init__(self, parent=None):
super(label, self).__init__(parent)
connector.signal.connect(self.labelFunction)
self.show()
def labelFunction(self, arg=None):
print '...labelFunction: arg =', arg
class button(QtGui.QPushButton):
def __init__(self, parent=None):
super(button, self).__init__(parent)
self.clicked.connect(self.click)
self.show()
def click(self):
connector.signal.emit("Hello World")
lbl = label()
btn = button()
Approach 2
We can achieve the same functionality by passing label.labelFunction as a callback argument. The button's connector is to be connected to it:
from PySide import QtCore, QtGui
class Communicate(QtCore.QObject):
signal = QtCore.Signal(str)
class label(QtGui.QLabel):
def __init__(self, parent=None):
super(label, self).__init__(parent)
self.show()
def labelFunction(self, arg=None):
print '...labelFunction: arg = %s'%arg
class button(QtGui.QPushButton):
def __init__(self, callback=None, parent=None):
super(button, self).__init__(parent)
self.callback=callback
self.clicked.connect(self.click)
self.show()
def click(self):
connector=Communicate()
connector.signal.connect(self.callback)
connector.signal.emit("Hello World")
lbl = label()
btn = button(callback=lbl.labelFunction)
Approach 3
Just like previously we still emit on button.clicked.
But a different syntax is used to connect label to the customSignal:
self.connect(btn, QtCore.SIGNAL('customSignal'), Function)
from PySide import QtCore, QtGui
def Function(arg=None):
print 'Function.arg: %r'%arg
class button(QtGui.QPushButton):
def __init__(self, parent=None):
super(button, self).__init__(parent)
self.clicked.connect(self.click)
self.show()
def click(self):
self.emit(QtCore.SIGNAL('customSignal'), 'String Arument')
btn = button()
class label(QtGui.QLabel):
def __init__(self, parent=None):
super(label, self).__init__(parent)
self.connect(btn, QtCore.SIGNAL('customSignal'), Function)
self.show()
lbl = label()

pyQt Hover event with Svg image

I've been working on this for some time now and I can't figure out what I'm doing wrong. I hope someone here can help.
I'm trying to get hover events to work when I mouse over an Svg item that's in a QGraphicsScene. Here's the code that I've been playing with.
#!/usr/bin/python
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from PyQt4.QtSvg import *
class Main(QWidget):
def __init__(self):
super(Main, self).__init__()
hbox = QHBoxLayout()
self.setLayout(hbox)
self.view = MyView(self)
self.scene = QGraphicsScene()
self.view.setScene(self.scene)
hbox.addWidget(self.view)
class MyView(QGraphicsView):
def __init__(self, parent):
super(MyView, self).__init__(parent)
self.parent = parent
def mousePressEvent(self, event):
super(MyView, self).mousePressEvent(event)
test = MySvg()
self.parent.scene.addItem(test.image)
class MySvg(QGraphicsSvgItem):
def __init__(self):
super(MySvg, self).__init__()
self.image = QGraphicsSvgItem('ubuntu.svg')
self.image.setFlags(QGraphicsItem.ItemIsSelectable|
QGraphicsItem.ItemIsMovable)
self.setAcceptsHoverEvents(True)
def hoverEnterEvent(self, event):
print 'Enter'
def hoverLeaveEvent(self, event):
print 'Leave'
def hoverMoveEvent(self, event):
print 'Moving'
def runMain():
app = QApplication(sys.argv)
ex = Main()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
runMain()
Hope someone can help.
You are monitoring hover events for MySvg but you are adding another QGraphicsSvgItem to the view that is just an instance (MySvg.image) in MySvg. Your MySvg is not even in the view. Try like this:
#!/usr/bin/python
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from PyQt4.QtSvg import *
class Main(QWidget):
def __init__(self):
super(Main, self).__init__()
hbox = QHBoxLayout()
self.setLayout(hbox)
self.view = MyView(self)
self.scene = QGraphicsScene()
self.view.setScene(self.scene)
hbox.addWidget(self.view)
class MyView(QGraphicsView):
def __init__(self, parent):
super(MyView, self).__init__(parent)
self.parent = parent
def mousePressEvent(self, event):
super(MyView, self).mousePressEvent(event)
test = MySvg()
self.parent.scene.addItem(test)
class MySvg(QGraphicsSvgItem):
def __init__(self):
super(MySvg, self).__init__('ubuntu.svg')
self.setFlags(QGraphicsItem.ItemIsSelectable|
QGraphicsItem.ItemIsMovable)
self.setAcceptsHoverEvents(True)
def hoverEnterEvent(self, event):
print 'Enter'
def hoverLeaveEvent(self, event):
print 'Leave'
def hoverMoveEvent(self, event):
print 'Moving'
def runMain():
app = QApplication(sys.argv)
ex = Main()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
runMain()

Categories

Resources