#!/usr/bin/env python3 import argparse import sys import time import tkinter as tk import tkinter.messagebox import tkinter.simpledialog import tkinter.ttk from threading import Thread class StdinBox(tk.Toplevel): def __init__(self, title="", message="", geometry="800x80"): tk.Toplevel.__init__(self) self.geometry(geometry) self.title(title) self.exit = False self.my_message = message self.my_title = title self.messageLabel = tk.Label(self, text=message, bg="#222", fg="#fff", font="TkFixedFont") self.messageLabel.pack(expand=True, fill=tk.BOTH) daemon = Thread(target=self.stream_stdin) daemon.start() self.update_timer() self.mainloop() def update_message(self, message): self.messageLabel.configure(text=message) def update_title(self, title): self.title(title) def update_timer(self): if self.exit: self.quit() return self.messageLabel.configure(text=self.my_message) self.update() self.after(333, self.update_timer) def stream_stdin(self): with open(sys.stdin.fileno(), mode="rt", encoding="utf-8", newline=None, buffering=1, closefd=True) as fp: for line in fp: self.my_message = line.rstrip("\r\n") self.exit = True class ListBox(tk.Toplevel): def __init__(self, title="", message="", geometry="800x800"): tk.Toplevel.__init__(self) self.geometry(geometry) # Must change these accordingly self.configure(background='#222') self.title(title) self.messageLabel = tk.Label(self, text=message, bg="#222", fg="#fff", font="TkFixedFont") self.choose = tk.Listbox(self,bg="#222", fg="#fff", font="TkFixedFont",selectmode='browse') with open(sys.stdin.fileno(), mode="rt", encoding="utf-8", newline=None, buffering=1, closefd=True) as fp: for i,line in enumerate(fp): self.choose.insert(i,line.rstrip("\r\n")) self.last_element = i self.choose.select_set(0) self.labelframe = tk.Frame(self, bg='#222') self.cancel_b = tk.Button(self.labelframe, text = "Cancel", command = self.quit) self.ok_b = tk.Button(self.labelframe, text = "OK", command = self.selected) self.messageLabel.pack()#expand=True, fill=tk.BOTH) self.choose.pack(expand=True,fill=tk.BOTH) self.cancel_b.pack(side='left') self.ok_b.pack(side='right') self.labelframe.pack() self.choose.focus_set() self.choose.bind('', self.selected) self.choose.bind('', self.selected) self.choose.bind("", self.key_handler) self.ok_b.bind('', self.selected) self.cancel_b.bind('', self.handle_exit) self.bind_all('', self.handle_exit) self.bind_all('', self.handle_exit) self.mainloop() def set_position(self,index): index = max(0, min( self.last_element, index )) self.choose.selection_clear(0,tk.END) self.choose.activate(index) self.choose.selection_set(index) def key_handler(self,event): if len(event.char) == 1 and event.char.isalnum(): # ~ print(event.char, event.keysym, event.keycode) for i in list(range(self.choose.curselection()[0] + 1, self.last_element+1)) + list(range(0,self.choose.curselection()[0]+1)): if self.choose.get(i).lower().startswith(event.char): self.set_position(i) return if event.keysym == 'Up': self.set_position(self.choose.curselection()[0] ) if event.keysym == 'Down': self.set_position(self.choose.curselection()[0] ) if event.keysym == 'Next': self.set_position(self.choose.curselection()[0] +10 ) if event.keysym == 'Prior': self.set_position(self.choose.curselection()[0] -10 ) if event.keysym == 'Home': self.set_position(0) if event.keysym == 'End': self.set_position(self.last_element) def selected(self, *args): for i in self.choose.curselection(): print(self.choose.get(i), end='') self.quit() def handle_exit(self, *args): self.quit() def get_opts(): parser = argparse.ArgumentParser() parser.add_argument( "--title", action="store", default=None, help="Window title", ) parser.add_argument("--text", action="store", default=None, help="Description text", nargs="?") parser.add_argument("--geometry", action="store", default=None, help="Geometry (not valid for all commands), ex. 800x200+50+50", nargs="?") parser.add_argument( "mode", action="store", default="info", const="info", nargs="?", choices=["info", "password", "entry", "stream","list"], help="operation mode. Note: stream mode expects stdin to update text and line starting with #.", ) parsed = parser.parse_args() return parsed def set_defaults(opts, title, text, geometry=None): if opts.text is None: opts.text = text if opts.title is None: opts.title = title if opts.geometry is None: opts.geometry = geometry return opts def main(): opts = get_opts() tk.Tk().withdraw() if opts.mode == "password": opts = set_defaults(opts, "Password", "Type password:") pw = tkinter.simpledialog.askstring(opts.title, opts.text, show="*") print(pw, end="") if opts.mode == "info": opts = set_defaults(opts, "Info", "Message") tkinter.messagebox.showinfo(title=opts.title, message=opts.text) if opts.mode == "entry": opts = set_defaults(opts, "Entry", "Type message:") msg = tkinter.simpledialog.askstring(title=opts.title, prompt=opts.text) print(msg, end="") if opts.mode == "stream": opts = set_defaults(opts, "Info", "", "800x80-50-50") StdinBox(title=opts.title, message=opts.text, geometry=opts.geometry) if opts.mode == "list": opts = set_defaults(opts, "Select", "Select from list:") ListBox(title=opts.title, message=opts.text) if __name__ == "__main__": main()