Adding shell command execution on click for shell module

This commit is contained in:
Andrei Lando 2023-09-12 16:36:37 +04:00
parent fded39fa81
commit 9b2ac5d521
2 changed files with 50 additions and 3 deletions

View file

@ -15,6 +15,12 @@ Parameters:
For example shell.command='echo $(date +'%H:%M:%S')' For example shell.command='echo $(date +'%H:%M:%S')'
But NOT shell.command='echo $(date +'%H:%M:%S')' But NOT shell.command='echo $(date +'%H:%M:%S')'
Second one will be evaluated only once at startup Second one will be evaluated only once at startup
* shell.left_click_command: Command to execute on
left mouse button click. Clicked commands are always
executed synchronously.
Command formatting rules described above applies here
* shell.right_click_command: Command to execute on
right mouse button click. See above.
* shell.interval: Update interval in seconds * shell.interval: Update interval in seconds
(defaults to 1s == every bumblebee-status update) (defaults to 1s == every bumblebee-status update)
* shell.async: Run update in async mode. Won't run next thread if * shell.async: Run update in async mode. Won't run next thread if
@ -28,19 +34,30 @@ contributed by `rrhuffy <https://github.com/rrhuffy>`_ - many thanks!
import os import os
import subprocess import subprocess
import threading import threading
import functools
import logging
import sys
import core.module import core.module
import core.widget import core.widget
import core.input import core.input
import core.event
import util.format import util.format
import util.cli import util.cli
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.get_output)) self.widget = core.widget.Widget(self.get_output)
super().__init__(config, theme, self.widget)
self.__command = self.parameter("command", 'echo "no command configured"') self.__command = self.parameter("command", 'echo "no command configured"')
self.__left_click_command = self.parameter(
"left_click_command",
'echo "no left_click_command configured"')
self.__right_click_command = self.parameter(
"right_click_command",
'echo "no right_click_command configured"')
self.__async = util.format.asbool(self.parameter("async")) self.__async = util.format.asbool(self.parameter("async"))
if self.__async: if self.__async:
@ -50,9 +67,27 @@ class Module(core.module.Module):
if self.parameter("scrolling.makewide") is None: if self.parameter("scrolling.makewide") is None:
self.set("scrolling.makewide", False) self.set("scrolling.makewide", False)
if self.__left_click_command is not None:
core.input.register(
self.widget,
button=core.input.LEFT_MOUSE,
cmd=functools.partial(
self.click_command,
command=self.__left_click_command))
if self.__right_click_command is not None:
core.input.register(
self.widget,
button=core.input.RIGHT_MOUSE,
cmd=functools.partial(
self.click_command,
command=self.__right_click_command))
def set_output(self, value): def set_output(self, value):
self.__output = value self.__output = value
def click_command(self, event, command=None):
util.cli.execute(command, shell=True, ignore_errors=True, wait=True)
@core.decorators.scrollable @core.decorators.scrollable
def get_output(self, _): def get_output(self, _):
return self.__output return self.__output
@ -60,7 +95,9 @@ class Module(core.module.Module):
def update(self): def update(self):
# if requested then run not async version and just execute command in this thread # if requested then run not async version and just execute command in this thread
if not self.__async: if not self.__async:
self.__output = util.cli.execute(self.__command, shell=True, ignore_errors=True).strip() self.set_output(
util.cli.execute(self.__command, shell=True, ignore_errors=True).strip()
)
return return
# if previous thread didn't end yet then don't do anything # if previous thread didn't end yet then don't do anything

View file

@ -52,7 +52,17 @@ def execute(
raise RuntimeError("{} not found".format(cmd)) raise RuntimeError("{} not found".format(cmd))
if wait: if wait:
out, _ = proc.communicate() timeout = 60 # seconds
try:
out, _ = proc.communicate(timeout=timeout)
except subprocess.TimeoutExpired as e:
logging.warning(f'''
Communication with process pid={proc.pid} hangs for more
than {timeout} seconds.
If this is not expected, the process is stale, or
you might have run in stdout / stderr deadlock.
''')
out, _ = proc.communicate()
if proc.returncode != 0: if proc.returncode != 0:
err = "{} exited with code {}".format(cmd, proc.returncode) err = "{} exited with code {}".format(cmd, proc.returncode)
logging.warning(err) logging.warning(err)