I want to connect a trigger generated from PushButton to my custom signal with an argument(Signal(int)).
It is possible to connect a signal without an argument to the Button. Currently, I am creating an extra slot to emit the Signal(int) triggered from Signal() and Button. Is there any simple way to this?
class GUI(QObject):
sig_x = Signal()
sig_y = Signal(int)
def __init__(self,parent = None):
super(GUI,self).__init__(parent)
self.value = 10
self.button = QPushButton("Click me")
self.button.clicked.connect(self.sig_x)
#self.button.clicked.connect(self.sig_y(self.value)) want to do something like this
self.sig_x.connect(self.pass_args)
self.sig_y.connect(self.final_function)
#connection slot for sig_x
def pass_args(self):
self.sig_y.emit(self.value)
#demo connection slot for sig_y
def final_function(self,passed_value):
print("passed value is " + str(passed_value))
The reason this won't work:
self.button.clicked.connect(self.sig_y(self.value))
is because connect() needs to be passed a function (or, more precisely anything that can be called). But written like this, you're not passing it the signal itself, you're calling the signal and passing in the result.
One solution is a lambda function. A lambda is a way to define a function inline; this way you define a new function right there, in the same line where you pass it to connect.
self.button.clicked.connect(lambda x: self.sig_y(self.value))
You can see some more context of how this works in this quesiton, even though the question itself is about getting a slightly more complex version to work.
This is all assuming that you need this signal structure for reasons not immediately evident in your example. If not, you could simply connect the button clicked signal directly to your final_function, with no need for custom signals.
class GUI(QObject):
def __init__(self, parent=None):
super().__init__(parent)
self.value = 10
self.button = QPushButton("Click me")
self.button.clicked.connect(self.final_function)
def final_function(self, state):
print(f"My value is {self.value}.")
And even then, if you need a signal for notifying other things, you could also add that to the method:
def final_function(self, state):
print(f"My value is {self.value}.")
self.sig_y.emit(self.value)
I'm a beginner in learning python..
I'm looking for help in solving an OOP problem
My main program has something simplified like below:
class abc(Frame):
def _init_(self, master)
Frame.__init__(self)
self.B1 = Mybutton(self.master, self.cmd)
def cmd(self):
print("hello world")
In the main program, I import Mybutton class in another file, which is simplified as below:
class Mybutton():
def _init_(self, parent, command):
self.command = command
def A_ramdom_fcn(self):
...
self.command() ------------------>> here I want to execute the command
in class abc, not in class Mybutton.
How to execute a method from another class that is passed as an instance method, you may ask why not just execute it in class abc, but I have event attached to button press, it needs to do a roundabout to achieve this..
First, fix the typos: missing : in abc's init method, and it should be __init__ (with two underscores) for both classes.
It seems like you've gotten yourself turned around. You've set things up correctly using composition: an abc has a Mybutton, and it looks like you correctly pass the function to Mybutton so that it can execute it. In fact, your code will work as written if you do, for example
a = abc(master) # no context for what master is, but I assume you have it
a.B1.A_ramdom_fcn()
With the way you've set things up, you don't want to import and make instances of Mybutton in your main program (what abc would they belong to?). You want to import and make instances of abc. You then access their internal Mybutton like I've shown in the example above. This works because when you pass self.cmd to the Mybutton constructor while inside the abc constructor, it's already a bound method of the abc you're constructing.
As an addendum, it looks like you might be having an XY problem with regards to why you need such a roundabout method. Is there any reason why you can't simply pass abc.cmd to the button press handler?
Theoretically, what you are trying is possible, you can capture the object method into variable and call it later (python 3):
class Window:
def __init__(self):
self.my_button = Mybutton(self.cmd)
def cmd(self):
print("hello world")
class Mybutton:
def __init__(self, command):
self.command = command
def a_ramdom_fcn(self):
self.command.__call__()
win = Window()
win.my_button.a_ramdom_fcn()
I assume you are trying to make the generic Button class which doesn't know what to do when it's clicked and you want to put the actual logic into your Window class.
That makes sense, but it would be even better to extract the logic into the third, Command class. This allows us to limit the Window responsibility and also avoid the trick with method-as-variable (the command we pass to the button object is just another object):
class HelloWorldCommand:
def execute(self):
print("Hello world")
class Window:
def __init__(self):
self.my_button = Mybutton(
HelloWorldCommand()
)
class Mybutton:
def __init__(self, command):
self.command = command
def a_ramdom_fcn(self):
self.command.execute()
win = Window()
win.my_button.a_ramdom_fcn()
How do i pass variables from one python module(1st module) to another(2nd module) which contains a main module and two Qdialog which appears when the buttons are pressed in the 2nd module
How can i send these values from 1st module to the Qdialog in the 2nd module
I am dealing with multiple modules and data needs to updated here and there and totally confused with Inheritance/ Creating instances of classes and Modules
# mainwindow.py(1st module)
from 2nd_module import window2, dialog1, dialog2
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.win2 =window2()
self.dia1 =dialog1()
if 1:
self.win2.bar()
# pass data
self.dia1.some_function(data)
.
# 2nd_module.py
from mainwindow import mainwindow
class dialog1 (QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
def update(self):
w = window2()
#do something
w.self.ma.foo()
.
.
class dialog2 (QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
.
.
class window2(QtGui.QMainWindow):
def __init__(self):
super(window2, self).__init__()
self.update_request = 0 # initilizing the variable here
self.ma = mainwindow()
self.connect(dialog1_bttn, Qt.SIGNAL("clicked()"), self.open_dialog1 )
def open_dialog1(self): #this is how i create the instance of dialog box
self.open1 =dialog1()
self.open1.show
def foo(self): # this updates the request value from 2 dialog boxes
#do something
self.update_request = 1
def bar(self): # this is called from 1st module to check if there are any update requests
if self.update_request ==1:
#do something
My struggles
so here when i call foo() from dialog1 it works fine and updates the value to 1. But now when i call bar() from 1st module the value self.update_request turns to be 0 all the time
my mainwindow(1st module) runs all the background tasks like exchanging data from serial port,etc. and i need to send that data to the dialog1 in 2nd_module to update the values
Can anyone please advise me on how to create proper instances of Qt Dialogs, windows and to pass data/variables/list from one class to another ..thanks in advance
I'm having troubles using PyQt4 slots/signals.
I'm using PyLIRC and I'm listening for button presses on a remote. This part I have gotten to work outside of Qt. My problem comes when emitting the signal from the button listening thread and attempting to call a slot in the main thread.
My button listener is a QObject initialized like so:
buttonPressed = pyqtSignal(int)
def __init__(self):
super(ButtonEvent, self).__init__()
self.buttonPressed.connect(self.onButtonPressed)
def run(self):
print 'running'
while(self._isListening):
s = pylirc.nextcode()
if (s):
print 'emitting'
self.buttonPressed.emit(int(s[0]))
The onButtonPressed slot is internal to the button listener for testing purposes.
To move the button listener to another thread to do the work, I use the following:
event = ButtonEvent()
eventThread = QThread()
event.moveToThread(eventThread)
eventThread.started.connect(event.run)
Then in the main thread, I have my VideoTableController class that contains the slot in the main thread that doesn't get called. Inside of __init__ I have this:
class VideoTableController(QObject):
def __init__(self, buttonEvent):
buttonEvent.buttonPressed.connect(self.onButtonPressed)
Where onButtonPressed in this case is:
#pyqtSlot(int)
def onButtonPressed(self, bid):
print 'handling button press'
if bid not in listenButtons: return
{ ButtonEnum.KEY_LEFT : self.handleBack,
#...
So when I start the event thread, it starts listening properly. When I press a button on the remote, the onButtonPressed slot internal to the ButtonEvent class is properly called, but the slot within VideoTableController, which resides in the main thread, is not called. I started my listening thread after connecting the slot to the signal, and I tested doing it the other way around, but to no avail.
I have looked around, but I haven't been able to find anything. I changed over to using QObject after reading You're doing it wrong. Any help with this is greatly appreciated. Let me know if you need anything else.
EDIT: Thanks for the responses! Here is a big chunk of code for you guys:
ButtonEvent (This class uses singleton pattern, excuse the poor coding because I'm somewhat new to this territory of Python also):
import pylirc
from PyQt4.QtCore import QObject, pyqtSignal, QThread, pyqtSlot
from PyQt4 import QtCore
class ButtonEvent(QObject):
"""
A class used for firing button events
"""
_instance = None
_blocking = 0
_isListening = False
buttonPressed = pyqtSignal(int)
def __new__(cls, configFileName="~/.lircrc", blocking=0, *args, **kwargs):
if not cls._instance:
cls._instance = super(ButtonEvent, cls).__new__(cls, args, kwargs)
cls._blocking = blocking
if not pylirc.init("irexec", configFileName, blocking):
raise RuntimeError("Problem initilizing PyLIRC")
cls._isListening = True
return cls._instance
def __init__(self):
"""
Creates an instance of the ButtonEvent class
"""
super(ButtonEvent, self).__init__()
self.buttonPressed.connect(self.button)
### init
def run(self):
print 'running'
while(self._isListening):
s = pylirc.nextcode()
if (s):
print 'emitting'
self.buttonPressed.emit(int(s[0]))
def stopListening(self):
print 'stopping'
self._isListening = False
#pyqtSlot(int)
def button(self, bid):
print 'Got ' + str(bid)
def setupAndConnectButtonEvent(configFileName="~/.lircrc", blocking=0):
"""
Initializes the ButtonEvent and puts it on a QThread.
Returns the QThread it is running on.
Does not start the thread
"""
event = ButtonEvent().__new__(ButtonEvent, configFileName, blocking)
eventThread = QThread()
event.moveToThread(eventThread)
eventThread.started.connect(event.run)
return eventThread
Here is the VideoTableController:
from ControllerBase import ControllerBase
from ButtonEnum import ButtonEnum
from ButtonEvent import ButtonEvent
from PyQt4.QtCore import pyqtSlot
from PyQt4 import QtCore
class VideoTableController(ControllerBase):
listenButtons = [ ButtonEnum.KEY_LEFT,
ButtonEnum.KEY_UP,
ButtonEnum.KEY_OK,
ButtonEnum.KEY_RIGHT,
ButtonEnum.KEY_DOWN,
ButtonEnum.KEY_BACK ]
def __init__(self, model, view, parent=None):
super(VideoTableController, self).__init__(model, view, parent)
self._currentRow = 0
buttonEvent = ButtonEvent()
buttonEvent.buttonPressed.connect(self.onButtonPressed)
self.selectRow(self._currentRow)
#pyqtSlot(int)
def onButtonPressed(self, bid):
print 'handling button press'
if bid not in listenButtons: return
{ ButtonEnum.KEY_LEFT : self.handleBack,
ButtonEnum.KEY_UP : self.handleUp,
ButtonEnum.KEY_OK : self.handleOk,
ButtonEnum.KEY_RIGHT : self.handleRight,
ButtonEnum.KEY_DOWN : self.handleDown,
ButtonEnum.KEY_BACK : self.handleBack,
}.get(bid, None)()
And here is my startup script:
import sys
from PyQt4 import QtCore, QtGui
from ui_main import Ui_MainWindow
from VideoTableModel import VideoTableModel
from VideoTableController import VideoTableController
from ButtonEvent import *
class Main(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.buttonEvent = ButtonEvent()
self.bEventThread = setupAndConnectButtonEvent()
model = VideoTableModel("/home/user/Videos")
self.ui.videoView.setModel(model)
controller = VideoTableController(model, self.ui.videoView)
self.bEventThread.start()
def closeEvent(self, event):
self.buttonEvent.stopListening()
self.bEventThread.quit()
event.accept()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
buttonEvent = ButtonEvent()
myapp = Main()
myapp.show()
sys.exit(app.exec_())
It turns out I was just making a foolish Python mistake. The signal was being emitted correctly, and the event loop was running properly in all threads. My problem was that in my Main.__init__ function I made a VideoTableController object, but I did not keep a copy in Main, so my controller did not persist, meaning the slot also left. When changing it to
self.controller = VideoTableController(model, self.ui.videoView)
Everything stayed around and the slots were called properly.
Moral of the story: it's not always a misuse of the library, it may be a misuse of the language.
It seems that the quickest workaround would be change your ButtonEvent code here:
...
def run(self):
print 'running'
while(self._isListening):
s = pylirc.nextcode()
if (s):
print 'emitting'
self.buttonPressed.emit(int(s[0]))
...
to this:
#pyqtSlot()
def run(self):
print 'running'
while(self._isListening):
s = pylirc.nextcode()
if (s):
print 'emitting'
self.buttonPressed.emit(int(s[0]))
The short explanation to this issue is that PyQt uses a proxy internally, and this way you can make sure to avoid that. After all, your method is supposed to be a slot based on the connect statement.
Right... Now, I would encourage you to give some consideration for your current software design though. It seems that you are using a class in a dedicated thread for handling Qt button events. It may be good idea, I am not sure, but I have not seen this before at least.
I think you could get rid of that class altogether in the future with a better approach where you connect from the push button signals directly to your handler slot. That would not be the run "slot" in your dedicated thread, however, but the cannonical handler.
It is not a good design practice to introduce more complexity, especially in multi-threaded applications, than needed. Hope this helps.
I haven't actually tested this (because I don't have access to your compiled UI file), but I'm fairly certain I'm right.
Your run method of your ButtonEvent (which is supposed to be running in a thread) is likely running in the mainthread (you can test this by importing the python threading module and adding the line print threading.current_thread().name. To solve this, decorate your run method with #pyqtSlot()
If that doesn't solve it, add the above print statement to various places until you find something running in the main thread that shouldn't be. The lined SO answer below will likely contain the answer to fix it.
For more details, see this answer: https://stackoverflow.com/a/20818401/1994235
If I have a signal and I register an objects function to the signal will this keep the object live and stop the garbage collection of that object?
E.g.
class Signals():
signal = Qt.pyqtSignal()
def __init__(self):
QObject.__init__(self)
class Test();
def __init__(self, s):
s.connect(self.done)
def done(self):
print("Done")
s = Signals()
t = Test(s.signal)
t = None
s.signal.emit()
Will the Test objecct still get the signal?
No, it won't, it's not enough to keep the object alive. Just try it:
from PyQt4.QtCore import *
app = QCoreApplication([])
class Signals(QObject):
signal = pyqtSignal()
def __init__(self):
QObject.__init__(self)
class Test():
def __init__(self, s):
s.connect(self.done)
def done(self):
print("Done")
s = Signals()
t = Test(s.signal)
print("first")
s.signal.emit()
app.processEvents()
t = None
print("second")
s.signal.emit()
app.processEvents()
Output:
first
Done
second
This behaviour only applies when connecting a signal to a bound method. As an example, if you use:
s.connect(lambda: self.done())
instead, then it will work. If the library wouldn't keep a strong reference here, then you could never connect an anonymous function to a signal. So in this case pyqt has to ensure that it keeps a reference to the function, and the object (self) keeps to be referenced in the closure of the lambda.