Problem with moving window using custom title bar in PyQt5 - python

I'm trying to build a GUI with a custom title bar using along with setWindowFlags(FramelessWindowHint).
I have successfully created close/minimise buttons etc with their respective functions and they work perfectly (They are not included in my code below). However I haven't had the same success with making the window moveable using my custom title bar.
When I ran the code and attempted to drag the window, the cursor changed to the blue loading swirl then crashed. I also tried running it with debugger and when I tried to drag the window I got the error:
TypeError: unsupported operand type(s) for +: 'QPoint' and 'builtin_function_or_method'
Below is my a part of my code. (It may be a little messy as I cut it out of a larger project.)
Any help to solve my problem will be greatly appreciated, thank you.
from PyQt5 import QtCore, QtGui, QtWidgets, Qt
from PyQt5.QtCore import Qt
WINDOW_SIZE = 0
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(1200, 700)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
self.verticalLayout.setSpacing(0)
self.verticalLayout.setObjectName("verticalLayout")
self.top_bar = QtWidgets.QWidget(self.centralwidget)
self.top_bar.setMaximumSize(QtCore.QSize(16777215, 60))
self.top_bar.setStyleSheet("background-color: rgb(52, 56, 60)")
self.top_bar.setObjectName("top_bar")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.top_bar)
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_2.setSpacing(0)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.frame = QtWidgets.QFrame(self.top_bar)
self.frame.setMinimumSize(QtCore.QSize(80, 60))
self.frame.setMaximumSize(QtCore.QSize(80, 60))
self.frame.setStyleSheet("background-color:rgb(30, 34, 38);\n"
"border: 0px")
self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame.setObjectName("frame")
self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.frame)
self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_4.setSpacing(0)
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
self.horizontalLayout_2.addWidget(self.frame)
self.header_bar = QtWidgets.QFrame(self.top_bar)
self.header_bar.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.header_bar.setFrameShadow(QtWidgets.QFrame.Raised)
self.header_bar.setObjectName("header_bar")
self.horizontalLayout_2.addWidget(self.header_bar)
self.verticalLayout.addWidget(self.top_bar)
self.content = QtWidgets.QWidget(self.centralwidget)
self.content.setStyleSheet("")
self.content.setObjectName("content")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.content)
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout.setSpacing(0)
self.horizontalLayout.setObjectName("horizontalLayout")
self.verticalLayout.addWidget(self.content)
MainWindow.setCentralWidget(self.centralwidget)
self.header_bar.mouseMoveEvent = self.moveWindow
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
def mousePressEvent(self, event):
MainWindow.dragPos = event.globalPos()
def moveWindow(self, event):
if MainWindow.isMaximized():
MainWindow.showNormal()
else:
if event.buttons() == Qt.LeftButton:
MainWindow.move((MainWindow.pos() + event.globalPos() - MainWindow.dragPos))
MainWindow.dragPos = event.globalPos()
event.accept()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())

I marked the places I changed
from PyQt5 import QtCore, QtGui, QtWidgets, Qt
from PyQt5.QtCore import Qt
WINDOW_SIZE = 0
class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1200, 700)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setContentsMargins(0, 0, 0, 0)
        self.verticalLayout.setSpacing(0)
        self.verticalLayout.setObjectName("verticalLayout")
        self.top_bar = QtWidgets.QWidget(self.centralwidget)
        self.top_bar.setMaximumSize(QtCore.QSize(16777215, 60))
        self.top_bar.setStyleSheet("background-color: rgb(52, 56, 60)")
        self.top_bar.setObjectName("top_bar")
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.top_bar)
        self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout_2.setSpacing(0)
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.frame = QtWidgets.QFrame(self.top_bar)
        self.frame.setMinimumSize(QtCore.QSize(80, 60))
        self.frame.setMaximumSize(QtCore.QSize(80, 60))
        self.frame.setStyleSheet("background-color:rgb(30, 34, 38);\n"
                                                       "border: 0px")
        self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
        self.frame.setObjectName("frame")
        self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.frame)
        self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout_4.setSpacing(0)
        self.horizontalLayout_4.setObjectName("horizontalLayout_4")
        self.horizontalLayout_2.addWidget(self.frame)
        self.header_bar = QtWidgets.QFrame(self.top_bar)
        self.header_bar.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.header_bar.setFrameShadow(QtWidgets.QFrame.Raised)
        self.header_bar.setObjectName("header_bar")
        self.horizontalLayout_2.addWidget(self.header_bar)
        self.verticalLayout.addWidget(self.top_bar)
        self.content = QtWidgets.QWidget(self.centralwidget)
        self.content.setStyleSheet("")
        self.content.setObjectName("content")
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.content)
        self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout.setSpacing(0)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.verticalLayout.addWidget(self.content)
        MainWindow.setCentralWidget(self.centralwidget)
        
        self.header_bar.mouseMoveEvent = self.moveWindow
        #*************************************************************************************************************************************************************
        self.header_bar.mousePressEvent = self.mousePress
        #*************************************************************************************************************************************************************
        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
    #*************************************************************************************************************************************************************
    def mousePress(self, event):
        MainWindow.dragPos = MainWindow.pos()
        self.mouse_original_pos = MainWindow.mapToGlobal(event.pos())
    #*************************************************************************************************************************************************************
    def moveWindow(self, event):
        if MainWindow.isMaximized():
            MainWindow.showNormal()
        else:
            if event.buttons() == Qt.LeftButton:
                #******************************************************************************************************************************************************
                MainWindow_last_pos = MainWindow.dragPos + MainWindow.mapToGlobal(event.pos()) - self.mouse_original_pos
                MainWindow.move(MainWindow_last_pos)
                event.accept()
                #******************************************************************************************************************************************************
if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

Related

Keep aspect ratio of image in a QLabel whilst resizing window

Say i have a layout like this:
I want the ratio of the left object and the right object to always be like this:
So that i can add a small image in the top left corner that wont be gigantic or too small, but always in nice relation to the size of the window. How can i do this, either in the designer, or in the code?
I already found that you seem to be able to do this via selecting the layout and changing the LayoutStretch to something like 1,3 - this worked in the designer, however, when i inserted my image in the code, it did not respect it and blew the layout out of proption again.
I added a stretcher and used a QLabel to display the image, and then added the file via self.LogoLabel.setPixmap(QtGui.QPixmap('res/image.png')) , so i do not understand what i need to change in order to get the image to always be nice and small in the top left corner.
A test example, in case the question wasnt clear enough - the image i need is 1000x710px large.
from PyQt5 import QtCore, QtWidgets, QtGui
import sys
class Ui_ZEBRA(object):
def setupUi(self, ZEBRA):
ZEBRA.setObjectName("ZEBRA")
ZEBRA.resize(315, 134)
self.centralwidget = QtWidgets.QWidget(ZEBRA)
self.centralwidget.setObjectName("centralwidget")
self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.centralwidget)
self.verticalLayout_4.setObjectName("verticalLayout_4")
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setContentsMargins(-1, -1, -1, 0)
self.horizontalLayout.setSpacing(0)
self.horizontalLayout.setObjectName("horizontalLayout")
self.verticalLayout_3 = QtWidgets.QVBoxLayout()
self.verticalLayout_3.setObjectName("verticalLayout_3")
self.LogoLabel = QtWidgets.QLabel(self.centralwidget)
self.LogoLabel.setText("")
self.LogoLabel.setScaledContents(True)
self.LogoLabel.setObjectName("LogoLabel")
self.verticalLayout_3.addWidget(self.LogoLabel)
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout_3.addItem(spacerItem)
self.ComboBox_InputType = QtWidgets.QComboBox(self.centralwidget)
self.ComboBox_InputType.setObjectName("ComboBox_InputType")
self.ComboBox_InputType.addItem("")
self.ComboBox_InputType.addItem("")
self.ComboBox_InputType.addItem("")
self.ComboBox_InputType.addItem("")
self.verticalLayout_3.addWidget(self.ComboBox_InputType)
self.horizontalLayout.addLayout(self.verticalLayout_3)
self.TextInput_Devices = QtWidgets.QPlainTextEdit(self.centralwidget)
self.TextInput_Devices.setObjectName("TextInput_Devices")
self.horizontalLayout.addWidget(self.TextInput_Devices)
self.horizontalLayout.setStretch(0, 1)
self.horizontalLayout.setStretch(1, 3)
self.verticalLayout_4.addLayout(self.horizontalLayout)
ZEBRA.setCentralWidget(self.centralwidget)
self.menuBar_EnterToken = QtWidgets.QAction(ZEBRA)
self.menuBar_EnterToken.setObjectName("menuBar_EnterToken")
self.menuBar_TestToken = QtWidgets.QAction(ZEBRA)
self.menuBar_TestToken.setObjectName("menuBar_TestToken")
self.menuBar_About = QtWidgets.QAction(ZEBRA)
self.menuBar_About.setObjectName("menuBar_About")
self.retranslateUi(ZEBRA)
QtCore.QMetaObject.connectSlotsByName(ZEBRA)
def retranslateUi(self, ZEBRA):
_translate = QtCore.QCoreApplication.translate
ZEBRA.setWindowTitle(_translate("ZEBRA", "ZEBRA"))
self.ComboBox_InputType.setItemText(0, _translate("ZEBRA", "ip"))
self.ComboBox_InputType.setItemText(1, _translate("ZEBRA", "Use all devices"))
self.ComboBox_InputType.setItemText(2, _translate("ZEBRA", "displayName"))
self.ComboBox_InputType.setItemText(3, _translate("ZEBRA", "id"))
self.menuBar_EnterToken.setText(_translate("ZEBRA", "Enter Accesstoken"))
self.menuBar_TestToken.setText(_translate("ZEBRA", "Test Accesstoken"))
self.menuBar_About.setText(_translate("ZEBRA", "About..."))
class Test(QtWidgets.QMainWindow, Ui_ZEBRA):
def __init__(self, parent=None):
super(Test, self).__init__(parent)
self.setupUi(self)
self.LogoLabel.setPixmap(QtGui.QPixmap('res/image.png'))
def main():
app = QtWidgets.QApplication(sys.argv)
form = Test()
form.show()
app.exec_()
if __name__ == '__main__':
main()
EDIT: Weirdly enough, i could not find a single working example on how to use an image in a QLabel and scale its size while changing the window size, while also keeping the aspect ratio. Such a basic thing that is nowhere to be found?
You firstly need to change the layout so that it uses alignment rather than expanding spacers to keep the label at the top left corner. Also, some properties of the label need adjustment so that it can resize itself freely. This can all be done in Qt Designer, but your example code can also be fixed manually like this:
self.LogoLabel = QtWidgets.QLabel(self.centralwidget)
self.LogoLabel.setText("")
self.LogoLabel.setObjectName("LogoLabel")
# new stuff
self.LogoLabel.setScaledContents(False)
self.LogoLabel.setMinimumSize(1, 1)
self.LogoLabel.setSizePolicy(
QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
self.LogoLabel.setAlignment(QtCore.Qt.AlignTop)
self.verticalLayout_3.setAlignment(QtCore.Qt.AlignTop)
self.verticalLayout_3.addWidget(self.LogoLabel)
The dynamic resizing can then be handled using an event-filter, like this:
class Test(QtWidgets.QMainWindow, Ui_ZEBRA):
def __init__(self, parent=None):
super(Test, self).__init__(parent)
self.setupUi(self)
self._logo = QtGui.QPixmap('res/image.png')
self.LogoLabel.setPixmap(self._logo)
self.LogoLabel.installEventFilter(self)
def eventFilter(self, widget, event):
if event.type() == QtCore.QEvent.Resize and widget is self.LogoLabel:
self.LogoLabel.setPixmap(self._logo.scaled(
self.LogoLabel.width(), self.LogoLabel.height(),
QtCore.Qt.KeepAspectRatio))
return True
return super(Test, self).eventFilter(widget, event)
To scale the image down as you wanted and keep the ratio between the two elements, you need to set setScaledContents(True) like this:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(559, 289)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.centralwidget)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.verticalLayout = QtWidgets.QVBoxLayout()
self.verticalLayout.setObjectName("verticalLayout")
self.label = QtWidgets.QLabel(self.centralwidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Ignored)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
self.label.setSizePolicy(sizePolicy)
self.label.setScaledContents(True)
self.label.setAlignment(QtCore.Qt.AlignCenter)
self.label.setObjectName("label")
self.verticalLayout.addWidget(self.label)
self.comboBox = QtWidgets.QComboBox(self.centralwidget)
self.comboBox.setObjectName("comboBox")
self.verticalLayout.addWidget(self.comboBox)
self.horizontalLayout.addLayout(self.verticalLayout)
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem)
self.textBrowser = QtWidgets.QTextBrowser(self.centralwidget)
self.textBrowser.setObjectName("textBrowser")
self.horizontalLayout.addWidget(self.textBrowser)
self.horizontalLayout.setStretch(0, 1)
self.horizontalLayout.setStretch(2, 3)
self.verticalLayout_2.addLayout(self.horizontalLayout)
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.label.setText(_translate("MainWindow", "TextLabel"))
import sys
class Test(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(Test, self).__init__(parent)
self.setupUi(self)
pixmap = QtGui.QPixmap('res/image.png')
pixmap = pixmap.scaled(256, 128, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
self.label.setPixmap(pixmap)
self.label.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop)
def main():
app = QtWidgets.QApplication(sys.argv)
form = Test()
form.show()
app.exec_()
if __name__ == '__main__':
main()
In order to keep the aspect ratio - i think you have to use a resizeEvent of the Widget containing the Label which changes the size to the correct aspect ratio whenever the event is triggered.

PyQt5 DatePicker Popup

I am not able to make datepicker in pyqt5. I am using calendarWidget and it working fine now. But i want dropdown datepicker in my menu bar and want to show selected date in lineEdit.
I have created a layout in QDesigner and adding 'DateEdit" widget. But i want same exactly as image shown.
I searched for datepicker and getting this link :How to add Today Button in QDateEdit Pop-up QCalendarWidget
I tried many approaches but it's not working.
Note: when i run my file dateEdit widget always show the Date:[01-01-2000]. it not showing the current date.
datepicker.py
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
self.verticalLayout.setObjectName("verticalLayout")
self.frame_2 = QtWidgets.QFrame(self.centralwidget)
self.frame_2.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame_2.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame_2.setObjectName("frame_2")
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.frame_2)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.frame = QtWidgets.QFrame(self.frame_2)
self.frame.setStyleSheet("background-color: rgb(56, 122, 179);")
self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame.setObjectName("frame")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.frame)
self.horizontalLayout.setObjectName("horizontalLayout")
self.label = QtWidgets.QLabel(self.frame)
self.label.setStyleSheet("color: rgb(243, 243, 243);")
self.label.setObjectName("label")
self.horizontalLayout.addWidget(self.label)
self.dateEdit = QtWidgets.QDateEdit(self.frame)
self.dateEdit.setObjectName("dateEdit")
self.horizontalLayout.addWidget(self.dateEdit)
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem)
self.verticalLayout_2.addWidget(self.frame)
self.tableWidget = QtWidgets.QTableWidget(self.frame_2)
self.tableWidget.setObjectName("tableWidget")
self.tableWidget.setColumnCount(0)
self.tableWidget.setRowCount(0)
self.verticalLayout_2.addWidget(self.tableWidget)
self.verticalLayout.addWidget(self.frame_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.label.setText(_translate("MainWindow", "Selected Date is :________________________"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
The QDateEdit already provides a QCalendarWidget so you only need to enable the calendarPopup property:
import sys
from PyQt5 import QtCore, QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.dateedit = QtWidgets.QDateEdit(calendarPopup=True)
self.menuBar().setCornerWidget(self.dateedit, QtCore.Qt.TopLeftCorner)
self.dateedit.setDateTime(QtCore.QDateTime.currentDateTime())
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
For PyQt6 I had to slightly modify eyllanesc's code. Here's what worked for in in PyQt6. Changes to the original are placed below the commented out code.
import sys
from PyQt6 import QtCore, QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.dateedit = QtWidgets.QDateEdit(calendarPopup=True)
# self.menuBar().setCornerWidget(self.dateedit, QtCore.Qt.TopLeftCorner)
self.menuBar().setCornerWidget(self.dateedit, QtCore.Qt.Corner.TopLeftCorner)
self.dateedit.setDateTime(QtCore.QDateTime.currentDateTime())
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
# sys.exit(app.exec_())
sys.exit(app.exec())

Adding grid layout to tab widget changes spacing and size of inner widget

When using Qt Designer to make a GUI I am getting inconsistent results between what is previewed in designer versus when it's actually used in code. The actual size of the widgets doesn't change but applying grid layout (or any layout) to a tab widget causes anything inside of the tab widget to be stretched out and not evenly padded as originally shown in Qt Designer.
Preview in Qt Designer (left) and preview when actually initialized in python (right):
Here's the .ui file generated in Qt Designer:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.frame = QtWidgets.QFrame(self.centralwidget)
self.frame.setGeometry(QtCore.QRect(10, 10, 231, 201))
self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame.setObjectName("frame")
self.gridLayout_4 = QtWidgets.QGridLayout(self.frame)
self.gridLayout_4.setObjectName("gridLayout_4")
self.groupBox = QtWidgets.QGroupBox(self.frame)
self.groupBox.setObjectName("groupBox")
self.gridLayout_3 = QtWidgets.QGridLayout(self.groupBox)
self.gridLayout_3.setObjectName("gridLayout_3")
self.tabWidget = QtWidgets.QTabWidget(self.groupBox)
self.tabWidget.setObjectName("tabWidget")
self.tab = QtWidgets.QWidget()
self.tab.setObjectName("tab")
self.gridLayout_2 = QtWidgets.QGridLayout(self.tab)
self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
self.gridLayout_2.setObjectName("gridLayout_2")
self.widget = QtWidgets.QWidget(self.tab)
self.widget.setObjectName("widget")
self.gridLayout_5 = QtWidgets.QGridLayout(self.widget)
self.gridLayout_5.setContentsMargins(0, 0, 0, 0)
self.gridLayout_5.setObjectName("gridLayout_5")
self.groupBox_2 = QtWidgets.QGroupBox(self.widget)
self.groupBox_2.setAutoFillBackground(True)
self.groupBox_2.setTitle("")
self.groupBox_2.setObjectName("groupBox_2")
self.gridLayout = QtWidgets.QGridLayout(self.groupBox_2)
self.gridLayout.setObjectName("gridLayout")
self.pushButton = QtWidgets.QPushButton(self.groupBox_2)
self.pushButton.setObjectName("pushButton")
self.gridLayout.addWidget(self.pushButton, 0, 0, 1, 1)
self.gridLayout_5.addWidget(self.groupBox_2, 0, 0, 1, 1)
self.gridLayout_2.addWidget(self.widget, 0, 0, 1, 1)
self.tabWidget.addTab(self.tab, "")
self.tab_2 = QtWidgets.QWidget()
self.tab_2.setObjectName("tab_2")
self.tabWidget.addTab(self.tab_2, "")
self.gridLayout_3.addWidget(self.tabWidget, 0, 0, 1, 1)
self.gridLayout_4.addWidget(self.groupBox, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.groupBox.setTitle(_translate("MainWindow", "GroupBox"))
self.pushButton.setText(_translate("MainWindow", "PushButton"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "Tab 1"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("MainWindow", "Tab 2"))
And here is the code used to initialize the gui:
from PyQt5.QtWidgets import QMainWindow, QApplication
import sys
from ui import Ui_MainWindow
class App(QMainWindow, Ui_MainWindow):
def __init__(self, parent: object = None) -> object:
super(App, self).__init__(parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
if __name__ == "__main__":
app = QApplication(sys.argv)
ex = App()
ex.show()
sys.exit(app.exec_())
You can also verify this yourself by creating a MainWindow > centralwidget > frame > groupbox > tabWidget > groupbox all with grid layout or some kind of layout applied.
I've also tried playing around a bunch with the sizing options for the widgets and the results are still the same. I really hope I don't have to resort to adding spacers all around the inside widget as that would be tedious and result in inconsistent spacing.
Any suggestions would be greatly appreciated as this is very frustrating and surprising it hasn't been addressed before. Thanks!
Qt Designer automatically applies its default margins and spacings to layouts both when designing and previewing, even if they are not set in the "Form Settings..." dialog ("Form" menu).
If you go to that dialog and check the "Layout Default" option, and then save the ui file again, you'll probably see the same result in both Designer and your program.
When designing GUIs in Designer you have to remember that every OS and style has different default "metrics", including layout margins and spacings. If you want specific values for every layout you'll need to manually set them in the layout properties, otherwise you can set default values for all the form layouts (and sub-layouts) using the aforementioned option, and then ensure that every layout has the default value set (not marked in "bold").
There's also another possibility: in PyQt5 it is possible to use QProxyStyle (which unfortunately wasn't available on PyQt4), allowing you to override specific methods that are used to draw and "organize" widgets. If you create a subclass of QProxyStyle you can override pixelMetric() and automatically set margins and spacings for the whole program, as long as you apply it to the main QApplication.
class MyStyle(QtWidgets.QProxyStyle):
defaultMargin = 4
defaultSpacing = 2
margins = QtWidgets.QStyle.PM_LayoutLeftMargin, QtWidgets.QStyle.PM_LayoutTopMargin, QtWidgets.QStyle.PM_LayoutRightMargin, QtWidgets.QStyle.PM_LayoutBottomMargin
spacings = QtWidgets.QStyle.PM_LayoutHorizontalSpacing, QtWidgets.QStyle.PM_LayoutVerticalSpacing
def pixelMetric(self, metric, option=None, widget=None):
if metric in self.margins:
return self.defaultMargin
elif metric in self.spacings:
return self.defaultSpacing
return QtWidgets.QProxyStyle.pixelMetric(self, metric, option, widget)
if __name__ == '__main__':
app = QtWidges.QApplication(sys.argv)
app.setStyle(MyStyle())
[...]

How to get Icon size to completely fill PushButton/be rescalable in PyQt5 python

I have a class called TwoStateButton which inherits QPushButton so that the icon can change when the button is being clicked on and so the icon gets resized in proportion to the button when the whole window gets resized.
When I run this code, the button takes up the whole window and the icon is about 10% of the button. I'm trying to get it so that the icon image takes up the entire pushbutton and that they both get resized by the same amount when the window is resized.
What modifications to my code should I do to achieve this?
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class TwoStateButton(QtWidgets.QPushButton):
def __init__(self, on, hover, parent=None):
super(TwoStateButton, self).__init__(parent)
self.pad = 4 # padding between the icon and the button frame
self.minSize = 8 # minimum size of the icon
sizePolicy = QtWidgets.QSizePolicy(
QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding
)
self.setSizePolicy(sizePolicy)
self.setIcon(QtGui.QIcon(on))
self.on = on
self.hover = hover
def mousePressEvent(self, event):
super(TwoStateButton, self).mousePressEvent(event)
self.setIcon(QtGui.QIcon(self.hover))
def mouseReleaseEvent(self, event):
super(TwoStateButton, self).mouseReleaseEvent(event)
self.setIcon(QtGui.QIcon("%s" % (self.on)))
def paintEvent(self, event):
qp = QtGui.QPainter(self)
opt = QtWidgets.QStyleOptionButton()
self.initStyleOption(opt)
r = opt.rect
h = r.height()
w = r.width()
iconSize = max(min(h, w) - 2 * self.pad, self.minSize)
opt.iconSize = QtCore.QSize(iconSize, iconSize)
self.style().drawControl(QtWidgets.QStyle.CE_PushButton, opt, qp,self)
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(1090, 681)
MainWindow.setStyleSheet("")
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
self.horizontalLayout.setObjectName("horizontalLayout")
self.pushButton_2 = TwoStateButton(
"images/Button5-3.png", "images/Button5-4.png", self.centralwidget
)
self.pushButton_2.setGeometry(QtCore.QRect(430, 310, 141, 81))
self.pushButton_2.setText("")
self.pushButton_2.setCheckable(False)
self.pushButton_2.setObjectName("pushButton_2")
self.horizontalLayout.addWidget(self.pushButton_2)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 1090, 22))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setStyleSheet(
"border-image: url(:/images/Button5-3.png)"
)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())

How to paint over a child widget?

So I have this Ui_MainWindow class generated by pyuic5.
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'MainWindow.ui'
#
# Created by: PyQt5 UI code generator 5.9
#
# 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(800, 450)
self.centralWidget = QtWidgets.QWidget(MainWindow)
self.centralWidget.setObjectName("centralWidget")
self.canvas = QtWidgets.QWidget(self.centralWidget)
self.canvas.setGeometry(QtCore.QRect(70, 30, 621, 341))
self.canvas.setAutoFillBackground(False)
self.canvas.setObjectName("canvas")
self.gview = QtWidgets.QGraphicsView(self.canvas)
self.gview.setGeometry(QtCore.QRect(80, 50, 256, 192))
self.gview.setObjectName("gview")
MainWindow.setCentralWidget(self.centralWidget)
self.menuBar = QtWidgets.QMenuBar(MainWindow)
self.menuBar.setGeometry(QtCore.QRect(0, 0, 800, 22))
self.menuBar.setObjectName("menuBar")
self.menuhola = QtWidgets.QMenu(self.menuBar)
self.menuhola.setObjectName("menuhola")
self.menust = QtWidgets.QMenu(self.menuBar)
self.menust.setObjectName("menust")
MainWindow.setMenuBar(self.menuBar)
self.menuBar.addAction(self.menuhola.menuAction())
self.menuBar.addAction(self.menust.menuAction())
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.menuhola.setTitle(_translate("MainWindow", "hola"))
self.menust.setTitle(_translate("MainWindow", "splendid"))
And I want to paint on something, as long as it's not the main window. However, no matter what I try, it just doesn't work.
Here's my code:
import sys
from PyQt5 import QtWidgets
from PyQt5.QtGui import QPainter
from src.qt_design.main_window import Ui_MainWindow
class SystemApp(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
def paintEvent(self, event):
# Nope
painter = QPainter(self.ui.gview)
painter.drawLine(0, 0, 50, 50)
# Nope
painter = QPainter(self.ui.canvas)
painter.drawLine(0, 0, 50, 50)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = SystemApp()
window.show()
sys.exit(app.exec_())
I'm not sure if I should either:
1. Paint with this parent on its children (like what I tried above)
2. Override children's paintEvent, but it's maybe a bad idea, see here
Any help will be appreciated!
You can get what you are trying to accomplish with an event filter
class SystemApp(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.gview.installEventFilter(self)
self.ui.canvas.installEventFilter(self)
def eventFilter(self, obj, e):
if e.type() == QtCore.QEvent.Paint:
painter = QPainter()
painter.begin(obj)
if obj == self.ui.canvas:
painter.setPen(QtCore.Qt.red) # Some specific painting
painter.drawLine(0, 0, 50, 50)
painter.end()
return True
return super().eventFilter(obj, e)

Categories

Resources