pyControl / code

pyControl GUI and framework code
https://pycontrol.readthedocs.io
GNU General Public License v3.0
23 stars 20 forks source link

Event buttons and task API #90

Closed alustig3 closed 1 year ago

alustig3 commented 1 year ago

Event buttons

Right now you can update task variables from the GUI. The ability to manually send events from the GUI would also be useful (feature requested here)

This pull request adds a special button variable. When a user adds btn_examplebutton = "event_name" to their task, the Controls dialog will automatically create a button, that when clicked, will generate that event within the task. See example/button.py

Task API

An API for could be quite useful for...

Huge thanks to @neuromantic99! All of the hard work was done on his api_dev branch

some minor changes I made/didn't include:

Summary of changes to framework

Before:

CleanShot 2023-06-25 at 17 13 24

After:

CleanShot 2023-06-25 at 17 13 31

@ThomasAkam one concern is that the added user_classes could potentially effect task logic but is not included in the data file or accounted for in the versioned task hash. This could harm reproducibility. Hopefully this versioning issue for user_classes and hardware definitions https://github.com/pyControl/code/issues/52 can be addressed with the new data file format you are working on?

ThomasAkam commented 1 year ago

Many thanks @alustig3, this is really fantastic, both bits of functionallity will be super useful. A couple of comments, questions and suggestions:

Implementation

Code

ThomasAkam commented 1 year ago

@alustig3 I think the new_data_format branch is ready to merge back into the dev branch. Do you want me to wait till this is merged in or shall I go ahead and do that now?

alustig3 commented 1 year ago

@ThomasAkam please go ahead and merge the new_data_format branch

ThomasAkam commented 1 year ago

I've merged the new_data_format branch into dev. I've also formatted the dev branch with the Black formatter using a max line length of 120 as discussed in #82.

alustig3 commented 1 year ago

For the GUI event triggering functionallity, rather than having users specify which events they want to be able to trigger in advance in the task file, I would be inclined to have a row in the 'Controls' dialog which has a lablel saying 'Trigger event:' then a dropdown menu to select which event, and a button to trigger it. This seems both simpler for the user and more flexible. What do you think?

Yes that is a great solution. I like that it avoids the need for another special variable type. I made the changes as you described for the standard control dialog. Individual custom event triggering buttons can still be created using the custom controls dialog editor.

I would be inclined to have one or more dedicated task to show the API functionallity, as the blinker and button examples are the simplest tasks and are likely a starting point for users familiarising themselves with task coding, so I would rather not complicate them.

I reverted the changes to the example tasks and created a separate (rough) example api task (please change as you see fit).

Pycboard.send_serial_data() : Could header and subheader have more meaningful names? Or be combined into a single header argument?

changed to command and cmd_type

Pycboard.generate_event() : Would trigger_event be a clearer function name? 'event_name' might be clearer than 'event' for the argument to differentiate from the event ID.

yes changes made

How is the Api.update() call frequency determined, this could be stated in the update docstring.

based on plotting update interval setting. I believe it was added for the purposed of being used with matplotlib or equivalent. I gave it a clearer function name and updated the docstring.

The various named tuples (e.g. event_tup) could be defined at the module level rather than inside API.interface().

I believe it was written this way because if the tuples are in the API __init__function, they could potentially be left out when the user writes their sub_class(API) if they end up defining their own __init__ function and forget to include super().__init__(). I'm not sure though? Also I might be misunderstanding what you are asking about.

framework.py: Why move the code in the read_data function out from the recieve_data function. If the code is not used elsewhere there is no reason to have a seperate function, and function calls incur some amount of overhead.

fixed

@ThomasAkam I think for the most part this is ready to merge, and then you can make the final fixes to get this across the finish line?

ThomasAkam commented 1 year ago

@alustig3 Many thanks for making these changes, I'll merge this into the dev branch now.

I noticed there are quite a lot of formatting changes in bits of the code unrelated to the new functionallity. Please do these in seperate pull request in future as it makes reviewing the pull requests harder.

Also, regarding the reformatting of comments, for comments that are at the end of a line and indicate the meaning of a variable, I think that moving them to the line above is confusing as they can be taken for a section heading. I would rather move them to the line below and indent them, or leave the variable definion split across multiple lines with brackets and keep the comment at the end of the line defining the variable.

alustig3 commented 1 year ago

Please do these in seperate pull request in future as it makes reviewing the pull requests harder.

will do

I would rather move them to the line below and indent them, or leave the variable definion split across multiple lines with brackets and keep the comment at the end of the line defining the variable.

sounds good

thanks @ThomasAkam for cleaning things up and doing the merge.

alustig3 commented 1 year ago

I've attempted to add event types for calls made by the API: https://github.com/alustig3/pyControl/commit/51f5919ec451fbe92a65f418f42a147c43ae3b7c

I don't know if it is the right approach, and I'm not sure how to proceed with giving API triggered events its own type since unlike varbl_typ, the event_typ doesn't have subtypes that I could add to.

@ThomasAkam could you take a look at implementing this?

Also, is there a way to send a print_typ event from the API? Right now the API class is written to use a print_to_log function that prints directly to the GUI log without a timestamp and without getting written to the data log.

ThomasAkam commented 1 year ago

Hi @alustig3,

If I understand correctly the new code that you linked to is for recording whether a set_variable is due to the api or the user, but does not implement indicating whether events are triggered by the api, is that correct?

I can see that it would be desirable to indicate when events are api triggered, and I think the way to do this would be to have a single event_typ which had an additional field in the event tuple which indicated whether it was triggered by a timer, external input or API. This could be recorded in the value column of the data tsv. Alternatively we could have an additional column called e.g. 'operation' which would indicate the operation that generated the row, so {'timer', 'input', 'api'} for events and {'run_start', 'run_end', 'user_set', 'api_set', 'get', 'print'} for variables. This might be preferable as it is already a bit ugly to use the 'name' column for the operation that generated a 'variable' row and it would get even worse if we then use the 'value' column to indicate the operation that generated an event row. What do you think?

alustig3 commented 1 year ago

@ThomasAkam,

If I understand correctly the new code that you linked to is for recording whether a set_variable is due to the api or the user, but does not implement indicating whether events are triggered by the api, is that correct?

correct

the following is my understanding: the dev branch currently has

time type name/ID value
timestamp state state name -
timestamp event event name -
timestamp print - print text
timestamp variable get/set/print/run_start/run_end {"var":value,"var2":value2}
- warning - warning message text
- error - error message text

option 1:

have a single event_typ which had an additional field in the event tuple which indicated whether it was triggered by a timer, external input or API. This could be recorded in the value column of the data tsv

time type name/ID value
timestamp state state name -
timestamp event event name timer/ext_input/api
timestamp print - print text
timestamp variable get/user_set/api_set/print/run_start/run_end {"var":value,"var2":value2}
- warning - warning message text
- error - error message text

option 2:

Alternatively we could have an additional column called e.g. 'operation' which would indicate the operation that generated the row, so {'timer', 'input', 'api'} for events and {'run_start', 'run_end', 'user_set', 'api_set', 'get', 'print'} for variables

time type name operation value
timestamp state state name - -
timestamp event event name timer/ext_input/api -
timestamp print - - print text
timestamp variable - get/user_set/api_set/print/run_start/run_end {"var":value,"var2":value2}
- warning - - warning message text
- error - - error message text

I agree that option 1 is a bit ugly/inconsistent. I propose a variation of option 2, but combine the name/value columns.

time type operation name/value
timestamp state - state name
timestamp event timer/ext_input/user/api event name
timestamp print task/api print text
timestamp variable get/user_set/api_set/print/run_start/run_end {"var":value,"var2":value2}
- warning - warning message text
- error - error message text

what do you think?

ThomasAkam commented 1 year ago

I agree it would be desirable to stick with 4 columns given we don't need 5, but 'operation' and 'name/value' might be confusing for info rows. How about 'subtype' and 'content'.

time type subtype content
timestamp info info_type info_value
timestamp state - state_name
timestamp event timer/input/user/api event_name
timestamp print task/api print_text
timestamp variable get/user_set/api_set/print/run_start/run_end {"var":value,"var2":value2}
- warning - warning message text
- error - error message text
alustig3 commented 1 year ago

looks good to me 👍