I am quite new to programming GUIs with Python... as any experienced programmer will soon find out. The program I am working on is supposed to have a MainWindow with a nested QTab widget which in turn holds serveral seperate widgets. I have been trying for some time now to get the following piece of simplified code to work. Unfortunately I get the AttributeError: 'bool' object has no attribute 'label_00'.
import sys
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow, QAction, QWidget, QVBoxLayout, QTabWidget, QToolBar
from PyQt5.QtCore import Qt, QSize, pyqtSignal, pyqtSlot
#from PyQt5 import QtGui
#from LftQAFs import *
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.initUI()
def initUI(self):
self.setWindowTitle("Light")
self.setGeometry(100,100,750,120)
toolbar = QToolBar("Tool Bar")
toolbar.setIconSize(QSize(24,24))
self.addToolBar(toolbar)
button_loadLftQAF = QAction("Load Data", self)
toolbar.addAction(button_loadLftQAF)
button_loadLftQAF.triggered.connect(SubWidget.funcTest)
self.setCentralWidget(SubWidget())
self.show()
class SubWidget(QWidget):
signalFuncTest = pyqtSignal()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.layout_main = QVBoxLayout(self)
self.label_00 = QLabel("Label")
self.layout_main.addWidget(self.label_00)
self.setLayout(self.layout_main)
#pyqtSlot()
def funcTest(self):
print("works")
self.label_00.setText('Changed Label')
app = QApplication([])
window = MainWindow()
window.show()
app.exec()
I would be very happy to get a solution to this problem as well as a hint to what topic I should read up on to understand what I did wrong. Any structural advise is also highly appreciated...:)
Thanks, Max
SubWidget.funcTest is an instance method, which means you need to create an instance of SubWidget first, e.g. sub_widget = SubWidget(), and connect sub_widget.funcTest to button_loadLftQAF.triggered instead of SubWidget.funcTest, e.g.
class MainWindow(QMainWindow):
...
def initUI(self):
...
sub_widget = SubWidget(self)
button_loadLftQAF.triggered.connect(sub_widget.funcTest)
self.setCentralWidget(sub_widget)
...
Related
from PyQt6.QtWidgets import (
QMainWindow, QApplication, QDialog, QDialogButtonBox, QLabel, QTextEdit, QPushButton, QMessageBox, QMdiArea,
QTableWidgetItem, QStackedWidget
)
from PyQt6 import uic
import sys
class UI(QMainWindow):
def __init__(self):
super(UI, self).__init__()
uic.loadUi(r"C:\Users\csc\Documents\Rentour\front.ui", self)
self.show()
# define widgets
self.button = self.findChild(QPushButton, "signinButton")
self.signinButton.clicked.connect(self.OpenSignUp)
def OpenSignUp(self):
Sign_Up = Second()
widget.addWidget(Sign_Up)
widget.setCurrentIndex(widget.currentIndex()+1)
class Second(QMainWindow):
def __init__(self):
super(Second, self).__init__()
uic.loadUi(r"C:\Users\csc\Documents\Rentour\signpopup.ui", self)
# define widgets
self.button = self.findChild(QPushButton, "SubmitSignButton")
self.SubmitSignButton.clicked.connect(self.SignUpSave)
def SignUpSave(self):
email =self.EmailLine.text()
phoneno =self.PhonenoLine.text()
name =self.NameLine.text()
password = self.PasswordLine.text()
print(password)
app = QApplication(sys.argv)
mainwindow = UI()
widget = QStackedWidget()
widget.addWidget(mainwindow)
widget.show()
app.exec()
this is my code. Am trying to create a login/signup page. So when i click the signinButton, i want it to load the ui for the page which will have a bunch of line edits whose inputs im attempting to store in variables.
The ui files were made using qt designer and and i made this file from scratch. I also referred code with Hala(Youtuber). I Am trying to create a login/signup page. So when i click the signinButton, i want it to load the ui for the page which will have a bunch of line edits whose inputs im attempting to store in variables.
The problem is in your OpenSignUp function. The line widget.addWidget(Sign_Up) is not a valid command for a couple of reasons.
Also Sign_Up varable is holding a newly constructed QMainWindow, which are meant to be top level widgets and shouldn't be added to a layout.
It isn't totally clear what you are trying to achieve, but I am going to assume that you are trying to launch a secondary sign in window, In which case you want to use the open() method to launch the new window.
For example:
def OpenSignUp(self):
self.Sign_Up = Second()
self.Sign_Up.open()
Since you are using a stacked widget it is also possible that your goal is to simply switch to a different widget in the same window. In which case your Second class should probably just be a standard QWidget, and not a QMainWindow. and your stacked widget should be set as the central widget of your UI class.
So that would probably look something like this:
from PyQt6.QtWidgets import (
QMainWindow, QApplication, QDialog, QDialogButtonBox, QLabel, QTextEdit, QPushButton, QMessageBox, QMdiArea,
QTableWidgetItem, QStackedWidget
)
from PyQt6 import uic
import sys
class UI(QMainWindow):
def __init__(self):
super(UI, self).__init__()
# uic.loadUi(r"C:\Users\csc\Documents\Rentour\front.ui", self)
self.widget = QStackedWidget()
self.widget.addWidget(mainwindow)
self.setCentralWidget(self.widget)
self.Sign_Up = Second()
self.widget.addWidget(self.Sign_Up)
# define widgets
self.button = self.findChild(QPushButton, "signinButton")
self.signinButton.clicked.connect(self.OpenSignUp)
def OpenSignUp(self):
self.widget.setCurrentIndex(widget.currentIndex()+1)
class Second(QWidget):
def __init__(self):
super(Second, self).__init__()
uic.loadUi(r"C:\Users\csc\Documents\Rentour\signpopup.ui", self)
# define widgets
self.button = self.findChild(QPushButton, "SubmitSignButton")
self.SubmitSignButton.clicked.connect(self.SignUpSave)
def SignUpSave(self):
email =self.EmailLine.text()
phoneno =self.PhonenoLine.text()
name =self.NameLine.text()
password = self.PasswordLine.text()
print(password)
app = QApplication(sys.argv)
mainwindow = UI()
mainwindow.show()
app.exec()
Except you will need to rearrange the .ui file for UI class so that it is applied on top of the stacked widget as well.
So my expected outcome is for the login.ui to show when the login button is clicked. My code reached the def gotologin function and the class LoginScreen , but it doesn't load the ui
import sys
from PyQt5.uic import loadUi
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QDialog, QApplication
class WelcomeScreen(QDialog):
def __init__(self):
super(WelcomeScreen, self).__init__()
loadUi('welcomescreen.ui', self)
self.login.clicked.connect(self.gotologin)
def gotologin(self):
login = LoginScreen()
widget.addWidget(login)
widget.setCurrentIndex(widget.currentIndex()+1)
class LoginScreen(QDialog):
def __init__(self):
super(LoginScreen, self).__init__()
loadUi('login.ui', self)
app = QApplication(sys.argv)
welcome = WelcomeScreen()
widget = QtWidgets.QStackedWidget()
widget.addWidget(welcome)
widget.setFixedHeight(800)
widget.setFixedWidth(1200)
window = WelcomeScreen()
window.show()
try:
sys.exit(app.exec_())
except:
print('Exiting')
Your logic is strange since you are creating 2 WelcomeScreen: One added to the QStackedWidget and the second as a window. Besides that, the QStackedWidget has never been shown. And as the icing on the cake you don't limit the scopes of the variables.
Considering the above, it is better to create a controller that implements the logic of switching widgets and limits scopes.
import sys
from functools import cached_property
from PyQt5.uic import loadUi
from PyQt5.QtWidgets import QApplication, QDialog, QStackedWidget
class WelcomeScreen(QDialog):
def __init__(self):
super(WelcomeScreen, self).__init__()
loadUi("welcomescreen.ui", self)
class LoginScreen(QDialog):
def __init__(self):
super(LoginScreen, self).__init__()
loadUi("login.ui", self)
class Controller:
def __init__(self):
self.stacked_widget.addWidget(self.welcome)
self.stacked_widget.addWidget(self.login)
self.welcome.login.clicked.connect(self.goto_login)
#cached_property
def stacked_widget(self):
return QStackedWidget()
#cached_property
def welcome(self):
return WelcomeScreen()
#cached_property
def login(self):
return LoginScreen()
def goto_login(self):
self.stacked_widget.setCurrentWidget(self.login)
def main(args):
app = QApplication(args)
controller = Controller()
controller.stacked_widget.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main(sys.argv)
And finally, do not silence the exceptions since their reason for being is to indicate that something is wrong. I prefer that when the program fails then it shouts at me that a silent error since the latter is the cause of many bugs.
What I can't do
I'm not able to send data back from a child to a parent window.
What I have
I've got a complex GUI with several windows sendÃng data to child windows. Each window represents a unique Python-script in the same directory. There was no need to explicitely specify parents and childs, as the communication was always unidirectional (parent to child). However, now I need to send back data from childs to parents and can't figure out how to do this as each window (i.e. each class) has its own file.
Example
Here's a minimal example showing the base of what I want to accomplish.
What it does: win01 opens win02 and win02 triggers func in win01.
# testfile01.py
import sys
from PyQt5.QtWidgets import *
import testfile02 as t02
class win01(QWidget):
def __init__(self, parent=None):
super(win01, self).__init__(parent)
self.win02 = t02.win02()
self.button = QPushButton("open win02", self)
self.button.move(100, 100)
self.button.clicked.connect(self.show_t02)
def initUI(self):
self.center
def show_t02(self):
self.win02.show()
def func(self):
print("yes!")
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = win01()
ex.show()
sys.exit(app.exec_())
##########################################################################
# testfile02.py
from PyQt5.QtWidgets import *
import testfile01 as t01
class win02(QWidget):
def __init__(self, parent=None):
super(win02, self).__init__(parent)
self.win01 = t01.win01()
self.button = QPushButton()
self.button.clicked.connect(self.win01.func)
def initUI(self):
self.center()
What I tried
Importing testfile01 in the second window always leads to the error:
RecursionError: maximum recursion depth exceeded.
Then, I tried the following approaches, but they didn't work either:
Not importing testfile01 in win02 and adjusting parent=None to different other objects
Importing testfile01 within the __init__ call of win02
Creating a signal in win02 to trigger func in win01
The Question
Is there a solution how to properly trigger func in win01 from win02?
Why are you getting RecursionError: maximum recursion depth exceeded?
You are getting it because you have a circular import that generates an infinite loop, in testfile01 you are importing the file testfile02, and in testfile02 you are importing testfile01, .... So that is a shows of a bad design.
Qt offers the mechanism of signals for objects to communicate information to other objects, and this has the advantage of non-dependence between classes that is a great long-term benefit (as for example that avoids circular import), so for that reason I think it is the most appropriate.
For this I will create the clicked signal in the class win02 that will be triggered by the clicked signal of the button, and make that clicked signal call the func:
testfile01.py
import sys
from PyQt5.QtWidgets import QWidget, QPushButton, QApplication
import testfile02 as t02
class win01(QWidget):
def __init__(self, parent=None):
super(win01, self).__init__(parent)
self.win02 = t02.win02()
self.win02.clicked.connect(self.func)
self.button = QPushButton("open win02", self)
self.button.move(100, 100)
self.button.clicked.connect(self.show_t02)
def show_t02(self):
self.win02.show()
def func(self):
print("yes!")
if __name__ == "__main__":
app = QApplication(sys.argv)
ex = win01()
ex.show()
sys.exit(app.exec_())
testfile02.py
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QWidget, QPushButton, QVBoxLayout
class win02(QWidget):
clicked = pyqtSignal()
def __init__(self, parent=None):
super(win02, self).__init__(parent)
self.button = QPushButton("call to func")
self.button.clicked.connect(self.clicked)
lay = QVBoxLayout(self)
lay.addWidget(self.button)
I recommend you read:
https://doc.qt.io/qt-5/signalsandslots.html
Both the Widgets are independent and have no link in between.
Set win01 parent of win02.
In class win01
Replace :
self.win01 = t02.win02()
#and
self.win02.show()
with:
self.win01 = t02.win02(self)
#and
self.win01.show()
and in class win02
Replace:
self.win02 = t01.win01()
#and
self.button.clicked.connect(self.win01.func)
with:
self.win02 = self.parent()
#and
self.button.clicked.connect(self.win02.func)
I'm trying to make a view and controller in PyQt where the view is emitting a custom signal when a button is clicked, and the controller has one of its methods connected to the emitted signal. It does not work, however. The respond method is not called when I click the button. Any idea what I did wrong ?
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import QPushButton, QVBoxLayout, QDialog, QApplication
class TestView(QDialog):
def __init__(self, parent=None):
super(TestView, self).__init__(parent)
self.button = QPushButton('Click')
layout = QVBoxLayout()
layout.addWidget(self.button)
self.setLayout(layout)
self.connect(self.button, SIGNAL('clicked()'), self.buttonClicked)
def buttonClicked(self):
self.emit(SIGNAL('request'))
class TestController(QObject):
def __init__(self, view):
self.view = view
self.connect(self.view, SIGNAL('request'), self.respond)
def respond(self):
print 'respond'
app = QApplication(sys.argv)
dialog = TestView()
controller = TestController(dialog)
dialog.show()
app.exec_()
Works for me - might be the version of Qt/PyQt you're using, but there are a couple things you can try:
Use a proper method syntax - so SIGNAL('request()') vs. SIGNAL('request')
Use new-style signal syntax
The style you are using is the old-style PyQt syntax and the new-style signal/slot definition is recommended:
import sys
from PyQt4.QtCore import QObject, pyqtSignal # really shouldn't import * here...QtCore library is quite large
from PyQt4.QtGui import QPushButton, QVBoxLayout, QDialog, QApplication
class TestView(QDialog):
request = pyqtSignal()
def __init__(self, parent=None):
super(TestView, self).__init__(parent)
self.button = QPushButton('Click')
layout = QVBoxLayout()
layout.addWidget(self.button)
self.setLayout(layout)
self.button.clicked.connect(self.buttonClicked)
def buttonClicked(self):
self.request.emit()
class TestController(QObject):
def __init__(self, view):
super(QObject, self).__init__()
self.view = view
self.view.request.connect(self.respond)
def respond(self):
print 'respond'
app = QApplication(sys.argv)
dialog = TestView()
controller = TestController(dialog)
dialog.show()
app.exec_()
Again tho, I would really, really discourage building your code this way...you are creating a lot of unnecessary work and duplication of objects when you don't need to.
so I'm creating a simple windows application with Python and PyQt4. I've designed my UI the way I want it in QtCreator and I've created the necessary .py file from the .ui file. When I try to actually open an instance of the window however I'm given the following error:
AttributeError: 'Window' object has no attribute 'setCentralWidget'
So I go back into the ui_mainwindow.py file and comment out the following line:
MainWindow.setCentralWidget(self.centralWidget)
Now when I run main.py it will generate an instance of the window but it loses its grid layout and the UI elements just sort of float there. Any idea what I'm doing wrong?
My main.py file:
import sys
from PyQt4.QtGui import QApplication
from window import Window
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
and my window.py file:
from PyQt4.QtCore import Qt, SIGNAL
from PyQt4.QtGui import *
from ui_mainwindow import Ui_MainWindow
class Window(QWidget, Ui_MainWindow):
def __init__(self, parent = None):
QWidget.__init__(self, parent)
self.setupUi(self)
You need to inherit from QMainWindow, not QWidget. setCentralWidget is a method of QMainWindow.
from PyQt4.QtCore import Qt, SIGNAL
from PyQt4.QtGui import *
from ui_mainwindow import Ui_MainWindow
class Window(QMainWindow, Ui_MainWindow):
def __init__(self, parent = None):
QMainWindow.__init__(self, parent)
# or better
# super(Window, self).__init__(parent)
self.setupUi(self)