Package: rofi-menu
rofi-menuRofi allows defining custom modes (see the spec).This lib is a reference implementation with some extra "sugar".Features:simple menu definition via pythonextendableasync in first placeallows keeping state during rofi sessionSimple demo:Requirementsrofi >= 1.5.4python >= 3.6InstallationUsing pip$pipinstallrofi-menuExampleCreate a python script which will be used for rofi mode
e.g.example.py(don't forget to mark it as executable --chmod +x ./example.py)Assuming you installedrofi-menuinto a virtual environment (let's say it's~/.pyenv/versions/rofi/bin/python).
Make sure shebang points to the right python executable, e.g.#!/home/user/pyenv/versions/rofi/bin/python.#!/home/user/.pyenv/versions/rofi/bin/pythonimportrofi_menuclassProjectsMenu(rofi_menu.Menu):prompt="Projects"items=[rofi_menu.BackItem(),rofi_menu.ShellItem("Project 1","code-insiders ~/Develop/project1"),rofi_menu.ShellItem("Project 2","code-insiders ~/Develop/project2"),rofi_menu.ShellItem("Project X","code-insiders ~/Develop/projectx"),]classLogoutMenu(rofi_menu.Menu):prompt="Logout"items=[rofi_menu.ShellItem("Yes","i3-msg exit",flags={rofi_menu.FLAG_STYLE_URGENT}),rofi_menu.ExitItem("No",flags={rofi_menu.FLAG_STYLE_ACTIVE}),]classMainMenu(rofi_menu.Menu):prompt="menu"items=[rofi_menu.TouchpadItem(),rofi_menu.NestedMenu("Projects >",ProjectsMenu()),rofi_menu.ShellItem("Downloads (show size)","du -csh ~/Downloads",show_output=True),rofi_menu.NestedMenu("Second monitor",rofi_menu.SecondMonitorMenu()),rofi_menu.ShellItem("Lock screen","i3lock -i ~/.config/i3/bg.png"),rofi_menu.ShellItem("Sleep","systemctl suspend"),rofi_menu.NestedMenu("Logout",LogoutMenu()),]if__name__=="__main__":rofi_menu.run(MainMenu(),rofi_version="1.6")# change to 1.5 if you use older rofi versionRun it as:$rofi-modimymenu:/path/to/example.py-showmymenu-show-iconsIt'll result inAdvanced example#!/home/user/pyenv/versions/rofi/bin/pythonimportasynciofromdatetimeimportdatetimeimportosimportrofi_menuclassOutputSomeTextItem(rofi_menu.Item):"""Output arbitrary text on selection"""asyncdefon_select(self,meta):# any python codeawaitasyncio.sleep(0.1)returnrofi_menu.Operation(rofi_menu.OP_OUTPUT,("💢 simple\n""💥 multi-\n""💫 <b>line</b>\n""💣 <i>text</i>\n"))classDoAndExitItem(rofi_menu.Item):"""Do something and exit"""asyncdefon_select(self,meta):os.system('notify-send msg')returnrofi_menu.Operation(rofi_menu.OP_EXIT)classCurrentDatetimeItem(rofi_menu.Item):"""Show current datetime inside menu item"""asyncdefload(self,meta):self.state=datetime.now().strftime('%A%d. %B %Y (%H:%M:%S)')asyncdefrender(self,meta):returnf"🕑{self.state}"classCounterItem(rofi_menu.Item):"""Increment counter on selection"""asyncdefload(self,meta):awaitsuper().load(meta)self.state=self.stateor0meta.session.setdefault("counter_total",0)asyncdefon_select(self,meta):self.state+=1meta.session["counter_total"]+=1returnawaitsuper().on_select(meta)asyncdefrender(self,meta):per_menu_item=self.statetotal=meta.session["counter_total"]returnf"🏃 Selected #{per_menu_item}time(s) (across menu items #{total})"classHandleUserInputMenu(rofi_menu.Menu):allow_user_input=TrueclassCustomItem(rofi_menu.Item):asyncdefrender(self,meta):entered_text=meta.session.get("text","[ no text ]")returnf"You entered:{entered_text}"items=[CustomItem()]asyncdefon_user_input(self,meta):meta.session['text']=meta.user_inputreturnrofi_menu.Operation(rofi_menu.OP_REFRESH_MENU)main_menu=rofi_menu.Menu(prompt="menu",items=[OutputSomeTextItem("Output anything"),DoAndExitItem("Do something and exit"),CurrentDatetimeItem(),CounterItem(),CounterItem(),rofi_menu.NestedMenu("User input",HandleUserInputMenu()),],)if__name__=="__main__":rofi_menu.run(main_menu)TODOdocumentation of APIexamplestests