如何使用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
相关产品推荐
相关产品推荐

