Taurus application, process events

            
#!/usr/bin/env python
#
# this script demonstrates some basic features of taurus programs
# - the application is started as a mainWindow
# - a motor can be moved from a button or a slider
# - a sequence (move motor up and down) is executed while the GUI stays active
# - a dscan sequence is executed while the GUI stays active
# - motor moves can be stopped
# - the dscan macro can be aborted
# - a QTimer() is included, needs to be uncommented
#
import sys, time, os
from taurus.external.qt import Qt
from taurus.external.qt import QtCore
from taurus.external.qt import QtGui
from taurus.qt.qtgui.application import TaurusApplication
from taurus.qt.qtgui.display import TaurusLabel
from taurus.core.taurushelper import changeDefaultPollingPeriod
import HasyUtils
import PyTango

SLIDER_RESOLUTION = 500
POSITION_WIDTH = 120

TIMEOUT_REFRESH = 500

class moveMotor( Qt.QMainWindow):
    def __init__( self, dev, parent = None):
        super( moveMotor, self).__init__( parent)
        self.dev = dev

        self.setWindowTitle( "Move %s" % dev[ 'name'])
        self.move( 10, 750)

        self.prepareWidgets()

        self.sliderPosition = None

        self.motor = dev[ 'proxy']

        self.updateWidgets()

        #self.updateTimer.start( TIMEOUT_REFRESH)

    def prepareWidgets( self):
        w = Qt.QWidget()
        self.layout_v = Qt.QVBoxLayout()
        w.setLayout( self.layout_v)
        self.setCentralWidget( w)
        #
        # the alias line
        #
        hBox = Qt.QHBoxLayout()
        self.w_alias = Qt.QPushButton()
        hBox.addWidget( self.w_alias)
        hBox.addStretch()
        self.layout_v.addLayout( hBox)
        #
        # position, moveTo
        #
        hBox = Qt.QHBoxLayout()
        hBox.addWidget( Qt.QLabel( "Position"))
        self.w_motorPosition = TaurusLabel()
        self.w_motorPosition.setFixedWidth( POSITION_WIDTH)
        hBox.addWidget( self.w_motorPosition)
        hBox.addStretch()            
        hBox.addWidget( Qt.QLabel( "Target"))
        hBox.addStretch()            
        self.moveButton = Qt.QPushButton(self.tr("&Move")) 
        self.moveButton.setShortcut( "Alt+m")
        self.moveButton.setToolTip( "Start move with backlash")
        hBox.addWidget( self.moveButton)
        self.moveButton.clicked.connect( self.moveTo)
        self.moveToLine = Qt.QLineEdit()
        self.moveToLine.setFixedWidth( POSITION_WIDTH)
        self.moveToLine.setAlignment( QtCore.Qt.AlignRight)
        hBox.addWidget( self.moveToLine)
        self.layout_v.addLayout( hBox)
        #
        # the slider
        #
        frame = Qt.QFrame()
        frame.setFrameShape( Qt.QFrame.Box)
        self.layout_v.addWidget( frame)
        self.layout_frame_v = Qt.QVBoxLayout()
        frame.setLayout( self.layout_frame_v)
        self.w_slider = Qt.QSlider()
        #
        # install an event filter for the slider, basically to catch 
        # arrow-up and arrow-down events. Up/Down are not caught, if
        # the filter is applied to self.
        #
        self.w_slider.installEventFilter( self)
        self.w_slider.setOrientation( 1) # 1 horizontal, 2 vertical
        self.w_slider.setToolTip( "Moving the slider moves the motor, upon mouse-release.")
        self.layout_frame_v.addWidget( self.w_slider)
        self.w_slider.sliderReleased.connect( self.cb_sliderReleased)
        self.w_slider.valueChanged.connect( self.cb_sliderValueChanged)
        #
        # the slider: min, sliderPosition, max
        #
        hBox = Qt.QHBoxLayout()
        self.w_motorLimitMin = TaurusLabel()
        self.w_motorLimitMin.setMinimumWidth( POSITION_WIDTH)
        hBox.addWidget( self.w_motorLimitMin)
        hBox.addStretch()
        self.w_sliderPosition = Qt.QLabel()
        hBox.addWidget( self.w_sliderPosition)
        hBox.addStretch()
        self.w_motorLimitMax = TaurusLabel()
        self.w_motorLimitMax.setMinimumWidth( POSITION_WIDTH)
        hBox.addWidget( self.w_motorLimitMax)
        self.layout_frame_v.addLayout( hBox)

        # stop
        self.flagStopped = False
        self.w_stop = Qt.QPushButton(self.tr("&Stop")) 
        self.w_stop.setFixedWidth( 70)
        self.w_stop.clicked.connect( self.cb_stopMove)
        self.w_stop.setShortcut( "Alt+s")
        hBox.addWidget( self.w_stop)

        #
        # Menu Bar
        #
        self.menuBar = Qt.QMenuBar()
        self.setMenuBar( self.menuBar)
        self.prepareMenuBar()
        #
        # Status Bar
        #
        self.statusBar = Qt.QStatusBar()
        self.setStatusBar( self.statusBar)
        #
        # create the log widget, if necessary
        #
        self.logWidget = Qt.QTextEdit()
        self.logWidget.setMaximumHeight( 150)
        self.logWidget.setReadOnly( 1)        
        self.layout_v.addWidget( self.logWidget)
        
        self.w_stop = Qt.QPushButton(self.tr("Stop")) 
        self.w_stop.setToolTip( "Stop move")
        self.statusBar.addPermanentWidget( self.w_stop) # 'permanent' to shift it right
        self.w_stop.clicked.connect( self.cb_stopMove)
        
        self.w_abort = Qt.QPushButton(self.tr("Abort")) 
        self.w_abort.setToolTip( "Abort macro")
        self.statusBar.addPermanentWidget( self.w_abort) # 
        self.w_abort.clicked.connect( self.cb_abort)
        
        self.w_clear = Qt.QPushButton(self.tr("Clear")) 
        self.w_clear.setToolTip( "Clear log widget")
        self.statusBar.addPermanentWidget( self.w_clear) 
        self.w_clear.clicked.connect( self.logWidget.clear)

        self.exit = Qt.QPushButton(self.tr("&Exit")) 
        self.statusBar.addPermanentWidget( self.exit) 
        self.exit.clicked.connect( self.cb_closeMoveMotor)
        self.exit.setShortcut( "Alt+x")

        self.w_testMv = Qt.QPushButton(self.tr("Test: mv")) 
        self.w_testMv.setToolTip( "move up by 1, then down again")
        self.statusBar.addWidget( self.w_testMv)
        self.w_testMv.clicked.connect( self.cb_testMv)
        
        self.w_testDscan = Qt.QPushButton(self.tr("Test: dscan")) 
        self.w_testDscan.setToolTip( "exec dscan")
        self.statusBar.addWidget( self.w_testDscan)
        self.w_testDscan.clicked.connect( self.cb_testDscan)

        #
        # the update timer, don't want to poll all devices at high speed
        #
        self.updateTimer = QtCore.QTimer(self)
        self.updateTimer.timeout.connect( self.cb_refresh)
        
    def prepareMenuBar( self):
        self.fileMenu = self.menuBar.addMenu('&File')

        self.exitAction = Qt.QAction('E&xit', self)        
        self.exitAction.setStatusTip('Exit application')
        self.exitAction.triggered.connect(Qt.qApp.quit)
        self.fileMenu.addAction( self.exitAction                  )

        
    def updateWidgets( self): 
        #
        # fill the widgets
        #
        self.w_alias.setText( self.dev[ 'name'])
        self.w_motorPosition.setModel( '%s/position' % self.dev[ 'device'])
        self.w_motorPosition.setBgRole( 'state')
        self.w_stop.setToolTip( "Stop %s" % self.dev[ 'name'])
        self.setSliderScale()
        self.w_motorLimitMin.setModel( '%s/unitlimitmin' % self.dev[ 'device'])
        self.w_motorLimitMax.setModel( '%s/unitlimitmax' % self.dev[ 'device'])
        
    def cb_refresh():
        self.logWidget.append( "this is refresh")
        
    def cb_testMv( self):
        ”'
        move up by 1 then down again
        ”'
        self.flagStopped = False
        oldPos = self.motor.position
        self.logWidget.append( "mv up by 1, then back down")
        self.motor.position += 1
        while self.motor.state() == PyTango.DevState.MOVING:
            self.logWidget.append( "testMv: moving UP %s %g" % (self.dev[ 'name'], self.motor.position))
            QtGui.qApp.processEvents()
            time.sleep( 0.2)

        if self.flagStopped:
            self.logWidget.append( "testMv: sensed flagStopped")
            return 

        self.motor.position = oldPos
        while self.motor.state() == PyTango.DevState.MOVING:
            self.logWidget.append( "testMv: moving DOWN %s %g" % (self.dev[ 'name'], self.motor.position))
            QtGui.qApp.processEvents()
            time.sleep( 0.2)

    def cb_testDscan( self):
        ”'
        exec: dscan d1_mot65 -1 1 10 0.1
        ”'
        self.flagStopped = False
        door = PyTango.DeviceProxy( HasyUtils.getDoorNames()[0])

        cmd = ["dscan", "d1_mot65", "-1", "1", "10", "0.1"]
        self.logWidget.append( str(cmd))
        
        door.runmacro( cmd)

        while door.state() == PyTango.DevState.RUNNING:
            QtGui.qApp.processEvents()
            time.sleep( 0.2)
        self.logWidget.append( "dscan done")

    def cb_abort( self):
        self.logWidget.append( "abort")
        door = PyTango.DeviceProxy( HasyUtils.getDoorNames()[0])
        door.abort()
        
    def cb_sliderReleased( self):
        value = self.w_slider.value()

        if self.motor.state() == PyTango.DevState.MOVING:
            execStopMove( self.dev)
            while self.motor.state() == PyTango.DevState.MOVING:
                time.sleep(0.01)

        posReq = (self.motor.unitlimitmax - self.motor.unitlimitmin)*value/\
                 float(SLIDER_RESOLUTION) + self.motor.unitlimitmin

        self.moveTarget( posReq)
            
    def cb_sliderValueChanged( self, value):
        #
        # set the slider position label on valueChanged
        #
        posSlider = (self.motor.unitlimitmax - self.motor.unitlimitmin)*value/\
                    float(SLIDER_RESOLUTION) + self.motor.unitlimitmin
        if self.w_sliderPosition:
            self.w_sliderPosition.setText( "%g" % posSlider)

    def setSliderScale( self):
        self.w_slider.setMinimum( 0)
        self.w_slider.setMaximum( int(SLIDER_RESOLUTION))
        try:
            value = int( float(SLIDER_RESOLUTION)*(self.motor.position - self.motor.unitlimitmin)/
                         (self.motor.unitlimitmax - self.motor.unitlimitmin))
            self.w_slider.setValue( value)
        except: 
            self.logWidget.append( "setSliderScale: Failed to set slider scale")


    def closeEvent( self, e):
        ”'
        the closeEvent is called when the window is closed by 
        clicking the X at the right-upper corner of the frame
        ”'
        self.cb_closeMoveMotor()
        #e.ignore()

    def cb_closeMoveMotor( self):

        if self.motor.state() == PyTango.DevState.MOVING:
            print( "cb_closeMoveMotor: stopping motor")
            self.motor.stopMove()
            while self.motor.state() == PyTango.DevState.MOVING:
                time.sleep(0.01)

        if hasattr( self, "scan"): 
            del self.scan
        self.close()

    def cb_stopMove( self): 
        ”'
        stop the motor, if MOVING, and waits until is stopped
        ”'
        self.logWidget.append( "stopMove")
        self.flagStopped = True
        if self.motor.state() == PyTango.DevState.MOVING:
            self.motor.stopMove()
            while self.motor.state() == PyTango.DevState.MOVING:
                time.sleep(0.01)

    def moveTo( self):
        ”'
        read the moveToLine widget and move the motor
        ”'
        temp = self.moveToLine.text()
        self.moveToLine.clear()
        if len(temp) == 0:
            return

        self.moveTarget( float(temp))

    def moveTarget( self, posReq):
        ”'
        moves a motor to the requested position, 
        checks the status before and prints an error,
        if the move does not start properly
        ”'
        if self.motor.state() != PyTango.DevState.ON:
            QtGui.QMessageBox.critical(self, 'Error', 
                                       "moveTarget: state != ON (%s)" % self.dev[ 'name'], 
                                       QtGui.QMessageBox.Ok)
            return
            
        try:
            self.motor.position = posReq
        except Exception as e:
            self.logWidget.append( "%s, setting the position causes an error" % (self.dev[ 'name'])) 
            for arg in e.args:
                if hasattr( arg, 'desc'):
                    self.logWidget.append( " desc:   %s" % arg.desc) 
                    self.logWidget.append( " origin: %s" % arg.origin)
                    self.logWidget.append( " reason: %s" % arg.reason)
                    self.logWidget.append( "")
                else:
                    self.logWidget.append( repr( e))
            return

def main():
    app = TaurusApplication( sys.argv)
    
    dev = { 'control': 'tango',
            'name': 'd1_mot65', 
            'hostname': 'haspp99:10000', 
            'module': 'oms58', 
            'device': 'p99/motor/d1.65', 
            'type': 'stepping_motor',
            'flagPoolMotor': False}
    
    try:
        dev[ 'proxy'] = PyTango.DeviceProxy( dev['device'])
    except:
        print( "failed to create proxy to", dev['name'])
        return

    changeDefaultPollingPeriod( 100)
    
    w = moveMotor( dev)
    w.show()
        
    sys.exit( app.exec_())
        
if __name__ == "__main__":
    main()