如何防止Python线程停止PyQt GUI

时间:2014-01-02 03:08:42

标签: multithreading loops methods while-loop pyqt

我正在制作一个实用程序,可以轻松控制笔记本电脑平板电脑设备的使用模式(即,便于在笔记本电脑模式,手写笔写入模式等之间切换)。我正在尝试将Python threading用于持续监视循环中特定状态的进程,如果该状态发生更改,则会执行某些操作。此监视与PyQt GUI同时运行,但GUI永远不会启动,我不确定原因。我会重视一些指导。

在代码中,方法stylusProximityControl正在进行线程化。代码如下:

import os
import sys
import subprocess
import threading
import time
from PyQt4 import QtGui
import logging

# logging
logger = logging.getLogger(__name__)
logging.basicConfig()
logger.level = logging.INFO

class interface(QtGui.QWidget):
    def __init__(self):
        super(interface, self).__init__()
        logger.info("running spin")
        # engage stylus proximity control
        self.stylusProximityControlOn()
        # create buttons
        buttonsList = []
        # button: tablet mode
        buttonModeTablet = QtGui.QPushButton('tablet mode', self)
        buttonModeTablet.clicked.connect(self.engageModeTablet)
        buttonsList.append(buttonModeTablet)
        # button: laptop mode
        buttonModeLaptop = QtGui.QPushButton('laptop mode', self)
        buttonModeLaptop.clicked.connect(self.engageModeLaptop)
        buttonsList.append(buttonModeLaptop)
        # button: left
        buttonLeft = QtGui.QPushButton('left', self)
        buttonLeft.clicked.connect(self.engageLeft)
        buttonsList.append(buttonLeft)
        # button: right
        buttonRight = QtGui.QPushButton('right', self)
        buttonRight.clicked.connect(self.engageRight)
        buttonsList.append(buttonRight)
        # button: inverted
        buttonInverted = QtGui.QPushButton('inverted', self)
        buttonInverted.clicked.connect(self.engageInverted)
        buttonsList.append(buttonInverted)
        # button: normal
        buttonNormal = QtGui.QPushButton('normal', self)
        buttonNormal.clicked.connect(self.engageNormal)
        buttonsList.append(buttonNormal)
        # button: touchscreen on
        buttonTouchscreenOn = QtGui.QPushButton('touchscreen on', self)
        buttonTouchscreenOn.clicked.connect(self.engageTouchscreenOn)
        buttonsList.append(buttonTouchscreenOn)
        # button: touchscreen off
        buttonTouchscreenOff = QtGui.QPushButton('touchscreen off', self)
        buttonTouchscreenOff.clicked.connect(self.engageTouchscreenOff)
        buttonsList.append(buttonTouchscreenOff)
        # button: touchpad on
        buttonTouchpadOn = QtGui.QPushButton('touchpad on', self)
        buttonTouchpadOn.clicked.connect(self.engageTouchpadOn)
        buttonsList.append(buttonTouchpadOn)
        # button: touchpad off
        buttonTouchpadOff = QtGui.QPushButton('touchpad off', self)
        buttonTouchpadOff.clicked.connect(self.engageTouchpadOff)
        buttonsList.append(buttonTouchpadOff)
        # button: nipple on
        buttonNippleOn = QtGui.QPushButton('nipple on', self)
        buttonNippleOn.clicked.connect(self.engageNippleOn)
        buttonsList.append(buttonNippleOn)
        # button: nipple off
        buttonNippleOff = QtGui.QPushButton('nipple off', self)
        buttonNippleOff.clicked.connect(self.engageNippleOff)
        buttonsList.append(buttonNippleOff)
        # button: stylus proximity on
        buttonStylusProximityControlOn = QtGui.QPushButton('stylus proximity on', self)
        buttonStylusProximityControlOn.clicked.connect(self.engageStylusProximityControlOn)
        buttonsList.append(buttonStylusProximityControlOn)
        # button: stylus proximity off
        buttonStylusProximityControlOff = QtGui.QPushButton('stylus proximity off', self)
        buttonStylusProximityControlOff.clicked.connect(self.engageStylusProximityControlOff)
        buttonsList.append(buttonStylusProximityControlOff)
        # set button dimensions
        buttonsWidth=150
        buttonsHeight=60
        for button in buttonsList:
            button.setFixedSize(buttonsWidth, buttonsHeight)
        # set layout
        vbox = QtGui.QVBoxLayout()
        vbox.addStretch(1)
        for button in buttonsList:
            vbox.addWidget(button)
            vbox.addStretch(1)  
        self.setLayout(vbox)
        # window
        self.setGeometry(200, 200, 150, 100)
        self.setWindowTitle('spin')
        self.show()
    def displayLeft(self):
        logger.info("changing display to left")
        os.system('xrandr -o left')
    def displayRight(self):
        logger.info("changing display to right")
        os.system('xrandr -o right')
    def displayInverted(self):
        logger.info("changing display to inverted")
        os.system('xrandr -o inverted')
    def displayNormal(self):
        logger.info("changing display to normal")
        os.system('xrandr -o normal')
    def touchscreenLeft(self):
        logger.info("changing touchscreen to left")
        os.system('xinput set-prop "ELAN Touchscreen" "Coordinate Transformation Matrix" 0 -1 1 1 0 0 0 0 1')
    def touchscreenRight(self):
        logger.info("changing touchscreen to right")
        os.system('xinput set-prop "ELAN Touchscreen" "Coordinate Transformation Matrix" 0 1 0 -1 0 1 0 0 1')
    def touchscreenInverted(self):
        logger.info("changing touchscreen to inverted")
        os.system('xinput set-prop "ELAN Touchscreen" "Coordinate Transformation Matrix" -1 0 1 0 -1 1 0 0 1')
    def touchscreenNormal(self):
        logger.info("changing touchscreen to normal")
        os.system('xinput set-prop "ELAN Touchscreen" "Coordinate Transformation Matrix" 1 0 0 0 1 0 0 0 1')
    def touchscreenOn(self):
        logger.info("changing touchscreen to on")
        os.system('xinput enable "ELAN Touchscreen"')
    def touchscreenOff(self):
        logger.info("changing touchscreen to off")
        os.system('xinput disable "ELAN Touchscreen"')
    def touchpadOn(self):
        logger.info("changing touchpad to on")
        os.system('xinput enable "SynPS/2 Synaptics TouchPad"')
    def touchpadOff(self):
        logger.info("changing touchpad to off")
        os.system('xinput disable "SynPS/2 Synaptics TouchPad"')
    def nippleOn(self):
        logger.info("changing nipple to on")
        os.system('xinput enable "TPPS/2 IBM TrackPoint"')
    def nippleOff(self):
        logger.info("changing nipple to off")
        os.system('xinput disable "TPPS/2 IBM TrackPoint"')
    def stylusProximityControl(self):
        previousProximityStatus = None
        while True:
            proximityCommand = 'xinput query-state "Wacom ISDv4 EC Pen stylus" | grep Proximity | cut -d " " -f3 | cut -d "=" -f2'
            proximityStatus = subprocess.check_output(proximityCommand, shell=True).lower().rstrip()
            if (proximityStatus == "out") and (previousProximityStatus != "out"):
                logger.info("stylus inactive")
        self.touchscreenOn()
            elif (proximityStatus == "in") and (previousProximityStatus != "in"):
                logger.info("stylus active")
        self.touchscreenOff()
        previousProximityStatus = proximityStatus
            time.sleep(0.25)
    def stylusProximityControlOn(self):
        logger.info("changing stylus proximity control to on")
        self.thread1=threading.Thread(target=self.stylusProximityControl()).start()
    def stylusProximityControlOff(self):
        logger.info("changing stylus proximity control to off")
        self.thread1.join()
    def engageModeTablet(self):
        logger.info("engaging mode tablet")
        self.displayLeft()
    self.touchscreenLeft()
        self.touchscreenOff()
        self.touchpadOff()
        self.nippleOff()
    def engageModeLaptop(self):
        logger.info("engaging mode laptop")
        self.displayNormal()
        self.touchscreenNormal()
        self.touchscreenOn()
        self.touchpadOn()
        self.nippleOn()
    def engageLeft(self):
        logger.info("engaging mode left")
        self.displayLeft()
        self.touchscreenLeft()
    def engageRight(self):
        logger.info("engaging mode right")
        self.displayRight()
        self.touchscreenRight()
    def engageInverted(self):
        logger.info("engaging mode inverted")
        self.displayInverted()
        self.touchscreenInverted()
    def engageNormal(self):
        logger.info("engaging mode normal")
        self.displayNormal()
        self.touchscreenNormal()
    def engageTouchscreenOn(self):
        self.touchscreenOn()
    def engageTouchscreenOff(self):
        self.touchscreenOff()
    def engageTouchpadOn(self):
        self.touchpadOn()
    def engageTouchpadOff(self):
        self.touchpadOff()
    def engageNippleOn(self):
        self.nippleOn()
    def engageNippleOff(self):
        self.nippleOff()
    def engageStylusProximityControlOn(self):
        self.stylusProximityControlOn()
    def engageStylusProximityControlOff(self):
        self.stylusProximityControlOff()
def main():
    application = QtGui.QApplication(sys.argv)
    interface1 = interface()
    sys.exit(application.exec_())
if __name__ == '__main__':
    main()

编辑:three_pineapples highlighted代码中出错。此错误已得到解决,代码已更新。

2 个答案:

答案 0 :(得分:1)

好的,终于找到了运行代码的时间。简单的错误(你会踢自己!)

您需要更改行

self.thread1=threading.Thread(target=self.stylusProximityControl()).start()

self.thread1=threading.Thread(target=self.stylusProximityControl).start()

请注意stylusProximityControl后删除括号。您有效地要求您的方法stylusProximityControl运行并返回一个方法,以作为threading.Thread的目标。当然,你的方法中有一个while True,所以没有返回任何函数来调用threading.Thread来在新线程中启动。希望这对你有意义!

这会让你超越你的直接问题,虽然你需要考虑使用thread.join(我没有看到线程会加入,因为循环永远不会停止)。您可能希望while循环仅在某个布尔值为True时循环,以便在程序退出后它可以清理子进程并退出循环。

无论如何,祝你好运,我希望你把开发放在网上(bitbucket或类似),因为它看起来很有用!

答案 1 :(得分:0)

我通过使用multiprocessing来推进解决方案。

我做了以下更改:

  • 来自:import threading
    • 至:from multiprocessing import Process
  • 来自:self.thread1 = threading.Thread(target=self.stylusProximityControl()).start()
    • 至:self.process1 = Process(target=self.stylusProximityControl().start()
  • 来自:self.thread1.join()
    • 至:self.process1.join()

我现在的问题是当选择窗口上的“X”时,接近控制的多处理过程结束。这可能需要执行方法stylusProximityControlOff()并可能修改方法。