PyQt: 计时器不能从其他线程启动

1 投票
1 回答
5238 浏览
提问于 2025-04-16 18:38

我正在用Python做一个Qt的图形界面,但遇到了一个错误:QObject::startTimer: 计时器不能从另一个线程启动。这个错误发生在我运行readModemSnap这个方法的时候。我已经花了快一周的时间,尝试了很多网上找到的Qt线程设计模式,但都没有成功。

class ModemScopeWindow(QMainWindow, Ui_ModemScope):
def __init__(self, parent=None):
    super(ModemScopeWindow, self).__init__(parent)

    # Set up the user interface from Designer.
    self.setupUi(self)

    self.thread = MainThread()

    """
    signal connections
    """

    self.thread.newSnap.connect(self.updateScene)       
    self.thread.updateStatus.connect(self.setStatus) 


    self.thread.connectionLock.lock()
    self.thread.runLock.lock()

    self.connect(self.runButton, SIGNAL("clicked()"), self.thread.runLock.unlock, Qt.QueuedConnection)

    self.connect(self.connectButton, SIGNAL("clicked()"), self.thread.connectionLock.unlock, Qt.QueuedConnection)


class MainThread(QThread):

newSnap = pyqtSignal(QGraphicsScene)
updateStatus = pyqtSignal(str)
initConnect = pyqtSignal()

def __init__(self, parent = None):
    super(MainThread, self).__init__(parent)

    self.samples = []

    self.connectionLock = QMutex()
    self.runLock = QMutex()        
    self.cliMute = QMutex()

    self._displayCrosshairs = True
    self._displayGrid = True
    self.persistantMode = False
    self.sampleDepth = 1

    self._currentHaam = "4"

    color = QColor(10,255,71)
    self.plotPen = QPen(color)


    self._leftXscene = -VIEW_SIZE/2
    self._topYscene = -VIEW_SIZE/2
    self._rightXscene = VIEW_SIZE/2
    self._bottomYscene = VIEW_SIZE/2
    self._leftXworld = -10.0
    self._topYworld = 10.0
    self._rightXworld = 10.0
    self._bottomYworld = -10.0
    self._scene = QGraphicsScene(self._leftXscene, self._topYscene, VIEW_SIZE, VIEW_SIZE, self)

    self.start(QThread.HighestPriority)

def run(self):

    self.updateStatus.emit("Enter target IP address and press Connect")

    self.connectionLock.lock()
    self.connectModem()

    while(1):
        self.runLock.lock() 
        #compile scene

        self.readModemSnap()
        self.newSnap.emit(self._scene)
        self.runLock.unlock()

def readModemSnap(self):
    self.updateStatus.emit("Reading Modem Snap...")

    print len(self.samples)
    if len(self.samples) >= self.sampleDepth:# and not self.persistantMode:
        self.samples.pop(0)

    self.cliMute.lock()
    temp = cli.getModemSnap()
    self.cliMute.unlock()
    self.samples.append(temp)


    self.cliMute.lock()
    modType = cli.modemRead(80)
    self.cliMute.unlock()

    if((modType | 0x0FFFFFFF) == 0x0FFFFFFF):
        modType = "0";

    else:
        modType = "%x"%modType
        modType = str(modType)


    modType = "0"
    self.updateStatus.emit("Done") 

    self.refresh()

    self._currentHaam = modType[0]
    if self._displayGrid:
        self.plotModulation(self._currentHaam)

    self.handleSnapshotResponse()

    self.updateStatus.emit("Ready to Run")
def refresh(self):

    #delete scene
    items = self._scene.items()

    for x in items:
        self._scene.removeItem(x)

    #repaint the crosshairs
    if self._displayCrosshairs:
        self.plotLine(-VIEW_SIZE,0,+VIEW_SIZE,0, self.plotPen)
        self.plotLine(0, -VIEW_SIZE,0, +VIEW_SIZE, self.plotPen)
        self.plotScaleTicks()

    #repaint grid
    if self._displayGrid:
        self.plotModulation(self._currentHaam)

    self.newSnap.emit(self._scene)

def handleSnapshotResponse(self):

    for x in range(len(self.samples)):
        for sample in self.samples[x]:
            upper = (sample >> 16) & 0xffff;
            lower = sample & 0xffff
            if (upper & 0x8000):
                upper -= 0x10000
            if (lower & 0x8000):
                lower -= 0x10000
            upper = float(upper)/128.0
            lower = float(lower)/128.0
            self.plot(upper, lower)

如你所见,我并没有从另一个线程启动任何线程。我是用主线程来启动用户界面,这样会创建一个主线程(MainThread),它在构造时会自己启动。当我注释掉一些代码行以找出问题时,我发现问题出现在readModemSnap方法中调用self.refresh()和self.handleSnapshotResponse()的时候。有没有人能告诉我我哪里做错了?或者有没有关于QThreading的教程?提前谢谢大家。

1 个回答

2

这是一个规则:你不能在除了主线程以外的其他线程中调用任何图形用户界面(GUI)相关的函数。也就是说,只有主线程可以处理这些界面相关的操作。当你看到关于QTimer的错误时,通常是因为界面中的某个地方使用了定时器,而这个定时器却是在其他线程中被触发的。

在你的情况中,最可能的问题是你在工作线程中操作了QGraphicsScene。我建议你调整一下代码,让MainThread.reload这个函数在接收到newSnap信号后再调用,而不是在它之前。

撰写回答