Как поправить работу кастомизированного окна приложения?

Недавно я задавал вопрос о том, как кастомизировать шапку окна - Как кастомизировать шапку окна приложения определенным образом?

Мне показали пример кода, состоящий из двух python-файлов, который мог бы решить мой вопрос. Пока я настраивал его под свои нужды, заметил одну проблему.

Если потянуть за шапку окна большинства приложений, когда оно находится в развернутом на весь экран состоянии, то это окно перейдет в свое нормальное состояние (свернется до определенного размера) и будет готово к перемещению по экрану. Но окно, которое создает предложенный мне код, никак не реагирует на попытки переместить его, если находится в развернутом состоянии. Автор кода специально заблокировал такую возможность, поэтому я внес в него некоторые его:

  1. Окно теперь может быть перемещено, даже если оно находится в развернутом состоянии.

  2. Если начать перемещать развернутое окно, то оно перейдет в свое нормальное состояние (свернется до определенного размера).

Но этих изменений оказалось недостаточно. Если начать перемещать развернутое окно, то, свернувшись до своих обычных размеров, оно переместится сначала верхний левый угол экрана. Мне же нужно, чтобы в данном случае горизонтальная координата центра окна была равна координате курсора мыши. Иначе говоря, нужно, чтобы окно, которое было развернуто, при перемещении сворачивалось и его горизонтальный центр оказывался там же, где курсор мыши. Пожалуйста, скажите, как мне это сделать?

Main.py

from PyQt5 import QtCore, QtWidgets, QtGui
import FramelessWindow


class MainWindow(QtWidgets.QWidget):

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

        layout = QtWidgets.QVBoxLayout(self, spacing=0)
        layout.addWidget(QtWidgets.QPushButton('Button', self))


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

FramelessWindow.py

from PyQt5 import QtCore, QtGui, QtWidgets

class TitleBar(QtWidgets.QWidget):

    windowMinimumed = QtCore.pyqtSignal()
    windowMaximumed = QtCore.pyqtSignal()
    windowNormaled = QtCore.pyqtSignal()
    windowClosed = QtCore.pyqtSignal()
    windowMoved = QtCore.pyqtSignal(QtCore.QPoint)

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

        self.not_Maximum = True
        self.setAttribute(QtCore.Qt.WA_StyledBackground, True)
        self.mPos = None

        self.setAutoFillBackground(True)
        palette = self.palette()
        palette.setColor(palette.Window, QtGui.QColor('#36393F'))
        self.setPalette(palette)

        layout = QtWidgets.QHBoxLayout(self, spacing=0)
        layout.setContentsMargins(0, 0, 0, 0)
 
        layout.addSpacerItem(QtWidgets.QSpacerItem(
            40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum))

        self.buttonMinimum = QtWidgets.QPushButton(self, clicked=self.windowMinimumed.emit, objectName='buttonMinimum')
        self.buttonMinimum.setFixedSize(30, 20)
        self.buttonMinimum.setStyleSheet(qss_1)
        layout.addWidget(self.buttonMinimum)

        self.buttonMaximum = QtWidgets.QPushButton(self, clicked=self.showMaximized, objectName='buttonMaximum')
        self.buttonMaximum.setFixedSize(30, 20)
        self.buttonMaximum.setStyleSheet(qss_2)
        layout.addWidget(self.buttonMaximum)

        self.buttonClose = QtWidgets.QPushButton(self, clicked=self.windowClosed.emit, objectName='buttonClose')
        self.buttonClose.setFixedSize(30, 20)
        self.buttonClose.setStyleSheet(qss_4)
        layout.addWidget(self.buttonClose)

        self.setFixedHeight(20)

    def showMaximized(self):
        if self.not_Maximum == True:
            self.not_Maximum = False
            self.buttonMaximum.setStyleSheet(qss_3)
            self.windowMaximumed.emit()
        else:
            self.not_Maximum = True
            self.buttonMaximum.setStyleSheet(qss_2)
            self.windowNormaled.emit()

    def enterEvent(self, event):
        self.setCursor(QtCore.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() == QtCore.Qt.LeftButton:
            self.mPos = event.pos()
        event.accept()

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

    def mouseMoveEvent(self, event):
        if event.buttons() == QtCore.Qt.LeftButton and self.mPos:
            if self.not_Maximum == False:
                self.not_Maximum = True
                self.buttonMaximum.setStyleSheet(qss_2)
                self.windowNormaled.emit()
            self.windowMoved.emit(self.mapToGlobal(event.pos() - self.mPos))
        event.accept()

Left, Top, Right, Bottom, LeftTop, RightTop, LeftBottom, RightBottom = range(8)


class FramelessWindow(QtWidgets.QWidget):

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

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

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

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

        # макет
        layout = QtWidgets.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)

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

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

    #def move(self, pos):
        #if self.windowState() == QtCore.Qt.WindowMaximized or self.windowState() == QtCore.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, QtGui.QEnterEvent):
            self.setCursor(QtCore.Qt.ArrowCursor)
        return super(FramelessWindow, self).eventFilter(obj, event)

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

    def mousePressEvent(self, event):
        """ Событие клика мыши """
        super(FramelessWindow, self).mousePressEvent(event)
        if event.button() == QtCore.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(QtCore.Qt.ArrowCursor)
            return
        if event.buttons() == QtCore.Qt.LeftButton and self._pressed:
            self._resizeWidget(pos)
            return
        if xPos <= self.Margins and yPos <= self.Margins:
            # Верхний левый угол
            self.Direction = LeftTop
            self.setCursor(QtCore.Qt.SizeFDiagCursor)
        elif wm <= xPos <= self.width() and hm <= yPos <= self.height():
            # Нижний правый угол
            self.Direction = RightBottom
            self.setCursor(QtCore.Qt.SizeFDiagCursor)
        elif wm <= xPos and yPos <= self.Margins:
            # верхний правый угол
            self.Direction = RightTop
            self.setCursor(QtCore.Qt.SizeBDiagCursor)
        elif xPos <= self.Margins and hm <= yPos:
            # Нижний левый угол
            self.Direction = LeftBottom
            self.setCursor(QtCore.Qt.SizeBDiagCursor)
        elif 0 <= xPos <= self.Margins and self.Margins <= yPos <= hm:
            # Влево
            self.Direction = Left
            self.setCursor(QtCore.Qt.SizeHorCursor)
        elif wm <= xPos <= self.width() and self.Margins <= yPos <= hm:
            # Право
            self.Direction = Right
            self.setCursor(QtCore.Qt.SizeHorCursor)
        elif self.Margins <= xPos <= wm and 0 <= yPos <= self.Margins:
            # выше
            self.Direction = Top
            self.setCursor(QtCore.Qt.SizeVerCursor)
        elif self.Margins <= xPos <= wm and hm <= yPos <= self.height():
            # ниже
            self.Direction = Bottom
            self.setCursor(QtCore.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)

qss_1 = '''
QPushButton {
    border: none;
    margin: 0px;
    padding: 0px;
    border-image: url(-_1.png);        
}
QPushButton:hover {
    border-image: url(-_2.png);
}
'''

qss_2 = '''
QPushButton {
    border: none;
    margin: 0px;
    padding: 0px;
    border-image: url(rectangle_1.png);        
}
QPushButton:hover {
    border-image: url(rectangle_2.png);
}
'''

qss_3 = '''
QPushButton {
    border: none;
    margin: 0px;
    padding: 0px;
    border-image: url(duo_rectangle_1.png);        
}
QPushButton:hover {
    border-image: url(duo_rectangle_2.png);
}
'''

qss_4 = '''
QPushButton {
    border: none;
    margin: 0px;
    padding: 0px;
    border-image: url(x_1.png);        
}
QPushButton:hover {
    border-image: url(x_2.png);
}
'''

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