Кастомный Title Bar, с помощью PyQt5 в Qt Designer

state_window.py

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'state_window.ui'
#
# Created by: PyQt5 UI code generator 5.15.2
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QHBoxLayout
from PyQt5.QtWidgets import QLabel
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtWidgets import QVBoxLayout
from PyQt5.QtWidgets import QWidget


class Ui_MainWindow(QtWidgets.QMainWindow):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1440, 950)
        MainWindow.setStyleSheet("*{\n"
                                 "   font: 19pt \"Arial\";\n"
                                 "   color: #fff;\n"
                                 "}\n"
                                 "\n"
                                 "QWidget{\n"
                                 "    background-color: #9B9B9A;\n"
                                 "    border-radius: 10px;\n"
                                 "}\n"
                                 "\n"
                                 "#practical_btn,\n"
                                 "#settings_btn,\n"
                                 "#achiev_btn{\n"
                                 "    background-color: #E88327;\n"
                                 "    border-radius: 10px;\n"
                                 "}\n"
                                 "\n"
                                 "#state_btn{\n"
                                 "    background-color: #FFAA5C;\n"
                                 "    border-radius: 10px;\n"
                                 "}\n"
                                 "\n"
                                 "#achiev_btn:hover,\n"
                                 "#practical_btn:hover,\n"
                                 "#settings_btn:hover,\n"
                                 "#state_btn:hover{\n"
                                 "    background-color: #FFAA5C;\n"
                                 "}\n"
                                 "\n"
                                 "#edit_students,\n"
                                 "#edit_streams\n"
                                 "{\n"
                                 "    image: url(:/image/pen.png);\n"
                                 "    border: none;\n"
                                 "}\n"
                                 "\n"
                                 "#table,\n"
                                 "#streams\n"
                                 "{\n"
                                 "    background-color: #E88327;\n"
                                 "    border-radius: 10px;\n"
                                 "}\n"
                                 "\n"
                                 "#btn_close:hover,\n"
                                 "#btn_min:hover,\n"
                                 "#btn_max:hover\n"
                                 "{\n"
                                 "    background-color:#ccc;\n"
                                 "    border-radius: 0px;\n"
                                 "}")
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setStyleSheet("")
        self.centralwidget.setObjectName("centralwidget")
        self.streams = QtWidgets.QListWidget(self.centralwidget)
        self.streams.setGeometry(QtCore.QRect(30, 170, 256, 681))
        self.streams.setStyleSheet("")
        self.streams.setObjectName("streams")
        self.edit_streams = QtWidgets.QPushButton(self.centralwidget)
        self.edit_streams.setGeometry(QtCore.QRect(195, 137, 18, 18))
        self.edit_streams.setCursor(
            QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.edit_streams.setStyleSheet("")
        self.edit_streams.setText("")
        self.edit_streams.setObjectName("edit_streams")
        self.table = QtWidgets.QTableWidget(self.centralwidget)
        self.table.setGeometry(QtCore.QRect(300, 170, 1111, 681))
        self.table.setStyleSheet("")
        self.table.setObjectName("table")
        self.table.setColumnCount(0)
        self.table.setRowCount(0)
        self.edit_students = QtWidgets.QPushButton(self.centralwidget)
        self.edit_students.setGeometry(QtCore.QRect(945, 142, 18, 18))
        self.edit_students.setCursor(
            QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.edit_students.setStyleSheet("")
        self.edit_students.setText("")
        self.edit_students.setObjectName("edit_students")
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(830, 135, 111, 31))
        self.label.setObjectName("label")
        self.label_2 = QtWidgets.QLabel(self.centralwidget)
        self.label_2.setGeometry(QtCore.QRect(110, 135, 81, 21))
        self.label_2.setObjectName("label_2")
        self.achiev_btn = QtWidgets.QPushButton(self.centralwidget)
        self.achiev_btn.setEnabled(True)
        self.achiev_btn.setGeometry(QtCore.QRect(470, 50, 221, 50))
        self.achiev_btn.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.achiev_btn.setStyleSheet("")
        self.achiev_btn.setObjectName("achiev_btn")
        self.state_btn = QtWidgets.QPushButton(self.centralwidget)
        self.state_btn.setGeometry(QtCore.QRect(160, 50, 221, 50))
        self.state_btn.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.state_btn.setObjectName("state_btn")
        self.practical_btn = QtWidgets.QPushButton(self.centralwidget)
        self.practical_btn.setGeometry(QtCore.QRect(780, 50, 221, 50))
        self.practical_btn.setCursor(
            QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.practical_btn.setObjectName("practical_btn")
        self.settings_btn = QtWidgets.QPushButton(self.centralwidget)
        self.settings_btn.setGeometry(QtCore.QRect(1090, 50, 221, 50))
        self.settings_btn.setCursor(
            QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.settings_btn.setObjectName("settings_btn")
        self.widget = QtWidgets.QWidget(self.centralwidget)
        self.widget.setGeometry(QtCore.QRect(0, 0, 1441, 41))
        self.widget.setObjectName("widget")
        self.btn_close = QtWidgets.QPushButton(self.widget)
        self.btn_close.setGeometry(QtCore.QRect(1310, 10, 75, 23))
        self.btn_close.setObjectName("btn_close")
        self.btn_max = QtWidgets.QPushButton(self.widget)
        self.btn_max.setGeometry(QtCore.QRect(1200, 10, 75, 23))
        self.btn_max.setObjectName("btn_max")
        self.btn_min = QtWidgets.QPushButton(self.widget)
        self.btn_min.setGeometry(QtCore.QRect(1090, 10, 75, 23))
        self.btn_min.setObjectName("btn_min")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 1440, 34))
        self.menubar.setStyleSheet("")
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setStyleSheet("")
        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.label.setText(_translate("MainWindow", "студенты"))
        self.label_2.setText(_translate("MainWindow", "потоки"))
        self.achiev_btn.setText(_translate("MainWindow", "достижения"))
        self.state_btn.setText(_translate("MainWindow", "ведомость"))
        self.practical_btn.setText(_translate("MainWindow", "практические"))
        self.settings_btn.setText(_translate("MainWindow", "настройки"))
        self.btn_close.setText(_translate("MainWindow", "3"))
        self.btn_max.setText(_translate("MainWindow", "2"))
        self.btn_min.setText(_translate("MainWindow", "1"))


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_())

FramelessWindow.py (все точь-в-точь как из 1 скинутого вами вопроса)

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from PyQt5.QtCore import Qt, pyqtSignal, QPoint
from PyQt5.QtGui import QFont, QEnterEvent, QPainter, QColor, QPen
from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel,
                             QSpacerItem, QSizePolicy, QPushButton)


class TitleBar(QWidget):

    # Сигнал минимизации окна
    windowMinimumed = pyqtSignal()
    # увеличить максимальный сигнал окна
    windowMaximumed = pyqtSignal()
    # сигнал восстановления окна
    windowNormaled = pyqtSignal()
    # сигнал закрытия окна
    windowClosed = pyqtSignal()
    # Окно мобильных
    windowMoved = pyqtSignal(QPoint)
    # Сигнал Своя Кнопка +++
    signalButtonMy = pyqtSignal()

    def __init__(self, *args, **kwargs):
        super(TitleBar, self).__init__(*args, **kwargs)

        # Поддержка настройки фона qss
        self.setAttribute(Qt.WA_StyledBackground, True)
        self.mPos = None
        self.iconSize = 20                       # Размер значка по умолчанию

        # Установите цвет фона по умолчанию, иначе он будет прозрачным из-за влияния родительского окна
        self.setAutoFillBackground(True)
        palette = self.palette()
        palette.setColor(palette.Window, QColor(240, 240, 240))
        self.setPalette(palette)

        # макет
        layout = QHBoxLayout(self, spacing=0)
        layout.setContentsMargins(0, 0, 0, 0)

        # значок окна
        self.iconLabel = QLabel(self)
#         self.iconLabel.setScaledContents(True)
        layout.addWidget(self.iconLabel)

        # название окна
        self.titleLabel = QLabel(self)
        self.titleLabel.setMargin(2)
        layout.addWidget(self.titleLabel)

        # Средний телескопический бар
        layout.addSpacerItem(QSpacerItem(
            40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))

        # Использовать шрифты Webdings для отображения значков
        font = self.font() or QFont()
        font.setFamily('Webdings')

        # Своя Кнопка ++++++++++++++++++++++++++
        self.buttonMy = QPushButton(
            '@', self, clicked=self.showButtonMy, font=font, objectName='buttonMy')
        layout.addWidget(self.buttonMy)

        # Свернуть кнопку
        self.buttonMinimum = QPushButton(
            '0', self, clicked=self.windowMinimumed.emit, font=font, objectName='buttonMinimum')
        layout.addWidget(self.buttonMinimum)

        # Кнопка Max / restore
        self.buttonMaximum = QPushButton(
            '1', self, clicked=self.showMaximized, font=font, objectName='buttonMaximum')
        layout.addWidget(self.buttonMaximum)

        # Кнопка закрытия
        self.buttonClose = QPushButton(
            'r', self, clicked=self.windowClosed.emit, font=font, objectName='buttonClose')
        layout.addWidget(self.buttonClose)

        # начальная высота
        self.setHeight()

    # +++ Вызывается по нажатию кнопки buttonMy
    def showButtonMy(self):
        print("Своя Кнопка ")
        self.signalButtonMy.emit()

    def showMaximized(self):
        if self.buttonMaximum.text() == '1':
            # Максимизировать
            self.buttonMaximum.setText('2')
            self.windowMaximumed.emit()
        else:  # Восстановить
            self.buttonMaximum.setText('1')
            self.windowNormaled.emit()

    def setHeight(self, height=38):
        """ Установка высоты строки заголовка """
        self.setMinimumHeight(height)
        self.setMaximumHeight(height)
        # Задайте размер правой кнопки  ?
        self.buttonMinimum.setMinimumSize(height, height)
        self.buttonMinimum.setMaximumSize(height, height)
        self.buttonMaximum.setMinimumSize(height, height)
        self.buttonMaximum.setMaximumSize(height, height)
        self.buttonClose.setMinimumSize(height, height)
        self.buttonClose.setMaximumSize(height, height)

        self.buttonMy.setMinimumSize(height, height)
        self.buttonMy.setMaximumSize(height, height)

    def setTitle(self, title):
        """ Установить заголовок """
        self.titleLabel.setText(title)

    def setIcon(self, icon):
        """ настройки значокa """
        self.iconLabel.setPixmap(icon.pixmap(self.iconSize, self.iconSize))

    def setIconSize(self, size):
        """ Установить размер значка """
        self.iconSize = size

    def enterEvent(self, event):
        self.setCursor(Qt.ArrowCursor)
        super(TitleBar, self).enterEvent(event)

    def mouseDoubleClickEvent(self, event):
        super(TitleBar, self).mouseDoubleClickEvent(event)
        self.showMaximized()

    def mousePressEvent(self, event):
        """ Событие клика мыши """
        if event.button() == Qt.LeftButton:
            self.mPos = event.pos()
        event.accept()

    def mouseReleaseEvent(self, event):
        ''' Событие отказов мыши '''
        self.mPos = None
        event.accept()

    def mouseMoveEvent(self, event):
        if event.buttons() == Qt.LeftButton and self.mPos:
            self.windowMoved.emit(self.mapToGlobal(event.pos() - self.mPos))
        event.accept()


# Перечислить верхнюю левую, нижнюю правую и четыре неподвижные точки
Left, Top, Right, Bottom, LeftTop, RightTop, LeftBottom, RightBottom = range(8)


class FramelessWindow(QWidget):

    # Четыре периметра
    Margins = 5

    def __init__(self, *args, **kwargs):
        super(FramelessWindow, self).__init__(*args, **kwargs)
        self._pressed = False
        self.Direction = None

        # Фон прозрачный
        self.setAttribute(Qt.WA_TranslucentBackground, True)

        # Нет границы
        self.setWindowFlag(Qt.FramelessWindowHint)
        # Отслеживание мыши
        self.setMouseTracking(True)

        # макет
        layout = QVBoxLayout(self, spacing=0)
        # Зарезервировать границы для изменения размера окна без полей
        layout.setContentsMargins(
            self.Margins, self.Margins, self.Margins, self.Margins)
        # Панель заголовка
        self.titleBar = TitleBar(self)
        layout.addWidget(self.titleBar)

        # слот сигнала
        self.titleBar.windowMinimumed.connect(self.showMinimized)
        self.titleBar.windowMaximumed.connect(self.showMaximized)
        self.titleBar.windowNormaled.connect(self.showNormal)
        self.titleBar.windowClosed.connect(self.close)
        self.titleBar.windowMoved.connect(self.move)
        self.windowTitleChanged.connect(self.titleBar.setTitle)
        self.windowIconChanged.connect(self.titleBar.setIcon)

    def setTitleBarHeight(self, height=38):
        """ Установка высоты строки заголовка """
        self.titleBar.setHeight(height)

    def setIconSize(self, size):
        """ Установка размера значка """
        self.titleBar.setIconSize(size)

    def setWidget(self, widget):
        """ Настройте свои собственные элементы управления """
        if hasattr(self, '_widget'):
            return
        self._widget = widget
        # Установите цвет фона по умолчанию, иначе он будет прозрачным из-за влияния родительского окна
        self._widget.setAutoFillBackground(True)
        palette = self._widget.palette()
        palette.setColor(palette.Window, QColor(240, 240, 240))
        self._widget.setPalette(palette)
        self._widget.installEventFilter(self)
        self.layout().addWidget(self._widget)

    def move(self, pos):
        if self.windowState() == Qt.WindowMaximized or self.windowState() == Qt.WindowFullScreen:
            # Максимизировать или полноэкранный режим не допускается
            return
        super(FramelessWindow, self).move(pos)

    def showMaximized(self):
        """ Чтобы максимизировать, удалите верхнюю, нижнюю, левую и правую границы. 
            Если вы не удалите его, в пограничной области будут пробелы. """
        super(FramelessWindow, self).showMaximized()
        self.layout().setContentsMargins(0, 0, 0, 0)

    def showNormal(self):
        """ Восстановить, сохранить верхнюю и нижнюю левую и правую границы, 
            иначе нет границы, которую нельзя отрегулировать """
        super(FramelessWindow, self).showNormal()
        self.layout().setContentsMargins(
            self.Margins, self.Margins, self.Margins, self.Margins)

    def eventFilter(self, obj, event):
        """ Фильтр событий, используемый для решения мыши в других элементах 
            управления и восстановления стандартного стиля мыши """
        if isinstance(event, QEnterEvent):
            self.setCursor(Qt.ArrowCursor)
        return super(FramelessWindow, self).eventFilter(obj, event)

    def paintEvent(self, event):
        """ Поскольку это полностью прозрачное фоновое окно, жесткая для поиска 
            граница с прозрачностью 1 рисуется в событии перерисовывания, чтобы отрегулировать размер окна. """
        super(FramelessWindow, self).paintEvent(event)
        painter = QPainter(self)
        painter.setPen(QPen(QColor(255, 255, 255, 1), 2 * self.Margins))
        painter.drawRect(self.rect())

    def mousePressEvent(self, event):
        """ Событие клика мыши """
        super(FramelessWindow, self).mousePressEvent(event)
        if event.button() == Qt.LeftButton:
            self._mpos = event.pos()
            self._pressed = True

    def mouseReleaseEvent(self, event):
        ''' Событие отказов мыши '''
        super(FramelessWindow, self).mouseReleaseEvent(event)
        self._pressed = False
        self.Direction = None

    def mouseMoveEvent(self, event):
        """ Событие перемещения мыши """
        super(FramelessWindow, self).mouseMoveEvent(event)
        pos = event.pos()
        xPos, yPos = pos.x(), pos.y()
        wm, hm = self.width() - self.Margins, self.height() - self.Margins
        if self.isMaximized() or self.isFullScreen():
            self.Direction = None
            self.setCursor(Qt.ArrowCursor)
            return
        if event.buttons() == Qt.LeftButton and self._pressed:
            self._resizeWidget(pos)
            return
        if xPos <= self.Margins and yPos <= self.Margins:
            # Верхний левый угол
            self.Direction = LeftTop
            self.setCursor(Qt.SizeFDiagCursor)
        elif wm <= xPos <= self.width() and hm <= yPos <= self.height():
            # Нижний правый угол
            self.Direction = RightBottom
            self.setCursor(Qt.SizeFDiagCursor)
        elif wm <= xPos and yPos <= self.Margins:
            # верхний правый угол
            self.Direction = RightTop
            self.setCursor(Qt.SizeBDiagCursor)
        elif xPos <= self.Margins and hm <= yPos:
            # Нижний левый угол
            self.Direction = LeftBottom
            self.setCursor(Qt.SizeBDiagCursor)
        elif 0 <= xPos <= self.Margins and self.Margins <= yPos <= hm:
            # Влево
            self.Direction = Left
            self.setCursor(Qt.SizeHorCursor)
        elif wm <= xPos <= self.width() and self.Margins <= yPos <= hm:
            # Право
            self.Direction = Right
            self.setCursor(Qt.SizeHorCursor)
        elif self.Margins <= xPos <= wm and 0 <= yPos <= self.Margins:
            # выше
            self.Direction = Top
            self.setCursor(Qt.SizeVerCursor)
        elif self.Margins <= xPos <= wm and hm <= yPos <= self.height():
            # ниже
            self.Direction = Bottom
            self.setCursor(Qt.SizeVerCursor)

    def _resizeWidget(self, pos):
        """ Отрегулируйте размер окна """
        if self.Direction == None:
            return
        mpos = pos - self._mpos
        xPos, yPos = mpos.x(), mpos.y()
        geometry = self.geometry()
        x, y, w, h = geometry.x(), geometry.y(), geometry.width(), geometry.height()
        if self.Direction == LeftTop:          # Верхний левый угол
            if w - xPos > self.minimumWidth():
                x += xPos
                w -= xPos
            if h - yPos > self.minimumHeight():
                y += yPos
                h -= yPos
        elif self.Direction == RightBottom:    # Нижний правый угол
            if w + xPos > self.minimumWidth():
                w += xPos
                self._mpos = pos
            if h + yPos > self.minimumHeight():
                h += yPos
                self._mpos = pos
        elif self.Direction == RightTop:       # верхний правый угол
            if h - yPos > self.minimumHeight():
                y += yPos
                h -= yPos
            if w + xPos > self.minimumWidth():
                w += xPos
                self._mpos.setX(pos.x())
        elif self.Direction == LeftBottom:     # Нижний левый угол
            if w - xPos > self.minimumWidth():
                x += xPos
                w -= xPos
            if h + yPos > self.minimumHeight():
                h += yPos
                self._mpos.setY(pos.y())
        elif self.Direction == Left:            # Влево
            if w - xPos > self.minimumWidth():
                x += xPos
                w -= xPos
            else:
                return
        elif self.Direction == Right:           # Право
            if w + xPos > self.minimumWidth():
                w += xPos
                self._mpos = pos
            else:
                return
        elif self.Direction == Top:             # выше
            if h - yPos > self.minimumHeight():
                y += yPos
                h -= yPos
            else:
                return
        elif self.Direction == Bottom:          # ниже
            if h + yPos > self.minimumHeight():
                h += yPos
                self._mpos = pos
            else:
                return
        self.setGeometry(x, y, w, h)

main.py

from app import *
from FramelessWindow import FramelessWindow


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    application = StateWindow()
    application.show()
    MainWindow = QtWidgets.QMainWindow()
    w = FramelessWindow()
    w.setWindowTitle('Тестовая строка заголовка')
    w.setWindowIcon(QIcon('Qt.ico'))
    w.setWidget(StateWindow(w))          # Добавить свое окно
    w.show()
    ui = Ui_MainWindow()
    sys.exit(app.exec_())

app.py

import sys
from PyQt5 import QtGui
from PyQt5 import QtCore
from PyQt5 import QtWidgets
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from state_window import Ui_MainWindow
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QTextEdit
from PyQt5.QtCore import pyqtSignal
from FramelessWindow import FramelessWindow


class StateWindow(QtWidgets.QMainWindow):

    def __init__(self, *args, **kwargs):
        super(StateWindow, self).__init__(*args, **kwargs)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.setMinimumSize(1440, 950)

Подскажите, пожалуйста, как сделать кастомный Title Bar?
Уже сутки ищу ответ на этот вопрос, что на русских форумах, что на зарубежных. Пока ничего не нашел.


Ответы (1 шт):

Автор решения: S. Nick

Вы выбрали самый продвинутый пример и мне показалось, что для вас это еще рановато.
Пример, который я привожу, будет попроще.

Некоторые вам советы:

  1. НИКОГДА НЕ ИЗМЕНЯЙТЕ код, сгенерированный Qt Designer. Создайте другой класс, который наследуется от соответствующего виджета, и используйте созданный класс для его заполнения.

  2. Изучите как расположить виджеты используя менеджеры компоновки. Класс QLayout - это базовый класс менеджеров геометрии, начните с него https://doc.qt.io/qt-5/qlayout.html .

Я немного оживил ваш пример (q1252882.py), запустите его, для начала отдельно:

python q1252882.py

Затем запустите q1252882-222.py куда я его вставил.

python q1252882-222.py

Да и измените путь к вашему изображению для кнопки.


q1252882.py

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.Qt import *
#import resorses_rc


#class Ui_MainWindow(QtWidgets.QMainWindow):                                   # ???
class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1000, 600)
        MainWindow.setStyleSheet("*{\n"
                                 "   font: 19pt \"Arial\";\n"
                                 "   color: #fff;\n"
                                 "}\n"
                                 "\n"
                                 "QWidget{\n"
                                 "    background-color: #9B9B9A;\n"
                                 "    border-radius: 0px;\n"
                                 "}\n"
                                 "\n"
                                 "#practical_btn,\n"
                                 "#settings_btn,\n"
                                 "#achiev_btn{\n"
                                 "    background-color: #E88327;\n"
                                 "    border-radius: 10px;\n"
                                 "}\n"
                                 "\n"
                                 "#state_btn{\n"
                                 "    background-color: #FFAA5C;\n"
                                 "    border-radius: 10px;\n"
                                 "}\n"
                                 "\n"
                                 "#achiev_btn:hover,\n"
                                 "#practical_btn:hover,\n"
                                 "#settings_btn:hover,\n"
                                 "#state_btn:hover{\n"
                                 "    background-color: #FFAA5C;\n"
                                 "}\n"
                                 "\n"
                                 "#edit_students,\n"
                                 "#edit_streams\n"
                                 "{\n"
                                 "    image: url(images/okV.png);\n"          
                                 "    border: none;\n"
                                 "}\n"
                                 "\n"
                                 "#table,\n"
                                 "#streams\n"
                                 "{\n"
                                 "    background-color: #E88327;\n"
                                 "    border-radius: 10px;\n"
                                 "}\n"
                                 "\n"
                                 "#btn_close:hover,\n"
                                 "#btn_min:hover,\n"
                                 "#btn_max:hover\n"
                                 "{\n"
                                 "    background-color:#ccc;\n"
                                 "    border-radius: 0px;\n"
                                 "}")
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setStyleSheet("")
        self.centralwidget.setObjectName("centralwidget")
        self.streams = QtWidgets.QListWidget(self.centralwidget)
        self.streams.setGeometry(QtCore.QRect(30, 170, 256, 681))
        self.streams.setStyleSheet("")
        self.streams.setObjectName("streams")
        self.edit_streams = QtWidgets.QPushButton(self.centralwidget)
        self.edit_streams.setGeometry(QtCore.QRect(195, 137, 18, 18))
        self.edit_streams.setCursor(
            QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.edit_streams.setStyleSheet("")             
        self.edit_streams.setText("")                                             
        self.edit_streams.setObjectName("edit_streams")
        self.table = QtWidgets.QTableWidget(self.centralwidget)        
        self.table.setGeometry(QtCore.QRect(300, 170, 1111, 681))
        self.table.setStyleSheet("")
        self.table.setObjectName("table")
        self.table.setColumnCount(0)
        self.table.setRowCount(0)
        self.edit_students = QtWidgets.QPushButton(self.centralwidget)
        self.edit_students.setGeometry(QtCore.QRect(945, 142, 18, 18))
        self.edit_students.setCursor(
            QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.edit_students.setStyleSheet("")
        self.edit_students.setText("")                 
        self.edit_students.setObjectName("edit_students")
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(830, 135, 111, 31))
        self.label.setObjectName("label")
        self.label_2 = QtWidgets.QLabel(self.centralwidget)
        self.label_2.setGeometry(QtCore.QRect(110, 135, 81, 21))
        self.label_2.setObjectName("label_2")
        self.achiev_btn = QtWidgets.QPushButton(self.centralwidget)
        self.achiev_btn.setEnabled(True)
        self.achiev_btn.setGeometry(QtCore.QRect(470, 50, 221, 50))
        self.achiev_btn.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.achiev_btn.setStyleSheet("")
        self.achiev_btn.setObjectName("achiev_btn")
        self.state_btn = QtWidgets.QPushButton(self.centralwidget)
        self.state_btn.setGeometry(QtCore.QRect(160, 50, 221, 50))
        self.state_btn.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.state_btn.setObjectName("state_btn")
        self.practical_btn = QtWidgets.QPushButton(self.centralwidget)
        self.practical_btn.setGeometry(QtCore.QRect(780, 50, 221, 50))
        self.practical_btn.setCursor(
            QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.practical_btn.setObjectName("practical_btn")
        self.settings_btn = QtWidgets.QPushButton(self.centralwidget)
        self.settings_btn.setGeometry(QtCore.QRect(1090, 50, 221, 50))
        self.settings_btn.setCursor(
            QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.settings_btn.setObjectName("settings_btn")
        
        '''
        self.widget = QtWidgets.QWidget(self.centralwidget)
        self.widget.setGeometry(QtCore.QRect(0, 0, 1441, 41))
        self.widget.setObjectName("widget")
        self.btn_close = QtWidgets.QPushButton(self.widget)
        self.btn_close.setGeometry(QtCore.QRect(1310, 10, 75, 23))
        self.btn_close.setObjectName("btn_close")
        self.btn_max = QtWidgets.QPushButton(self.widget)
        self.btn_max.setGeometry(QtCore.QRect(1200, 10, 75, 23))
        self.btn_max.setObjectName("btn_max")
        self.btn_min = QtWidgets.QPushButton(self.widget)
        self.btn_min.setGeometry(QtCore.QRect(1090, 10, 75, 23))
        self.btn_min.setObjectName("btn_min")
        '''
        
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 1440, 34))
        self.menubar.setStyleSheet("")
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setStyleSheet("")
        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.label.setText(_translate("MainWindow", "студенты"))
        self.label_2.setText(_translate("MainWindow", "потоки"))
        
        self.achiev_btn.setText(_translate("MainWindow", "достижения"))
        self.state_btn.setText(_translate("MainWindow", "ведомость"))
        self.practical_btn.setText(_translate("MainWindow", "практические"))
        self.settings_btn.setText(_translate("MainWindow", "настройки"))
        
#        self.btn_close.setText(_translate("MainWindow", "3"))
#        self.btn_max.setText(_translate("MainWindow", "2"))
#        self.btn_min.setText(_translate("MainWindow", "1"))
        

# +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)

        layout_maim = QVBoxLayout(self.centralwidget)
        
        lay_h = QHBoxLayout()
        lay_h.addWidget(self.state_btn)
        lay_h.addWidget(self.achiev_btn)
        lay_h.addWidget(self.practical_btn)
        lay_h.addWidget(self.settings_btn)
        layout_maim.addLayout(lay_h)
        
        lay_h = QHBoxLayout()
        lay_h.addStretch(0)
        lay_h.addWidget(self.label)
        lay_h.addWidget(self.edit_streams)
        lay_h.addStretch(2)
        lay_h.addWidget(self.label_2)
        lay_h.addWidget(self.edit_students)
        lay_h.addStretch(2)
        layout_maim.addLayout(lay_h)
        
        lay_h = QHBoxLayout()
        lay_h.addWidget(self.streams, 2)
        lay_h.addWidget(self.table, 3)
        layout_maim.addLayout(lay_h)    

        labels = ("Item 1", "Item 2", "Item 3", "Item 4", "Item 5", "Item 6", "Item 7") 
        self.streams.addItems(labels)

        self.table.setRowCount(7)
        self.table.setColumnCount(5)
        self.table.setHorizontalHeaderLabels(['V1','V2','V3','V4','V5'])
        self.table.verticalHeader().hide()
        header    = self.table.horizontalHeader()        
        for i in range(7):
            for j in range(5):
                item = QtWidgets.QTableWidgetItem("Item {}-{}".format(i, j))
                item.setTextAlignment(QtCore.Qt.AlignHCenter)
                self.table.setItem(i, j, item)
# +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())        

q1252882-222.py

from PyQt5.QtCore import Qt, QPointF
from PyQt5.QtGui import QWindowStateChangeEvent, QFont, QMouseEvent
from PyQt5.QtWidgets import QWidget, QHBoxLayout, QSpacerItem, QSizePolicy, \
    QLabel, QPushButton, QApplication, QVBoxLayout, QDialog
from PyQt5 import QtCore, QtGui, QtWidgets

from q1252882 import MainWindow                                      # Импорт вашнго приложения !!!


class CTitleBar(QWidget):
    Radius = 38

    def __init__(self, *args, title='заглавие, название', **kwargs):
        super(CTitleBar, self).__init__(*args, **kwargs)
        self.setupUi()
        
        # Поддержка настройки фона
        self.setAttribute(Qt.WA_StyledBackground, True)
        self.mPos = None
        # Найти родительский элемент управления self.parent() (или self)
        self._root = self.window()             
        self.labelTitle.setText(title)
        
        # Вам нужно скрыть кнопку свернуть или развернуть
        self.showMinimizeButton(self.isMinimizeable())
        self.showNormalButton(False)
        self.showMaximizeButton(self.isMaximizeable())

        # Связывающий сигнал
        # windowTitleChanged сигнал испускается при изменении заголовка окна 
        # с новым заголовком в качестве аргумента.
        self._root.windowTitleChanged.connect(self.setWindowTitle)
        self.buttonMinimum.clicked.connect(self.showMinimized)
        self.buttonMaximum.clicked.connect(self.showMaximized)
        self.buttonNormal.clicked.connect(self.showNormal)
        self.buttonClose.clicked.connect(self._root.close)
        
        # Установите фильтр событий на родительский элемент управления (или на себя)
        self._root.installEventFilter(self)

    def showMinimized(self):
        self._root.showMinimized()
        QApplication.sendEvent(self.buttonMinimum, QMouseEvent(
            QMouseEvent.Leave, QPointF(), Qt.LeftButton, Qt.NoButton, Qt.NoModifier))

    def showNormal(self):
        ''' Восстанавливает виджет после того, как он был развернут или свернут. '''
        self._root.showNormal()
        QApplication.sendEvent(self.buttonMaximum, QMouseEvent(
            QMouseEvent.Leave, QPointF(), Qt.LeftButton, Qt.NoButton, Qt.NoModifier))

    def showMaximized(self):
        self._root.showMaximized()
        QApplication.sendEvent(self.buttonNormal, QMouseEvent(
            QMouseEvent.Leave, QPointF(), Qt.LeftButton, Qt.NoButton, Qt.NoModifier))

    def isMinimizeable(self):
        return self.testWindowFlags(Qt.WindowMinimizeButtonHint)

    def isMaximizeable(self):
        return self.testWindowFlags(Qt.WindowMaximizeButtonHint)

    def isResizable(self):
        return self._root.minimumSize() != self._root.maximumSize()

    def showMinimizeButton(self, show=True):
        """ Показать скрытую кнопку сворачивания """
        self.buttonMinimum.setVisible(show)
        self.widgetMinimum.setVisible(show)

    def showMaximizeButton(self, show=True):
        """ Показать скрытую кнопку максимизации """
        self.buttonMaximum.setVisible(show)
        self.widgetMaximum.setVisible(show)

    def showNormalButton(self, show=True):
        """ Показать скрытую кнопку восстановления """
        self.buttonNormal.setVisible(show)
        self.widgetNormal.setVisible(show)

    def showEvent(self, event):
        super(CTitleBar, self).showEvent(event)
        if not self.isResizable():
            self.showMaximizeButton(False)
            self.showNormalButton(False)
        else:
            self.showMaximizeButton(
                self.isMaximizeable() and not self._root.isMaximized())
            self.showNormalButton(self.isMaximizeable()
                                  and self._root.isMaximized())

    def eventFilter(self, target, event):
        if isinstance(event, QWindowStateChangeEvent):
            if self._root.isVisible() and not self._root.isMinimized() and \
                    self.testWindowFlags(Qt.WindowMinMaxButtonsHint):
                maximized = self._root.isMaximized()
                self.showMaximizeButton(not maximized)
                self.showNormalButton(maximized)
                
                if maximized:
                    self._oldMargins = self._root.layout().getContentsMargins()
                    self._root.layout().setContentsMargins(0, 0, 0, 0)
                else:
                    if hasattr(self, '_oldMargins'):
                        self._root.layout().setContentsMargins(*self._oldMargins)
        return super(CTitleBar, self).eventFilter(target, event)

    def mouseDoubleClickEvent(self, event):
        """ Дважды щелкните строку заголовка
        :param event:
        """
        if not self.isMaximizeable() or not self.isResizable():
            return
        if self._root.isMaximized():
            self._root.showNormal()
        else:
            self._root.showMaximized()

    def mousePressEvent(self, event):
        """ Координаты записи нажатия мышью
        :param event:
        """
        if event.button() == Qt.LeftButton:
            self.mPos = event.pos()

    def mouseReleaseEvent(self, event):
        """ Мышь отпущена, удалить координаты
        :param event:
        """
        self.mPos = None

    def mouseMoveEvent(self, event):
        """ Мышь двигает окно
        :param event:
        """
        if self._root.isMaximized():
            # Не двигается при максимизации
            return
        if event.buttons() == Qt.LeftButton and self.mPos:
            pos = event.pos() - self.mPos
            self._root.move(self._root.pos() + pos)

    def testWindowFlags(self, windowFlags):
        """ Определите, есть ли у текущего окна флаги
        :param windowFlags:
        """
        return bool(self._root.windowFlags() & windowFlags)

    def setWindowTitle(self, title):
        """ Установка заголовка
        :param title:
        """
        self.labelTitle.setText(title)

    def setupUi(self):
        """ Создать пользовательский интерфейс """
        self.setMinimumSize(0, self.Radius)
        self.setMaximumSize(0xFFFFFF, self.Radius)
        
        layout = QHBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        
        for name in ('widgetMinimum', 'widgetMaximum', 'widgetNormal', 'widgetClose'):
            widget = QWidget(self)
            widget.setMinimumSize(self.Radius, self.Radius)
            widget.setMaximumSize(self.Radius, self.Radius)
            widget.setObjectName('CTitleBar_%s' % name)
            setattr(self, name, widget)
            layout.addWidget(widget)
            
        layout.addItem(QSpacerItem(
            40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
            
        # Название
        self.labelTitle = QLabel(self, alignment=Qt.AlignCenter)
        self.labelTitle.setObjectName('CTitleBar_labelTitle')
        layout.addWidget(self.labelTitle)
        layout.addItem(QSpacerItem(
            40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
            
        # Свернуть, развернуть, восстановить, кнопкa закрыть 
        for name, text in (('buttonMinimum', '0'), ('buttonMaximum', '1'),
                           ('buttonNormal', '2'), ('buttonClose', 'r')):
            button = QPushButton(text, self, font=QFont('Webdings'))
            button.setMinimumSize(self.Radius, self.Radius)
            button.setMaximumSize(self.Radius, self.Radius)
            button.setObjectName('CTitleBar_%s' % name)
            setattr(self, name, button)
            layout.addWidget(button)


class TestCTitleBarBase:
    def __init__(self, *args, **kwargs):
        super(TestCTitleBarBase, self).__init__(*args, **kwargs)
        self.resize(1000, 600)
        
        # Установите прозрачность фона
        self.setAttribute(Qt.WA_TranslucentBackground, True)
        
        # Установите без границ
        self.setWindowFlags(self.windowFlags() | Qt.FramelessWindowHint)
        
        layout = QVBoxLayout(self)
        layout.setSpacing(0)
        
        # Добавить пользовательскую строку заголовка
        layout.addWidget(CTitleBar(self, title='Custom TitleBar.'))
       
        # нижний Widget
        self.widget = QWidget(self, objectName='bottomWidget')
        layout.addWidget(self.widget)

        # это ваш код :)
        self.mainWindow = MainWindow()                                 # +++ вот ваше приложение !!!
        box = QHBoxLayout(self.widget)
        box.setContentsMargins(0, 0, 0, 0)
        box.addWidget(self.mainWindow)                                 # +++ вот ваше приложение !!!


class TestCTitleBarWidget(QWidget, TestCTitleBarBase):
    pass


# стиль строки заголовка
Style = """
/* Цвет строки заголовка */
CTitleBar {
    background: rgb(65, 148, 216);
}
/* Окно заголовка закруглено */
CTitleBar {
    border-top-right-radius: 10px;
    border-top-left-radius:  10px;
}
#CTitleBar_buttonClose {
    /*  Вам нужно принять во внимание (закруглить) кнопку закрытия на правой стороне */
    border-top-right-radius: 10px;
}

/* Нижние закругленные углы и фон */
#bottomWidget {
    background: white;
    border-bottom-right-radius: 10px;
    border-bottom-left-radius: 10px;
}

/*  Свернуть, развернуть, кнопка восстановления  */
CTitleBar > QPushButton {
    background: transparent;
}
CTitleBar > QPushButton:hover {
    background: rgba(0, 0, 0, 30);
}
CTitleBar > QPushButton:pressed {
    background: rgba(0, 0, 0, 60);
}

/*  Кнопка закрытия  */
#CTitleBar_buttonClose:hover {
    color: white;
    background: rgb(232, 17, 35);
}
#CTitleBar_buttonClose:pressed {
    color: white;
    background: rgb(165, 69, 106);
}
"""

if __name__ == '__main__':
    import sys
    
    import cgitb
    sys.excepthook = cgitb.enable(1, None, 5, '')
    
    app = QApplication(sys.argv)
    app.setStyleSheet(Style)
    w = TestCTitleBarWidget()
    w.setWindowTitle('Главное окно')
    w.show()
    sys.exit(app.exec_())

введите сюда описание изображения

→ Ссылка