Closed e100 closed 6 months ago
The menu system has been one of the more difficult problems in pyEfis. I was trying to basically duplicate a lot of the functionality built into QActions because sometimes we also want these functions to be in buttons, sometimes in menu selections and sometimes with external buttons or keypresses. I don't remember why we couldn't actually use QActions for all of these things (probably had something to do with interacting with FiXGateway). There needs to be an abstraction layer between the buttons/menus/keyboard and the actual functions so that we are not duplicating code for functions like switching display units. Perhaps the right answer is to simply modify data in FiXGateway and use that change in data to activate the function. This seems like what you are suggesting? One thing for sure is that my way was WAAYY to complicated. What you are suggesting is probably more manageable.
I'd say go for it, but let's keep in in a feature branch for now since it will likely require breaking a lot of the existing code.
@birkelbach
Yes, we would keep track of button states in FIXGateway or simply have buttons react to data in FIXGateway My thought was to implement buttons and one could use the buttons as menu items. Buttons can auto hide/show based on screen so you have have different buttons on each screen in the same location. Buttons can be flagged to hide or show when the actions hide or show take place, or they can be persistent on all screens or just select screens.
I think first we need a solid schema to define the buttons, I think this same schema could work for 'keyboard buttons' too Here is my first go at it, I think the only thing that bothers me is this would be difficult to adapt to a different resolution since each button has an explicit location
menu:
colors: <- define colors that cen be referenced by name
red: FF0000
grey: 808080
yellow: FFFF00
orange: FFA500
white: FFFFFF
black: 000000
buttons <- list of buttons to define
NAME: <-Name of the button, a unique logical identifier
height: height in px
width: width in px
x: horizontal location
Y: vertical location
db_item: <- Name of DB item to monitor, must exist in the database
initial_value: <- Optional, On startup, set this value for db_item
tol: <- Optional
display: What to display on 2nd line of button when tol is reached, only valid if db_item is specified
fg_color: black
bg_color: red
fail: <- Optional, Same as tol but for fail
display: XXXXX
fg_color: black
bg_color: red
title: <- Optional, Text to display on first line of button
displayOn: <- Optional, list of screens where this button should appear, on other screens it will be hidden, when omitted shows on all screens
- SCREEN1
- SCREEN2
conditions: <- list of conditions that changes the click action and display of the button, first matching condition is used if multiple match.
- value: <- Optional, a string or number, this condition is activated when db_item equals the value defined
lte: <- Optional, condition activated if db_item is less than or equal to the value here
gte: <- Optional, condition activated if db_item is greater than or equall to the value here
and: <- Optional True|False when True evaluated as lte AND gte, when False lte OR gte
NOTE: While value, lte, gte and and are optional one must specify value or one or both of lte and gte
display: What do display on 2nd line of button, if omitted will display the value of the data item, could be an empty string ""
bg_color: <- button color when this condition is true
fg_color: <- button color when this condition is true
actions: <- when this condition is activated, these actions that will be taken when button is pressed
set: <- list of DB items and a value to set
- DB_ITEM1: <- name of DB item to modify
value: <- Optional value to set
increment: <- optional, amount to add to existing value of db_item, can be negative value
NOTE: While value and increment are both optional one or the other must be specified
goto: <- Optional name of screen to switch to
internal: <- Optional (hide,show,next,back,exit)
not_if: <- list of conditions that if true prevent a click from performing the action(s), takes precedence over only_if
- DB_ITEM1 <- check value of this item in the DB
value: <- Optional, a string or number, this condition is true when db_item equalls the value defined
lte: <- Optional, condition is true if db_item is less than or equall to the value here
gte: <- Optional, condition is true if db_item is greater than or equall to the value here
and: <- Optional True|False when True evaluated as lte AND gte, when False lte OR gte
NOTE: While value, lte, gte and and are optional one must specify value or one or both of lte and gte
only_if: <- list of conditions that when true allow the action to take place
- DB_ITEM1 <- check value of this item in the DB
value: <- Optional, a string or number, this condition is true when db_item equalls the value defined
lte: <- Optional, condition is true if db_item is less than or equall to the value here
gte: <- Optional, condition is true if db_item is greater than or equall to the value here
and: <- Optional True|False when True evaluated as lte AND gte, when False lte OR gte
NOTE: While value, lte, gte and and are optional one must specify value or one or both of lte and gte
@birkelbach
Is the databinding feature only used to simulate button presses? If so, I believe what I am working on could replace that too.
A config something like this would do the same as the existing data binding for physical buttons:
buttons:
button1: <- just a unique identifier
db_item: BTN1
title: Next
shortcut: "n" <- associates keyboard shortcut
order: 2 <- This is the 2nd button in the menu
displayOn:
- SCREEN1
- SCREEN2
conditions:
- value: true
simulate_click: true <- causes the actions below to happen immediately instead of waiting for a click
actions:
set:
- key: BTN1
value: false <- set BTN1 bool back to false to be ready for next click
internal: next <- internal action to move to next screen
- value: false <- whenever BTN1 is false the action below will happen if you click the button with the mouse or use shortcut
actions:
internal: next
The data binding is more of a way to activate actions from external sources. Mainly so that external buttons or encoders can be used and the data fed through FiXGateway. It's not there for simulation, rather for replacement. I'd rather use an encoder with a switch over a touchscreen, so that is the direction that my brain goes when working on these types of things.
@birkelbach
I just submitted a pull request that implements the basic features to make this work. I basically started as a copy of the old menu and modified it until I had what I wanted.
You can set BTN1/2/3/4/5 etc to true and it will be just as if a user pressed the button. Most of the documentation is inline in the buttonmenu section of the main.yaml If you read through it and use fix gateway client to set values you can see how the buttons change.
What we are missing compared the the existing features, might not be complete:
It does not play nice with the old menu, databinding and keybindings but I see no reason that someone could not choose to use the old way or change to the new menubuttons, just don't do both at the same time.
What does work:
Looking forward to your feedback after you give it a try
@birkelbach I see you merged into develop branch.
How should we handle the key binding? In my new button feature I can specify shortcuts, but those could conflict with a keybinding. The options I see:
I think we have some overlap in functionality with the data binding too.
This is implemented: https://github.com/makerplane/pyEfis/blob/master/docs/screenbuilder.md#button
I want to create a new feature and wanted to get some feedback before I get started. This might work to also replace the existing menu.
The idea is to define buttons in the configuration file. Each button, when clicked could set one or more data values to fix gateway. Buttons could display a static text or could monitor a data item and change display based on the value. For example it could monitor a data item and change color based on value or change to defined text based on value. or maybe even become disabled when seeing some values. Buttons could be flagged for what screens they display on and if they hide or not when pressing hide. Each button would have its size and location defined in the config. Since buttons can monitor data and set one or more data values, buttons can interact with one another.
Example Use case: Three buttons AP(Auto Pilot) HH(Heading Hold) FP(Flight Plan) Initially HH and FP are disabled and AP is off. They all display as grey indicating they are off. When AP is pressed it sets a data requesting to enable the auto pilot, button turns yellow The flight computer sets a value indicating it is armed. Now the AP button turns green after seeing that data Now that HH and FP see the auto pilot is armed they become enabled. Pressing HH will set data requesting heading hold to the flight computer, HH turns yellow Flight computer engages heading hold, sends message indicating the mode, HH turns green Pressing HH will send message to auto pilot to disengage and turns yellow. Once AP disengages heading hold it sends data and HH turns to grey indicating it is off.