问题
I'm trying to have a method called when the "X" close button of a PyQt window is selected. Briskly, I have a class of the object QtGui.QWidget and I want it to call one of its methods when the window is closed using the "X" close button in order to wrap up some subprocesses. How could this be done?
The code is shown below. The method of the class interface
that I want to have called is stylusProximityControlOff()
. This method terminates a subprocess which is potentially a little messy, but that is a separate problem.
Anyway, I would appreciate suggestions on having that method called when the "X" is selected.
#!/usr/bin/env python
"""
spin, a small utility to assist in setting usage modes of laptop-tablet devices
Usage:
spin.py
spin.py -h | --help
spin.py --nogui
Options:
-h,--help : show this help message
--nogui : non-GUI mode
"""
from docopt import docopt
import os
import sys
import subprocess
from multiprocessing import Process
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,
docopt_args=None
):
self.docopt_args=docopt_args
super(interface, self).__init__()
logger.info("running spin")
# engage stylus proximity control
self.stylusProximityControlOn()
if not docopt_args["--nogui"]:
# 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()
elif docopt_args["--nogui"]:
logger.info("non-GUI mode")
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.process1 = Process(target=self.stylusProximityControl)
self.process1.start()
def stylusProximityControlOff(self):
logger.info("changing stylus proximity control to off")
self.process1.terminate()
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(docopt_args):
application = QtGui.QApplication(sys.argv)
interface1 = interface(docopt_args)
sys.exit(application.exec_())
if __name__ == '__main__':
args = docopt(__doc__)
main(args)
回答1:
One approach is to reimplement the closeEvent
method of the QWidget, having it call the method that joins (or terminates) subprocesses and then having it call either deleteLater()
or destroy()
.
def closeEvent(self, event):
logger.info("stopping spin")
self.stylusProximityControlOff()
self.deleteLater()
来源:https://stackoverflow.com/questions/20895645/how-to-have-a-pyqt-window-call-a-method-when-its-x-close-button-is-selected