protel 99 se 中的“root has been deleted”问题

本文介绍了解决Protel99SE无法打开DDB文件的问题及新建项目时出现的错误的方法。对于‘fileisnotrecognized’错误,可以通过修复工具尝试解决;若遇到‘Roothasbeendeleted’错误,则需要安装sp6补丁包。文中还提到由于360修复Office漏洞导致的兼容性问题,并提供了解决方案:重新注册msjtes40.dll。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.打开PROTEL 99 SE后先不要开DDB文件,看左上角有一个向下的箭头,按鼠标左键点击一下,选择DESIGN UTILITIES......
2.在出现的对话筐中选择REPAIR,再点击BROWSE......,然后选择你打不开的DDB文件,看是否可以修复吧,一般情况下是可以的。
除非文件已经被删掉。

 

上面的是一种常规方法。如果你是新建也会出错,那就是因为win的升级造成部分组件的不兼容。你需要安装protel的sp6补丁包。

出现的错误:      

        1. protel 99se 加载扩展名为DDB的库文件时出现"file is not recognized"

        2.   新建项目文件时出现"Root has been deleted"

解决方法如下:

       出现的原因是: 360为了修复一个OFFICE的溢出漏洞用了一种临时的解决方法,将一个可以访问access的ACTIVEX控件给反注册掉了 (msjtes40.dll)

       解决方法: 重新注册一下这个dll文件,在"运行"里输入"regsvr32.exe msjtes40.dll",然后点"确定",出现注册成功提示后即可解决问题.

资源下载链接为: https://pan.quark.cn/s/1bfadf00ae14 华为移动服务(Huawei Mobile Services,简称 HMS)是一个全面开放的移动服务生态系统,为企业和开发者提供了丰富的工具和 API,助力他们构建、运营和推广应用。其中,HMS Scankit 是华为推出的一款扫描服务 SDK,支持快速集成到安卓应用中,能够提供高效且稳定的二维码和条形码扫描功能,适用于商品扫码、支付验证、信息获取等多种场景。 集成 HMS Scankit SDK 主要包括以下步骤:首先,在项目的 build.gradle 文件中添加 HMS Core 库和 Scankit 依赖;其次,在 AndroidManifest.xml 文件中添加相机访问和互联网访问权限;然后,在应用程序的 onCreate 方法中调用 HmsClient 进行初始化;接着,可以选择自定义扫描界面或使用 Scankit 提供的默认扫描界面;最后,实现 ScanCallback 接口以处理扫描成功和失败的回调。 HMS Scankit 内部集成了开源的 Zxing(Zebra Crossing)库,这是一个功能强大的条码和二维码处理库,提供了解码、生成、解析等多种功能,既可以单独使用,也可以与其他扫描框架结合使用。在 HMS Scankit 中,Zxing 经过优化,以更好地适应华为设备,从而提升扫描性能。 通常,ScanKitDemoGuide 包含了集成 HMS Scankit 的示例代码,涵盖扫描界面的布局、扫描操作的启动和停止以及扫描结果的处理等内容。开发者可以参考这些代码,快速掌握在自己的应用中实现扫码功能的方法。例如,启动扫描的方法如下: 处理扫描结果的回调如下: HMS Scankit 支持所有安卓手机,但在华为设备上能够提供最佳性能和体验,因为它针对华为硬件进行了
<think>我们面对的问题是:在使用QPlainTextEdit或QTextEdit进行实时日志显示时,出现“RuntimeError: wrapped C/C++ object has been deleted”错误。这个错误通常发生在尝试访问已经被Qt销毁的C++对象(即Python中包装的对象)时。在PyQt中,当一个QObject被删除(例如,其父对象被删除或显式删除)后,再访问它就会导致此错误。 在日志显示的上下文中,这个问题通常发生在: 1. 日志处理器(自定义的QTextEditLogger)试图向已经被销毁的QPlainTextEdit对象发送日志消息。 2. 多线程环境下,工作线程在GUI对象已经销毁后仍然尝试发送日志信号。 解决方案的核心是确保在销毁窗口或日志控件时,正确地移除日志处理器并断开信号连接,同时确保工作线程在窗口关闭时停止。 以下是具体的修复步骤和代码调整: ### 1. 在窗口关闭时移除日志处理器并断开信号 在`LogViewer`类中添加关闭事件处理,移除日志处理器并断开信号连接。 ```python class LogViewer(QMainWindow): # ... 其他代码 ... def closeEvent(self, event): # 移除日志处理器并断开信号 logger = logging.getLogger() if hasattr(self, 'log_handler'): logger.removeHandler(self.log_handler) self.log_handler.append_log.disconnect() # 断开所有连接 self.log_handler = None # 如果有工作线程,需要停止工作线程(如果有的话) # 例如:self.worker_thread.quit() event.accept() ``` ### 2. 确保工作线程在窗口关闭前停止 如果工作线程还在运行,并且会继续产生日志,那么窗口关闭后,日志处理器试图更新已经不存在的GUI控件就会导致错误。因此,我们需要在关闭窗口前停止所有工作线程。 ```python class LogViewer(QMainWindow): # ... 其他代码 ... def __init__(self): # ... 初始化代码 ... self.worker_thread = None # 用于存储工作线程引用 def start_worker(self): # 启动工作线程 self.worker_thread = WorkerThread() self.worker_thread.start() def closeEvent(self, event): # 停止工作线程 if self.worker_thread is not None and self.worker_thread.isRunning(): self.worker_thread.quit() # 或者terminate(),但更推荐quit() self.worker_thread.wait() # 等待线程结束 # 移除日志处理器 logger = logging.getLogger() if hasattr(self, 'log_handler'): logger.removeHandler(self.log_handler) self.log_handler.append_log.disconnect(self.append_to_log) # 断开特定连接 self.log_handler.deleteLater() # 安排删除 self.log_handler = None event.accept() ``` ### 3. 在日志处理器中增加对象存活性检查 在`QTextEditLogger.emit`方法中,我们可以检查目标对象是否还存在。但是,由于我们使用了信号,信号连接时已经使用了`Qt.QueuedConnection`(默认),所以当接收对象被删除时,信号连接会自动断开。但为了更安全,我们可以在发射信号前检查接收端是否存在。然而,在PyQt中,我们无法直接检查C++对象是否被删除,但可以通过`QObject`的`signalsBlocked`或者使用`QPointer`来跟踪。 但是,更简单的方法是在`LogViewer`类中确保在窗口关闭时移除日志处理器,这样日志就不会再被发送到已经销毁的控件。 ### 4. 使用弱引用(可选) 另一种方法是使用弱引用来引用QPlainTextEdit,然后在日志处理器中使用弱引用来判断对象是否还存在。但是,由于我们已经在窗口关闭时移除了处理器,所以这一步通常不是必须的。 ### 完整修正代码示例 ```python import sys import logging import time import random from PyQt5.QtWidgets import QApplication, QMainWindow, QPlainTextEdit, QVBoxLayout, QWidget from PyQt5.QtCore import QObject, pyqtSignal, QThread, pyqtSlot class QTextEditLogger(logging.Handler, QObject): append_log = pyqtSignal(str) def __init__(self, parent=None): super().__init__() QObject.__init__(self, parent) def emit(self, record): try: msg = self.format(record) self.append_log.emit(msg) except Exception as e: print(f"日志处理器错误: {e}") class LogViewer(QMainWindow): def __init__(self): super().__init__() self.log_handler = None self.worker_thread = None self.init_ui() self.setup_logging() self.start_worker() # 启动工作线程 def init_ui(self): central_widget = QWidget() self.setCentralWidget(central_widget) layout = QVBoxLayout(central_widget) self.log_view = QPlainTextEdit() self.log_view.setReadOnly(True) layout.addWidget(self.log_view) self.setWindowTitle('日志窗口') self.resize(800, 600) def setup_logging(self): self.log_handler = QTextEditLogger() self.log_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s', datefmt='%H:%M:%S')) self.log_handler.append_log.connect(self.append_to_log, type=Qt.QueuedConnection) # 确保线程安全 logging.getLogger().addHandler(self.log_handler) logging.getLogger().setLevel(logging.DEBUG) @pyqtSlot(str) def append_to_log(self, message): if self.log_view: # 检查控件是否还存在(虽然不一定能检测到C++对象删除,但可以防止在Python层被删除) self.log_view.appendPlainText(message) def start_worker(self): self.worker_thread = WorkerThread() self.worker_thread.start() def closeEvent(self, event): # 停止工作线程 if self.worker_thread is not None and self.worker_thread.isRunning(): self.worker_thread.requestInterruption() # 设置中断标志 self.worker_thread.quit() self.worker_thread.wait(2000) # 等待2秒 # 移除日志处理器 if self.log_handler is not None: logging.getLogger().removeHandler(self.log_handler) # 断开信号连接 self.log_handler.append_log.disconnect(self.append_to_log) self.log_handler.deleteLater() self.log_handler = None event.accept() class WorkerThread(QThread): def run(self): logger = logging.getLogger() for i in range(100): if self.isInterruptionRequested(): break level = random.choice([logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR]) logger.log(level, f"消息 {i}: 随机数 {random.randint(1, 100)}") time.sleep(0.1) if __name__ == '__main__': app = QApplication(sys.argv) window = LogViewer() window.show() sys.exit(app.exec_()) ``` ### 关键修正点说明 1. **窗口关闭事件处理**: - 在`closeEvent`中停止工作线程(通过设置中断标志和调用`quit()`),并等待线程结束。 - 移除日志处理器并断开信号连接,防止后续日志继续尝试更新已经不存在的控件。 2. **工作线程安全退出**: - 在`WorkerThread`的`run`方法中检查中断标志(`isInterruptionRequested()`),以便及时退出循环。 3. **使用`pyqtSlot`装饰器**: - 将`append_to_log`方法标记为槽,并指定参数类型,确保类型安全。 4. **信号连接方式**: - 在连接信号时指定连接类型为`Qt.QueuedConnection`(默认就是队列连接,但显式写出更清晰),确保跨线程安全。 5. **日志处理器的清理**: - 使用`deleteLater()`安排日志处理器在事件循环中安全删除。 ### 注意事项 - 如果工作线程中有长时间阻塞的操作(如没有`time.sleep`的循环),则可能无法及时响应中断请求。在这种情况下,需要更频繁地检查中断标志。 - 确保所有工作线程都在窗口关闭前被停止。 ### 相关问题 1. 如何在PyQt中安全地停止一个长时间运行的工作线程?[^1] 2. 除了移除日志处理器,还有哪些方法可以避免“C++对象已被删除”的错误?[^1] 3. 如何在多线程环境下确保PyQt GUI控件的线程安全?[^1]</think>### 解决 PyQt5 QPlainTextEdit 实时日志显示中的 "C++ object has been deleted" 错误 当使用 `QPlainTextEdit` 实现实时日志显示时,"RuntimeError: wrapped C/C++ object has been deleted" 错误通常发生在以下场景: 1. 尝试访问已被销毁的 Qt 对象 2. 多线程环境下对象生命周期管理不当 3. 信号槽连接未正确处理对象删除 #### 根本原因分析 - **对象生命周期问题**:日志处理器尝试访问已销毁的 GUI 组件 - **线程安全问题**:工作线程在 GUI 对象销毁后仍尝试更新 UI - **信号槽连接问题**:未正确处理信号槽连接的断开 #### 完整解决方案 ```python import sys import logging import weakref # 关键修复:使用弱引用 from PyQt5.QtWidgets import QApplication, QMainWindow, QPlainTextEdit, QVBoxLayout, QWidget from PyQt5.QtCore import QObject, pyqtSignal, QThread, Qt class SafeQTextEditLogger(logging.Handler, QObject): append_log = pyqtSignal(str) def __init__(self, parent=None): super().__init__() QObject.__init__(self, parent) # 使用弱引用存储目标控件 self._text_edit_ref = None self.setFormatter(logging.Formatter( '%(asctime)s - %(levelname)s - %(message)s', datefmt='%H:%M:%S' )) def set_text_edit(self, text_edit): """安全设置目标控件(使用弱引用)""" self._text_edit_ref = weakref.ref(text_edit) def emit(self, record): """线程安全的日志发射方法""" try: # 检查目标控件是否仍然存在 if self._text_edit_ref is None or self._text_edit_ref() is None: return msg = self.format(record) self.append_log.emit(msg) except RuntimeError as e: # 处理对象已被删除的情况 if "wrapped C/C++" in str(e): print("安全忽略已删除对象") else: raise class LogViewer(QMainWindow): def __init__(self): super().__init__() self.log_handler = None self.init_ui() self.setup_logging() def init_ui(self): self.setWindowTitle('安全日志查看器') self.setGeometry(300, 300, 800, 600) central_widget = QWidget() self.setCentralWidget(central_widget) layout = QVBoxLayout(central_widget) self.log_view = QPlainTextEdit() self.log_view.setReadOnly(True) self.log_view.setMaximumBlockCount(10000) # 限制日志行数 layout.addWidget(self.log_view) def setup_logging(self): # 创建安全的日志处理器 self.log_handler = SafeQTextEditLogger() self.log_handler.set_text_edit(self.log_view) # 设置弱引用 self.log_handler.append_log.connect(self.append_to_log, Qt.QueuedConnection) # 配置日志系统 logger = logging.getLogger() logger.setLevel(logging.DEBUG) logger.addHandler(self.log_handler) def append_to_log(self, message): """安全追加日志到显示区域""" try: if self.log_view is not None: self.log_view.appendPlainText(message) except RuntimeError as e: if "wrapped C/C++" in str(e): print("安全处理:控件已被删除") self.log_view = None def closeEvent(self, event): """窗口关闭时安全清理资源""" # 断开所有信号连接 if self.log_handler: self.log_handler.append_log.disconnect() # 从日志系统中移除处理器 logger = logging.getLogger() logger.removeHandler(self.log_handler) # 删除引用 self.log_handler = None self.log_view = None event.accept() class WorkerThread(QThread): def run(self): import time import random levels = [logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR] for i in range(100): if self.isInterruptionRequested(): break level = random.choice(levels) logging.log(level, f"任务进度: {i+1}/100 - 随机消息 {random.randint(1000, 9999)}") time.sleep(0.1) if __name__ == '__main__': app = QApplication(sys.argv) viewer = LogViewer() viewer.show() worker = WorkerThread() worker.start() # 确保应用退出时停止所有线程 app.aboutToQuit.connect(worker.requestInterruption) app.aboutToQuit.connect(worker.quit) app.aboutToQuit.connect(worker.wait) sys.exit(app.exec_()) ``` #### 关键修复技术 1. **弱引用技术**(解决对象生命周期问题) ```python import weakref class SafeQTextEditLogger(logging.Handler, QObject): def set_text_edit(self, text_edit): self._text_edit_ref = weakref.ref(text_edit) # 使用弱引用 ``` 2. **安全信号连接**(确保线程安全) ```python self.log_handler.append_log.connect( self.append_to_log, Qt.QueuedConnection # 强制使用队列连接 ) ``` 3. **显式资源清理**(窗口关闭时) ```python def closeEvent(self, event): if self.log_handler: self.log_handler.append_log.disconnect() # 断开信号 logger.removeHandler(self.log_handler) # 移除日志处理器 self.log_handler = None # 清除引用 ``` 4. **异常安全处理** ```python def append_to_log(self, message): try: if self.log_view is not None: self.log_view.appendPlainText(message) except RuntimeError as e: if "wrapped C/C++" in str(e): print("安全处理:控件已被删除") self.log_view = None # 清除无效引用 ``` 5. **线程安全终止** ```python app.aboutToQuit.connect(worker.requestInterruption) app.aboutToQuit.connect(worker.quit) app.aboutToQuit.connect(worker.wait) # 等待线程结束 ``` #### 预防措施最佳实践 1. **对象生命周期管理**: - 使用 `weakref` 代替强引用 - 在 `closeEvent()` 中显式清除引用 - 遵循 Qt 对象树父子关系 2. **线程安全实践**: ```python # 在QThread子类中安全停止 def run(self): while not self.isInterruptionRequested(): # 执行任务 ``` 3. **信号槽安全连接**: ```python # 始终指定连接类型 signal.connect(slot, Qt.QueuedConnection) ``` 4. **防御式编程**: ```python # 在所有GUI操作前检查对象有效性 if self.log_view and self.log_view.isValid(): self.log_view.appendPlainText(message) ``` 5. **使用QPointer检测对象有效性**(PyQt特有): ```python from PyQt5.QtCore import QPointer class LogViewer(QMainWindow): def __init__(self): self.log_view_ptr = QPointer(self.log_view) # 创建指针 def append_to_log(self, message): if self.log_view_ptr: # 自动检测对象有效性 self.log_view.appendPlainText(message) ``` #### 常见触发场景及解决方案 | 场景 | 原因 | 解决方案 | |------|------|----------| | 窗口关闭后日志继续输出 | 对象已删除但日志线程仍在运行 | 在 `closeEvent` 中停止线程 | | 快速打开/关闭多个窗口 | 对象引用未及时清除 | 使用弱引用和显式清理 | | 多线程同时访问 | 线程竞争条件 | 使用 `Qt.QueuedConnection` | | 复杂界面动态创建/销毁 | 对象父子关系不当 | 正确设置父对象 | 通过以上方法,你可以彻底解决 "C++ object has been deleted" 错误,并创建健壮的实时日志显示系统。 ### 相关问题 1. 如何在 PyQt5 中安全地管理多线程与 GUI 的交互?[^1] 2. 弱引用在 PyQt5 对象生命周期管理中有哪些应用场景?[^1] 3. Qt 的信号槽机制有哪些连接类型?如何选择正确的连接类型?[^1] 4. 如何为 PyQt5 应用程序实现全面的异常安全处理?[^1] 5. QPointer 在 PyQt5 开发中有哪些具体应用和优势?[^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值