Python Menu System
To fit my specific requirements for a media center PC, I made a simple custom menu system. I also used the opportunity to learn some Python in the process. Basically, it reads a textual configuration file and sets up a graphical menu using Tk. The menu entries will execute a specific command when clicked, and can be wrapped in a file or numeric selection mechanism, if required.
Sorry for not using objects in the code, and I will admit that it's ugly with global variables in Python. But I'm a C-guy, and old habits die hard. :-)
#!/usr/bin/python import sys import os import Tkinter import tkFileDialog _num_value = 1 _num_label = None _num_top = None def read_config_file(path): try: fh = open(path, "r") list = [] for s in fh: list.append(s.split("#")) return list except IOError: print "Error: Unable to open config file for reading!" return None def menu_direct(command): print "Info: Direct execute: " + command os.system(command) def num_increment(): global _num_value, _num_label _num_value += 1 _num_label.config(text=str(_num_value)) def num_decrement(): global _num_value, _num_label _num_value -= 1 _num_label.config(text=str(_num_value)) def num_exec(command): global _num_value, _num_top new = command.replace("$", str(_num_value)) print "Info: Num execute: " + new os.system(new) _num_top.destroy() def menu_num_select(command): global _num_top, _num_label, _num_value _num_top = Tkinter.Tk() label = Tkinter.Label(_num_top, text="Number:") label.pack(fill="x", expand=True) plus = Tkinter.Button(_num_top, text="+", command=num_increment) plus.pack(fill="x", expand=True) _num_label = Tkinter.Label(_num_top, text=str(_num_value)) _num_label.pack(fill="x", expand=True) plus = Tkinter.Button(_num_top, text="-", command=num_decrement) plus.pack(fill="x", expand=True) f = lambda x = command: num_exec(x) go = Tkinter.Button(_num_top, text="GO!", command=f) go.pack(fill="x", expand=True) Tkinter.mainloop() def menu_file_select(command): file = tkFileDialog.askopenfilename() if file == (): return new = command.replace("$", str(file)) print "Info: File execute: " + new os.system(new) def main(): if len(sys.argv) != 2: print "Usage: %s <config file>" % sys.argv[0] sys.exit(1) list = read_config_file(sys.argv[1]) if list == None: sys.exit(1) top = Tkinter.Tk() for sublist in list: if sublist[0] == "Direct": # NOTE: This does not work: # f = lambda: menu_direct(sublist[2]) f = lambda x = sublist[2]: menu_direct(x) elif sublist[0] == "NumSelect": f = lambda x = sublist[2]: menu_num_select(x) elif sublist[0] == "FileSelect": f = lambda x = sublist[2]: menu_file_select(x) else: print "Error: Unknown menu type!" sys.exit(1) button = Tkinter.Button(top, text=sublist[1], command=f) button.pack(fill="x", expand=True) Tkinter.mainloop() if __name__ == "__main__": main()
Here is a typical config file (not very unlike the one I currently use):
FileSelect#Play File#/usr/bin/xine -pfq "$" NumSelect#Play DVD#/usr/local/bin/mplayer -fs -zoom dvd://$ Direct#Power Off#/sbin/poweroff
I used to use tabulators instead of '#' characters to separate the columns, but this really messes up any copy/paste/transfer, etc, so I just had to pick a character that is unlikely to be used in a command. Please just change the code if you don't like it. The '$' characters, are interpolated with the value from the file or numeric selection mechanism.
The final graphical representation of the menu will actually depend heavily on what version of Tk is installed, what window manager is used and font is used as default. Here is how it looks on one of my systems: