diff --git a/controldeck.py b/controldeck.py
index 0762e6a..cc516d2 100755
--- a/controldeck.py
+++ b/controldeck.py
@@ -52,6 +52,7 @@ from justpy import (
QCard,
QCardSection,
QDialog,
+ QDiv,
QEditor,
QHeader,
QIcon,
@@ -126,12 +127,50 @@ def config_load(conf=''):
#print(config.sections())
return config
+# TODO: colors definable in config
+class Label(QDiv):
+ """
+ Args:
+ **kwargs:
+ - wtype: if 'empty' then: empty slot, for horizontal arrangement, text is
+ ignored, no bg color etc.
+ """
+ def __init__(self, **kwargs):
+ super().__init__(**kwargs)
+ self.style = "width: 90px;"
+ self.style += "min-height: 70px;"
+ if self.wtype != 'empty':
+ self.classes = f"q-pa-sm text-blue-grey-4 text-bold text-center bg-light-blue-10"
+ #self.style += "font-size: 14px;"
+ self.style += "line-height: 1em;"
+ self.style += "border-radius: 7px;"
+ self.text = self.text.upper()
+ #print()
+ #print(self)
+
+# TODO: distinguish between non-command and comand inactive stage?
class Button(QBtn):
"""
- usage
- Button(text, text_alt, btype, command, command_alt,
+ Args:
+ **kwargs:
+ - text: button text in normal state (unpressed)
+ - text_alt: button text in active state (pressed)
+ - wtype: 'button' (any string atm) for a button
+ - command: command to execute on click
+ - command_alt: command to execute on click in active state
+ - color_bg: background color
+ - color_fg: foreground color
+ - state_pattern: string defining the normal state (unpressed) [NEDDED?]
+ - state_pattern_alt: string defining the alternative state
+ (active, pressed)
+ - state_command: command to execute to compare with state_pattern*
+ - icon: icon in normal state (unpressed)
+ - icon_alt: icon in active state
+
+ Usage:
+ Button(text, text_alt, wtype, command, command_alt,
color_bg=, color_fg=, state_pattern, state_pattern_alt,
- state_command, state_command_alt,
+ state_command,
icon, icon_alt, image, image_alt,
a)
"""
@@ -140,55 +179,72 @@ class Button(QBtn):
# self.outline = True # is set via style, see below
# self.color = "blue-grey-8" # is overwritten via text_color
self.text_color = COLOR_PRIME_TEXT
- self.size = 'md'
+ self.size = 'md' # button / font size
self.push = True
# self.padding = 'none' # not working, also not inside init
self.dense = True
# default **kwargs
- self.btype = None # button or empty
+ self.wtype = None # button or empty
self.image = '' # used for files like svg and png
self.command = '' # command to run on click
self.state = '' # output of the state check command
self.state_command = '' # command to check the unclicked state
super().__init__(**kwargs)
+
self.style = "width: 90px;"
- self.style += "min-height: 70px;"
+ self.style += "min-height: 77px;" # image + 2 text lines
+ self.style += "border: 1px solid #455a64;" # #455a64 blue-grey-8
+ self.style += "line-height: 1em;"
- if self.btype == 'empty':
- self.classes = 'invisible' # invisible; not shown but still stakes up space
- else:
- self.style += "border: 1px solid #455a64;" # #455a64 blue-grey-8
+ if self.image and path.exists(self.image):
+ # copy image files into the static folder
+ basename = path.basename(self.image)
+ staticfile = path.join(STATIC_DIR, basename)
+ if not path.exists(staticfile):
+ shutil.copy2(self.image, staticfile)
+ if DEBUG:
+ print(f'[DEBUG] copy {self.image} to {staticfile}')
+ self.icon = f"img:/static/{basename}"
+ #
+ #
- self.style += "width: 90px;"
- self.style += "min-height: 77px;" # image + 2 text lines
- self.style += "line-height: 1em;"
- if self.image and path.exists(self.image):
- # copy image files into the static folder
- basename = path.basename(self.image)
- staticfile = path.join(STATIC_DIR, basename)
- if not path.exists(staticfile):
- shutil.copy2(self.image, staticfile)
- if DEBUG:
- print(f'[DEBUG] copy {self.image} to {staticfile}')
- self.icon = f"img:/static/{basename}"
- #
- #
-
- if self.command != '':
- QTooltip(a=self, text=self.command.strip(), delay=500) # setting style white-space:pre does not work, see wp.css below
- def click(self, msg):
+ if self.command != '':
+ self.update_state()
+ tt = f"command: {self.command.strip()}"
+ if self.state_command:
+ tt += f"\nstate: {self.state}"
+ QTooltip(a=self, text=tt, delay=500) # setting style white-space:pre does not work, see wp.css below
+ def click(self, msg):
+ if self.command != '':
self.update_state()
- if self.command != '':
+ if DEBUG:
print()
print(datetime.datetime.now())
- print(self.command)
- process(self.command, shell=True, output=False) # output=True freezes controldeck until process finished (until e.g. an emacs button is closed)
- self.on('click', click)
+ print(f"command: {self.command}")
+ process(self.command, shell=True, output=False) # output=True freezes controldeck until process finished (until e.g. an emacs button is closed)
+ self.on('click', click)
+
+ def is_state_alt(self):
+ return self.state == self.state_pattern_alt
def update_state(self):
if self.state_command != '':
self.state = process(self.state_command, shell=True)
+ if DEBUG:
+ print()
+ print(datetime.datetime.now())
+ print("update btn state")
+ print(f"text: {self.text}")
+ print(f"state (before click): {self.state}")
+ print(f"state_command: {self.state_command}")
+ print(f"state_pattern: {self.state_pattern}")
+ print(f"state_pattern_alt: {self.state_pattern_alt}")
+ print(f"is_state_alt: {self.is_state_alt()}")
+ if self.is_state_alt():
+ # self.style += "border: 1px solid green;"
+ self.style += "border-bottom: 1px solid green;"
+
class Volume(Div):
# class variables
@@ -202,7 +258,7 @@ class Volume(Div):
self.slider = None # for handle methods to access slider
# default **kwargs
- self.vtype = 'sink' # sink (loudspeaker) or sink-input (app output)
+ self.wtype = 'sink' # sink (loudspeaker) or sink-input (app output)
self.name = '' # pulseaudio sink name
self.description = '' # badge name
@@ -211,10 +267,10 @@ class Volume(Div):
self.update_state() # get self.pa_state
if self.pa_state:
- if self.vtype == 'sink':
+ if self.wtype == 'sink':
cmdl_toggle = 'pactl set-sink-mute {name} toggle'
cmdl_value = 'pactl set-sink-volume {name} {value}%'
- elif self.vtype == 'sink-input':
+ elif self.wtype == 'sink-input':
cmdl_toggle = 'pactl set-sink-input-mute {name} toggle'
cmdl_value = 'pactl set-sink-input-volume {name} {value}%'
app_name = self.pa_state['properties']['application.process.binary'] if 'application.process.binary' in self.pa_state['properties'] else ''
@@ -333,11 +389,11 @@ class Volume(Div):
self.update_states()
tmp = []
# filter for the given pa name, empty list if not found
- if self.vtype == 'sink':
+ if self.wtype == 'sink':
# match pa name with self.name
tmp = list(filter(lambda item: item['name'] == self.name,
Volume.data['sinks']))
- elif self.vtype == 'sink-input':
+ elif self.wtype == 'sink-input':
# match pa index with self.name
try: # for int casting
tmp = list(filter(lambda item: item['index'] == int(self.name),
@@ -354,7 +410,7 @@ class VolumeGroup():
a = kwargs.pop('a', None) # add Volume widgets to the specified component
Volume.update_states()
for i in Volume.data['sink-inputs']:
- Volume(a=a, name=i['index'], vtype='sink-input')
+ Volume(a=a, name=i['index'], wtype='sink-input')
async def reload(self, msg):
await msg.page.reload()
@@ -390,7 +446,7 @@ def widget_load(config) -> dict:
iname = None
#iname = re.search(r"^([0-9]*:?)([0-9]*\.?)(button|empty)", i, flags=re.IGNORECASE)
iname = re.search(
- r"^([0-9a-z]*:)?([0-9]*\.)?(button|empty|sink-inputs|sink|source)", # sink-inputs BEFORE sink
+ r"^([0-9a-z]*:)?([0-9]*\.)?(empty|label|button|sink-inputs|sink|source)", # sink-inputs BEFORE sink!
i, flags=re.IGNORECASE)
if iname is not None:
tab_name = iname.group(1)[:-1] if iname.group(1) is not None else '' # remove collon, id is '' if nothing is given
@@ -403,7 +459,14 @@ def widget_load(config) -> dict:
# print('wid_type', wid_type)
# print('wid_name', wid_name)
# print('')
- if wid_type in ['button', 'empty']:
+ if wid_type == 'empty':
+ args = [{'widget-class': 'Empty',
+ 'type': wid_type}]
+ if wid_type == 'label':
+ args = [{'widget-class': 'Label',
+ 'type': wid_type,
+ 'text': wid_name}]
+ elif wid_type == 'button':
# button or empty
args = [{'widget-class': 'Button',
'type': wid_type,
@@ -416,7 +479,6 @@ def widget_load(config) -> dict:
'state': config.get(i, 'state', fallback=''),
'state-alt': config.get(i, 'state-alt', fallback=''),
'state-command': config.get(i, 'state-command', fallback=''),
- 'state-command-alt': config.get(i, 'state-command-alt', fallback=''),
'icon': config.get(i, 'icon', fallback=''),
'icon-alt': config.get(i, 'icon-alt', fallback=''),
'image': config.get(i, 'image', fallback=''),
@@ -776,22 +838,30 @@ async def application(request):
name=var,
classes="row q-pa-sm q-gutter-sm",
a=tab_panel[tab_name])
+ if j['widget-class'] == 'Empty':
+ Label(text='',
+ wtype=j['type'],
+ a=eval(var))
+ if j['widget-class'] == 'Label':
+ Label(text=j['text'],
+ wtype=j['type'],
+ a=eval(var))
if j['widget-class'] == 'Button':
Button(text=j['text'], text_alt=j['text-alt'],
- btype=j['type'],
+ wtype=j['type'],
command=j['command'], command_alt=j['command-alt'],
color_bg=j['color-bg'], color_fg=j['color-fg'],
state_pattern=j['state'], state_pattern_alt=j['state-alt'],
- state_command=j['state-command'], state_command_alt=j['state-command-alt'],
+ state_command=j['state-command'],
icon=j['icon'], icon_alt=j['icon-alt'],
image=j['image'], image_alt=j['image-alt'],
a=eval(var))
elif j['widget-class'] == 'Volume':
Volume(name=j['name'], description=j['description'],
- vtype=j['type'],
+ wtype=j['type'],
a=eval(var))
elif j['widget-class'] == 'VolumeGroup':
- VolumeGroup(vtype=j['type'], a=eval(var))
+ VolumeGroup(wtype=j['type'], a=eval(var))
# TODO: change reference wp.components to ...
if not wp.components: