在调用 deleteLater() 后直接删除对 Qt 对象的 Python 引用是否安全?

编程入门 行业动态 更新时间:2024-10-27 11:15:16
本文介绍了在调用 deleteLater() 后直接删除对 Qt 对象的 Python 引用是否安全?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

请考虑下面的最小示例,它实现了一个自定义 QNetworkAccessManager,它维护了一个未完成的 QNetworkReply 实例列表.

Please consider the minimal example below, which implements a custom QNetworkAccessManager that maintains a list of unfinished QNetworkReply instances.

当回复finished时,它会从unfinished_replies列表中删除.

When a reply is finished, it is removed from the unfinished_replies list.

如PyQt/PySide 中是否需要 deleteLater() 中所述?,QNetworkReply.deleteLater() 用于在 finished 槽内安排要删除的 Qt 对象.

As discussed in Is deleteLater() necessary in PyQt/PySide?, QNetworkReply.deleteLater() is used, inside the finished slot, to schedule the Qt object for deletion.

但是,我不确定删除对回复对象的 Python 引用的最佳方法是什么.我可以想到两个(互斥的)删除 Python 引用的选项,如下例所示:

However, I am not sure what would be the best way to remove the Python reference to the reply object. I can think of two (mutually exclusive) options for removing the Python reference, as shown in the example below:

  • 调用deleteLater()

    在发出 QNetworkReply.destroyed 信号时移除 (文档)

    remove when the QNetworkReply.destroyed signal is emitted (docs)

    这两个选项似乎都可以正常工作.我更喜欢选项 1,但我不确定这是否会在极少数情况下导致意外.哪个最好?或者还有其他选择吗?

    Both options seem to work just fine. I would prefer option 1, but I'm not sure if this could lead to surprises in rare cases. Which would be best? Or is there another alternative?

    import sys from PyQt5 import QtNetwork, QtWidgets, QtCore class CustomNetworkAccessManager(QtNetwork.QNetworkAccessManager): def __init__(self): super(CustomNetworkAccessManager, self).__init__() self.unfinished_replies = [] self.finished.connect(self.slot) def get(self, *args, **kwargs): reply = super(CustomNetworkAccessManager, self).get(*args, **kwargs) reply.index = i # just for printing self.unfinished_replies.append(reply) def remove_from_list(self, reply): self.unfinished_replies.remove(reply) print('{} unfinished replies left'.format(len(self.unfinished_replies))) if not self.unfinished_replies: QtCore.QCoreApplication.quit() def slot(self, reply): print('reply {} finished'.format(reply.index)) # handle the Qt side: reply.deleteLater() # handle the Python side: # either # OPTION 1 - remove now self.remove_from_list(reply) # or # OPTION 2 - remove when destroyed # reply.destroyed.connect(lambda: self.remove_from_list(reply)) if __name__ == '__main__': # Initialize app = QtWidgets.QApplication(sys.argv) manager = CustomNetworkAccessManager() # Schedule requests url = 'httpbin/get' for i in range(6): manager.get(QtNetwork.QNetworkRequest(QtCore.QUrl(url))) # Start event loop app.exec_()

    附言抱歉 Python 2 代码

    p.s. sorry for the Python 2 code

    推荐答案

    两者是等价的,只是在被移除的那一刻不同.但是要更详细地了解您必须了解 PyQt5/PySide2 绑定的工作原理.这些库在 C++ 对象周围创建了一个包装器,例如:

    Both are equivalent, they only differ the moment they are removed. But to understand more in detail you have to understand how the PyQt5/PySide2 binding works. Those libraries create a wrapper around the C++ object, something like:

    class FooWrapper: def __new__(self, cls, *args, **kwargs): # ... instance = ... instance._cpp_object = create_cpp_object() # ... return instance def foo_method(self, *args, **kwargs): return execute_foo_method(self._cpp_object, *args, **kwargs) def __del__(self): destroyed_cpp_object(self._cpp_object)

    因此,当调用 deleteLater 时,仅删除 cpp_object 而不是包装器,您可以验证是否使用:

    So when calling deleteLater only the cpp_object is deleted and not the wrapper, you can verify that if you use:

    reply.destroyed.connect(self.remove_from_list)

    Traceback (most recent call last): File "main.py", line 32, in <lambda> reply.destroyed.connect(self.remove_from_list) File "main.py", line 17, in remove_from_list self.unfinished_replies.remove(reply) ValueError: list.remove(x): x not in list

    由于destroy 传递的参数是一个无效的包装器,因此会出现上述错误.对于这种情况,解决方案是使用 sip.isdeleted() 检查 cpp_object 是否已删除:

    since the parameter passed by destroyed is an invalid wrapper getting the above error. For this case, a solution is to check if the cpp_object has been removed with sip.isdeleted():

    from PyQt5 import QtNetwork, QtWidgets, QtCore import sip # ... class CustomNetworkAccessManager(QtNetwork.QNetworkAccessManager): # ... def remove_from_list(self): self.unfinished_replies = [ reply for reply in self.unfinished_replies if not sip.isdeleted(reply) ] print("{} unfinished replies left".format(len(self.unfinished_replies))) if not self.unfinished_replies: QtCore.QCoreApplication.quit() def slot(self, reply): print("reply {} finished".format(reply.index)) # handle the Qt side: reply.deleteLater() # handle the Python side: reply.destroyed.connect(self.remove_from_list)

    回到对你的方法的研究,这些可以绘制如下:

    Returning to the study of your methods, these can be graphed as follows:

    (FIRST METHOD) ------------┬------------------┬---------------------┬----------------------- | | | call_deleteLater remove_reply_from_list destroyed

    (SECOND METHOD) ------------┬-----------------------------------------┬-----------------┬------ | | | call_deleteLater destroyed remove_reply_from_list

    而且由于您使用的是原始包装器,因此您应该没有任何问题.

    And since you are using the original wrappers you should not have any problem.

    结论:两者等效且安全.

  • 更多推荐

    在调用 deleteLater() 后直接删除对 Qt 对象的 Python 引用是否安全?

    本文发布于:2023-11-09 23:16:05,感谢您对本站的认可!
    本文链接:https://www.elefans.com/category/jswz/34/1573708.html
    版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
    本文标签:对象   deleteLater   Qt   Python

    发布评论

    评论列表 (有 0 条评论)
    草根站长

    >www.elefans.com

    编程频道|电子爱好者 - 技术资讯及电子产品介绍!