Source code for qrainbowstyle.windows.FramelessWindow

import logging

from qtpy.QtCore import QObject, QRect, Qt, QPoint, QSize, QEvent
from qtpy.QtGui import QMouseEvent, QCursor
from qtpy.QtWidgets import QApplication, QWidget

from .base import FramelessWindowBase


[docs]class FramelessWindow(FramelessWindowBase): """Frameless window for non-Windows OS like Linux and Darwin. Reimplements features: - window moving - window resizing - maximize on double click on titlebar - snap to borders """ def __init__(self, parent=None): super(FramelessWindow, self).__init__(parent) self.__titlebarHeight = 45 self.__gripsize = 3 self.__sideGripIgnore = 20 self.__griprect = None self.__hgriprect = None self.__vgriprect = None self.__moving = False self.__move_offset = QPoint() self.__resizing = False self.__horizontalResizing = False self.__verticalResizing = False self.installEventFilter(self) self.__updateGripRect() self.setMouseTracking(True)
[docs] def setMouseTracking(self, flag): """Recursively enables mouse tracking for all child widgets. This is required because eventFilter does not catch hover events. Args: flag (bool): Enable or disable mouse tracking. """ def recursive_set(parent): for child in parent.findChildren(QObject): try: child.setMouseTracking(flag) except AttributeError: pass recursive_set(child) QWidget.setMouseTracking(self, flag) recursive_set(self)
def __updateGripRect(self): """Update rects for current window geometry.""" # corner grip self.__griprect = QRect(self.width() - self.__gripsize, self.height() - self.__gripsize, self.__gripsize, self.__gripsize) # right grip self.__hgriprect = QRect(self.width() - self.__gripsize, self.__titlebarHeight, self.__gripsize, self.height() - self.__gripsize - self.__sideGripIgnore - self.__titlebarHeight) # bottom grip self.__vgriprect = QRect(self.__gripsize, self.height() - self.__gripsize, self.width() - self.__gripsize - self.__sideGripIgnore, self.__gripsize)
[docs] def eventFilter(self, widget, event: QEvent): """Handle frameless window events. Args: widget (QObject): Widget. event (QEvent): Event. """ if (hasattr(widget, "window") and widget.window() is self): self.__updateGripRect() if (hasattr(event, "x") and self.titlebar().rect().contains(self.mapFromParent(QCursor.pos())) and self.titlebar().mouseOverTitlebar(event.x(), event.y())): # when cursor is over titlebar if event.type() == QMouseEvent.MouseButtonPress and event.buttons() == Qt.LeftButton: # if titlebar clicked with left button self.__moving = True margins = self.titlebar().contentsMargins() self.__move_offset = event.pos() + self.titlebar().pos() - QPoint(margins.left(), margins.top()) elif event.type() == QMouseEvent.MouseMove and event.buttons() == Qt.LeftButton: # if holding left button and moving window if self.__moving: if self.windowState() == Qt.WindowMaximized: self.setWindowState(Qt.WindowNoState) self.titlebar().on_btnRestore_clicked() self.move(event.pos() - self.mapFromParent(event.pos())) self.__moving = False else: self.move(event.globalPos() - self.__move_offset) elif event.type() == QMouseEvent.MouseButtonRelease and event.button() == Qt.LeftButton: # if left button released self.__moving = False screen = QApplication.instance().desktop().availableGeometry() if event.globalY() == 0: # snap to top edge self.titlebar().on_btnMaximize_clicked() elif event.globalX() == 0: # snap to left edge self.move(QPoint(0, 0)) self.resize(QSize(screen.width() / 2, screen.height())) self.setWindowState(Qt.WindowNoState) elif event.globalX() + 1 >= screen.width(): # snap to right edge self.move(QPoint(screen.width() / 2, 0)) self.resize(QSize(screen.width() / 2, screen.height())) self.setWindowState(Qt.WindowNoState) elif self.geometry().y() < 0: # move window if top of window is outside display self.move(QPoint(self.geometry().x(), 0)) elif event.type() == QMouseEvent.MouseButtonDblClick: # maximize/restore on double click if self.windowState() == Qt.WindowMaximized: self.setWindowState(Qt.WindowNoState) elif self.windowState() == Qt.WindowNoState: self.showMaximized() elif event.type() == QEvent.Leave and widget is self.titlebar(): # fixes __moving remain True after clicking titlebar on border and leaving titlebar self.__moving = False elif (self.windowState() not in (Qt.WindowFullScreen, Qt.WindowMaximized) and self.isResizingEnabled()): # when cursor is not over titlebar # and window is not maximized or in full screen mode # button pressed if event.type() == QMouseEvent.MouseButtonPress: if self.__griprect.contains( event.pos()) and not self.__horizontalResizing and not self.__verticalResizing: # corner grip self.__resizing = True elif self.__hgriprect.contains( event.pos()) and not self.__resizing and not self.__verticalResizing: # right grip self.__horizontalResizing = True elif self.__vgriprect.contains( event.pos()) and not self.__resizing and not self.__horizontalResizing: # bottom grip self.__verticalResizing = True if event.type() == QMouseEvent.MouseButtonRelease: # stop resizing if self.__resizing or self.__horizontalResizing or self.__verticalResizing: self.__resizing = False self.__horizontalResizing = False self.__verticalResizing = False self.setCursor(Qt.ArrowCursor) if event.type() == QMouseEvent.MouseMove: # moving cursor while holding left button -> resize if self.__resizing and not self.__horizontalResizing and not self.__verticalResizing: if event.buttons() == Qt.LeftButton: self.setCursor(Qt.SizeFDiagCursor) self.resize(QSize(event.x(), event.y())) elif self.__horizontalResizing and not self.__resizing and not self.__verticalResizing: if event.buttons() == Qt.LeftButton: self.setCursor(Qt.SizeHorCursor) self.resize(QSize(event.x(), self.height())) elif self.__verticalResizing and not self.__resizing and not self.__horizontalResizing: if event.buttons() == Qt.LeftButton: self.setCursor(Qt.SizeVerCursor) self.resize(QSize(self.width(), event.y())) else: if (self.__griprect.contains(event.pos()) and not self.__horizontalResizing and not self.__verticalResizing): self.setCursor(Qt.SizeFDiagCursor) elif (self.__hgriprect.contains(event.pos()) and not self.__resizing and not self.__verticalResizing): self.setCursor(Qt.SizeHorCursor) elif (self.__vgriprect.contains(event.pos()) and not self.__resizing and not self.__horizontalResizing): self.setCursor(Qt.SizeVerCursor) else: self.setCursor(Qt.ArrowCursor) return super().eventFilter(widget, event)