While teaching myself python3 wanting to send data to the terminal using print(message). This was relatively straight forward and i can generate the console list of CAN data (details of code omitted here, just have bare min code) using three lines:
import os #windows os only,
os.system('cls'),
print(myStr) (also not shown here in code, but it does work!)
Wanting to pretty it all up and start expanding on the data and displaying it, over the last week been learning qt5 designer and pyqt5 and pyuic to generate the GUI for python. So far so good and I can get the thread working to read the CAN port (could have as easily been a UART port for those not familiar with CAN bus) confirmed this with console window still receiving messages, and the GUI window shows up. Then I read a bunch about MVC and watched tutorials and that's were the wheels started to fall off. I cant get in my brain/head around how to load the data into my model to display to the listview window in the GUI. I think i might have the signal and slot correctly done (sure someone will correct me if that isn't the case) but am struggling to get the data in the correct presentation to be displayed from line ??????self.listView (str)
from PyQt5 import QtCore, QtGui, QtWidgets, QtSvg
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtCore import pyqtSignal
import sys
import can
import threading
import time
class MainWindow(QMainWindow):
app = None
Form = None
receiveUpdateSignal = pyqtSignal(str)
def __init__(self,app):
super().__init__()
self.app = app
self.initTool()
self.initEvent()
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(400, 300)
self.listView = QtWidgets.QListView(Form)
self.listView.setGeometry(QtCore.QRect(40, 40, 256, 192))
self.listView.setObjectName("listView")
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
def receiveCan(self):
while True:
try:
message = self.bus.recv(0.2)
if message is not None:
print(message)
#data = message.data
self.receiveUpdateSignal.emit("messge in")
except Exception as e:
if hasattr(e, 'message'):
print(e.message)
else:
print(e)
break
time.sleep(0.005)
def initTool(self):
self. bus = can.interface.Bus(bustype='pcan', channel='PCAN_USBBUS1', bitrate=250000)
return
def startReceive(self):
receiveProcess = threading.Thread(target=self.receiveCan)
receiveProcess.setDaemon(True)
receiveProcess.start()
def initEvent(self):
self.receiveUpdateSignal.connect(self.updateReceivedDataDisplay)
def updateReceivedDataDisplay(self,str):
if str != "":
try:
**??????self.listView (str)**
except Exception as e:
if hasattr(e, 'message'):
print(e.message)
else:
print(e)
return
def main():
app = QtWidgets.QApplication(sys.argv)
Form = QtWidgets.QWidget()
ui = MainWindow(app)
ui.setupUi(Form)
ui.startReceive()
Form.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
First of all do not use str, it is a reserved word in Python and its use is considered bad programming practice.
On the other hand in the case of QListView this is based on the MVC pattern, where the model has the information, the view shows it and the controller handles when and what information is displayed. In this case the model must be a class that inherits from QAbstractListModel, the easiest class to handle is QStandardItemModel, we create an object of that class and we establish it to QListView, after the information is added to the model, the model will internally notify the view causing it to update.
class MainWindow(QMainWindow):
...
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(400, 300)
self.listView = QtWidgets.QListView(Form)
self.listView.setGeometry(QtCore.QRect(40, 40, 256, 192))
self.listView.setObjectName("listView")
self.model = QtGui.QStandardItemModel() # <----
self.listView.setModel(self.model) # <----
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
...
def updateReceivedDataDisplay(self, text):
if text:
it = QtGui.QStandardItem(text)
self.model.appendRow(it)
Related
I am retired and teaching myself to write code. I am working on a program that requires a thread to operate in the background (GUI developed with PYQT5) so I can still use the GUI buttons (to pause, resume, stop, etc.). I have the thread counting to 10, and I want the steps sent back to the progress bar's setValue. That is not working. I can see the thread counting to 10 and I can see the data coming back from the thread. Just can't get the progress bar to move.
I have spent the last two days searching the internet and have reviewed and tried to follow many examples. To be honest, I am not sure I understand the answers.
I have crated an example of what I am seeing in my program. In this example I have a progress bar in a GUI with two buttons. Start will start the thread and test will just print out test statements while the thread is running. I am using Designer, so the GUI is in a separate file.
I am not even sure if I am asking the questions correctly in my searches. I decided to post in the error I see a lot, but hopefully you can see what is happening when you run the code.
Main program
#!/usr/bin/env python3
import sys, sqlite3, os.path, string, time
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton,
QProgressBar
from PyQt5.QtCore import pyqtSlot, QObject, QThread, pyqtSignal
from Thread_Test import Ui_MainWindow
class ExecuteSession(QThread):
PBValueSig = pyqtSignal(int)
def __init__(self, dur=0):
QThread.__init__(self)
self.dur = dur
def __del__(self):
self.wait()
def run(self):
i = 0
while i <= self.dur:
self.RPTApp = RPTApp()
print(i)
i = i + 1
self.PBValueSig.emit(self.RPTApp.updateProgressBar(i))
time.sleep(1)
class RPTApp(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(RPTApp, self).__init__(parent)
self.setupUi(self)
self.pushButton.clicked.connect(self.PB)
self.pushButton_2.clicked.connect(self.PB2)
def PB2(self):
print("TEST")
def PB(self):
dur = 10
self.progressBar.setMinimum(0)
self.progressBar.setMaximum(dur)
self.progressBar.setValue(0)
#thread
self.exeThread = ExecuteSession(dur)
self.exeThread.start()
#pyqtSlot(int)
def updateProgressBar(self, int):
print("INT + " +str(int))
#self.classES.PBValueSig.connect(self.progressBar.setValue)
self.progressBar.setValue(int)
def main():
app = QApplication(sys.argv)
window = RPTApp()
window.show()
app.exec_()
if __name__ == '__main__':
main()
This is the GUI code:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'Thread_Test.ui'
#
# Created by: PyQt5 UI code generator 5.7
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(640, 480)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.progressBar = QtWidgets.QProgressBar(self.centralwidget)
self.progressBar.setGeometry(QtCore.QRect(90, 320, 471, 23))
self.progressBar.setProperty("value", 24)
self.progressBar.setObjectName("progressBar")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(270, 140, 91, 29))
self.pushButton.setObjectName("pushButton")
self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_2.setGeometry(QtCore.QRect(270, 200, 91, 29))
self.pushButton_2.setObjectName("pushButton_2")
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "START"))
self.pushButton_2.setText(_translate("MainWindow", "TEST"))
This is the error I am receiving:
(python3:11942): WARNING: Error retrieving accessibility bus address: org.freedesktop.DBus.Error.ServiceUnknown: The name org.a11y.Bus was not provided by any .service files
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QApplication(0x26a1a58), parent's thread is QThread(0x26a6218), current thread is ExecuteSession(0x28f4048)
I am not a software developer, so much of this is new to me. I appreciate any help I can receive, and please...don't assume I know anything. Please be descriptive. Thank you!
Your code has several errors since PyQt has certain minimum rules:
The thread where the GUI application is created is called GUI thread because there must be created and live any graphic component, but you are creating unnecessary RPTApp inside the QThread, I say unnecessary since the thread is created inside RPTApp so it does not it is necessary to create another.
Another error is in the emission of the signal in the wire, you do not have to call the function that uses the data that emits the signal but you must connect it to the slot. The application will be in charge of transporting the data and invoking the slot.
All of the above is corrected in the following section:
class ExecuteSession(QThread):
PBValueSig = pyqtSignal(int)
[...]
def run(self):
i = 0
while i <= self.dur:
print(i)
i = i + 1
self.PBValueSig.emit(i)
time.sleep(1)
class RPTApp(QMainWindow, Ui_MainWindow):
[..]
def PB(self):
dur = 10
self.progressBar.setMinimum(0)
self.progressBar.setMaximum(dur)
self.progressBar.setValue(0)
# thread
self.exeThread = ExecuteSession(dur)
self.exeThread.PBValueSig.connect(self.updateProgressBar)
self.exeThread.start()
#pyqtSlot(int)
def updateProgressBar(self, value):
print("INT + " + str(value))
self.progressBar.setValue(value)
Note: It is not recommended to use int as a variable since it is the name of a preloaded function, there are thousands of other names.
I understand (more or less) content of the page of official documentation except the the final example, which is essencially as follows (I do not care the button):
from ui_imagedialog import ImageDialog
class MyImageDialog(ImageDialog):
def __init__(self):
super(MyImageDialog, self).__init__()
# Connect up the buttons.
self.okButton.clicked.connect(self.accept)
Problem: I'm stuck a bit trying to understand how to make this snippet work. It rises an error: 'cannon import name ImageDialog'.
What should I add from the first example of the aforementioned documentation page to make this code to show up a Dialog window?
What I've tried:
I've made the file with generated Python code with the name ui_imagedialog.py as requiered. It has the the following content, which obviously works on its own:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_ImageDialog(object):
def setupUi(self, ImageDialog):
ImageDialog.setObjectName("ImageDialog")
ImageDialog.resize(303, 204)
self.pushButton = QtWidgets.QPushButton(ImageDialog)
self.pushButton.setGeometry(QtCore.QRect(200, 160, 75, 23))
self.pushButton.setObjectName("pushButton")
self.retranslateUi(ImageDialog)
QtCore.QMetaObject.connectSlotsByName(ImageDialog)
def retranslateUi(self, ImageDialog):
_translate = QtCore.QCoreApplication.translate
ImageDialog.setWindowTitle(_translate("ImageDialog", "Dialog"))
self.pushButton.setText(_translate("ImageDialog", "PushButton"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
ImageDialog = QtWidgets.QDialog()
ui = Ui_ImageDialog()
ui.setupUi(ImageDialog)
ImageDialog.show()
sys.exit(app.exec_())
Any constructive help is appreciated.
Qt Designer is used to create the graphic part, but not for the logic, you must create the logical part depending on the widget you used in the design. In your case by the name I think it is QDialog.
from ui_imagedialog import Ui_ImageDialog
from PyQt5 import QtCore, QtGui, QtWidgets
class ImageDialog(QtWidgets.QDialog, Ui_ImageDialog):
def __init__(self, parent=None):
super(ImageDialog, self).__init__(parent=parent)
self.setupUi(self)
self.pushButton.clicked.connect(self.accept)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = ImageDialog()
w.show()
sys.exit(app.exec_())
Observations: In the ui_imagedialog.py file there is no ImageDialog class, only the Ui_ImageDialog class, so I generated the error. Also in the design the button is called self.pushButton, so you can not call it self.okButton.
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)
I'm trying to write a program that will interact with QGraphicsView. I want to gather mouse and keyboard events when the happen in the QGraphicsView. For example, if the user clicks on the QGraphicsView widget I will get the mouse position, something like that. I can hard code it rather easily, but I want to use QtDesigner because the UI will be changing frequently.
This is the code that I have for the gui.py. A simple widget with a QGraphicsView in it.
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
_fromUtf8 = lambda s: s
class Ui_graphicsViewWidget(object):
def setupUi(self, graphicsViewWidget):
graphicsViewWidget.setObjectName(_fromUtf8("graphicsViewWidget"))
graphicsViewWidget.resize(400, 300)
graphicsViewWidget.setMouseTracking(True)
self.graphicsView = QtGui.QGraphicsView(graphicsViewWidget)
self.graphicsView.setGeometry(QtCore.QRect(70, 40, 256, 192))
self.graphicsView.setObjectName(_fromUtf8("graphicsView"))
self.retranslateUi(graphicsViewWidget)
QtCore.QMetaObject.connectSlotsByName(graphicsViewWidget)
def retranslateUi(self, graphicsViewWidget):
graphicsViewWidget.setWindowTitle(QtGui.QApplication.translate("graphicsViewWidget", "Form", None, QtGui.QApplication.UnicodeUTF8))
The code for the program:
#!/usr/bin/python -d
import sys
from PyQt4 import QtCore, QtGui
from gui import Ui_graphicsViewWidget
class MyForm(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_graphicsViewWidget()
self.ui.setupUi(self)
QtCore.QObject.connect(self.ui.graphicsView, QtCore.SIGNAL("moved"), self.test)
def mouseMoveEvent(self, event):
print "Mouse Pointer is currently hovering at: ", event.pos()
self.emit(QtCore.SIGNAL("moved"), event)
def test(self, event):
print('in test')
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = MyForm()
myapp.show()
sys.exit(app.exec_())
When I run this code, it gives me the opposite of what I want. I get the mouse position everywhere except for inside the QGraphicsView.
I'm sure it's a problem with my QObject.connect. But every time I go back and read about signals and slots it makes sense but I can't get it.
Please help, I've been banging my head for the past few days now. I'm sorry if this as been asked before but I've been through all the threads on this topic and I can't get anywhere.
Thanks
The signal must come from the QGraphicsView object that was defined in the ui.
You can create a class derived from QGraphicsView like this
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class MyView(QGraphicsView):
moved = pyqtSignal(QMouseEvent)
def __init__(self, parent = None):
super(MyView, self).__init__(parent)
def mouseMoveEvent(self, event):
# call the base method to be sure the events are forwarded to the scene
super(MyView, self).mouseMoveEvent(event)
print "Mouse Pointer is currently hovering at: ", event.pos()
self.moved.emit(event)
Then, in the designer:
right-click on the QGraphicsView then Promote to
write the class name in the Promoted Class Name field (e.g. "MyView"),
write the file name where that class is in the Header file field but without the .py extension,
click on the Add button and then on the Promote button.
And you can regenerate your file gui.py with pyuic4.
I couldn't understand the connectSlotsByName() method which is predominently used by pyuic4.. As far the class is single in a PyQt file it's ok since we can use self which will be associated with a single object throughout.. But when we try to use various classes from different files the problem and the need to use connectSlotsByName() arises.. Here's what i encountered which is weird..
I created a stacked widget..
I placed my first widget on it.. It
has a button called "Next >".
On clicking next it hides the current
widget and adds another widget which has the "click me" button..
The problem here is the click event for "click me" button in second is not captured.. It's a minimal example that i can give for my original problem.. Please help me..
This is file No.1..(which has the parent stacked widget and it's first page). On clicking next it adds the second page which has "clickme" button in file2..
from PyQt4 import QtCore, QtGui
import file2
class Ui_StackedWidget(QtGui.QStackedWidget):
def __init__(self,parent=None):
QtGui.QStackedWidget.__init__(self,parent)
self.setObjectName("self")
self.resize(484, 370)
self.setWindowTitle(QtGui.QApplication.translate("self", "stacked widget", None, QtGui.QApplication.UnicodeUTF8))
self.createWidget1()
def createWidget1(self):
self.page=QtGui.QWidget()
self.page.setObjectName("widget1")
self.pushButton=QtGui.QPushButton(self.page)
self.pushButton.setGeometry(QtCore.QRect(150, 230, 91, 31))
self.pushButton.setText(QtGui.QApplication.translate("self", "Next >", None, QtGui.QApplication.UnicodeUTF8))
self.addWidget(self.page)
QtCore.QMetaObject.connectSlotsByName(self.page)
QtCore.QObject.connect(self.pushButton,QtCore.SIGNAL('clicked()'),self.showWidget2)
def showWidget2(self):
self.page.hide()
obj=file2.widget2()
obj.createWidget2(self)
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
ui = Ui_StackedWidget()
ui.show()
sys.exit(app.exec_())
Here's file2
from PyQt4 import QtGui,QtCore
class widget2():
def createWidget2(self,parent):
self.page = QtGui.QWidget()
self.page.setObjectName("page")
self.parent=parent
self.groupBox = QtGui.QGroupBox(self.page)
self.groupBox.setGeometry(QtCore.QRect(30, 20, 421, 311))
self.groupBox.setObjectName("groupBox")
self.groupBox.setTitle(QtGui.QApplication.translate("self", "TestGroupBox", None, QtGui.QApplication.UnicodeUTF8))
self.pushButton = QtGui.QPushButton(self.groupBox)
self.pushButton.setGeometry(QtCore.QRect(150, 120, 92, 28))
self.pushButton.setObjectName("pushButton")
self.pushButton.setText(QtGui.QApplication.translate("self", "Click Me", None, QtGui.QApplication.UnicodeUTF8))
self.parent.addWidget(self.page)
self.parent.setCurrentWidget(self.page)
QtCore.QMetaObject.connectSlotsByName(self.page)
QtCore.QObject.connect(self.pushButton,QtCore.SIGNAL('clicked()'),self.printMessage)
def printMessage(self):
print("Hai")
Though in both the widgets(i mean pages)
QtCore.QMetaObject.connectSlotsByName(self.page)
the clicked signal in second dialog isn't getting processed. Thanks in advance.. Might be a beginner question..
A better question is "Why not just use new-style signals and slots?". They're much simpler and don't require any weird naming conventions:
from sys import argv, exit
from PyQt4 import QtCore, QtGui
class MyWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(MyWidget, self).__init__(parent)
self._layout = QtGui.QVBoxLayout()
self.setLayout(self._layout)
self._button = QtGui.QPushButton()
self._button.setText('Click NOW!')
self._layout.addWidget(self._button)
self._button.clicked.connect(self._printMessage)
#QtCore.pyqtSlot()
def _printMessage(self):
print("Hai")
if __name__ == "__main__":
app = QtGui.QApplication(argv)
main = MyWidget()
main.show()
exit(app.exec_())
At first, here is the minimal working example:
from sys import argv, exit
from PyQt4 import QtCore, QtGui
class widget2(QtGui.QWidget):
def __init__(self, args):
self.app = MainApp(args)
QtGui.QWidget.__init__(self)
self.setObjectName('I')
self._layout = QtGui.QVBoxLayout(self)
self.setLayout(self._layout)
self.pushButtoninWidget2 = QtGui.QPushButton(self)
self.pushButtoninWidget2.setObjectName("pushButtoninWidget2")
self.pushButtoninWidget2.setText('Click NOW!')
self._layout.addWidget(self.pushButtoninWidget2)
QtCore.QMetaObject.connectSlotsByName(self)
#QtCore.pyqtSlot()
def on_pushButtoninWidget2_clicked(self):
print("Hai")
class MainApp(QtGui.QApplication):
def __init__(self, args):
QtGui.QApplication.__init__(self, args)
if __name__ == "__main__":
main = widget2(argv)
main.show()
exit(main.app.exec_())
When you trying to connect slots by name, you must give proper names to the slots and then someone (moc, uic, or you by calling connectSlotsByName) must connect them. Proper name for such a slot is: "on_PyQtObjectName_PyQtSignalName".
Note, that, if I'd omitted #QtCore.pyqtSlot() in the example, slot would be executed once for every appropriate overload (twice in this case).
You DO need to call connectSlotsByNames directly, cause there is no moc, which do it for you when you use QT in C++, and you do not use uic and .ui file. If you want to connect slots implicitly (I'm always doing so, except slots, connected directly in .ui), you'd better use more pytonish syntaxe: button.clicked.connect(self._mySlot).
And take a look at https://riverbankcomputing.com/static/Docs/PyQt5/signals_slots.html#connecting-slots-by-name
You do not need to call connectSlotsByName(), just remove those lines.
In file2, calling QtCore.QMetaObject.connectSlotsByName(self.page) tries to do this:
QtCore.QObject.connect(self.pushButton, QtCore.SIGNAL('clicked()'), self.on_pushButton_clicked())
That will not work for you since self.on_pushBotton_clicked() slot is not defined.
I find it is easiest to create your own connections in PyQt... I recommend removing the calls to connectSlotsByName from your both classes... you do not need it.
Also, your wdiget1 class should set the name of it's pushButton (preferably something other then "pushButton" to avoid confusion with the button in widget2).
Thank you so much jcoon for your reply.. But after a very long time banging my head against the wall i found the solution..
The problem was..
self.obj=test_reuse_stacked1.widget2()
self.obj.createWidget2(self)
instead of obj..
Here is #MarkVisser's QT4 code updated to QT5:
from sys import argv, exit
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QApplication, QWidget
class MyWidget(QWidget):
def __init__(self, parent=None):
super(MyWidget, self).__init__(parent)
self._layout = QtWidgets.QVBoxLayout()
self.setLayout(self._layout)
self._button = QtWidgets.QPushButton()
self._button.setText('Click NOW!')
self._layout.addWidget(self._button)
self._button.clicked.connect(self._print_message)
#QtCore.pyqtSlot()
def _print_message(self):
print("Hai")
if __name__ == "__main__":
app = QApplication(argv)
main = MyWidget()
main.show()
exit(app.exec_())
Another minimal working example with Qt for Python aka PySide2/6.
Key ingredients:
widget to connect MUST have .setObjectName
function to connect MUST be decorated with #QtCore.Slot()
both objects (function AND widget) MUST be members of passed object (self here)
from PySide2 import QtCore, QtWidgets
# or from PySide6 import QtCore, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self):
super(Widget, self).__init__()
layout = QtWidgets.QVBoxLayout(self)
self.button = QtWidgets.QPushButton(self)
self.button.setObjectName('button')
self.button.setText('Click Me!')
layout.addWidget(self.button)
QtCore.QMetaObject.connectSlotsByName(self)
#QtCore.Slot()
def on_button_clicked(self):
print(f'Hai from {self.sender()}')
if __name__ == '__main__':
app = QtWidgets.QApplication([])
main = Widget()
main.show()
app.exec_()
I couldn't get mit any smaller really 🤔