PyQt5: Creation of Windows with Classes [duplicate] - python

This question already has answers here:
Understanding Python super() with __init__() methods [duplicate]
(7 answers)
Closed 4 years ago.
I have a PyQt5 application built that looks like so (I know I have a lot of imports, I am learning so I want complete freedom at the moment):
import sys
from PyQt5.QtGui import *
from PyQt5.QWidgets import *
from PyQt5.QtCore import *
class Menu(QMainWindow):
def __init__(self)
super().__init__()
#create bar
bar = self.menuBar()
#create bar menus
file = bar.addMenu("File")
about = bar.addMenu("About")
#create actions
quit_action = QAction("&Quit", self)
quit_action.setShortcut('Ctrl+Q')
about_action = QAction("&About...", self)
#add actions
file.addAction(quit_action)
about.addAction(about_action)
#what to do with actions
quit_action.triggered.connect(self.quit_func)
about_action.triggered.connect(self.about_func)
#window properties
self.setWindowTitle("Hello World")
self.resize(600, 400)
self.show()
def quit_func(self):
sys.exit()
def about_func(self):
pass
class About(QWidget):
def __init__(self):
super().__init__(parent)
#widgets
self.l1 = QLabel('Hello World')
self.l1.setAlignment(Qt.AlignCenter)
self.l2 = QLabel('Description of the Application')
self.l2.setAlignment(Qt.AlignCenter)
#horiz box
h_box = QHBoxLayout()
h_box.addStretch()
h_box.addWidget(self.l2)
h_box.addStretch()
#vert box
v_box = QVBoxLayout()
v_box.addWidget(self.l1)
v_box.addLayout(h_box)
v_box.addStretch()
self.setLayout(v_box)
#window properties
self.setWindowTitle("About Hello World")
self.setFixedSize(250,150)
self.show()
if not QApplication.instance()
app = QApplication(sys.argv)
else:
app = QApplication.instance()
main = Menu()
main.show()
sys.exit(app.exec())
I want the about_func() function to call to the About() class, so I can open a window separate from my Main Window created by Menu() class.
This code is throwing the error:
TypeError: QMainWindow(parent: QWidget = None, flags: Union[Qt.WindowFlags, Qt.WindowType] = Qt.WindowFlags()): argument 1 has unexpected type 'sip.wrappertype'
in reference to the super().__init__() in line 9.
How could I implement this in a working fashion? Feel free to criticize any aspect of my code.
(Edited to clarify question)

From your code it's not very clear if you are using Python 2 or 3, anyway, the basic syntax of super is:
super(yourClass, instance).method(args)
So, in your case they are both wrong :-) The first one should be:
class Menu(QMainWindow):
def __init__(self, parent=None):
super(Menu, self).__init__(parent)
Also, from Python3 the arguments of super() can be omitted, so the second example could be:
class About(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
Read carefully the Built-in Functions. I know it's a long page, but it contains some of the fundamentals of Python, and studying/understanding them is almost mandatory.

Related

Accessing parent attribute from inside child window

My main window has a variable self.x setup in the INIT. Then I need to access this value from the popup password box that is created. This is just a test script to get my point accross. I'm assuming it's something with the inheritance stuff, all that is still a little foreign to me.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
from PyQt5 import QtWidgets
# Created by MyWindow
class LoginDlg(QtWidgets.QDialog):
def __init__(self):
super(LoginDlg, self).__init__()
self.password = QtWidgets.QLineEdit()
# THIS IS THE LINE I NEED IT TO PULL X FROM PARENT
self.password.setText(self.x)
self.button_box = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel)
self.button_box.accepted.connect(self.accept)
self.button_box.rejected.connect(self.reject)
layout = QtWidgets.QFormLayout()
layout.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow)
layout.addRow('Password', self.password)
layout.addWidget(self.button_box)
self.setLayout(layout)
self.setWindowTitle("Login")
self.setMinimumWidth(350)
# Main parent window
class MyWindow(QtWidgets.QWidget):
def __init__(self):
super(MyWindow, self).__init__()
self.edit = QtWidgets.QLineEdit()
button = QtWidgets.QPushButton("Get input from dialog")
button.clicked.connect(self.get_login)
layout = QtWidgets.QHBoxLayout()
layout.addWidget(self.edit)
layout.addWidget(button)
self.setLayout(layout)
self.x = "PASS THIS STRING"
def get_login(self):
login = LoginDlg()
if login.exec_():
self.edit.setText(login.password.text())
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec_())
Normally I would just pass this data through the constructor, but let's say I have a lot of data that I don't want to pass back and forth, is there a way to access parent attributes another way?
Don't get too complicated and just pass the value of that variable through the constructor:
class LoginDlg(QtWidgets.QDialog):
def __init__(self, x):
super(LoginDlg, self).__init__()
self.password = QtWidgets.QLineEdit()
self.password.setText(x)
# ...
class MyWindow(QtWidgets.QWidget):
# ...
def get_login(self):
login = LoginDlg(self.x)
if login.exec_():
self.edit.setText(login.password.text())
Another similar option is to access "password" in get_login:
class LoginDlg(QtWidgets.QDialog):
def __init__(self):
super(LoginDlg, self).__init__()
self.password = QtWidgets.QLineEdit()
# self.password.setText(self.x)
self.button_box = QtWidgets.QDialogButtonBox(
QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel
)
# ...
class MyWindow(QtWidgets.QWidget):
# ...
def get_login(self):
login = LoginDlg()
login.password.setText(self.x)
if login.exec_():
self.edit.setText(login.password.text())
Note: my answer does not try to implement what the OP asks is to "access a class from another" (there is no relationship so using parent-child is incorrect) since that complicates the modularization because if a object could affect the other, in general. I think the OP has an XY problem as it asks how to implement a possible solution instead of the underlying problem which is "how to share information between classes"

PyQt5 insertPlainText from Another Class Python 3.8 [duplicate]

This question already has answers here:
Variable scopes in Python classes
(4 answers)
Closed 2 years ago.
Clicking button "OK" on "otherWindow" should cause the MainWindow's QTextEdit to insert text "WORKS!".
Problem is, it does execute print("Print Works"), but insertPlainText seems to do nothing when called from another function.
The def printText(self, message): function itself isn't broken, it works as it's supposed to, as you can verify by clicking on the "Message" button on Main Window.
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QTextEdit, QPushButton
class MainWindow(QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.myLayout = QVBoxLayout()
self.status = QTextEdit()
self.status.setStyleSheet("QTextEdit {min-width:500px;min-height:200px;}")
self.status.insertPlainText("test")
self.btnYes = QPushButton("other window")
self.btnPrint = QPushButton("Message")
self.btnYes.clicked.connect(self.showOtherWindow)
self.btnPrint.clicked.connect(self.btnPrintClick)
self.myLayout.addWidget(self.btnPrint)
self.myLayout.addWidget(self.btnYes)
self.myLayout.addWidget(self.status)
self.setLayout(self.myLayout)
def setMainText(self, message):
self.status.insertPlainText("test")
def showOtherWindow(self):
self.otherWindow = otherWindow()
self.otherWindow.show()
def btnPrintClick(self):
self.printText("button clicked")
def printText(self, message):
self.status.insertPlainText("\n" + message)
print("Print Works")
class otherWindow(QWidget):
def __init__(self):
super(otherWindow, self).__init__()
self.button = QPushButton("OK")
self.layout2 = QVBoxLayout()
self.button.clicked.connect(self.btnClick)
self.layout2.addWidget(self.button)
self.setLayout(self.layout2)
self.setFixedSize(200,150)
def btnClick(self):
MainWindow().printText("WORKS!")
self.close()
if __name__ == "__main__":
app = QApplication(sys.argv)
mw = MainWindow()
mw.show()
sys.exit(app.exec_())
It doesn't work because you're setting the text on a new window (which is closed immediately).
def btnClick(self):
MainWindow().printText("WORKS!")
When you call MainWindow(), you're actually creating a NEW instance of MainWindow, and the text is actually updated for that window, but you can't see it as it is immediately garbage collected and deleted right after the function returns.
You need to access the existing instance, or find a way to communicate with it (usually using signals).
In the following example, I'm adding a reference to the main window to the OtherWindow constructor, and then access it's method afterwards:
class MainWindow(QWidget):
# ...
def showOtherWindow(self):
self.otherWindow = OtherWindow(self)
self.otherWindow.show()
class OtherWindow(QWidget):
def __init__(self, mainWindow=None):
super(OtherWindow, self).__init__()
self.mainWindow = mainWindow
# ...
def btnClick(self):
if self.mainWindow:
self.mainWindow.printText("WORKS!")
self.close()
Note: I capitalized the OtherWindow class name, lower case names should be only used for variables and attributes.

I want to know how to use pyqt5 to load the created widget at once

Sorry. I will modify the contents. I would like to load a widget inside def test by pressing Qbutton. Can not you use QStackedWidget to load the widget's configured functions? I've compiled the class and called it, but only a = QLineEdit ('Qline', self). I wonder what should be done to switch widgets.
You can also create a table like html using pyqt5.
import sys
from PyQt5.QtWidgets import *
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.stacked = QStackedWidget(self)
self.FirstpUI()
def FirstpUI(self):
self.btn1 = QPushButton('test1', self)
self.btn1.move(50,50)
self.btn1.clicked.connect(self.btn1_click)
def test(self):
a = QLineEdit('Qline', self)
b = QLineEdit('Qline2', self)
c = QPushButton('button', self)
a.move(0, 0)
b.move(100, 0)
c.move(50,50)
c.clicked.connect(self.btn2_click)
def btn1_click(self):
self.btn1.deleteLater()
self.stacked.addWidget(self.test())
self.stacked.setCurrentIndex(self.stacked.currentIndex()+1)
def btn2_click(self):
QMessageBox.about(self,'hello','hello2')
if __name__ == "__main__":
app = QApplication(sys.argv)
fream = MainWindow()
fream.show()
app.exec_()
May be I don't know what you real want,because I know that little, I think You can use QtDesigner,it's very useful

Call a function from another class by clicking a button PyQt4

I have a checkbox and a run button. When the checkbox is checked, I want to run some functions by clicking the button. The problem is that the function is in another class outside the button's class. My example codes are as below.
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class Tab1Widget1(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.Tab1Widget1initUI()
def Tab1Widget1initUI(self):
self.setLayout(QGridLayout())
self.T1W1checkBox1 = QCheckBox('a', self)
self.layout().addWidget(self.T1W1checkBox1, 1, 0)
def run(self):
if self.T1W1checkBox1.isChecked() == True:
pass
class Tab1Layout(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.setLayout(QGridLayout())
self.group1 = Tab1Widget1(self)
self.layout().addWidget(self.group1, 0, 0)
btn = QPushButton('Run', self)
self.layout().addWidget(btn, 1, 0)
btn.clicked.connect(Tab1Widget1().run()) ##the problem is in this line.
class Page1(QTabWidget):
def __init__(self, parent=None):
super(Page1, self).__init__(parent)
self.tab1 = Tab1Layout()
self.addTab(self.tab1, "Tab1")
self.tab2 = QWidget()
self.tab3 = QWidget()
self.addTab(self.tab2, "Tab2")
self.addTab(self.tab3, "Tab3")
self.tab2_initUI()
self.tab3_initUI()
def tab2_initUI(self):
grid = QGridLayout()
self.tab2.setLayout(grid)
def tab3_initUI(self):
grid = QGridLayout()
self.tab3.setLayout(grid)
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setGeometry(300, 200, 600, 370)
self.startPage1()
def startPage1(self):
x = Page1(self)
self.setWindowTitle("Auto Benchmark")
self.setCentralWidget(x)
self.show()
def main():
app = QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
As you can see, I want to run the "run" function in "Tab1Widget1" class. However, the button is in "Tab1Layout" class.
When I run the codes, it returns to me "TypeError: connect() slot argument should be a callable or a signal, not 'NoneType'"
If anyone knows how to solve this, pls let me know. Appreciated!
There is no problem in connecting any callable to a button click regardless of what object it is in. But your code has two specific problems. You write
btn.clicked.connect(Tab1Widget1().run())
The first problem here is that Tab1Widget1() is creating a new Tab1Widget1 but presumably you don't want that. You want to call run on the Tab1Widget1 you have already created and stored in self.group.
The second problem is that when you connect a signal you need to connect it to a callable: the method you want to call. Instead here you are calling the run method at connect time and trying to connect to the result of that call (which is None). So you are trying to connect the signal to None which will of course fail. You need to refer to the method without calling it: just remove the calling brackets.
Putting it together:
btn.clicked.connect(self.group1.run)
That seems to work.

PySide (or PyQt) signals and slots basics

Consider a simple example like this which links two sliders using signals and slots:
from PySide.QtCore import *
from PySide.QtGui import *
import sys
class MyMainWindow(QWidget):
def __init__(self):
QWidget.__init__(self, None)
vbox = QVBoxLayout()
sone = QSlider(Qt.Horizontal)
vbox.addWidget(sone)
stwo = QSlider(Qt.Horizontal)
vbox.addWidget(stwo)
sone.valueChanged.connect(stwo.setValue)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyMainWindow()
w.show()
sys.exit(app.exec_())
How would you change this so that the second slider moves in the opposite direction as the first? Slider one would be initialized with these values:
sone.setRange(0,99)
sone.setValue(0)
And slider two would be initialized with these values:
stwo.setRange(0,99)
stwo.setValue(99)
And then the value of stwo would be 99 - sone.sliderPosition.
How would you implement the signal and slot to make this work? I would appreciate a working example that builds on the simple example above.
Your example is a bit broken, because you forgot to set the parent of the layout, and also to save the slider widgets as member attributes to be accessed later... But to answer your question, its really as simple as just pointing your connection to your own function:
class MyMainWindow(QWidget):
def __init__(self):
QWidget.__init__(self, None)
vbox = QVBoxLayout(self)
self.sone = QSlider(Qt.Horizontal)
self.sone.setRange(0,99)
self.sone.setValue(0)
vbox.addWidget(self.sone)
self.stwo = QSlider(Qt.Horizontal)
self.stwo.setRange(0,99)
self.stwo.setValue(99)
vbox.addWidget(self.stwo)
self.sone.valueChanged.connect(self.sliderChanged)
def sliderChanged(self, val):
self.stwo.setValue(self.stwo.maximum() - val)
Note how sliderChanged() has the same signature as the original setValue() slot. Instead of connecting one widget directly to the other, you connect it to a custom method and then transform the value to what you want, and act how you want (setting a custom value on stwo)
You can connect signals to functions that do things. Your code isn't structured to do that easily and required refactoring, so you can do it the easy way:
stwo.setInvertedAppearance(True)
sone.valueChanged.connect(stwo.setValue)
Here's the way I did it. I added this class which reimplements setValue. (I got the idea from http://zetcode.com/tutorials/pyqt4/eventsandsignals/)
class MySlider(QSlider):
def __init__(self):
QSlider.__init__(self, Qt.Horizontal)
def setValue(self, int):
QSlider.setValue(self, 99-int)
Here's the complete code. Is this a good approach?
from PySide.QtCore import *
from PySide.QtGui import *
import sys
class MySlider(QSlider):
def __init__(self):
QSlider.__init__(self, Qt.Horizontal)
def setValue(self, int):
QSlider.setValue(self, 99-int)
class MyMainWindow(QWidget):
def __init__(self):
QWidget.__init__(self, None)
vbox = QVBoxLayout()
sone = QSlider(Qt.Horizontal)
sone.setRange(0,99)
sone.setValue(0)
vbox.addWidget(sone)
stwo = MySlider()
stwo.setRange(0,99)
stwo.setValue(0)
vbox.addWidget(stwo)
sone.valueChanged.connect(stwo.setValue)
self.setLayout(vbox)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyMainWindow()
w.show()
sys.exit(app.exec_())

Categories