Pyqt在一个类里面创建并显示另外一个类的实例,但是界面卡顿一下就自动关闭怎么解决?

原因分析

代码展示:

我在 main_window.py 中的 receive_signal 方法中创建并显示了 SearchInterface 实例: 

self.search_interface = SearchInterface(self)
self.search_interface.setGeometry(0, start_y, width, height)
self.search_interface.show()
self.search_interface.raise_()

但出现“界面卡顿一下就自动关闭”的现象,最可能的原因是:SearchInterface 被局部变量回收导致提前销毁(GC)。 

核心问题:对象生命周期控制不当

在 Python + PyQt 中,如果一个窗口部件(如 QWidget)没有被父控件强引用或保存为类成员变量,它会在函数执行结束后被垃圾回收器回收,从而导致窗口一闪而过。 

✅ 正确做法:

应该将 SearchInterface 实例作为 MainWindow 类的一个属性保存下来,防止其被回收。

❌ 当前写法(危险): 

def __receive_signal__(self, action_str, additional_info=None):
    if action_str == SEARCH:
        self.search_interface = SearchInterface(self)  # 局部变量?还是类成员?
        self.search_interface.show()

如果只是在方法里临时创建,没有赋值给 self.xxx,那就会出现窗口一闪而过的情况。

✅ 解决方案

✅ 确保 self.search_interface 是类成员变量

def __init__(self, parent=None):
    ...
    self.search_interface = None  # 初始化为 None

并在 receive_signal 中确保只创建一次

if action_str == SEARCH:
    print("搜索功能被触发")

    cb_geo = self.checkbox_interface.geometry()
    eng_geo = self.engineering_interface.geometry()

    start_y = cb_geo.top()
    end_y = eng_geo.bottom()
    height = end_y - start_y
    width = self.width()

    if not self.search_interface:
        self.search_interface = SearchInterface(self)

    self.search_interface.setGeometry(0, start_y, width, height)
    self.search_interface.show()
    self.search_interface.raise_()

 这样可以避免每次触发都新建对象,也能保证其不被 GC 回收。

❗又发现了一个新问题,怎么没有运行成功

TypeError: SearchInterface.__init__() takes 1 positional argument but 2 were given

  具体分析

我在 main_window.py 中写了这一行:

self.search_interface = SearchInterface(self)

这表示我希望将 self(即当前 MainWindow)作为父控件传给 SearchInterface。
但查看我的 SearchInterface.init 定义:

class SearchInterface(QWidget):
    def __init__(self):
        super().__init__()

没有定义 parent 参数,因此不能传入任何参数(包括 self),所以会报错。

✅ 解决方法 

需要修改 SearchInterface 的构造函数,让它接受一个可选的 parent 参数,就像所有 Qt 控件一样:

✅ 修改为:

class SearchInterface(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.initUI()
        self.set_qss()
        self.perform_initial_query()

这样就可以像标准 Qt 控件那样传入父控件了:

self.search_interface = SearchInterface(self)  # ✅ 正确

延伸说明:为什么需要 parent?

在 PyQt 中,如果你希望一个控件显示在另一个控件之上,并且其位置是相对于父控件(比如使用 setGeometry(...)),你就必须设置父控件。否则:setGeometry(...) 不会生效(因为相对坐标找不到参考对象),控件可能显示在屏幕左上角或完全不显示,控件不会随父控件自动隐藏/销毁,容易造成内存泄漏或界面混乱。

 

❗❗什么?又又又发现了一个新问题:SearchInterface 被当作一个独立窗口弹出,显示了默认的标题栏和关闭按钮(操作系统级别的窗口控件),但我已经自定义了一个关闭按钮,并希望它只是作为主窗口中的一个覆盖层(overlay)存在。

✅ 问题原因

我在 main_window.py 中创建了 SearchInterface 实例并设置父控件为 self:

self.search_interface = SearchInterface(self)

这是正确的做法。
但如果没有正确地将 SearchInterface 设置为 子控件模式,即:
没有清除窗口标志(window flags)
或者设置了 show() 后被当作新窗口处理
那么 PyQt 会把它当作一个“顶级窗口”展示,从而出现系统默认的关闭按钮、边框等。

✅ 解决方案:移除窗口标题栏和系统按钮 

需要在 SearchInterface.init 中设置窗口标志(windowFlags)来禁用系统默认的标题栏和按钮。 

修改方式如下: 

✅ 在 SearchInterface.init 方法中添加:

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

        # 设置无系统标题栏和边框
        self.setWindowFlags(Qt.Widget)  # 或 Qt.FramelessWindowHint | Qt.Window

        self.initUI()
        self.set_qss()
        self.perform_initial_query()

这样可以确保:
它作为一个普通的 QWidget 子控件嵌入到主界面中
不会触发系统级窗口行为(如标题栏、最小化/最大化按钮)
只会显示你自己定义的关闭按钮 

参数说明:

标志 效果
Qt.Widget 默认值,表示该控件是普通部件,不会作为独立窗口弹出
Qt.FramelessWindowHint 去掉边框(可选)
Qt.Window 表示这是一个独立窗口(错误地用于子组件时就会弹出新窗口)

 ✌️大功告成,没有问题了!

你可能感兴趣的:(pyqt)