PySide6 - 如何制作透明但可点击的窗口?

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

我正在使用 PySide6 制作一个屏幕截图工具,我想要一个 不可见的无框窗口 来让用户拖动和捕获。但我知道的每一种方法都会让鼠标点击窗口后面而不是窗口上。

有没有办法让窗口透明并接收鼠标输入?(我用Tkinter成功制作了截图工具,但我不想用tkinter)

这是我使用 PySide6 的代码:

from PySide6 import QtWidgets, QtCore, QtGui
from PySide6.QtWidgets import QApplication, QWidget
from PySide6.QtGui import QScreen, QPixmap
import sys

using_debug_mode = None

class DraggingPanel(QWidget):
    def __init__(self, callback=None, cancel_callback=None):
        super().__init__()
        self.callback = callback
        self.cancel_callback = cancel_callback
        self.start_x = None
        self.start_y = None
        self.rect:QtCore.QRect = None
        
        window_tools.set_frameless(self)

        ### just look at here is enough ###

        # method 1: cannot click on the window
        'widget.setAttribute(QtCore.Qt.WA_TranslucentBackground)'

        # method 2: cannot click on the window
        '''
        self.setAttribute(QtCore.Qt.WA_StyledBackground, True)
        self.setStyleSheet('background-color: #00000000;')
        '''

        # method 3: when opacity <= 0.5, same as method 1, it will become totally transparent and not receiving mouse input.
        'self.setWindowOpacity(0.01)'

        height = screen.height()
        width = screen.width()
        self.setGeometry(0, 0, width, height)
        self.setMouseTracking(True)

    def paintEvent(self, event):
        if self.rect:
            painter = QtGui.QPainter(self)
            painter.setPen(QtGui.QPen(QtGui.Qt.white, 2))
            painter.drawRect(self.rect)

    def mousePressEvent(self, event):
        if event.button() == QtCore.Qt.LeftButton:
            self.start_x = event.globalPosition().x()
            self.start_y = event.globalPosition().y()
            self.rect = QtCore.QRect(self.start_x, self.start_y, 0, 0)

    def mouseMoveEvent(self, event):
        if event.buttons() & QtCore.Qt.LeftButton:
            self.rect.setBottomRight(event.globalPosition().toPoint())
            self.update()

    def mouseReleaseEvent(self, event):
        if event.button() == QtCore.Qt.LeftButton:
            self.capture_screen()

    def capture_screen(self):
        xs = [self.rect.x(), self.rect.x() + self.rect.width()]
        ys = [self.rect.y(), self.rect.y() + self.rect.height()]
        xs.sort()
        ys.sort()
        x1, x2 = xs
        y1, y2 = ys
        w = x2-x1
        h = y2-y1
        self.hide()

        if self.rect and w > 0 and h > 0:
            def do_capture():
                screen_cap = QScreen.grabWindow(QApplication.primaryScreen(), 0, 0, 
                                                screen.width(),
                                                screen.height())
                to_img_scale_x = lambda val: round(val * screen_cap.width()/screen.width())
                to_img_scale_y = lambda val: round(val * screen_cap.height()/screen.height())
                screen_cap = screen_cap.copy(to_img_scale_x(x1),
                                                to_img_scale_y(y1),
                                                to_img_scale_x(w),
                                                to_img_scale_y(h)) if True else screen_cap
                if using_debug_mode: print("Screenshot taken!")
                QtCore.QTimer.singleShot(1, lambda: self.callback(screen_cap) and self.deleteLater())
            QtCore.QTimer.singleShot(1, do_capture)
        else:
            if using_debug_mode: print("Screenshot canceled!")
            QtCore.QTimer.singleShot(1, lambda: self.cancel_callback() and self.deleteLater())

        





### Tools ###

class window_tools:
    @staticmethod
    def set_flag(widget:QWidget, flag: QtCore.Qt, on=True):
        # Get current flags and remove the FramelessWindowHint flag
        if on:
            widget.setWindowFlags(flag)
        else:
            all_flags = widget.windowFlags()
            widget.setWindowFlags(all_flags & ~flag)
    @staticmethod
    def set_no_background(widget:QWidget, transparent=True, frameless = True):
        "beware that when alpha is 0, the mouse click penetrates the window."
        window_tools.set_flag(widget, QtCore.Qt.FramelessWindowHint, on=frameless)
        widget.setAttribute(QtCore.Qt.WA_TranslucentBackground, on=transparent)
    @staticmethod
    def set_frameless(widget, on = True):
        widget.setWindowFlags(QtCore.Qt.FramelessWindowHint)
    @staticmethod
    def set_always_on_top(widget, on = True):
        window_tools.set_flag(widget, QtCore.Qt.WindowStaysOnTopHint, on=on)
    @staticmethod
    def create_window(title="", borderless=False, always_on_top=False, width=300, height=300):
        # Create a new QWidget (or QMainWindow)
        new_window = QWidget()
        new_window.setWindowTitle(title)
        
        # Set window flags based on parameters
        flags = QtCore.Qt.Window
        if borderless:
            new_window.setWindowFlags(QtCore.Qt.FramelessWindowHint)
        if always_on_top:
            new_window.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
        
        # Set the size of the window
        new_window.resize(width, height)
        
        return new_window

class screen:
    @staticmethod
    def size():
        return app_using.primaryScreen().size()
    @staticmethod
    def width():
        return screen.size().width()
    @staticmethod
    def height():
        return screen.size().height()
    

### main function ###

app_using: QApplication = None
def launch_screenshot_panel(Qapp: QApplication, on_screenshot=None, on_cancel=None, debug_logging=False):
    "Please STORE the returned widget to prevent garbage collection"
    global using_debug_mode, app_using
    using_debug_mode = debug_logging
    app_using = Qapp
    panel = DraggingPanel(on_screenshot, on_cancel)
    panel.show()
    return panel
    


if __name__ == "__main__":
    if not QtWidgets.QApplication.instance():
        app = QtWidgets.QApplication(sys.argv)
    else:
        app = QtWidgets.QApplication.instance()
    
    def on_screenshot(img: QPixmap):
        img.save("a.png")
    def on_screenshot_cancelled():
        print("cancelled callback!")
    panel = launch_screenshot_panel(app, on_screenshot=on_screenshot, on_cancel=on_screenshot_cancelled, debug_logging=True)

    sys.exit(app_using.exec())
    
python pyside pyside6
1个回答
0
投票

我找到了一个解决方案,尽管不是最优雅的。

只需使用样式表设置背景颜色,不透明度为 1/255:

window.setStyleSheet('background-color: #01000000;')
© www.soinside.com 2019 - 2024. All rights reserved.