You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用QStackedWidget切换窗口?整合代码运行异常求助

问题描述

尝试使用QStackedWidget()切换到下一个窗口,但整合代码运行时出现单独运行.py文件不会出现的错误。

应用预期流程:

  • 点击激活分组框
  • 点击按钮弹出带有鼠标十字监听器的透明窗口
  • 点击任意位置后恢复正常鼠标光标并关闭该透明窗口

以下是原始代码:

window1.py

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtGui import QColor, QKeySequence, QIcon, QCursor
from window2 import *

class Ui_Form(QtWidgets.QWidget):
    def __init__(self):
        super(Ui_Form, self).__init__()

    ##if i use this method, it does not show my transparent window, also it shows an error that i dont get when run it separately.

    def goToTransparentWindowMethod(self):
        self.goToTransparentWindow = TransparentWindowClass()
        myWindow.addWidget(self.goToTransparentWindow)
        myWindow.setCurrentIndex(myWindow.currentIndex()+1)

    def setupUi(self, myWindow):
        myWindow.setObjectName("myWindow")
        myWindow.resize(627, 327)

        self.horizontalLayoutWidget = QtWidgets.QWidget(myWindow)
        self.horizontalLayoutWidget.setGeometry(QtCore.QRect(10, 10, 300, 270))
        self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")

        self.horizontalLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
        self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout.setObjectName("horizontalLayout")

        self.gpb_main = QtWidgets.QGroupBox(self.horizontalLayoutWidget)
        self.gpb_main.setCheckable(True)
        self.gpb_main.setChecked(False)
        self.gpb_main.setObjectName("gpb_spell_main")

        self.btn_main = QtWidgets.QPushButton(self.gpb_main)
        self.btn_main.setGeometry(QtCore.QRect(10, 40, 88, 27))
        self.btn_main.setObjectName("btn_main")
        self.btn_main.clicked.connect(self.goToTransparentWindowMethod)

        self.horizontalLayout.addWidget(self.gpb_main)

        self.retranslateUi(myWindow)
        QtCore.QMetaObject.connectSlotsByName(myWindow)

    def retranslateUi(self, myWindow):
        _translate = QtCore.QCoreApplication.translate
        myWindow.setWindowTitle(_translate("myWindow", "Window1"))
        self.gpb_main.setTitle(_translate("myWindow", "example"))
        self.btn_main.setText(_translate("myWindow", "click me"))


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    myWindow = QtWidgets.QStackedWidget()
    ui = Ui_Form()
    ui.setupUi(myWindow)
    myWindow.show()
    sys.exit(app.exec_())

window2.py

from PyQt5 import QtCore, QtGui, QtWidgets
from pynput.mouse import Listener
import pyautogui


class TransparentWindowThreadClass(QtCore.QObject):

    def __init__(self, parent=None):
        super(TransparentWindowThreadClass, self).__init__()

    @QtCore.pyqtSlot()
    def on_click_main(self, x, y, button, pressed):
        try:
            if pressed:
                self.pos_main = pyautogui.position()
                self.get_rgb_main = pyautogui.pixel(self.pos_main[0], self.pos_main[1])
                r = self.get_rgb_main.red
                g = self.get_rgb_main.green
                b = self.get_rgb_main.blue
                self.get_rgb_main = r,g,b

                Transparent_Window.unsetCursor()#error here when is called from window1
                Transparent_Window.close()#error here when is called from window1
             
                #Pressed
                self.pressed_Msg = 'Pressed at x:{0}  y:{1} RGB:{2}'.format(x, y, self.get_rgb_main)
                print(self.pressed_Msg)
            else: 
                #Released msg
                self.released_Msg = 'Released at x:{0} y:{1} RGB:{2}'.format(x, y, self.get_rgb_main)
                print(self.released_Msg)

            if not pressed:
                return False

        except KeyboardInterrupt:
            print('you pressed ctrl+c')
        except NameError:
            print("Error on_click_main")
        except RuntimeError:
            print('run time error')
        except TypeError:
            print('ype error')
        except AttributeError:
            print('Attribute Error')

    @QtCore.pyqtSlot()
    def loop_onclick_listener(self):
        try:
            with Listener(on_click=self.on_click_main) as listener:             
                listener.join()
        except KeyboardInterrupt:
            print('you pressed ctrl+c')
        except NameError:
            print("Error onclick_listener")
        except RuntimeError:
            print('run time error')
        except TypeError:
            print('ype error')
        except AttributeError:
            print('Attribute Error')

class TransparentWindowClass(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(TransparentWindowClass, self).__init__()

        self.monitorResolution = pyautogui.size()

        # create a QThread and start the thread that handles
        self.thread = QtCore.QThread()
        self.thread.start()

        # create the worker without a parent so you can move it
        self.worker = TransparentWindowThreadClass()## my custom thread class
        # the worker is moved to another thread
        self.worker.moveToThread(self.thread)

        # if the thread started, connect it
        self.thread.started.connect(self.worker.loop_onclick_listener)


    def setupUi(self, Transparent_Window):
        Transparent_Window.setObjectName("Transparent_Window")
        Transparent_Window.resize(self.monitorResolution[0], self.monitorResolution[1])
        Transparent_Window.setWindowFlags(QtCore.Qt.FramelessWindowHint)
        Transparent_Window.setAttribute(QtCore.Qt.WA_TranslucentBackground, True)
        Transparent_Window.setCursor(QtGui.QCursor(QtCore.Qt.CrossCursor))

        self.retranslateUi(Transparent_Window)
        QtCore.QMetaObject.connectSlotsByName(Transparent_Window)

    def retranslateUi(self, Transparent_Window):
        _translate = QtCore.QCoreApplication.translate
        Transparent_Window.setWindowTitle(_translate("Transparent", "Transparent Window"))


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    Transparent_Window = QtWidgets.QWidget()
    ui = TransparentWindowClass()
    ui.setupUi(Transparent_Window)
    Transparent_Window.show()
    sys.exit(app.exec_())

问题根源及修复方案

1. 全局变量引用错误

TransparentWindowThreadClass中直接调用Transparent_Window.unsetCursor()和Transparent_Window.close(),但这个变量仅在window2单独运行时存在,整合后属于未定义的全局变量,导致NameError。

修复:给工作线程添加信号,通过信号槽通知主线程处理UI操作;同时将窗口实例关联到工作线程的信号响应逻辑中。

2. 透明窗口未初始化

在goToTransparentWindowMethod中创建TransparentWindowClass实例后,没有调用setupUi方法,导致窗口没有设置透明、全屏等属性,无法正常显示。

修复:创建实例后调用setupUi,直接将实例作为窗口本身(因为TransparentWindowClass继承自QWidget,无需额外创建QWidget)。

3. 线程操作UI违反Qt规则

Qt要求所有UI操作必须在主线程执行,工作线程直接操作窗口会导致运行时错误。

修复:工作线程触发点击事件后,发出信号让主线程处理窗口关闭和光标重置。


修正后的代码

window1.py

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtGui import QColor, QKeySequence, QIcon, QCursor
from window2 import TransparentWindowClass

class Ui_Form(QtWidgets.QWidget):
    def __init__(self):
        super(Ui_Form, self).__init__()

    def goToTransparentWindowMethod(self):
        # 创建透明窗口实例并初始化UI
        self.trans_win = TransparentWindowClass()
        self.trans_win.setupUi(self.trans_win)
        # 添加到堆叠窗口并切换显示
        myWindow.addWidget(self.trans_win)
        myWindow.setCurrentIndex(myWindow.currentIndex() + 1)

    def setupUi(self, myWindow):
        myWindow.setObjectName("myWindow")
        myWindow.resize(627, 327)

        self.horizontalLayoutWidget = QtWidgets.QWidget(myWindow)
        self.horizontalLayoutWidget.setGeometry(QtCore.QRect(10, 10, 300, 270))
        self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")

        self.horizontalLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
        self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout.setObjectName("horizontalLayout")

        self.gpb_main = QtWidgets.QGroupBox(self.horizontalLayoutWidget)
        self.gpb_main.setCheckable(True)
        self.gpb_main.setChecked(False)
        self.gpb_main.setObjectName("gpb_spell_main")

        self.btn_main = QtWidgets.QPushButton(self.gpb_main)
        self.btn_main.setGeometry(QtCore.QRect(10, 40, 88, 27))
        self.btn_main.setObjectName("btn_main")
        # 只有分组框激活后按钮才可用
        self.btn_main.setEnabled(False)
        self.gpb_main.toggled.connect(lambda checked: self.btn_main.setEnabled(checked))
        self.btn_main.clicked.connect(self.goToTransparentWindowMethod)

        self.horizontalLayout.addWidget(self.gpb_main)

        self.retranslateUi(myWindow)
        QtCore.QMetaObject.connectSlotsByName(myWindow)

    def retranslateUi(self, myWindow):
        _translate = QtCore.QCoreApplication.translate
        myWindow.setWindowTitle(_translate("myWindow", "Window1"))
        self.gpb_main.setTitle(_translate("myWindow", "example"))
        self.btn_main.setText(_translate("myWindow", "click me"))


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    myWindow = QtWidgets.QStackedWidget()
    ui = Ui_Form()
    ui.setupUi(myWindow)
    myWindow.show()
    sys.exit(app.exec_())

window2.py

from PyQt5 import QtCore, QtGui, QtWidgets
from pynput.mouse import Listener
import pyautogui

class TransparentWindowThreadClass(QtCore.QObject):
    # 定义信号,通知主线程关闭窗口
    close_window_signal = QtCore.pyqtSignal()

    def __init__(self, parent=None):
        super(TransparentWindowThreadClass, self).__init__()

    def on_click_main(self, x, y, button, pressed):
        try:
            if pressed:
                self.pos_main = pyautogui.position()
                self.get_rgb_main = pyautogui.pixel(self.pos_main[0], self.pos_main[1])
                r = self.get_rgb_main.red
                g = self.get_rgb_main.green
                b = self.get_rgb_main.blue
                self.get_rgb_main = (r, g, b)

                # 发出关闭信号,由主线程处理UI操作
                self.close_window_signal.emit()
             
                print(f'Pressed at x:{x}  y:{y} RGB:{self.get_rgb_main}')
            else: 
                print(f'Released at x:{x} y:{y} RGB:{self.get_rgb_main}')

            if not pressed:
                return False

        except Exception as e:
            print(f'Error on_click_main: {str(e)}')

    @QtCore.pyqtSlot()
    def loop_onclick_listener(self):
        try:
            with Listener(on_click=self.on_click_main) as listener:             
                listener.join()
        except Exception as e:
            print(f'Error onclick_listener: {str(e)}')

class TransparentWindowClass(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(TransparentWindowClass, self).__init__(parent)

        self.monitorResolution = pyautogui.size()

        # 初始化线程和工作对象
        self.thread = QtCore.QThread()
        self.worker = TransparentWindowThreadClass()
        self.worker.moveToThread(self.thread)
        # 连接信号:工作线程发出关闭信号时,执行窗口关闭逻辑
        self.worker.close_window_signal.connect(self.handle_close)
        self.thread.started.connect(self.worker.loop_onclick_listener)
        self.thread.start()

    def setupUi(self, Transparent_Window):
        Transparent_Window.setObjectName("Transparent_Window")
        Transparent_Window.resize(self.monitorResolution[0], self.monitorResolution[1])
        Transparent_Window.setWindowFlags(QtCore.Qt.FramelessWindowHint)
        Transparent_Window.setAttribute(QtCore.Qt.WA_TranslucentBackground, True)
        Transparent_Window.setCursor(QtGui.QCursor(QtCore.Qt.CrossCursor))

        self.retranslateUi(Transparent_Window)
        QtCore.QMetaObject.connectSlotsByName(Transparent_Window)

    def retranslateUi(self, Transparent_Window):
        _translate = QtCore.QCoreApplication.translate
        Transparent_Window.setWindowTitle(_translate("Transparent", "Transparent Window"))

    def handle_close(self):
        # 重置光标并关闭窗口
        self.unsetCursor()
        self.close()
        # 停止并回收线程资源
        self.thread.quit()
        self.thread.wait()


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    Transparent_Window = TransparentWindowClass()
    Transparent_Window.setupUi(Transparent_Window)
    Transparent_Window.show()
    sys.exit(app.exec_())

内容的提问来源于stack exchange,提问作者devxen21

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 07:50:46