PyQt5为何会阻塞对服务器端第三方程序的写入访问?
Let's break down why you're seeing this behavior and how to fix it. The key issue here comes down to thread context and timing relative to PyQt's event loop, not a direct block by PyQt itself.
Why This Happens
Your Jupyter and __main__-level code works because it runs in a standard Python thread context, with no active Qt event loop competing for message handling. When you move the code into QMainWindow.__init__, you're executing the WLM write calls before Qt's main event loop (app.exec_()) has fully started.
HighFinesse's control server relies on Windows' message queue system to handle requests. When you call SetExposure during __init__, Qt's event loop hasn't finished setting up its message handling pipeline. While read operations might still work (they often don't require the same level of message synchronization), write operations that need to send commands to the server get stuck or rejected—hence the -3 error code, which likely indicates a command couldn't be processed in the current context.
Additionally, using a local wlm variable in __init__ could lead to premature garbage collection, though your read success makes this less likely—it's still worth fixing to eliminate variables.
Fixes to Try
1. Delay WLM Operations Until Qt's Event Loop is Running
The simplest fix is to move your WLM setup code to run after the event loop has started. Use QTimer.singleShot to defer execution:
from wlm import WavelengthMeter from PyQt5 import QtWidgets, QtCore import sys class MainWindow(QtWidgets.QMainWindow): def __init__(self): super().__init__() # Store WLM as a class member to avoid premature garbage collection self.wlm = WavelengthMeter(dllpath="C:\\mydllPath\\wlmData.dll") # Defer setup until the event loop is fully ready QtCore.QTimer.singleShot(100, self.configure_wlm) def configure_wlm(self): print("Secondary exposure loop beginning now...") print(f"Exposure 1 acquired: {self.wlm.GetExposure(array_index=1)} ") print(f"Exposure 2 acquired: {self.wlm.GetExposure(array_index=2)}") print(f"Exposure 1 set: {self.wlm.SetExposure(10, array_index=1)}") print(f"Exposure 2 set: {self.wlm.SetExposure(20, array_index=2)}") print(f"New exposure 1 acquired: {self.wlm.GetExposure(array_index=1)}") print(f"New exposure 2 acquired: {self.wlm.GetExposure(array_index=2)}") if __name__ == "__main__": app = QtWidgets.QApplication(sys.argv) MainWindow_object = MainWindow() MainWindow_object.show() sys.exit(app.exec_())
2. Use showEvent for Window-Ready Execution
Alternatively, run the code when the window is first shown, ensuring the event loop is fully active:
def showEvent(self, event): super().showEvent(event) # Only run configuration once if not hasattr(self, '_wlm_configured'): self.configure_wlm() self._wlm_configured = True
3. Verify Thread Apartment Model (Advanced)
Some Windows DLLs require running in a Single-Threaded Apartment (STA) context. PyQt's main thread should default to STA, but you can explicitly enforce it by adding this at the start of your __main__ block:
if __name__ == "__main__": import sys from PyQt5.QtCore import Qt # Explicitly set STA mode for the main thread sys.coinit_flags = 0x0 # COINIT_APARTMENTTHREADED app = QtWidgets.QApplication(sys.argv) # ... rest of your code
Why This Works
By waiting for Qt's event loop to start, you ensure that the Windows message queue is properly managed, allowing the WLM DLL to communicate with HighFinesse's server as intended. Storing self.wlm as a class member also guarantees the instance stays alive for the lifetime of the window, preventing any unexpected cleanup that might interfere with commands.
内容的提问来源于stack exchange,提问作者Shaha Bhugra

