Как устранить ошибку (завершение программы) при попытке переместить окно без рамки за кнопку?

Проблема следующая - как основное окно использую класс QMainWindow, убрал рамку с помощью флага FramelessWindowHint, далее добавил функций для перемещения за любую область.

Всё работает отлично, но наткнулся на следующую ошибку. При попытке нажать на кнопку "свернуть", с задержкой примерно в 1 секунду и смещением мыши с зажатой кнопкой - приложение захлопывается и возвращает следующее исключение:

delta = QPoint(event.globalPos() - self.oldPos)
AttributeError: 'MainWindow' object has no attribute 'oldPos'
<class 'AttributeError'> 'MainWindow' object has no attribute 'oldPos' <traceback object at 
0x000001EFC7B2ABC0>
class MainWindow(QtWidgets.QMainWindow):

    def __init__(self, parent=None):
        QtWidgets.QMainWindow.__init__(self, parent, flags=QtCore.Qt.Window | QtCore.Qt.MSWindowsFixedSizeDialogHint |
                                       QtCore.Qt.FramelessWindowHint)
        self.setWindowTitle("blah-blah")
        self.setFixedSize(1000, 600)
        self.settings = QtCore.QSettings("blabla", "bla")
        self.SGM = Widget()
        self.setCentralWidget(self.SGM)

        quit_button = QtWidgets.QPushButton('X', self)  # кнопка закрытия окна
        quit_button.setFixedSize(20, 20)
        quit_button.move(980, 0)
        quit_button.setFlat(True)
        quit_button.clicked.connect(self.close)

        minimize_button = QtWidgets.QPushButton('_', self)  # кнопка сворачивания окна
        minimize_button.setFixedSize(20, 20)
        minimize_button.move(960, 0)
        minimize_button.setFlat(True)
        minimize_button.clicked.connect(self.showMinimized)

    def center(self):  # блок 3х функций для перетаскивания окна за любое место
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())

    def mousePressEvent(self, event):
        self.oldPos = event.globalPos()

    def mouseMoveEvent(self, event):
        delta = QPoint(event.globalPos() - self.oldPos)
        self.move(self.x() + delta.x(), self.y() + delta.y())
        self.oldPos = event.globalPos()

Решение возможно в трёх способах:

  1. Обработать ошибку так, чтобы программа не прекращала работу.
  2. Изменить каким-то образом поведение функций, чтобы они перетаскивали окно только за элемент QLabel, который присобачен к верхней части окна(к примеру)
  3. Отредактировать код так, чтобы ошибка не возникала.

Подойдет любое решение из трёх.


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

Автор решения: Exploit89

Искал не там, где нужно, пока оставил следующий костыль в виде try-except:

def mouseMoveEvent(self, event):
    try:
        delta = QPoint(event.globalPos() - self.oldPos)
        self.move(self.x() + delta.x(), self.y() + delta.y())
        self.oldPos = event.globalPos()
    except:
        pass

То есть таким образом тупо скипаем(пропускаем) какое-либо перемещение как событие.

Но всё же, если у кого-то найдется более эффективное решение - сообщите! Буду признателен!

→ Ссылка
Автор решения: S. Nick

Решение возможно в трёх способах:

1 Обработать ошибку так, чтобы программа не прекращала работу.
3 Отредактировать код так, чтобы ошибка не возникала.

Чтобы ошибка не возникала достаточно добавить флажок, например self.pressing,
и выполнять действия, прописанные в методе mouseMoveEvent, когда self.pressing имеет значение True.

Если у вас возникнут проблемы с перетаскиванием окна только за элемент QLabel, который находится в верхней части окна, то задайте новый вопрос, с котором покажите ваш QLabel и расскажите, что вы успели попробовать делать.

import sys
from PyQt5 import QtCore, QtGui, QtWidgets


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
        self.setWindowTitle("blah-blah")
        self.resize(1000, 600)

# ?        self.settings = QtCore.QSettings("blabla", "bla")              # ?

#        self.SGM = Widget()                                              # ? Widget
        self.centralWidget = QtWidgets.QWidget()                          # +++ 
        self.setCentralWidget(self.centralWidget)                         # +++

        quit_button = QtWidgets.QPushButton('X', self.centralWidget)      # self.centralWidget
        quit_button.setFixedSize(20, 20)
        quit_button.move(980, 0)
        quit_button.setFlat(True)
        quit_button.clicked.connect(self.close)

        minimize_button = QtWidgets.QPushButton('_', self.centralWidget)  # self.centralWidget
        minimize_button.setFixedSize(20, 20)
        minimize_button.move(960, 0)
        minimize_button.setFlat(True)
        minimize_button.clicked.connect(self.showMinimized)
        
        self.center()                                                     # +++
        self.pressing = False                                             # +++ !!!

    def center(self):                     
        qr = self.frameGeometry()
        cp = QtWidgets.QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())

    def mousePressEvent(self, event):
        self.oldPos = event.globalPos()
        self.pressing = True                                              # +++ !!!

    def mouseMoveEvent(self, event):
        if self.pressing:                                                 # +++ !!!
            delta = QtCore.QPoint(event.globalPos() - self.oldPos)
            self.move(self.x() + delta.x(), self.y() + delta.y())
            self.oldPos = event.globalPos()
            
    def mouseReleaseEvent(self, event):                                   # +
        self.pressing = False                                             # +++ !!!
        
        
if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())

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

→ Ссылка