【发布时间】:2021-10-03 19:41:59
【问题描述】:
我正在尝试从 menuBar 启动一个小部件,然后在用户单击按钮时弹出一个 TextEdit 对话框。这一切都很好,但是,如果我要关闭小部件并再次重新启动小部件,我会随机遇到“RuntimeError: QPlainTextEdit 类型的包装 C/C++ 对象已被删除”。
Traceback (most recent call last):
File "mydirectory", line 373, in startClicked
self.runF.logger.info('Initializing...')
File "C:\Program Files\Python\Python37\lib\logging\__init__.py", line 1332, in info
self._log(INFO, msg, args, **kwargs)
File "C:\Program Files\Python\Python37\lib\logging\__init__.py", line 1468, in _log
self.handle(record)
File "C:\Program Files\Python\Python37\lib\logging\__init__.py", line 1478, in handle
self.callHandlers(record)
File "C:\Program Files\Python\Python37\lib\logging\__init__.py", line 1540, in callHandlers
hdlr.handle(record)
File "C:\Program Files\Python\Python37\lib\logging\__init__.py", line 854, in handle
self.emit(record)
File "mydirectory", line 19, in emit
self.widget.appendPlainText(self.format(record))
RuntimeError: wrapped C/C++ object of type QPlainTextEdit has been deleted
我无法弄清楚为什么这是一个问题,因为我在小部件初始化期间创建了 TextEdit 对话框的新实例。
以下是我的精简代码,但我似乎无法重现此问题-
mainTest.py
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import logging
import test1
class mainTest(QMenuBar):
def __init__(self, parent=None):
super().__init__()
logging.basicConfig(filename=fr'testuser_debug.log', level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
self.setWindowState(self.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive | Qt.MSWindowsFixedSizeDialogHint)
self.setWindowTitle('test')
self.setAttribute(Qt.WA_DeleteOnClose)
self.viewMenu = self.addMenu('&Views')
self.viewSubMenu = QMenu(self.viewMenu)
self.viewSubMenu.setTitle('&Tools')
self.testTool = QAction("&test Tool", self)
self.viewSubMenu.addAction(self.testTool)
self.viewMenu.addAction(self.viewSubMenu.menuAction())
self.testTool.triggered.connect(self.testToolPopup)
self.setFixedHeight(self.sizeHint().height())
self.resize(500, self.height())
def testToolPopup(self):
try:
self.third_screen.setWindowState(
self.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive)
self.third_screen.activateWindow()
except Exception as e:
print(str(e))
self.third_screen = test1.test1GUI(self)
self.third_screen.show()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = mainTest()
w.show()
sys.exit(app.exec_())
test1.py
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import logging
from PyQt5.QtGui import *
class QPlainTextEditLogger(logging.Handler):
def __init__(self, parent=None):
super().__init__()
self.widget = QPlainTextEdit(parent)
self.widget.setReadOnly(True)
def emit(self, record):
self.widget.appendPlainText(self.format(record))
class emitLogging(QDialog, QPlainTextEdit):
def __init__(self, parent=None):
super().__init__()
self.parent = parent
self.logTextBox = QPlainTextEditLogger(self)
self.logTextBox.setFormatter(logging.Formatter('%(asctime)s - %(message)s'))
self.logger = logging.getLogger('auction')
self.logger.addHandler(self.logTextBox)
self.logger.setLevel(logging.INFO)
layout = QVBoxLayout()
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
self.runButton = QtWidgets.QPushButton()
self.runButton.setSizePolicy(sizePolicy)
sizePolicy.setHeightForWidth(self.runButton.sizePolicy().hasHeightForWidth())
self.runButton.setText("Run")
layout.addWidget(self.logTextBox.widget)
layout.addWidget(self.runButton)
self.setLayout(layout)
def closeEvent(self, event):
try:
self.parent.setEnabled(True)
except Exception as e:
logging.error(e)
print(e)
class test1GUI(QWidget):
def __init__(self, parent=None):
super().__init__()
try:
self.parent = parent
self.setWindowTitle("test1 Tool")
self.setAttribute(Qt.WA_DeleteOnClose)
self.runF = emitLogging(self)
self.genButton = QtWidgets.QPushButton()
self.genButton.setText("Generate")
self.genButton.clicked.connect(self.genClicked)
self.tda = TDA(self)
self.tda.status.connect(self.logggin)
self.vboxB = QVBoxLayout()
self.vboxB.addWidget(self.genButton)
self.setLayout(self.vboxB)
except Exception as e:
print(e)
logging.error(e)
def genClicked(self):
try:
self.setEnabled(False)
self.runF.setEnabled(True)
self.runF.logTextBox.widget.clear()
self.tda._process_call()
self.runF.show()
except Exception as e:
print(str(e))
@pyqtSlot(str)
def logggin(self, txt):
try:
self.runF.logger.info(txt)
except Exception as e:
print(e)
class TDA(QObject):
status = pyqtSignal(str)
def __init__(self, parent=None):
super().__init__()
self.parent = parent
def _process_call(self):
self.status.emit('test....')
print('here')
【问题讨论】:
-
嗯,你确实添加了
self.setAttribute(Qt.WA_DeleteOnClose)... -
@musicamante 应该没问题吧?当我关闭它时它会清理小部件,然后通过 self.third_screen = test1.test1GUI(self) 重新启动
-
@Ken
testToolPopup中的try/except块确保仅当self还没有'third_screen'属性时才创建新实例。关闭对话框将删除底层 C++ 对象,但 Python 包装器将被留下。 Qt 显然对 Python 一无所知,所以self.third_screen属性不会被删除。当再次调用testToolPopup时,您的代码会尝试调用self.third_screen.setWindowState(...),由于这是继承的 Qt 方法,因此将引发运行时错误(因为底层 C++ 对象不再存在)。