Loading launch.py +1 −0 Original line number Diff line number Diff line Loading @@ -130,6 +130,7 @@ def main(): if qtpy.API_NAME[-1] == '6': C.FLAG_QT6 = True else: C.FLAG_QT6 = False QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True) #enable highdpi scaling QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True) #use highdpi icons QApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough) Loading ui/canvas.py +25 −12 Original line number Diff line number Diff line Loading @@ -151,6 +151,8 @@ class Canvas(QGraphicsScene): textstack_changed = Signal() drop_open_folder = Signal(str) context_menu_requested = Signal(QPoint) def __init__(self, parent=None): super().__init__(parent) self.scale_factor = 1. Loading @@ -172,6 +174,9 @@ class Canvas(QGraphicsScene): self.gv.canvas = self self.gv.setAcceptDrops(True) self.gv.setContextMenuPolicy(Qt.ContextMenuPolicy.NoContextMenu) self.context_menu_requested.connect(self.createCustomContextMenu) if not C.FLAG_QT6: # mitigate https://bugreports.qt.io/browse/QTBUG-93417 # produce blurred result, saving imgs remain unaffected Loading Loading @@ -546,17 +551,20 @@ class Canvas(QGraphicsScene): def mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent) -> None: btn = event.button() self.hide_rubber_band() Qt.MouseButton.LeftButton if btn == Qt.MouseButton.MiddleButton: self.mid_btn_pressed = False elif self.creating_textblock: btn = 0 if btn == Qt.MouseButton.LeftButton else 1 return self.endCreateTextblock(btn=btn) elif btn == Qt.MouseButton.RightButton: if self.creating_textblock: tgt = 0 if btn == Qt.MouseButton.LeftButton else 1 self.endCreateTextblock(btn=tgt) if btn == Qt.MouseButton.RightButton: if self.stroke_img_item is not None: self.finish_erasing.emit(self.stroke_img_item) if self.rubber_band.isVisible(): self.rubber_band.hide() self.rubber_band_origin = None if self.textEditMode(): self.context_menu_requested.emit(event.screenPos()) elif btn == Qt.MouseButton.LeftButton: if self.stroke_img_item is not None: self.finish_painting.emit(self.stroke_img_item) Loading Loading @@ -631,8 +639,8 @@ class Canvas(QGraphicsScene): def setTextBlockMode(self, mode: bool): self.textblock_mode = mode def contextMenuEvent(self, event: QGraphicsSceneContextMenuEvent): if self.textEditMode(): def createCustomContextMenu(self, pos: QPoint): if self.textEditMode() and not self.creating_textblock: menu = QMenu(self.gv) copy_act = menu.addAction(self.tr("Copy")) paste_act = menu.addAction(self.tr("Paste")) Loading @@ -648,16 +656,16 @@ class Canvas(QGraphicsScene): ocr_translate_act = menu.addAction(self.tr("OCR and translate")) ocr_translate_inpaint_act = menu.addAction(self.tr("OCR, translate and inpaint")) rst = menu.exec_(event.screenPos()) rst = menu.exec(pos) if rst == delete_act: self.delete_textblks.emit(0) elif rst == delete_recover_act: self.delete_textblks.emit(1) elif rst == copy_act: self.copy_textblks.emit(event.scenePos()) self.copy_textblks.emit(pos.toPointF()) elif rst == paste_act: self.paste_textblks.emit(event.scenePos()) self.paste_textblks.emit(pos.toPointF()) elif rst == format_act: self.format_textblks.emit() elif rst == layout_act: Loading @@ -673,6 +681,11 @@ class Canvas(QGraphicsScene): elif rst == ocr_translate_inpaint_act: self.run_blktrans.emit(2) def hide_rubber_band(self): if self.rubber_band.isVisible(): self.rubber_band.hide() self.rubber_band_origin = None def on_hide_canvas(self): self.clear_states() Loading ui/framelesswindow/fw_qt6/linux/__init__.py +16 −11 Original line number Diff line number Diff line Loading @@ -17,16 +17,17 @@ class LinuxFramelessWindow(QWidget): super().__init__(parent=parent) self.windowEffect = LinuxWindowEffect(self) # self.titleBar = TitleBar(self) self._isResizeEnabled = True self.setWindowFlags(self.windowFlags() | Qt.WindowType.FramelessWindowHint) QCoreApplication.instance().installEventFilter(self) # self.titleBar.raise_() # self.resize(500, 500) self.resize(500, 500) # def resizeEvent(self, e): # super().resizeEvent(e) def resizeEvent(self, e): super().resizeEvent(e) # self.titleBar.resize(self.width(), self.titleBar.height()) # def setTitleBar(self, titleBar): Loading @@ -42,21 +43,25 @@ class LinuxFramelessWindow(QWidget): # self.titleBar.setParent(self) # self.titleBar.raise_() def setResizeEnabled(self, isEnabled: bool): """ set whether resizing is enabled """ self._isResizeEnabled = isEnabled def eventFilter(self, obj, event): et = event.type() if et != QEvent.Type.MouseButtonPress and et != QEvent.Type.MouseMove: if et != QEvent.Type.MouseButtonPress and et != QEvent.Type.MouseMove or not self._isResizeEnabled: return False edges = 0 edges = Qt.Edge(0) pos = event.globalPosition().toPoint() - self.pos() if pos.x() < self.BORDER_WIDTH: edges = edges | Qt.Edge.LeftEdge if edges != 0 else Qt.Edge.LeftEdge edges |= Qt.Edge.LeftEdge if pos.x() >= self.width()-self.BORDER_WIDTH: edges = edges | Qt.Edge.RightEdge if edges != 0 else Qt.Edge.RightEdge edges |= Qt.Edge.RightEdge if pos.y() < self.BORDER_WIDTH: edges = edges | Qt.Edge.TopEdge if edges != 0 else Qt.Edge.TopEdge edges |= Qt.Edge.TopEdge if pos.y() >= self.height()-self.BORDER_WIDTH: edges = edges | Qt.Edge.BottomEdge if edges != 0 else Qt.Edge.BottomEdge edges |= Qt.Edge.BottomEdge # change cursor if et == QEvent.Type.MouseMove and self.windowState() == Qt.WindowState.WindowNoState: Loading @@ -71,7 +76,7 @@ class LinuxFramelessWindow(QWidget): else: self.setCursor(Qt.CursorShape.ArrowCursor) elif obj in (self, self.titleBar) and et == QEvent.Type.MouseButtonPress and edges: elif obj == self and et == QEvent.Type.MouseButtonPress and edges: LinuxMoveResize.starSystemResize(self, event.globalPosition(), edges) return super().eventFilter(obj, event) ui/framelesswindow/fw_qt6/utils/linux_utils.py +2 −131 Original line number Diff line number Diff line # coding: utf-8 from enum import Enum import os import xcffib as xcb from PyQt6.QtCore import QPointF, Qt from xcffib.xproto import (ButtonIndex, ButtonMask, ButtonReleaseEvent, ClientMessageData, ClientMessageEvent, EventMask, xprotoExtension) class WindowMessage(Enum): """ Window message enum class """ # refer to: https://specifications.freedesktop.org/wm-spec/1.1/x170.html _NET_WM_MOVERESIZE_SIZE_TOPLEFT = 0 _NET_WM_MOVERESIZE_SIZE_TOP = 1 _NET_WM_MOVERESIZE_SIZE_TOPRIGHT = 2 _NET_WM_MOVERESIZE_SIZE_RIGHT = 3 _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT = 4 _NET_WM_MOVERESIZE_SIZE_BOTTOM = 5 _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT = 6 _NET_WM_MOVERESIZE_SIZE_LEFT = 7 _NET_WM_MOVERESIZE_MOVE = 8 _NET_WM_MOVERESIZE_SIZE_KEYBOARD = 9 _NET_WM_MOVERESIZE_MOVE_KEYBOARD = 10 _NET_WM_MOVERESIZE_CANCEL = 11 class LinuxMoveResize: """ Tool class for moving and resizing window """ moveResizeAtom = None @classmethod def sendButtonReleaseEvent(cls, window, globalPos): """ send button release event Parameters ---------- window: QWidget window to be moved or resized globalPos: QPoint the global point of mouse release event """ globalPos = QPointF(QPointF(globalPos) * window.devicePixelRatio()).toPoint() pos = window.mapFromGlobal(globalPos) # open the connection to X server conn = xcb.connect(os.environ.get('DISPLAY')) windowId = int(window.winId()) xproto = xprotoExtension(conn) # refer to: https://www.x.org/releases/X11R7.5/doc/libxcb/tutorial/ event = ButtonReleaseEvent.synthetic( detail=ButtonIndex._1, time=xcb.CurrentTime, root=conn.get_setup().roots[0].root, event=windowId, child=xcb.NONE, root_x=globalPos.x(), root_y=globalPos.y(), event_x=pos.x(), event_y=pos.y(), state=ButtonMask._1, same_screen=True, ) xproto.SendEvent(True, windowId, EventMask.ButtonRelease, event.pack()) conn.flush() @classmethod def startSystemMoveResize(cls, window, globalPos, message): """ resize window Parameters ---------- window: QWidget window to be moved or resized globalPos: QPoint the global point of mouse release event message: int window message """ cls.sendButtonReleaseEvent(window, globalPos) globalPos = QPointF(QPointF(globalPos) * window.devicePixelRatio()).toPoint() # open the connection to X server conn = xcb.connect(os.environ.get('DISPLAY')) xproto = xprotoExtension(conn) if not cls.moveResizeAtom: cls.moveResizeAtom = xproto.InternAtom( False, len("_NET_WM_MOVERESIZE"), "_NET_WM_MOVERESIZE").reply().atom union = ClientMessageData.synthetic([ globalPos.x(), globalPos.y(), message, ButtonIndex._1, 0 ], "I"*5) event = ClientMessageEvent.synthetic( format=32, window=int(window.winId()), type=cls.moveResizeAtom, data=union ) xproto.UngrabPointer(xcb.CurrentTime) xproto.SendEvent( False, conn.get_setup().roots[0].root, EventMask.SubstructureRedirect | EventMask.SubstructureNotify, event.pack() ) conn.flush() @classmethod def startSystemMove(cls, window, globalPos): """ move window """ cls.startSystemMoveResize( window, globalPos, WindowMessage._NET_WM_MOVERESIZE_MOVE.value) window.windowHandle().startSystemMove() @classmethod def starSystemResize(cls, window, globalPos, edges): Loading @@ -140,17 +24,4 @@ class LinuxMoveResize: edges: `Qt.Edges` window edges """ if not edges: return messageMap = { Qt.Edge.TopEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_TOP, Qt.Edge.TopEdge | Qt.Edge.LeftEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_TOPLEFT, Qt.Edge.TopEdge | Qt.Edge.RightEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_TOPRIGHT, Qt.Edge.BottomEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_BOTTOM, Qt.Edge.BottomEdge | Qt.Edge.LeftEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT, Qt.Edge.BottomEdge | Qt.Edge.RightEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT, Qt.Edge.LeftEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_LEFT, Qt.Edge.RightEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_RIGHT, } cls.startSystemMoveResize(window, globalPos, messageMap[edges].value) window.windowHandle().startSystemResize(edges) No newline at end of file Loading
launch.py +1 −0 Original line number Diff line number Diff line Loading @@ -130,6 +130,7 @@ def main(): if qtpy.API_NAME[-1] == '6': C.FLAG_QT6 = True else: C.FLAG_QT6 = False QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True) #enable highdpi scaling QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True) #use highdpi icons QApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough) Loading
ui/canvas.py +25 −12 Original line number Diff line number Diff line Loading @@ -151,6 +151,8 @@ class Canvas(QGraphicsScene): textstack_changed = Signal() drop_open_folder = Signal(str) context_menu_requested = Signal(QPoint) def __init__(self, parent=None): super().__init__(parent) self.scale_factor = 1. Loading @@ -172,6 +174,9 @@ class Canvas(QGraphicsScene): self.gv.canvas = self self.gv.setAcceptDrops(True) self.gv.setContextMenuPolicy(Qt.ContextMenuPolicy.NoContextMenu) self.context_menu_requested.connect(self.createCustomContextMenu) if not C.FLAG_QT6: # mitigate https://bugreports.qt.io/browse/QTBUG-93417 # produce blurred result, saving imgs remain unaffected Loading Loading @@ -546,17 +551,20 @@ class Canvas(QGraphicsScene): def mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent) -> None: btn = event.button() self.hide_rubber_band() Qt.MouseButton.LeftButton if btn == Qt.MouseButton.MiddleButton: self.mid_btn_pressed = False elif self.creating_textblock: btn = 0 if btn == Qt.MouseButton.LeftButton else 1 return self.endCreateTextblock(btn=btn) elif btn == Qt.MouseButton.RightButton: if self.creating_textblock: tgt = 0 if btn == Qt.MouseButton.LeftButton else 1 self.endCreateTextblock(btn=tgt) if btn == Qt.MouseButton.RightButton: if self.stroke_img_item is not None: self.finish_erasing.emit(self.stroke_img_item) if self.rubber_band.isVisible(): self.rubber_band.hide() self.rubber_band_origin = None if self.textEditMode(): self.context_menu_requested.emit(event.screenPos()) elif btn == Qt.MouseButton.LeftButton: if self.stroke_img_item is not None: self.finish_painting.emit(self.stroke_img_item) Loading Loading @@ -631,8 +639,8 @@ class Canvas(QGraphicsScene): def setTextBlockMode(self, mode: bool): self.textblock_mode = mode def contextMenuEvent(self, event: QGraphicsSceneContextMenuEvent): if self.textEditMode(): def createCustomContextMenu(self, pos: QPoint): if self.textEditMode() and not self.creating_textblock: menu = QMenu(self.gv) copy_act = menu.addAction(self.tr("Copy")) paste_act = menu.addAction(self.tr("Paste")) Loading @@ -648,16 +656,16 @@ class Canvas(QGraphicsScene): ocr_translate_act = menu.addAction(self.tr("OCR and translate")) ocr_translate_inpaint_act = menu.addAction(self.tr("OCR, translate and inpaint")) rst = menu.exec_(event.screenPos()) rst = menu.exec(pos) if rst == delete_act: self.delete_textblks.emit(0) elif rst == delete_recover_act: self.delete_textblks.emit(1) elif rst == copy_act: self.copy_textblks.emit(event.scenePos()) self.copy_textblks.emit(pos.toPointF()) elif rst == paste_act: self.paste_textblks.emit(event.scenePos()) self.paste_textblks.emit(pos.toPointF()) elif rst == format_act: self.format_textblks.emit() elif rst == layout_act: Loading @@ -673,6 +681,11 @@ class Canvas(QGraphicsScene): elif rst == ocr_translate_inpaint_act: self.run_blktrans.emit(2) def hide_rubber_band(self): if self.rubber_band.isVisible(): self.rubber_band.hide() self.rubber_band_origin = None def on_hide_canvas(self): self.clear_states() Loading
ui/framelesswindow/fw_qt6/linux/__init__.py +16 −11 Original line number Diff line number Diff line Loading @@ -17,16 +17,17 @@ class LinuxFramelessWindow(QWidget): super().__init__(parent=parent) self.windowEffect = LinuxWindowEffect(self) # self.titleBar = TitleBar(self) self._isResizeEnabled = True self.setWindowFlags(self.windowFlags() | Qt.WindowType.FramelessWindowHint) QCoreApplication.instance().installEventFilter(self) # self.titleBar.raise_() # self.resize(500, 500) self.resize(500, 500) # def resizeEvent(self, e): # super().resizeEvent(e) def resizeEvent(self, e): super().resizeEvent(e) # self.titleBar.resize(self.width(), self.titleBar.height()) # def setTitleBar(self, titleBar): Loading @@ -42,21 +43,25 @@ class LinuxFramelessWindow(QWidget): # self.titleBar.setParent(self) # self.titleBar.raise_() def setResizeEnabled(self, isEnabled: bool): """ set whether resizing is enabled """ self._isResizeEnabled = isEnabled def eventFilter(self, obj, event): et = event.type() if et != QEvent.Type.MouseButtonPress and et != QEvent.Type.MouseMove: if et != QEvent.Type.MouseButtonPress and et != QEvent.Type.MouseMove or not self._isResizeEnabled: return False edges = 0 edges = Qt.Edge(0) pos = event.globalPosition().toPoint() - self.pos() if pos.x() < self.BORDER_WIDTH: edges = edges | Qt.Edge.LeftEdge if edges != 0 else Qt.Edge.LeftEdge edges |= Qt.Edge.LeftEdge if pos.x() >= self.width()-self.BORDER_WIDTH: edges = edges | Qt.Edge.RightEdge if edges != 0 else Qt.Edge.RightEdge edges |= Qt.Edge.RightEdge if pos.y() < self.BORDER_WIDTH: edges = edges | Qt.Edge.TopEdge if edges != 0 else Qt.Edge.TopEdge edges |= Qt.Edge.TopEdge if pos.y() >= self.height()-self.BORDER_WIDTH: edges = edges | Qt.Edge.BottomEdge if edges != 0 else Qt.Edge.BottomEdge edges |= Qt.Edge.BottomEdge # change cursor if et == QEvent.Type.MouseMove and self.windowState() == Qt.WindowState.WindowNoState: Loading @@ -71,7 +76,7 @@ class LinuxFramelessWindow(QWidget): else: self.setCursor(Qt.CursorShape.ArrowCursor) elif obj in (self, self.titleBar) and et == QEvent.Type.MouseButtonPress and edges: elif obj == self and et == QEvent.Type.MouseButtonPress and edges: LinuxMoveResize.starSystemResize(self, event.globalPosition(), edges) return super().eventFilter(obj, event)
ui/framelesswindow/fw_qt6/utils/linux_utils.py +2 −131 Original line number Diff line number Diff line # coding: utf-8 from enum import Enum import os import xcffib as xcb from PyQt6.QtCore import QPointF, Qt from xcffib.xproto import (ButtonIndex, ButtonMask, ButtonReleaseEvent, ClientMessageData, ClientMessageEvent, EventMask, xprotoExtension) class WindowMessage(Enum): """ Window message enum class """ # refer to: https://specifications.freedesktop.org/wm-spec/1.1/x170.html _NET_WM_MOVERESIZE_SIZE_TOPLEFT = 0 _NET_WM_MOVERESIZE_SIZE_TOP = 1 _NET_WM_MOVERESIZE_SIZE_TOPRIGHT = 2 _NET_WM_MOVERESIZE_SIZE_RIGHT = 3 _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT = 4 _NET_WM_MOVERESIZE_SIZE_BOTTOM = 5 _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT = 6 _NET_WM_MOVERESIZE_SIZE_LEFT = 7 _NET_WM_MOVERESIZE_MOVE = 8 _NET_WM_MOVERESIZE_SIZE_KEYBOARD = 9 _NET_WM_MOVERESIZE_MOVE_KEYBOARD = 10 _NET_WM_MOVERESIZE_CANCEL = 11 class LinuxMoveResize: """ Tool class for moving and resizing window """ moveResizeAtom = None @classmethod def sendButtonReleaseEvent(cls, window, globalPos): """ send button release event Parameters ---------- window: QWidget window to be moved or resized globalPos: QPoint the global point of mouse release event """ globalPos = QPointF(QPointF(globalPos) * window.devicePixelRatio()).toPoint() pos = window.mapFromGlobal(globalPos) # open the connection to X server conn = xcb.connect(os.environ.get('DISPLAY')) windowId = int(window.winId()) xproto = xprotoExtension(conn) # refer to: https://www.x.org/releases/X11R7.5/doc/libxcb/tutorial/ event = ButtonReleaseEvent.synthetic( detail=ButtonIndex._1, time=xcb.CurrentTime, root=conn.get_setup().roots[0].root, event=windowId, child=xcb.NONE, root_x=globalPos.x(), root_y=globalPos.y(), event_x=pos.x(), event_y=pos.y(), state=ButtonMask._1, same_screen=True, ) xproto.SendEvent(True, windowId, EventMask.ButtonRelease, event.pack()) conn.flush() @classmethod def startSystemMoveResize(cls, window, globalPos, message): """ resize window Parameters ---------- window: QWidget window to be moved or resized globalPos: QPoint the global point of mouse release event message: int window message """ cls.sendButtonReleaseEvent(window, globalPos) globalPos = QPointF(QPointF(globalPos) * window.devicePixelRatio()).toPoint() # open the connection to X server conn = xcb.connect(os.environ.get('DISPLAY')) xproto = xprotoExtension(conn) if not cls.moveResizeAtom: cls.moveResizeAtom = xproto.InternAtom( False, len("_NET_WM_MOVERESIZE"), "_NET_WM_MOVERESIZE").reply().atom union = ClientMessageData.synthetic([ globalPos.x(), globalPos.y(), message, ButtonIndex._1, 0 ], "I"*5) event = ClientMessageEvent.synthetic( format=32, window=int(window.winId()), type=cls.moveResizeAtom, data=union ) xproto.UngrabPointer(xcb.CurrentTime) xproto.SendEvent( False, conn.get_setup().roots[0].root, EventMask.SubstructureRedirect | EventMask.SubstructureNotify, event.pack() ) conn.flush() @classmethod def startSystemMove(cls, window, globalPos): """ move window """ cls.startSystemMoveResize( window, globalPos, WindowMessage._NET_WM_MOVERESIZE_MOVE.value) window.windowHandle().startSystemMove() @classmethod def starSystemResize(cls, window, globalPos, edges): Loading @@ -140,17 +24,4 @@ class LinuxMoveResize: edges: `Qt.Edges` window edges """ if not edges: return messageMap = { Qt.Edge.TopEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_TOP, Qt.Edge.TopEdge | Qt.Edge.LeftEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_TOPLEFT, Qt.Edge.TopEdge | Qt.Edge.RightEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_TOPRIGHT, Qt.Edge.BottomEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_BOTTOM, Qt.Edge.BottomEdge | Qt.Edge.LeftEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT, Qt.Edge.BottomEdge | Qt.Edge.RightEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT, Qt.Edge.LeftEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_LEFT, Qt.Edge.RightEdge: WindowMessage._NET_WM_MOVERESIZE_SIZE_RIGHT, } cls.startSystemMoveResize(window, globalPos, messageMap[edges].value) window.windowHandle().startSystemResize(edges) No newline at end of file