Skip to content

Instantly share code, notes, and snippets.

@stuartlangridge
Created January 3, 2026 21:45
Show Gist options
  • Select an option

  • Save stuartlangridge/e008dfb7fec3905e2c73ae81cb71ccc0 to your computer and use it in GitHub Desktop.

Select an option

Save stuartlangridge/e008dfb7fec3905e2c73ae81cb71ccc0 to your computer and use it in GitHub Desktop.
Tiny calculator as a Search Action for XFCE whisker menu search box
#!/usr/bin/env python3
"""
A tiny calculator for the XFCE whisker menu search box
"Install" by right-clicking the XFCE top-left ("whisker") menu
and saying Properties. Then, in "Search Actions", add a new
Search Action with Name: calculator, Pattern: ^[0-9+/* -]+$
Command: python path/to/this/file/whisker-calculator.py
and tick "Regular expression".
You should now be able to type "2 + 3+4" into the search box
and get the answer in a notification.
"""
import sys, json, time
import ast
import operator
import gi
gi.require_version('Notify', '0.7')
from gi.repository import GObject
from gi.repository import Notify
def calc(expr: str):
return compute(ast.parse(expr, mode="eval").body)
# calculator from https://github.com/theRealProHacker/MathEvaluator
op_map = {
# binary
ast.Add: operator.__add__,
ast.Sub: operator.__sub__,
ast.Div: operator.__truediv__,
ast.Mult: operator.__mul__,
# unary
ast.UAdd: operator.__pos__,
ast.USub: operator.__neg__,
}
allowed_types = {int, float}
def compute(expr):
match expr:
case ast.Constant(value=value):
if type(value) not in allowed_types:
raise SyntaxError(
f"Not a number {value!r}"
)
return value
case ast.UnaryOp(op=op, operand=value):
try:
return op_map[type(op)](compute(value)) # type: ignore
except KeyError:
raise SyntaxError(f"Unknown operation {ast.unparse(expr)}")
case ast.BinOp(op=op, left=left, right=right):
try:
return op_map[type(op)](compute(left), compute(right)) # type: ignore
except KeyError:
raise SyntaxError(f"Unknown operation {ast.unparse(expr)}")
case x:
raise SyntaxError(f"Invalid Node {ast.dump(x)}")
class NotifyClass(GObject.Object):
def __init__(self):
super(NotifyClass, self).__init__()
# lets initialise with the application name
Notify.init("whisker-calculator")
def send_notification(self, title, text, file_path_to_icon=""):
n = Notify.Notification.new(title, text, file_path_to_icon)
n.set_timeout(5000)
n.show()
equation = " ".join(sys.argv[1:])
my = NotifyClass()
my.send_notification(f"Result of {equation}", f"{calc(equation)}",
"/usr/share/icons/Humanity/apps/48/gnome-calculator.svg")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment