0

I trying to make the ui with rounded corners on a QMainWindow with Qt Designer by also adding a custom title bar.

I'm using python and pyside 6: when i use no .ui file it works fine.

Whenever i load the ui file and try to apply the custom titlebar plus the rounded corners effect everything breaks.

Also what is the difference between loading a ui file and writing by code all the element?

Thank you all!

from PySide6.QtWidgets import (
    QApplication,
    QWidget,
    QHBoxLayout,
    QLabel,
    QVBoxLayout,
    QTextEdit,
    QToolButton,
    QStyle,
    QPushButton,
    QMainWindow,
)
import sys, os
from PySide6.QtCore import Qt, QSize, QEvent, QRect
from PySide6.QtGui import QPalette
from PySide6 import QtGui, QtCore
from qt_material import apply_stylesheet
from PySide6.QtUiTools import QUiLoader

os.chdir(os.path.dirname(__file__))


class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.main = QUiLoader().load("test.ui", self)
        self.selected_filter = None
        apply_stylesheet(self.main, theme="my_theme.xml")
        self.main.setWindowFlags(self.main.windowFlags() | QtCore.Qt.FramelessWindowHint)
        self.main.setAttribute(Qt.WA_TranslucentBackground)
        self.titleBar = MyBar(self.main)
        self.setContentsMargins(0, self.titleBar.height(), 0, 0)

        # self.resize(640, self.titleBar.height() + 480)

        self.backgroundColor = QtGui.QColor("#020b1a")
        self.foregroundColor = QtGui.QColor("#1f2e60")
        self.borderRadius = 5

    def changeEvent(self, event):
        if event.type() == QEvent.WindowStateChange:
            self.titleBar.windowStateChanged(self.windowState())

    def resizeEvent(self, event):
        self.titleBar.resize(self.width(), self.titleBar.height())

    def paintEvent(self, event):
        # get current window size
        s = self.size()
        qp = QtGui.QPainter()
        qp.begin(self)
        qp.setRenderHint(QtGui.QPainter.Antialiasing, True)
        qp.setPen(self.foregroundColor)
        qp.setBrush(self.backgroundColor)
        qp.drawRoundedRect(0, 0, s.width(), s.height(), self.borderRadius, self.borderRadius)
        qp.end()


class MyBar(QWidget):
    clickPos = None

    def __init__(self, parent):
        super(MyBar, self).__init__(parent)
        self.setAutoFillBackground(True)

        # self.setBackgroundRole(QPalette.Shadow)
        # alternatively:
        palette = self.palette()
        palette.setColor(QPalette.Window, Qt.black)
        palette.setColor(QPalette.WindowText, Qt.white)
        self.setPalette(palette)

        layout = QHBoxLayout(self)
        layout.setContentsMargins(1, 1, 1, 1)
        layout.addStretch()

        self.title = QLabel("My Own Bar", self, alignment=Qt.AlignLeft | Qt.AlignVCenter)
        # if setPalette() was used above, this is not required

        style = self.style()
        ref_size = self.fontMetrics().height()
        ref_size += style.pixelMetric(QStyle.PM_ButtonMargin) * 2
        self.setMaximumHeight(ref_size + 2)

        btn_size = QSize(ref_size, ref_size)
        for target in ("min", "normal", "max", "close"):
            btn = QPushButton(self, focusPolicy=Qt.NoFocus)
            layout.addWidget(btn)
            btn.setFixedSize(btn_size)

            iconType = getattr(QStyle, f"SP_TitleBar{target.capitalize()}Button")
            ico = style.standardIcon(iconType)
            btn.setIcon(style.standardIcon(iconType))
            colorHover = "#ffb900"
            colorNormal = "#020b1a"
            btn.setStyleSheet(
                f"""
                QPushButton {{
                    border-color: #020b1a;
                    background-color: {colorNormal};
                }}
                QPushButton:hover {{
                    background-color: {colorHover}
                }}
            """
            )

            signal = getattr(self, f"{target}Clicked")
            btn.clicked.connect(signal)

            setattr(self, f"{target}Button", btn)

        self.normalButton.hide()
        self.maxButton.hide()

        self.updateTitle(parent.windowTitle())
        parent.windowTitleChanged.connect(self.updateTitle)

    def updateTitle(self, title=None):
        if title is None:
            title = self.window().windowTitle()
        width = self.title.width()
        width -= self.style().pixelMetric(QStyle.PM_LayoutHorizontalSpacing) * 2
        self.title.setText(self.fontMetrics().elidedText(title, Qt.ElideRight, width))

    def windowStateChanged(self, state):
        self.normalButton.setVisible(state == Qt.WindowMaximized)
        self.maxButton.setVisible(state != Qt.WindowMaximized)

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.clickPos = event.windowPos().toPoint()

    def mouseMoveEvent(self, event):
        if self.clickPos is not None:
            self.window().move(event.globalPos() - self.clickPos)

    def mouseReleaseEvent(self, QMouseEvent):
        self.clickPos = None

    def closeClicked(self):
        self.window().close()

    def maxClicked(self):
        self.window().showMaximized()

    def normalClicked(self):
        self.window().showNormal()

    def minClicked(self):
        self.window().showMinimized()

    def resizeEvent(self, event):
        self.title.resize(self.minButton.x(), self.height())
        self.updateTitle()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    mw = MainWindow()
    mw.main.show()
    mw.setWindowTitle("My custom window with a very, very long title")
    sys.exit(app.exec())

main.py

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="QToolButton" name="toolButton">
    <property name="geometry">
     <rect>
      <x>0</x>
      <y>0</y>
      <width>371</width>
      <height>22</height>
     </rect>
    </property>
    <property name="text">
     <string>...</string>
    </property>
   </widget>
   <widget class="QTextEdit" name="textEdit">
    <property name="geometry">
     <rect>
      <x>300</x>
      <y>270</y>
      <width>104</width>
      <height>87</height>
     </rect>
    </property>
   </widget>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>

test.ui

marksoe
  • 58
  • 8
  • 1
    By default, QUiLoader "embeds" the loaded widget as a child, does not "set it" on the current one. Consider using the pyside-uic tool, [`loadUiType()`](https://doc.qt.io/qtforpython/PySide6/QtUiTools/loadUiType.html) (but ensure that `uic` is in the system path, or follow [this answer](https://stackoverflow.com/a/27610822). – musicamante Nov 25 '22 at 19:12
  • thank you so much i solved the problem! i was scratching my head for so long and didn't understand the difference... – marksoe Nov 27 '22 at 11:22

0 Answers0