So my problem is that instead of manually writing a ton of code for a bunch of buttons, I want to create a class for a QPushButton and then change so many variables upon calling that class to create my individual buttons.
My problem is that my button does not seem to be clickable despite calling the clicked.connect function and having no errors upon running the code. Here are the relevant parts of the button class:
class Button(QtGui.QPushButton):
def __init__(self, parent):
super(Button, self).__init__(parent)
self.setAcceptDrops(True)
self.setGeometry(QtCore.QRect(90, 90, 61, 51))
self.setText("Change Me!")
def retranslateUi(self, Form):
self.clicked.connect(self.printSomething)
def printSomething(self):
print "Hello"
Here is how I call the button class:
class MyWindow(QtGui.QWidget):
def __init__(self):
super(MyWindow,self).__init__()
self.btn = Button(self)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.btn)
self.setLayout(layout)
You should perform the connection to the clicked signal on the __init__ method:
from PyQt4 import QtGui,QtCore
class Button(QtGui.QPushButton):
def __init__(self, parent):
super(Button, self).__init__(parent)
self.setAcceptDrops(True)
self.setGeometry(QtCore.QRect(90, 90, 61, 51))
self.setText("Change Me!")
self.clicked.connect(self.printSomething) #connect here!
#no need for retranslateUi in your code example
def printSomething(self):
print "Hello"
class MyWindow(QtGui.QWidget):
def __init__(self):
super(MyWindow,self).__init__()
self.btn = Button(self)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.btn)
self.setLayout(layout)
app = QtGui.QApplication([])
w = MyWindow()
w.show()
app.exec_()
You can run it and will see the Hello printed on the console every time you click the button.
The retranslateUi method is for i18n. You can check here.
Related
I've been trying to make this work, but it just doesn't. I get no errors, just plain noncompliance. It just does not want to add the QListWidget items, or change the QLabel.
I made a MainWindowClass. It it's main widget I have a layout and a button. In it's dockwidget I have a QListWidget.
I made a signal from the button and connected it to a slot in the dockwidget's list.
The connection is there. When I press the button, the method in the dockwidgetcontents class is running.
But it does not add the items to the listwidget. And does not produce any errors.
Here is the code:
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class AppClass:
def __init__(self):
super().__init__()
app = QApplication(sys.argv)
window = MainWindowClass()
window.show()
sys.exit(app.exec_())
class MainWindowClass(QMainWindow):
def __init__(self):
super().__init__()
self.init_UI()
self.TheDockWidget()
def init_UI(self):
self.setGeometry(100, 100, 400, 200)
self.setWindowTitle("Test App")
self.setCentralWidget(MainWidgetClass())
def TheDockWidget(self):
self.dockWidget = QDockWidget('Status:')
self.dockWidget.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable)
self.addDockWidget(Qt.RightDockWidgetArea, self.dockWidget)
self.dockWidget.setSizePolicy(QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum))
self.dockWidget.setWidget(DockWidgetContents())
class DockWidgetContents(QWidget):
def __init__(self):
super().__init__()
self.init_UI()
def init_UI(self):
layout = QVBoxLayout()
self.setLayout(layout)
self.button3 = QPushButton()
self.button3.setText("button3")
layout.addWidget(self.button3)
self.listwidget = QListWidget()
layout.addWidget(self.listwidget)
self.listwidget.addItem("Ready.")
self.label = QLabel("initial")
layout.addWidget(self.label)
#pyqtSlot(str, str, int)
def _add_item(self, strA, strB, int1):
self.label.setText("yes") # why does this not work??
self.listwidget.addItem(strA) # why does this not work??
self.listwidget.addItem(strB) # why does this not work??
self.listwidget.addItem(str(int1)) # why does this not work??
print(strA, strB, int1) # but this works fine.
class MainWidgetClass(QWidget):
def __init__(self):
super().__init__()
self.init_UI()
def init_UI(self):
mainLayout = QGridLayout()
self.setLayout(mainLayout)
mainLayout.addWidget(TopLeftWidgetClass(), 0, 0)
class TopLeftWidgetClass(QWidget):
signal = pyqtSignal(str, str, int)
def __init__(self):
super().__init__()
self.init_UI()
def init_UI(self):
layout = QHBoxLayout()
self.setLayout(layout)
self.button1 = QPushButton("button1")
layout.addWidget(self.button1)
self.button1.clicked.connect(self.start)
def start(self):
otherClass = DockWidgetContents()
self.signal.connect(otherClass._add_item)
self.signal.emit("one", "two", 3)
if __name__ == '__main__':
AppClass()
I also read all the suggested questions when I typed my question into the form, but I must be not understanding something vital or prerequisite to this.
While I find all your answers to other questions extremely valuable, I'd appreciate if the answer would point me to the reference that I'm missing, so I can understand the problem, rather than just copy/paste the fixed code.
The main problem is that you're trying to connect to a new instance of DockWidgetContents, which gets also immediately deleted as soon as start() returns.
That new instance is obviously useless, as one already exists, but you have no direct ways to get it, because you create all classes "on the fly" when you add widgets to layouts and parents.
Remember that, while creating "instances on the fly" is not technically a problem, it doesn't allow you to keep references to those instances.
You have to create appropriate references to widgets and correctly connect them.
Here you can see the modifications required:
class MainWindowClass(QMainWindow):
def __init__(self):
super().__init__()
self.init_UI()
self.TheDockWidget()
self.mainWidget.topLeftWidget.signal.connect(
self.dockWidgetContents._add_item)
def init_UI(self):
self.setGeometry(100, 100, 400, 200)
self.setWindowTitle("Test App")
self.mainWidget = MainWidgetClass()
self.setCentralWidget(self.mainWidget)
def TheDockWidget(self):
self.dockWidget = QDockWidget('Status:')
self.dockWidget.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable)
self.addDockWidget(Qt.RightDockWidgetArea, self.dockWidget)
self.dockWidget.setSizePolicy(QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum))
self.dockWidgetContents = DockWidgetContents()
self.dockWidget.setWidget(self.dockWidgetContents)
class MainWidgetClass(QWidget):
def __init__(self):
super().__init__()
self.init_UI()
def init_UI(self):
mainLayout = QGridLayout()
self.setLayout(mainLayout)
self.topLeftWidget = TopLeftWidgetClass()
mainLayout.addWidget(self.topLeftWidget, 0, 0)
I create a main frame and a pushbutton ,
and let button clicked to open a dialog , but the dialog always on top ,
I try to used setWindowsFlag but not work.
from PySide2.QtWidgets import QApplication,QMainWindow,QTabWidget,QWidget
from PySide2.QtWidgets import QMessageBox,QFileDialog,QErrorMessage
from PySide2 import QtCore, QtGui, QtWidgets
class UI_Test20(object):
def setupUi(self, Test202):
Test202.setObjectName("Test202")
Test202.resize(100,100)
self.centralwidget = QtWidgets.QWidget(Test202)
self.centralwidget.setObjectName("centralwidget")
self.pb = QtWidgets.QPushButton(self.centralwidget)
self.pb.setText('push button!')
Test202.setCentralWidget(self.centralwidget)
self.pb.clicked.connect(self.btnClicked)
self.retranslateUi(Test202)
QtCore.QMetaObject.connectSlotsByName(Test202)
def retranslateUi(self, Test202):
Test202.setWindowTitle(QtWidgets.QApplication.translate("Test202", "MainWindow", None, -1))
def btnClicked(self):
ui = Ui_Dialog1(self)
ui.show()
class Test20(QMainWindow, UI_Test20) :
def __init__(self, parent):
super(Test20, self).__init__(parent)
self.setupUi(self)
and the dialog code
class Ui_Dialog1(QtWidgets.QDialog):
def __init__(self, parent=None):
super(Ui_Dialog1, self).__init__(parent)
self.p = parent
self.setupUi(self)
def setupUi(self, Dialog1):
Dialog1.setObjectName("Dialog1")
Dialog1.resize(333, 173)
main
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
ui = Test20(None)
ui.show()
sys.exit(app.exec_())
The problem is caused by the fact that the main window is the parent of the QDialog, so it must be removed, but if that is done, the garbage collector will delete it, so the QDialog member of the class must be made:
def btnClicked(self):
self.ui = Ui_Dialog1()
self.ui.show()
Plus: the correct thing is not to modify the design so I move the connection and the slot associated with the clicked button pb and if we want it to close when the window closes we overwrite the closeEvent() method:
class Test20(QMainWindow, UI_Test20):
def __init__(self, parent):
super(Test20, self).__init__(parent)
self.setupUi(self)
self.pb.clicked.connect(self.btnClicked)
def btnClicked(self):
self.ui = Ui_Dialog1()
self.ui.show()
def closeEvent(self, *args, **kwargs):
self.ui.close()
QMainWindow.closeEvent(self, *args, **kwargs)
I've written 3 separate widgets and each one contains a'closeEvent'. However the close events are NOT triggered when the widgets are placed as tabs inside the MainWindow. How can I emit a signal to properly fire the 'closeEvent' for each tab when the MainWindow closes?
I've done my best to simplify the code to focus on the main goal. I'd like to emit the close event for each tab so in the future when I add more tabs it will be more flexible and each tool will still be self contained.
import sys
from PySide import QtGui, QtCore
class TabA(QtGui.QWidget):
def __init__(self, parent=None):
super(TabA, self).__init__(parent)
self.resize(300, 300)
self.setWindowTitle('App A')
self.btn = QtGui.QPushButton("Button A")
grid = QtGui.QGridLayout()
grid.addWidget(self.btn)
self.setLayout(grid)
def closeEvent(self, event):
print "closing Tab A"
event.accept()
class TabB(QtGui.QWidget):
def __init__(self, parent=None):
super(TabB, self).__init__(parent)
self.resize(300, 300)
self.setWindowTitle('App A')
self.btn = QtGui.QPushButton("Button A")
grid = QtGui.QGridLayout()
grid.addWidget(self.btn)
self.setLayout(grid)
def closeEvent(self, event):
print "closing Tab A"
event.accept()
class TabC(QtGui.QWidget):
def __init__(self, parent=None):
super(TabC, self).__init__(parent)
self.resize(300, 300)
self.setWindowTitle('App A')
self.btn = QtGui.QPushButton("Button A")
grid = QtGui.QGridLayout()
grid.addWidget(self.btn)
self.setLayout(grid)
def closeEvent(self, event):
print "closing Tab A"
event.accept()
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.initUI()
def initUI(self):
self.resize(300, 300)
self.setWindowTitle('Tabs')
# Tabs
main_tabWidget = QtGui.QTabWidget()
main_tabWidget.addTab(TabA(), "TabA")
main_tabWidget.addTab(TabB(), "TabB")
main_tabWidget.addTab(TabC(), "TabC")
vbox = QtGui.QVBoxLayout()
vbox.addWidget(main_tabWidget)
vbox.setContentsMargins(0, 0, 0, 0)
main_widget = QtGui.QWidget()
main_widget.setLayout(vbox)
self.setCentralWidget(main_widget)
def main():
app = QtGui.QApplication(sys.argv)
ex = MainWindow()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
The closeEvent method is only called for a top-level window when a close request is received, so it is not suitable for your use-case. However, there are several signals that are emitted while the application is shutting down that you can connect to.
So for instance you could do this:
class TabA(QtGui.QWidget):
def __init__(self, parent=None):
super(TabA, self).__init__(parent)
...
QtGui.qApp.aboutToQuit.connect(self.handleClose)
def handleClose(self):
print "closing Tab A"
This signal is emitted just before the event-loop exits, and there's also a QtGui.qApp.lastWindowClosed signal which is emitted just after the main window closes.
(PS: don't be tempted to use __del__ for this purpose, because the exact behaviour during shutdown is strictly undefined in PyQt, and can change between versions).
I have a class BooleanButton contains extra boolean flag to toggle every click. After each click, I want it to emit a signal and the slot will receive the boolean flag. I just write the following, but of course, it won't work.
class BooleanButton(QPushButton):
def __init__(self, name):
QPushButton.__init__(self, name)
self.bool = False
def clicked(self, bool):
self.bool = not self.bool
self.emit(self.bool)
After creating the object, it connects to a slot. When I click this button, a swapping true-false signal will send to the slot.
bool_btn.isclicked[bool].connect(widget.func)
Thanks.
First, don't call a method clicked, that will hide the buttons clicked signal.
If you want to define a new signal, you need to do so using QtCore.pyqtSignal, then you can connect the clicked singal to a slot that will in turn emit your custom signal. Example:
class BooleanButton(QPushButton):
isclicked = pyqtSignal(bool)
def __init__(self, name):
QPushButton.__init__(self, name)
self.bool = False
self.clicked.connect(self.on_clicked)
def on_clicked(self, bool):
self.bool = not self.bool
self.isclicked.emit(self.bool)
As three_pineapples said, QPushButton comes with this feature built-in. Here's a simple example illustrating this behaviour.
from PyQt4 import QtGui, QtCore
class MyWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(MyWidget, self).__init__(parent)
self.button = QtGui.QPushButton("Click me", self)
self.button.setCheckable(True)
self.lineEdit = QtGui.QLineEdit(self)
self.button.clicked.connect(self.onClicked)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.button)
layout.addWidget(self.lineEdit)
def onClicked(self, checked):
if checked:
self.lineEdit.setText("Button checked")
else:
self.lineEdit.setText("Button unchecked")
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
widget = MyWidget()
widget.show()
sys.exit(app.exec_())
So your BooleanButton is actually just a QPushButton.
I want to get a string from the main window to use in a window triggered with a click. I know how to do it by putting all statements into a single class, but now I'm trying to do the same thing with one class per window. Here is the code:
import sys
from PyQt4 import QtGui
class Window(QtGui.QWidget):
def __init__(self, parent=None):
super().__init__()
self.initUI()
def initUI(self):
self.value = QtGui.QLineEdit('23')
self.button = QtGui.QPushButton('Open Dialog')
self.button.clicked.connect(self.openDialog)
vbox = QtGui.QVBoxLayout()
vbox.addWidget(self.value)
vbox.addWidget(self.button)
self.setLayout(vbox)
def openDialog(self):
self.entry = self.value.text()
print(self.entry)
Dialog().exec_()
class Dialog(QtGui.QDialog):
def __init__(self, parent=Window):
super().__init__()
win = Window()
self.text = win.entry
self.label = QtGui.QLabel(self.text)
hbox = QtGui.QHBoxLayout()
hbox.addWidget(self.label)
self.setLayout(hbox)
def main():
app = QtGui.QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
But I'm getting the error "AttributeError: 'Window' object has no attribute 'entry'" and I don't know any other way to try fix it. Can someone help me with it?
Create an instance of Dialog in the openDialog method, so that you can access its attributes directly. That way, the two classes can operate more independently, and you won't need to access the Window class from within the Dialog class:
def openDialog(self):
dialog = Dialog(self)
dialog.label.setText(self.value.text())
dialog.exec_()
print(dialog.label.text())
class Dialog(QtGui.QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.label = QtGui.QLabel(self)
hbox = QtGui.QHBoxLayout()
hbox.addWidget(self.label)
self.setLayout(hbox)
Here
win = Window()
self.text = win.entry
you declare a new window and accesing its entry field but on your window class
class Window(QtGui.QWidget):
def __init__(self, parent=None):
super().__init__()
self.initUI()
the entry field is not constructed.
So
Either you want to create a new window, so you have to put the self.entry on the constructor
You want to use the existing window an access its entry after calling openDialog
Edit:
Maybe here
class Dialog(QtGui.QDialog):
def __init__(self, parent=Window):
super().__init__()
you are initializing the class wrong. The parent constructor should be called with parent=Window too. Then from inside the dialog you could reference the window by doing self.parent