I am making a GUI with PyQt, and I am having issues with my MainWindow class. The window doesn't show widgets that I define in other classes, or it will show a small portion of the widgets in the top left corner, then cut off the rest of the widget.
Can someone please help me with this issue?
Here is some example code showing my issue.
import sys
from PyQt4 import QtGui, QtCore
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent=parent)
self.resize(300, 400)
self.centralWidget = QtGui.QWidget(self)
self.hbox = QtGui.QHBoxLayout(self.centralWidget)
self.setLayout(self.hbox)
names = ['button1', 'button2', 'button3']
testButtons = buttonFactory(names, parent=self)
self.hbox.addWidget(testButtons)
class buttonFactory(QtGui.QWidget):
def __init__(self, names, parent=None):
super(buttonFactory, self).__init__(parent=parent)
self.vbox = QtGui.QVBoxLayout()
self.setLayout(self.vbox)
for name in names:
btn = QtGui.QPushButton(name)
self.vbox.addWidget(btn)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
gui = MainWindow()
gui.show()
app.exec_()
A QMainWindow has a central widget that is a container in which you should add your widgets. It has its own layout. The layout of the QMainWindow is for toolbars and such. The centralWidget must be set with the setCentralWidget method. It isn't enough to just call it self.centralWidget
Use the following three lines instead.
self.setCentralWidget(QtGui.QWidget(self))
self.hbox = QtGui.QHBoxLayout()
self.centralWidget().setLayout(self.hbox)
I'm having a problem where a simple QTreeWidget drag and drop of items causes another item to disappear from the tree. This is on Mac Monterey 12.4, using Python 2.7 and PySide2. I swear that it had been previously working on a Windows platform, although it's been a while since I've tested that. I am also having problems with any widgets that had been added to the item getting lost when it re-creates the item in it's new place, so bonus points if anyone knows how to prevent that as well and can show a simple example.
(I'm working in Maya, so there is some code for displaying the UI there, and I haven't tested the section that would be more generic for use outside of Maya.)
Thanks so much for any help!...
import sys
from PySide2 import QtCore, QtWidgets
def main():
try:
app = QtWidgets.QApplication(sys.argv)
ui = TreeUI()
ui.show()
app.exec_()
except RuntimeError:
from maya import OpenMayaUI as omui
try:
import shiboken2 as shiboken
except ImportError:
import shiboken
pointer = omui.MQtUtil.mainWindow()
win = shiboken.wrapInstance(long(pointer), QtWidgets.QWidget)
ui = TreeUI(parent=win)
ui.show()
class Tree(QtWidgets.QTreeWidget):
def __init__(self, parent=None):
super(Tree, self).__init__(parent)
self.setHeaderLabels(('name', 'widget'))
self.setSelectionMode(self.SingleSelection)
self.setDragEnabled(True)
self.setDropIndicatorShown(True)
self.setDragDropMode(self.InternalMove)
class TreeUI(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(TreeUI, self).__init__(parent)
widget = QtWidgets.QWidget(self)
self.setCentralWidget(widget)
tree = Tree()
for x in range(0, 6):
item = QtWidgets.QTreeWidgetItem(tree, ('item{}'.format(x), None))
item.setFlags(item.flags() & ~QtCore.Qt.ItemIsDropEnabled)
button = QtWidgets.QPushButton('Button{}'.format(x))
tree.setItemWidget(item, 1, button)
layout = QtWidgets.QVBoxLayout(widget)
layout.addWidget(tree)
main()
Ah hah! I got it working, thanks to #musicamante, I was able to get it working. Adding in a custom dropEvent that calls the dropMimeData function, and then accepts the event, seems to fix it up:
class Tree(QtWidgets.QTreeWidget):
def __init__(self, parent=None):
super(Tree, self).__init__(parent)
self.setHeaderLabels(('name', 'widget'))
self.setSelectionMode(self.SingleSelection)
self.setDragEnabled(True)
self.setDropIndicatorShown(True)
self.setDragDropMode(self.InternalMove)
def dropEvent(self, event):
index = self.indexAt(event.pos())
parent = index.parent()
self.model().dropMimeData(
event.mimeData(),
event.dropAction(),
index.row(),
index.column(),
parent
)
event.accept()
It still has the problem of the associated widgets being lost, but I can post that as a separate issue.
Now I use Pyside2 to create a UI, but the style of the button is very old, just like winxp. I want it to be newer, but I don't know how to do it, do anyone know how to do?
now ui
what I want
My code is just like that:
class MainWindow(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
self.open_directory_button = QPushButton("打开文件夹")
self.open_directory_button.clicked.connect(self.open_directory_button_clicked)
self.path_layout = QHBoxLayout()
self.path_layout.addWidget(self.path_edit)
self.path_layout.addWidget(self.open_directory_button)
self.main_layout = QVBoxLayout()
self.main_layout.addLayout(self.path_layout)
self.frame = QWidget(self)
self.frame.setLayout(self.main_layout)
self.setCentralWidget(self.frame)
As I see the style of the second image is the "fusion" so a possible solution is:
import sys
from PySide2 import QtWidgets
app = QtWidgets.QApplication(sys.argv)
app.setStyle("fusion") # <----
# ...
You need to use the setStyleSheet, see this:
open_directory_button = QtWidgets.QPushButton()
open_directory_button.setStyleSheet("QPushButton:pressed{image:url(C:\image.png); border:none} QPushButton:hover{image:url(C:\image_hover.png); border:none}")
I'm trying to make a desktop application in pyqt5 that will stay on top of all windows. I've been looking around online and they all say that the solution is to set the window flags using the setWindowFlags(Qt.WindowStaysOnTopHint) method, but this isn't working for me. Is there some other way I can do this?
I'm on Windows 10 and using Python 3.6 + pyqt5 version 5.9.2. My code is as follows:
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class Main(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowFlags(Qt.WindowStaysOnTopHint)
self.initUI()
self.show()
def initUI(self):
self.alertWidget = AlertWidget()
self.setCentralWidget(self.alertWidget)
class AlertWidget(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
grid = QGridLayout()
self.setLayout(grid)
grid.setAlignment(Qt.AlignTop)
self.alertTextBox = QTextEdit()
grid.addWidget(self.alertTextBox, 0, 0)
if __name__ == '__main__':
app = QApplication(sys.argv)
main = Main()
sys.exit(app.exec_())
Assuming the rest of your code is good, change the following line of code:
self.setWindowFlags(Qt.WindowStaysOnTopHint)
to the following line of code:
self.setWindowFlags(QtCore.Qt.Window | QtCore.Qt.CustomizeWindowHint | Qt.WindowStaysOnTopHint)
Link to an answer explaining why the code change above is required for the Qt.WindowStaysOnTop flag to work.
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 🤔