2626from blacs import BLACS_DIR
2727from blacs .tab_base_classes import Tab , Worker , define_state
2828from blacs .tab_base_classes import MODE_MANUAL , MODE_TRANSITION_TO_BUFFERED , MODE_TRANSITION_TO_MANUAL , MODE_BUFFERED
29- from blacs .output_classes import AO , DO , DDS , Image
29+ from blacs .output_classes import AO , DO , DDS , Image , EO
3030from labscript_utils .qtwidgets .toolpalette import ToolPaletteGroup
3131from labscript_utils .shared_drive import path_to_agnostic
3232
@@ -41,6 +41,7 @@ def __init__(self,notebook,settings,restart=False):
4141 self ._DO = {}
4242 self ._DDS = {}
4343 self ._image = {}
44+ self ._EO = {}
4445
4546 self ._final_values = {}
4647 self ._last_programmed_values = {}
@@ -151,6 +152,13 @@ def supports_remote_value_check(self,support):
151152 # },
152153 # }
153154 #
155+ #
156+ # eo_properties = {'hardware_channel_reference': {'options':['option 1', 'option 2']},
157+ # 'eo1': {'options': {'option 1': {'index': 0, 'tooltip': 'description 1'},
158+ # 'option 2': {'index': 3, 'tooltip': 'description 2'},
159+ # 'option 3': 1},
160+ # 'return_index': True}}
161+
154162 def create_digital_outputs (self ,digital_properties ):
155163 for hardware_name ,properties in digital_properties .items ():
156164 # Save the DO object
@@ -222,7 +230,16 @@ def create_dds_outputs(self,dds_properties):
222230 sub_chnls ['gate' ] = self ._create_DO_object (connection_name ,hardware_name + '_gate' ,'gate' ,properties )
223231
224232 self ._DDS [hardware_name ] = DDS (hardware_name ,connection_name ,sub_chnls )
225-
233+
234+ def create_enum_outputs (self , eo_properties ):
235+ for output_name , properties in eo_properties .items ():
236+ self ._EO [output_name ] = self ._create_EO_object (self .device_name , output_name , properties )
237+
238+ def _create_EO_object (self , parent_device , device_property , properties ):
239+ properties .setdefault ('return_index' , False )
240+ return EO (device_property , parent_device , self .device_name , self .program_device , self .settings ,
241+ properties ['options' ], properties ['return_index' ])
242+
226243 def get_child_from_connection_table (self , parent_device_name , port ):
227244 return self .connection_table .find_child (parent_device_name , port )
228245
@@ -271,6 +288,23 @@ def create_dds_widgets(self,channel_properties):
271288
272289 return widgets
273290
291+ def create_enum_widgets (self ,device_properties ):
292+
293+ widgets = {}
294+ for output_name , properties in device_properties .items ():
295+ properties .setdefault ('display_name' ,output_name )
296+ properties .setdefault ('horizontal_alignment' ,False )
297+ properties .setdefault ('parent' ,None )
298+ widgets [output_name ] = self ._EO [output_name ].create_widget (properties ['display_name' ], properties ['horizontal_alignment' ], properties ['parent' ])
299+
300+ return widgets
301+
302+ def auto_create_enum_widgets (self ):
303+ eo_properties = {}
304+ for channel ,_ in self ._EO .items ():
305+ eo_properties [channel ] = {}
306+ return self .create_enum_widgets (eo_properties )
307+
274308 def auto_create_widgets (self ):
275309 dds_properties = {}
276310 for channel ,output in self ._DDS .items ():
@@ -361,7 +395,7 @@ def update_from_settings(self,settings):
361395 self .restore_save_data (settings ['saved_data' ])
362396
363397 self .settings = settings
364- for output in [self ._AO , self ._DO , self ._image ]:
398+ for output in [self ._AO , self ._DO , self ._image , self . _EO ]:
365399 for name ,channel in output .items ():
366400 if not channel ._locked :
367401 channel ._update_from_settings (settings )
@@ -374,8 +408,8 @@ def update_from_settings(self,settings):
374408 subchnl ._update_from_settings (settings )
375409
376410 def get_front_panel_values (self ):
377- return {channel :item .value for output in [self ._AO ,self ._DO ,self ._image ,self ._DDS ] for channel ,item in output .items ()}
378-
411+ return {channel :item .value for output in [self ._AO ,self ._DO ,self ._image ,self ._DDS , self . _EO ] for channel ,item in output .items ()}
412+
379413 def get_channel (self ,channel ):
380414 if channel in self ._AO :
381415 return self ._AO [channel ]
@@ -385,6 +419,8 @@ def get_channel(self,channel):
385419 return self ._image [channel ]
386420 elif channel in self ._DDS :
387421 return self ._DDS [channel ]
422+ elif channel in self ._EO :
423+ return self ._EO [channel ]
388424 else :
389425 return None
390426
@@ -519,6 +555,16 @@ def check_remote_values(self):
519555 ui .channel_label .setText (self ._AO [channel ].name )
520556 ui .front_value .setText (front_value )
521557 ui .remote_value .setText (remote_value )
558+ elif channel in self ._EO :
559+ # very easy case, values are strings
560+ front_value = str (self ._last_programmed_values [channel ])
561+ remote_value = str (remote_value )
562+ if front_value != remote_value :
563+ changed = True
564+ ui = UiLoader ().load (os .path .join (BLACS_DIR , 'tab_value_changed.ui' ))
565+ ui .channel_label .setText (self ._EO [channel ].name )
566+ ui .front_value .setText (front_value )
567+ ui .remote_value .setText (remote_value )
522568 else :
523569 raise RuntimeError ('device_base_class.py is not programmed to handle channel types other than DDS, AO and DO in check_remote_values' )
524570
@@ -666,7 +712,8 @@ def transition_to_manual(self,notify_queue,program=False):
666712 self ._image [channel ].set_value (value ,program = False )
667713 elif channel in self ._DDS :
668714 self ._DDS [channel ].set_value (value ,program = False )
669-
715+ elif channel in self ._EO :
716+ self ._EO [channel ].set_value (value ,program = False )
670717
671718
672719 if success :
@@ -707,18 +754,20 @@ def shutdown(self):
707754
708755 def program_manual (self ,front_panel_values ):
709756 for channel ,value in front_panel_values .items ():
710- if type (value ) != type ( True ):
757+ if isinstance (value , float ):
711758 front_panel_values [channel ] += 0.001
712759 self .fpv = front_panel_values
713760 return front_panel_values
714761
715762 def check_remote_values (self ):
716763 front_panel_values = {}
717764 for channel ,value in self .fpv .items ():
718- if type (value ) != type ( True ):
765+ if isinstance (value , float ):
719766 front_panel_values [channel ] = value + 1.1
720- else :
767+ elif isinstance ( value , bool ) :
721768 front_panel_values [channel ] = not value
769+ else :
770+ front_panel_values [channel ] = value
722771
723772 if not front_panel_values :
724773 front_panel_values ['ao0' ] = 0
@@ -728,7 +777,7 @@ def check_remote_values(self):
728777 def transition_to_buffered (self ,device_name ,h5file ,front_panel_values ,refresh ):
729778 time .sleep (3 )
730779 for channel ,value in front_panel_values .items ():
731- if type (value ) != type ( True ):
780+ if isinstance (value , float ):
732781 front_panel_values [channel ] += 0.003
733782 return front_panel_values
734783
@@ -766,10 +815,10 @@ def transition_to_manual(self):
766815if __name__ == '__main__' :
767816 # Test case!
768817
769- from connections import ConnectionTable
818+ from labscript_utils . connections import ConnectionTable
770819 from labscript_utils .qtwidgets .dragdroptab import DragDropTabWidget
771820
772- class MyTab (DeviceTab ):
821+ class MyDAQTab (DeviceTab ):
773822
774823 def initialise_GUI (self ):
775824 # Create Digital Output Objects
@@ -821,8 +870,79 @@ def sort(channel):
821870 button2 = QPushButton ("Transition to Manual" )
822871 button2 .clicked .connect (lambda : self .transition_to_manual (Queue ()))
823872 self .get_tab_layout ().addWidget (button2 )
873+
874+ class MyDummyTab (DeviceTab ):
875+
876+ def initialise_GUI (self ):
877+ # Create Digital Output Objects
878+ do_prop = {}
879+ for i in range (1 ):
880+ do_prop ['port0/line%d' ] = {}
881+ self .create_digital_outputs (do_prop )
882+
883+ # Create Analog Output objects
884+ ao_prop = {}
885+ for i in range (1 ):
886+ ao_prop ['ao%d' % i ] = {'base_unit' :'V' ,
887+ 'min' :- 10.0 ,
888+ 'max' :10.0 ,
889+ 'step' :0.01 ,
890+ 'decimals' :3
891+ }
892+ self .create_analog_outputs (ao_prop )
893+
894+ eo_prop = {
895+ 'Enum1' :{
896+ 'options' :['option 1' , 'option 2' ],
897+ 'return_index' :True ,
898+ },
899+ 'Enum2' :{
900+ 'options' :{
901+ 'option 1' :{'index' :2 , 'tooltip' :'description 1' },
902+ 'option 2' :4 ,
903+ }
904+ }
905+ }
906+ self .create_enum_outputs (eo_prop )
907+
908+ # Create widgets for output objects
909+ dds_widgets ,ao_widgets ,do_widgets = self .auto_create_widgets ()
910+ eo_widgets = self .auto_create_enum_widgets ()
911+
912+ # This function allows you do sort the order of widgets by hardware name.
913+ # it is pass to the Python 'sorted' function as key=sort when passed in as
914+ # the 3rd item of a tuple p(the tuple being an argument of self.auto_place_widgets()
915+ #
916+ # This function takes the channel name (hardware name) and returns a string (or whatever)
917+ # that when sorted alphabetically, returns the correct order
918+ def sort (channel ):
919+ port ,line = channel .replace ('port' ,'' ).replace ('line' ,'' ).split ('/' )
920+ port ,line = int (port ),int (line )
921+ return '%02d/%02d' % (port ,line )
922+
923+ # and auto place them in the UI
924+ self .auto_place_widgets (("DDS Outputs" ,dds_widgets ),
925+ ("Analog Outputs" ,ao_widgets ),
926+ ("Digital Outputs - Port 0" ,do_widgets ),
927+ ('Enums' , eo_widgets ))
928+
929+ # Set the primary worker
930+ self .create_worker ("my_worker_name" ,DeviceWorker ,{})
931+ self .primary_worker = "my_worker_name"
932+ self .create_worker ("my_secondary_worker_name" ,DeviceWorker ,{})
933+ self .add_secondary_worker ("my_secondary_worker_name" )
934+
935+ self .supports_remote_value_check (True )
936+
937+ # Create buttons to test things!
938+ button1 = QPushButton ("Transition to Buffered" )
939+ button1 .clicked .connect (lambda : self .transition_to_buffered ('' ,Queue ()))
940+ self .get_tab_layout ().addWidget (button1 )
941+ button2 = QPushButton ("Transition to Manual" )
942+ button2 .clicked .connect (lambda : self .transition_to_manual (Queue ()))
943+ self .get_tab_layout ().addWidget (button2 )
824944
825- connection_table = ConnectionTable (r'example_connection_table .h5' )
945+ connection_table = ConnectionTable ('../tests/device_base_classes_connection_table .h5' )
826946
827947 class MyWindow (QWidget ):
828948
@@ -833,14 +953,11 @@ def __init__(self,*args,**kwargs):
833953 def closeEvent (self ,event ):
834954 if not self .are_we_closed :
835955 event .ignore ()
836- self .my_tab .shutdown ()
956+ self .my_tab .close_tab ()
837957 self .are_we_closed = True
838958 QTimer .singleShot (1000 ,self .close )
839959 else :
840- if not self .my_tab .shutdown_complete :
841- QTimer .singleShot (1000 ,self .close )
842- else :
843- event .accept ()
960+ event .accept ()
844961
845962 def add_my_tab (self ,tab ):
846963 self .my_tab = tab
@@ -851,7 +968,8 @@ def add_my_tab(self,tab):
851968 notebook = DragDropTabWidget ()
852969 layout .addWidget (notebook )
853970
854- tab1 = MyTab (notebook ,settings = {'device_name' : 'ni_pcie_6363_0' , 'connection_table' :connection_table })
971+ tab1 = MyDAQTab (notebook ,settings = {'device_name' : 'ni_pcie_6363_0' , 'connection_table' :connection_table })
972+ tab2 = MyDummyTab (notebook ,settings = {'device_name' : 'intermediate_device' , 'connection_table' :connection_table })
855973 window .add_my_tab (tab1 )
856974 window .show ()
857975 def run ():
0 commit comments