PyQt6自定义标题栏导致窗口与光标移动方式不同

问题描述 投票:0回答:1

这是我的 GUI 程序,我已经工作了 2 个多月了。

我终于完成了它,并将其发布在 Code Review 上,并且我还为其打开了 GitHub repository。我已经实现了我最初想要的一切,并且修复了我发现的每个错误。

现在我想自定义标题栏,但不知何故它不起作用。由于项目庞大,仅脚本就包含113714个字符,我无法在这里发布完整的代码。我也无法发布最小的可重现示例,因为我不知道是什么导致了该错误,我改编的代码工作正常,但是当我将自定义标题栏与所有其他内容一起放置到主窗口时,它出现故障。

我找到了这个答案,并将其改编为PyQt6:

import sys

from PyQt6.QtCore import QPoint
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QApplication
from PyQt6.QtWidgets import QHBoxLayout
from PyQt6.QtWidgets import QLabel
from PyQt6.QtWidgets import QPushButton
from PyQt6.QtWidgets import QVBoxLayout
from PyQt6.QtWidgets import QWidget


class MainWindow(QWidget):
    def __init__(self):
        super(QWidget, self).__init__()
        self.layout = QVBoxLayout()
        self.layout.addWidget(MyBar())
        self.setLayout(self.layout)
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.layout.addStretch(-1)
        self.setMinimumSize(800, 400)
        self.setWindowFlags(Qt.WindowType.FramelessWindowHint)
        self.pressing = False


class MyBar(QWidget):
    def __init__(self, ):
        super(MyBar, self).__init__()
        self.layout = QHBoxLayout()
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.title = QLabel("My Own Bar")

        btn_size = 35

        self.btn_close = QPushButton("x")
        self.btn_close.clicked.connect(self.btn_close_clicked)
        self.btn_close.setFixedSize(btn_size, btn_size)
        self.btn_close.setStyleSheet("background-color: red;")

        self.btn_min = QPushButton("-")
        self.btn_min.clicked.connect(self.btn_min_clicked)
        self.btn_min.setFixedSize(btn_size, btn_size)
        self.btn_min.setStyleSheet("background-color: gray;")

        self.btn_max = QPushButton("+")
        self.btn_max.clicked.connect(self.btn_max_clicked)
        self.btn_max.setFixedSize(btn_size, btn_size)
        self.btn_max.setStyleSheet("background-color: gray;")

        self.title.setFixedHeight(35)
        self.title.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.layout.addWidget(self.title)
        self.layout.addWidget(self.btn_min)
        self.layout.addWidget(self.btn_max)
        self.layout.addWidget(self.btn_close)

        self.title.setStyleSheet(
            """
            background-color: black;
            color: white;
        """
        )
        self.setLayout(self.layout)


    def resizeEvent(self, QResizeEvent):
        super(MyBar, self).resizeEvent(QResizeEvent)
        self.title.setFixedWidth(self.window().width())

    def mousePressEvent(self, event):
        self.start = self.mapToGlobal(event.pos())
        self.pressing = True

    def mouseMoveEvent(self, event):
        print(event.pos())
        self.end = self.mapToGlobal(event.pos())
        delta = self.end - self.start
        self.window().setGeometry(
            self.mapToGlobal(delta).x(),
            self.mapToGlobal(delta).y(),
            self.window().width(),
            self.window().height(),
        )
        self.start = self.end

    def mouseReleaseEvent(self, QMouseEvent):
        self.pressing = False

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

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

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


if __name__ == "__main__":
    app = QApplication(sys.argv)
    app.setStyle("Fusion")
    mw = MainWindow()
    mw.show()
    sys.exit(app.exec())

工作正常。

所以我尝试将其添加到我的项目中,但不知何故它无法正常工作,相关代码如下:

class SquareButton(QPushButton):
    def __init__(self, text: str) -> None:
        super().__init__()
        self.setFont(FONT)
        self.setText(text)
        self.setFixedSize(32, 32)


class TitleBar(QWidget):
    def __init__(self, name: str, exitfunc: Callable) -> None:
        super().__init__()
        self.hbox = make_hbox(self, 0)
        self.name = name
        self.exitfunc = exitfunc
        self.setObjectName("Title")
        self.add_icon()
        self.add_title()
        self.add_buttons()
        self.setFixedHeight(35)

    def add_icon(self) -> None:
        self.icon = QLabel()
        self.icon.setPixmap(GLOBALS["Logo"])
        self.icon.setFixedSize(32, 32)
        self.hbox.addWidget(self.icon)
        self.hbox.addStretch()

    def add_title(self) -> None:
        self.title = Label(self.name)
        self.hbox.addWidget(self.title)
        self.hbox.addStretch()

    def add_buttons(self) -> None:
        self.minimize_button = SquareButton("—")
        self.minimize_button.clicked.connect(self.minimize)
        self.hbox.addWidget(self.minimize_button)
        self.close_button = SquareButton("X")
        self.close_button.clicked.connect(self.exitfunc)
        self.hbox.addWidget(self.close_button)

    def mousePressEvent(self, e: QMouseEvent) -> None:
        self.start = self.mapToGlobal(e.pos())
        self.pressing = True

    def mouseMoveEvent(self, e: QMouseEvent):
        if self.pressing:
            print(e.pos())
            self.end = self.mapToGlobal(e.pos())
            delta = self.end - self.start
            self.window().setGeometry(
                self.mapToGlobal(delta).x(),
                self.mapToGlobal(delta).y(),
                self.window().width(),
                self.window().height(),
            )
            self.start = self.end

    def mouseReleaseEvent(self, e: QMouseEvent) -> None:
        self.pressing = False

    def minimize(self) -> None:
        self.window().showMinimized()

我不知道是什么导致了问题,代码应该可以工作,但是当我运行它并尝试通过拖动标题栏来移动窗口时,窗口移动不规则,其移动与窗口的移动不对应光标。起初它似乎跟随光标,但稍微落后了一点,然后它根本不跟随光标。

窗口有向右下移动的趋势,如果鼠标向上移动窗口不知何故向下移动,窗口将移动到屏幕下边缘下方,窗口似乎与光标移动方向相同水平,但距离不一样。

所以我尝试通过打印事件的位置来调试它,并且工作示例打印所有正坐标:

PyQt6.QtCore.QPoint(575, 17)
PyQt6.QtCore.QPoint(575, 17)
PyQt6.QtCore.QPoint(575, 17)
PyQt6.QtCore.QPoint(575, 16)
PyQt6.QtCore.QPoint(574, 17)
PyQt6.QtCore.QPoint(575, 17)
PyQt6.QtCore.QPoint(574, 17)
PyQt6.QtCore.QPoint(571, 16)
PyQt6.QtCore.QPoint(575, 16)
PyQt6.QtCore.QPoint(575, 17)
PyQt6.QtCore.QPoint(571, 15)
PyQt6.QtCore.QPoint(573, 17)
PyQt6.QtCore.QPoint(568, 16)
PyQt6.QtCore.QPoint(567, 15)
PyQt6.QtCore.QPoint(570, 14)
PyQt6.QtCore.QPoint(567, 13)
PyQt6.QtCore.QPoint(564, 11)
PyQt6.QtCore.QPoint(564, 13)
PyQt6.QtCore.QPoint(565, 13)
PyQt6.QtCore.QPoint(565, 16)
PyQt6.QtCore.QPoint(562, 13)
PyQt6.QtCore.QPoint(564, 11)

但是不知何故,在我的项目中使用的代码会打印一些负坐标:

PyQt6.QtCore.QPoint(55, -322)
PyQt6.QtCore.QPoint(51, -327)
PyQt6.QtCore.QPoint(47, -330)
PyQt6.QtCore.QPoint(41, -334)
PyQt6.QtCore.QPoint(37, -337)
PyQt6.QtCore.QPoint(35, -341)
PyQt6.QtCore.QPoint(30, -343)
PyQt6.QtCore.QPoint(30, -346)
PyQt6.QtCore.QPoint(29, -348)
PyQt6.QtCore.QPoint(26, -351)
PyQt6.QtCore.QPoint(25, -352)
PyQt6.QtCore.QPoint(25, -354)
PyQt6.QtCore.QPoint(23, -356)
PyQt6.QtCore.QPoint(19, -358)
PyQt6.QtCore.QPoint(16, -362)
PyQt6.QtCore.QPoint(14, -365)
PyQt6.QtCore.QPoint(9, -368)
PyQt6.QtCore.QPoint(7, -372)
PyQt6.QtCore.QPoint(3, -374)
PyQt6.QtCore.QPoint(-2, -378)
PyQt6.QtCore.QPoint(-1, -379)
PyQt6.QtCore.QPoint(-7, -383)
PyQt6.QtCore.QPoint(-7, -386)
PyQt6.QtCore.QPoint(-10, -388)
PyQt6.QtCore.QPoint(-14, -392)
PyQt6.QtCore.QPoint(-17, -394)
PyQt6.QtCore.QPoint(-20, -399)
PyQt6.QtCore.QPoint(-22, -401)
PyQt6.QtCore.QPoint(-25, -403)
PyQt6.QtCore.QPoint(-30, -407)
PyQt6.QtCore.QPoint(-31, -409)

我已将完整代码打包为 zip 文件并上传到 Google Drive,以便您可以运行代码并进行调试。因为我使用pickle数据文件,所以在运行

analyze_tic_tac_toe_states.py
调用窗口之前,您可能需要运行
main.py
来生成数据文件。

是什么导致了这个奇怪的错误以及如何修复它?

python pyqt pyqt6
1个回答
0
投票

由于某种原因

QWidget.mapToGlobal(e.pos())
有时会输出不正确的值,这可能与待处理的几何更改有关。为了避免不稳定的运动,请使用
QMouseEvent.globalPosition().toPoint()

from PyQt6.QtWidgets import QWidget, QApplication
from PyQt6.QtGui import QMouseEvent
from PyQt6.QtCore import QRect, QPoint

class TitleBar(QWidget):

    def mousePressEvent(self, e: QMouseEvent) -> None:
        self.start = e.globalPosition().toPoint()
        self.pressing = True

    def mouseMoveEvent(self, e: QMouseEvent):
        if self.pressing:
            self.end = e.globalPosition().toPoint()
            delta = self.end - self.start
            self.window().setGeometry(
                self.mapToGlobal(delta).x(),
                self.mapToGlobal(delta).y(),
                self.window().width(),
                self.window().height(),
            )
            self.start = self.end

    def mouseReleaseEvent(self, e: QMouseEvent) -> None:
        self.pressing = False

if __name__ == "__main__":
    app = QApplication([])
    widget = TitleBar()
    widget.show()
    app.exec()

或者您可以在按下鼠标时保存距小部件左上角(局部坐标)的偏移量,并将窗口移动到以此值偏移的全局坐标,并忘记

start
end

from PyQt6.QtWidgets import QWidget, QApplication
from PyQt6.QtGui import QMouseEvent
from PyQt6.QtCore import QRect

class TitleBar(QWidget):

    def mousePressEvent(self, e: QMouseEvent) -> None:
        self.offset = e.pos()
        self.pressing = True
        
    def mouseMoveEvent(self, e: QMouseEvent):
        if self.pressing:
            geometry = self.geometry()
            newGeometry = QRect(e.globalPosition().toPoint() - self.offset, geometry.size())
            self.setGeometry(newGeometry)

    def mouseReleaseEvent(self, e: QMouseEvent) -> None:
        self.pressing = False

if __name__ == "__main__":
    app = QApplication([])
    widget = TitleBar()
    widget.show()
    app.exec()
© www.soinside.com 2019 - 2024. All rights reserved.