diff --git a/examples/testing_ioc/pydm-tutorial-ioc b/examples/testing_ioc/pydm-tutorial-ioc index 0ed74f55d..cb0ee4c4f 100755 --- a/examples/testing_ioc/pydm-tutorial-ioc +++ b/examples/testing_ioc/pydm-tutorial-ioc @@ -6,13 +6,13 @@ import time from pcaspy import Driver, SimpleServer -''' This file provides the server needed for for providing PVs to run the tutorial. +""" This file provides the server needed for for providing PVs to run the tutorial. You can follow along the tutorial here: https://slaclab.github.io/pydm-tutorial/. The server mimics variables used by the tutorial, which mimic a simulated camera and motor. Mimicking this behavior is done (over running simulator programs) in order to minimize the overhead to get the tutorial up-and-running, and since the simulated data is not necessarily needed to learn how to make PyDM user-interface. -''' +""" MAX_POINTS = 1000 FREQUENCY = 1000 @@ -22,42 +22,43 @@ MIN_UPDATE_TIME = 0.001 IMAGE_SIZE = 512 MESSAGE = "PyDM Rocks!" -prefix = 'IOC:' +prefix = "IOC:" pvdb = { - 'Run' : { 'type' : 'enum', - 'enums': ['STOP', 'RUN'], 'asg' : 'default'}, - 'ReadOnly' : { 'type' : 'enum', - 'enums': ['FALSE', 'TRUE'], 'value': 0 }, - 'XPos' : { 'prec' : 2, 'value' : 0.0, 'asg' : 'default' }, - 'YPos' : { 'prec' : 2, 'value' : 0.0, 'asg' : 'default' }, - 'Image' : { 'type' : 'char', 'count': IMAGE_SIZE ** 2, 'value': numpy.zeros(IMAGE_SIZE ** 2, dtype=numpy.uint8), 'asg' : 'default' }, - 'ImageWidth' : { 'type' : 'int', 'value' : IMAGE_SIZE, 'asg' : 'default' }, - - 'm1.DESC' : { 'type' : 'string', 'value': "Motor X", 'asg' : 'default'}, - 'm1' : { 'type' : 'float', 'value': 500.0, 'asg' : 'default' }, - 'm1.VAL' : { 'type' : 'float', 'value': 600.0, 'asg' : 'default' }, - 'm1.RBV' : { 'type' : 'float', 'value': 500.0, 'asg' : 'default' }, - 'm1.MOVN' : { 'type' : 'int', 'value': 0, 'asg' : 'default' }, - 'm1.STOP' : { 'type' : 'int', 'value': 0, 'asg' : 'default' }, - 'm1.ACCL' : { 'type' : 'float', 'value': 0.002, 'asg' : 'default' }, - 'm1.VELO' : { 'type' : 'float', 'value': 100.0, 'asg' : 'default' }, - - 'm2.DESC' : { 'type' : 'string', 'value': "Motor Y", 'asg' : 'default'}, - 'm2' : { 'type' : 'float', 'value': 500.0, 'asg' : 'default' }, - 'm2.VAL' : { 'type' : 'float', 'value': 700.0, 'asg' : 'default' }, - 'm2.RBV' : { 'type' : 'float', 'value': 500.0, 'asg' : 'default' }, - 'm2.MOVN' : { 'type' : 'int', 'value': 0, 'asg' : 'default' }, - 'm2.STOP' : { 'type' : 'int', 'value': 0, 'asg' : 'default' }, - 'm2.ACCL' : { 'type' : 'float', 'value': 0.002, 'asg' : 'default' }, - 'm2.VELO' : { 'type' : 'float', 'value': 100.0, 'asg' : 'default' }, - + "Run": {"type": "enum", "enums": ["STOP", "RUN"], "asg": "default"}, + "ReadOnly": {"type": "enum", "enums": ["FALSE", "TRUE"], "value": 0}, + "XPos": {"prec": 2, "value": 0.0, "asg": "default"}, + "YPos": {"prec": 2, "value": 0.0, "asg": "default"}, + "Image": { + "type": "char", + "count": IMAGE_SIZE**2, + "value": numpy.zeros(IMAGE_SIZE**2, dtype=numpy.uint8), + "asg": "default", + }, + "ImageWidth": {"type": "int", "value": IMAGE_SIZE, "asg": "default"}, + "m1.DESC": {"type": "string", "value": "Motor X", "asg": "default"}, + "m1": {"type": "float", "value": 500.0, "asg": "default"}, + "m1.VAL": {"type": "float", "value": 600.0, "asg": "default"}, + "m1.RBV": {"type": "float", "value": 500.0, "asg": "default"}, + "m1.MOVN": {"type": "int", "value": 0, "asg": "default"}, + "m1.STOP": {"type": "int", "value": 0, "asg": "default"}, + "m1.ACCL": {"type": "float", "value": 0.002, "asg": "default"}, + "m1.VELO": {"type": "float", "value": 100.0, "asg": "default"}, + "m2.DESC": {"type": "string", "value": "Motor Y", "asg": "default"}, + "m2": {"type": "float", "value": 500.0, "asg": "default"}, + "m2.VAL": {"type": "float", "value": 700.0, "asg": "default"}, + "m2.RBV": {"type": "float", "value": 500.0, "asg": "default"}, + "m2.MOVN": {"type": "int", "value": 0, "asg": "default"}, + "m2.STOP": {"type": "int", "value": 0, "asg": "default"}, + "m2.ACCL": {"type": "float", "value": 0.002, "asg": "default"}, + "m2.VELO": {"type": "float", "value": 100.0, "asg": "default"}, } + def gaussian_2d(x, y, x0, y0, xsig, ysig): return numpy.exp(-0.5 * (((x - x0) / xsig) ** 2 + ((y - y0) / ysig) ** 2)) -class myDriver(Driver): +class myDriver(Driver): def __init__(self): Driver.__init__(self) self.eid = threading.Event() @@ -65,18 +66,30 @@ class myDriver(Driver): self.tid.setDaemon(True) self.tid.start() - self.motorXThread = threading.Thread(target=self.updateMotor, args=('m1', 'XPos',)) + self.motorXThread = threading.Thread( + target=self.updateMotor, + args=( + "m1", + "XPos", + ), + ) self.motorXThread.setDaemon(True) self.motorXThread.start() - self.motorYThread = threading.Thread(target=self.updateMotor, args=('m2', 'YPos',)) + self.motorYThread = threading.Thread( + target=self.updateMotor, + args=( + "m2", + "YPos", + ), + ) self.motorYThread.setDaemon(True) self.motorYThread.start() def updateMotor(self, motorVarName, axisVarName): # mimic the functionality of fully-simulated motor and camera - - sleepTime = 0.2 # set arbitrarily to match timing when fully-simulated + + sleepTime = 0.2 # set arbitrarily to match timing when fully-simulated motorRbvName = motorVarName + ".RBV" motorMovingVarName = motorVarName + ".MOVN" motorStopVarName = motorVarName + ".STOP" @@ -86,9 +99,9 @@ class myDriver(Driver): time.sleep(sleepTime) motorParam = self.getParam(motorVarName) - if motorParam != motorRbv: # need to move motor + if motorParam != motorRbv: # need to move motor self.setParam(motorMovingVarName, 1) - + # for Motor X: 'Tw +10' = move right, 'Tw -10' = move left # for Motor Y:'Tw +10' = move up, 'Tw -10' = move down left motorMoveAmount = 10 if motorRbv < motorParam else -10 @@ -104,17 +117,17 @@ class myDriver(Driver): motorRbv = motorRbv + motorMoveAmount self.setParam(motorRbvName, motorRbv) axis_pos = self.getParam(axisVarName) - self.setParam(axisVarName, axis_pos + imageMoveAmount) + self.setParam(axisVarName, axis_pos + imageMoveAmount) self.updatePVs() time.sleep(sleepTime) - + self.setParam(motorStopVarName, 0) # if stopped, set motorVar line-edit to the motorRBV text-field value # (motor was stopped before reaching the originally entered value) if motorParam != motorRbv: - self.setParam(motorVarName, motorRbv) - + self.setParam(motorVarName, motorRbv) + self.setParam(motorMovingVarName, 0) self.updatePVs() @@ -126,32 +139,35 @@ class myDriver(Driver): while True: # Generate the image data - x0 = 0.5 * (numpy.random.rand() - 0.5) + self.getParam('XPos') - y0 = 0.5 * (numpy.random.rand() - 0.5) - self.getParam('YPos') + x0 = 0.5 * (numpy.random.rand() - 0.5) + self.getParam("XPos") + y0 = 0.5 * (numpy.random.rand() - 0.5) - self.getParam("YPos") xsig = 0.8 - 0.2 * numpy.random.rand() ysig = 0.8 - 0.2 * numpy.random.rand() z = gaussian_2d(xgrid, ygrid, x0, y0, xsig, ysig) - image_data = numpy.abs(256.0 * (z)).flatten(order='C').astype(numpy.uint8, copy=False) - self.setParam('Image', image_data) + image_data = numpy.abs(256.0 * (z)).flatten(order="C").astype(numpy.uint8, copy=False) + self.setParam("Image", image_data) # do updates so clients see the changes self.updatePVs() -if __name__ == '__main__': + +if __name__ == "__main__": try: - print('Starting testing-ioc') - print('To start processing records do: caput ' + prefix + 'Run 1') + print("Starting testing-ioc") + print("To start processing records do: caput " + prefix + "Run 1") server = SimpleServer() - server.initAccessSecurityFile(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'access_rules.as'), P=prefix) + server.initAccessSecurityFile( + os.path.join(os.path.dirname(os.path.realpath(__file__)), "access_rules.as"), P=prefix + ) server.createPV(prefix, pvdb) driver = myDriver() # Manually set the ReadOnly PV to force access rule calculation. # You can set ReadOnly to 1 to disable write access on all PVs. - driver.setParam('ReadOnly', 0) + driver.setParam("ReadOnly", 0) # process CA transactions while True: server.process(0.03) except KeyboardInterrupt: - print('\nInterrupted... finishing testing-ioc') + print("\nInterrupted... finishing testing-ioc") diff --git a/examples/tutorial/all_motors.py b/examples/tutorial/all_motors.py index 4877e4c51..56ebc4a30 100644 --- a/examples/tutorial/all_motors.py +++ b/examples/tutorial/all_motors.py @@ -1,12 +1,22 @@ import os import json from pydm import Display -from qtpy.QtWidgets import (QVBoxLayout, QHBoxLayout, QGroupBox, - QLabel, QLineEdit, QPushButton, QScrollArea, QFrame, - QApplication, QWidget) +from qtpy.QtWidgets import ( + QVBoxLayout, + QHBoxLayout, + QGroupBox, + QLabel, + QLineEdit, + QPushButton, + QScrollArea, + QFrame, + QApplication, + QWidget, +) from qtpy import QtCore from pydm.widgets import PyDMEmbeddedDisplay + class AllMotorsDisplay(Display): def __init__(self, parent=None, args=[], macros=None): super(AllMotorsDisplay, self).__init__(parent=parent, args=args, macros=None) @@ -27,7 +37,7 @@ def minimumSizeHint(self): def ui_filepath(self): # No UI file is being used return None - + def setup_ui(self): # Create the main layout main_layout = QVBoxLayout() @@ -36,7 +46,8 @@ def setup_ui(self): # Create a Label to be the title lbl_title = QLabel("Motors Diagnostic") # Add some StyleSheet to it - lbl_title.setStyleSheet("\ + lbl_title.setStyleSheet( + "\ QLabel {\ qproperty-alignment: AlignCenter;\ border: 1px solid #FF17365D;\ @@ -47,19 +58,20 @@ def setup_ui(self): color: rgb(255, 255, 255);\ max-height: 25px;\ font-size: 14px;\ - }") + }" + ) # Add the title label to the main layout main_layout.addWidget(lbl_title) - + # Create the Search Panel layout search_layout = QHBoxLayout() - + # Create a GroupBox with "Filtering" as Title gb_search = QGroupBox(parent=self) - gb_search.setTitle("Filtering") + gb_search.setTitle("Filtering") gb_search.setLayout(search_layout) - + # Create a label, line edit and button for filtering lbl_search = QLabel(text="Filter: ") self.txt_filter = QLineEdit() @@ -67,7 +79,7 @@ def setup_ui(self): btn_search = QPushButton() btn_search.setText("Search") btn_search.clicked.connect(self.do_search) - + # Add the created widgets to the layout search_layout.addWidget(lbl_search) search_layout.addWidget(self.txt_filter) @@ -81,7 +93,7 @@ def setup_ui(self): self.results_layout.setContentsMargins(0, 0, 0, 0) # Create a Frame to host the results of search - self.frm_result = QFrame(parent=self) + self.frm_result = QFrame(parent=self) self.frm_result.setLayout(self.results_layout) # Create a ScrollArea so we can properly handle @@ -103,7 +115,7 @@ def load_data(self): # Concatenate the directory with the file name... data_file = os.path.join(base_dir, "motor_db.txt") # Open the file so we can read the data... - with open(data_file, 'r') as f: + with open(data_file, "r") as f: # For each line in the file... for entry in f.readlines(): # Append to the list of data... @@ -114,7 +126,7 @@ def do_search(self): for widget in self.frm_result.findChildren(QWidget): widget.setParent(None) widget.deleteLater() - + # Grab the filter text filter_text = self.txt_filter.text() @@ -125,11 +137,10 @@ def do_search(self): continue # Create a PyDMEmbeddedDisplay for this entry disp = PyDMEmbeddedDisplay(parent=self) - disp.macros = json.dumps({"MOTOR":entry}) - disp.filename = 'inline_motor.ui' + disp.macros = json.dumps({"MOTOR": entry}) + disp.filename = "inline_motor.ui" disp.setMinimumWidth(700) disp.setMinimumHeight(40) disp.setMaximumHeight(100) # Add the Embedded Display to the Results Layout self.results_layout.addWidget(disp) - diff --git a/examples/tutorial/main.py b/examples/tutorial/main.py index 8b2ef0ae5..5188a3205 100644 --- a/examples/tutorial/main.py +++ b/examples/tutorial/main.py @@ -1,11 +1,9 @@ -import time from os import path from pydm import Display from scipy.ndimage.measurements import maximum_position class BeamPositioning(Display): - def __init__(self, parent=None, args=None): super(BeamPositioning, self).__init__(parent=parent, args=args) # Attach our custom process_image method @@ -18,7 +16,7 @@ def __init__(self, parent=None, args=None): def ui_filename(self): # Point to our UI file - return 'main.ui' + return "main.ui" def ui_filepath(self): # Return the full path to the UI file