class OSCRemote(object): def __init__(self): self.editor_issue = "1.3" # get command options self.command_options = remote_options() # get directory holding the code self.pp_dir = sys.path[0] if not os.path.exists(self.pp_dir + os.sep + "pp_oscremote.py"): tkinter.messagebox.showwarning("Pi Presents", "Bad Application Directory") exit() # Initialise logging Monitor.log_path = self.pp_dir self.mon = Monitor() self.mon.init() Monitor.classes = ['OSCRemote', 'OSCConfig', 'OSCEditor'] Monitor.log_level = int(self.command_options['debug']) self.mon.log(self, "Pi Presents Remote is starting") self.mon.log(self, " OS and separator " + os.name + ' ' + os.sep) self.mon.log(self, "sys.path[0] - location of code: code " + sys.path[0]) self.root = Tk() # OSC config class self.osc_config = OSCConfig() self.osc_config_file = self.pp_dir + os.sep + 'pp_config' + os.sep + 'pp_oscremote.cfg' self.read_create_osc() self.setup_gui() if self.osc_config.this_unit_ip == '': self.mon.err(self, 'IP of own unit must be provided in oscremote.cfg') self.init() #and start the system self.root.after(1000, self.run_app) self.root.mainloop() def init(self): #self.osc_config_file = self.pp_dir + os.sep + 'pp_config' + os.sep + 'pp_oscremote.cfg' #self.read_create_osc() self.pp_home_dir = self.command_options['home'] + '/pp_home' self.mon.log(self, "Data Home from options is " + self.pp_home_dir) self.pp_profile_dir = '' self.current_showlist = None self.current_show = None self.current_show_ref = '' self.shows_display.delete(0, END) self.results.set('') self.desc.set('Listening to replies from Slave on: ' + self.osc_config.this_unit_ip + ':' + self.osc_config.reply_listen_port) def add_status(self, text): self.status_display.insert(END, text + '\n') self.status_display.see(END) def run_app(self): self.output_client = None self.output_reply_server = None self.output_reply_thread = None if self.osc_config.master_enabled != 'yes': self.mon.err(self, 'OSC Master is not enabled in oscremote.cfg') return if self.osc_config.reply_listen_port == '': self.mon.err(self, 'Reply Listen port is not set in oscremote.cfg') return self.prefix = '/pipresents' self.this_unit = '/' + self.osc_config.this_unit_name self.add_status("This unit's OSC address is: " + self.this_unit) self.slave_unit = '/' + self.osc_config.slave_units_name self.add_status('Slave unit OSC address is: ' + self.slave_unit) #connect client sending commands to slave unit then start server to listen for replies self.output_client = self.init_client() self.add_status('Listening for replies from slave on: ' + self.osc_config.this_unit_ip + ':' + self.osc_config.reply_listen_port) self.output_reply_server = self.init_server( self.osc_config.this_unit_ip, self.osc_config.reply_listen_port, self.output_client) self.add_output_reply_handlers() self.output_reply_thread = self.start_server(self.output_reply_server) # *************************************** # RESPOND TO BUTTONS # *************************************** def open_show(self): self.msg_path = '/core/open ' self.msg_arg_text = self.current_show_ref self.display_msg_text() def close_show(self): self.msg_path = '/core/close ' self.msg_arg_text = self.current_show_ref self.display_msg_text() def exit_pipresents(self): self.msg_path = '/core/exitpipresents' self.msg_arg_text = '' self.display_msg_text() def play_event(self): self.msg_path = '/core/event' self.msg_arg_text = 'pp-play' self.display_msg_text() def pause_event(self): self.msg_path = '/core/event' self.msg_arg_text = 'pp-pause' self.display_msg_text() pass def stop_event(self): self.msg_path = '/core/event' self.msg_arg_text = 'pp-stop' self.display_msg_text() pass def up_event(self): self.msg_path = '/core/event' self.msg_arg_text = 'pp-up' self.display_msg_text() def down_event(self): self.msg_path = '/core/event' self.msg_arg_text = 'pp-down' self.display_msg_text() def animate(self): self.msg_path = '/core/animate' self.msg_arg_text = '' self.display_msg_text() def display_control(self): self.msg_path = '/core/monitor' self.msg_arg_text = '' self.display_msg_text() def loopback(self): self.msg_path = '/system/loopback' self.msg_arg_text = '' self.display_msg_text() def server_info(self): self.msg_path = '/system/server-info' self.msg_arg_text = '' self.display_msg_text() # and put the created text in the results box in the gui def display_msg_text(self): self.results.set(self.prefix + self.slave_unit + self.msg_path + ' ' + self.msg_arg_text) #respond to the Send button # parses the message string into fields and sends - NO error checking def send_message(self): if self.slave_unit == '/': self.mon.err(self, 'slave unit OSC name not set') return if self.osc_config.slave_units_ip == '': self.mon.err(self, 'slave unit IP not set') return msg_text = self.results.get() if msg_text == '': return fields = msg_text.split() osc_address = fields[0] arg_list = fields[1:] dest = (self.osc_config.slave_units_ip, int(self.osc_config.reply_listen_port)) self.add_status('Send message:' + msg_text + ' to ' + str(dest)) self.mon.log(self, 'send message: ' + msg_text) self.sendto(self.output_client, dest, osc_address, arg_list) # *************************************** # OSC CLIENT TO SEND MESSAGES # *************************************** def init_client(self): return OSC.OSCClient() def sendto(self, client, dest, address, arg_list): #print (' message to send',address,arg_list) msg = OSC.OSCMessage() msg.setAddress(address) for arg in arg_list: msg.append(arg) try: client.sendto(msg, dest) except Exception as e: self.mon.err(self, 'error in client when sending OSC command: ' + str(e)) def disconnect_client(self, client): if client != None: client.close() return # *************************************** # OSC SERVER TO LISTEN TO REPLIES # *************************************** def init_server(self, ip, port_text, client): self.mon.log(self, 'Start Server: ' + ip + ':' + port_text) return OSC.OSCServer((ip, int(port_text)), client) def start_server(self, server): st = threading.Thread(target=server.serve_forever) st.start() return st def close_server(self, server, st): if server != None: server.close() self.mon.log(self, 'Waiting for Server-thread to finish') if st != None: st.join() ##!!! self.mon.log(self, 'server thread closed') def add_output_reply_handlers(self): self.output_reply_server.addMsgHandler('default', self.no_match_handler) self.output_reply_server.addMsgHandler( self.prefix + "/system/loopback-reply", self.loopback_reply_handler) self.output_reply_server.addMsgHandler( self.prefix + "/system/server-info-reply", self.server_info_reply_handler) def no_match_handler(self, addr, tags, stuff, source): text = "No handler for message from %s" % OSC.getUrlStr(source) + '\n' text += " %s" % addr + self.pretty_list(stuff, '') self.add_status(text + '\n') def loopback_reply_handler(self, addr, tags, stuff, source): self.add_status('Loopback reply received from: ' + OSC.getUrlStr(source)) def server_info_reply_handler(self, addr, tags, stuff, source): unit = stuff[0] commands = stuff[1:] self.add_status('Server Information from: ' + OSC.getUrlStr(source)) self.add_status('OSC name: ' + unit) self.add_status('Commands:\n' + self.pretty_list(commands, '\n')) def pretty_list(self, fields, separator): text = ' ' for field in fields: text += str(field) + separator return text # *************************************** # INIT EXIT MISC # *************************************** def e_edit_osc(self): self.disconnect_client(self.output_client) self.output_client = None self.close_server(self.output_reply_server, self.output_reply_thread) self.output_reply_server = None self.output_reply_thread = None self.edit_osc() self.read_create_osc() self.init() self.add_status('\n\n\nRESTART') self.run_app() def app_exit(self): self.disconnect_client(self.output_client) self.close_server(self.output_reply_server, self.output_reply_thread) if self.root is not None: self.root.destroy() self.mon.finish() sys.exit() def show_help(self): tkinter.messagebox.showinfo("Help", "Read 'manual.pdf'") def about(self): tkinter.messagebox.showinfo( "About", "Simple Remote Control for Pi Presents\n" + "Author: Ken Thompson" + "\nWebsite: http://pipresents.wordpress.com/") def setup_gui(self): # set up the gui # root is the Tkinter root widget self.root.title("OSC Remote Control for Pi Presents") # self.root.configure(background='grey') self.root.resizable(False, False) # define response to main window closing self.root.protocol("WM_DELETE_WINDOW", self.app_exit) # bind some display fields self.desc = StringVar() self.filename = StringVar() self.display_show = StringVar() self.results = StringVar() self.status = StringVar() # define menu menubar = Menu(self.root) profilemenu = Menu(menubar, tearoff=0, bg="grey", fg="black") profilemenu.add_command(label='Select', command=self.open_existing_profile) menubar.add_cascade(label='Profile', menu=profilemenu) osc_configmenu = Menu(menubar, tearoff=0, bg="grey", fg="black") menubar.add_cascade(label='Options', menu=osc_configmenu) osc_configmenu.add_command(label='Edit', command=self.e_edit_osc) helpmenu = Menu(menubar, tearoff=0, bg="grey", fg="black") menubar.add_cascade(label='Help', menu=helpmenu) helpmenu.add_command(label='Help', command=self.show_help) helpmenu.add_command(label='About', command=self.about) self.root.config(menu=menubar) #top frame top_frame = Frame(self.root, padx=5, pady=5) top_frame.pack(side=TOP) # output info frame info_frame = Frame(top_frame, padx=5, pady=5) info_frame.pack(side=TOP, fill=BOTH, expand=1) info_name = Label(info_frame, text="Master's Name: " + self.osc_config.this_unit_name, font="arial 12 bold") info_name.pack(side=TOP) info_reply_address = Label(info_frame, textvariable=self.desc, font="arial 12 bold") info_reply_address.pack(side=TOP) results_label = Label(top_frame, text="Message to Send", font="arial 12 bold") results_label.pack(side=LEFT) results_display = Entry(top_frame, textvariable=self.results, width=70) results_display.pack(side=LEFT, fill=BOTH, expand=1) send_button = Button(top_frame, width=5, height=1, text='Send', fg='black', command=self.send_message, bg="light grey") send_button.pack(side=RIGHT) #bottom frame bottom_frame = Frame(self.root, padx=5, pady=5) bottom_frame.pack(side=TOP, fill=BOTH, expand=1) left_frame = Frame(bottom_frame, padx=5) left_frame.pack(side=LEFT) right_frame = Frame(bottom_frame, padx=5, pady=5) right_frame.pack(side=LEFT) suplabel_frame = Frame(right_frame, pady=5) suplabel_frame.pack(side=TOP) commands_label = Label(suplabel_frame, text="Show Control", font="arial 12 bold") commands_label.pack() supervisor_frame = Frame(right_frame, pady=5) supervisor_frame.pack(side=TOP) # supervisor buttons add_button = Button(supervisor_frame, width=5, height=1, text='Open\nShow', fg='black', command=self.open_show, bg="light grey") add_button.pack(side=LEFT) add_button = Button(supervisor_frame, width=5, height=1, text='Close\nShow', fg='black', command=self.close_show, bg="light grey") add_button.pack(side=LEFT) add_button = Button(supervisor_frame, width=10, height=1, text='Exit\nPi Presents', fg='black', command=self.exit_pipresents, bg="light grey") add_button.pack(side=LEFT) # events buttons oplabel_frame = Frame(right_frame, pady=5) oplabel_frame.pack(side=TOP) operations_label = Label(oplabel_frame, text="Input Events", font="arial 12 bold") operations_label.pack() operations_frame = Frame(right_frame, pady=5) operations_frame.pack(side=TOP) add_button = Button(operations_frame, width=5, height=1, text='Play', fg='black', command=self.play_event, bg="light grey") add_button.pack(side=LEFT) add_button = Button(operations_frame, width=5, height=1, text='Pause', fg='black', command=self.pause_event, bg="light grey") add_button.pack(side=LEFT) add_button = Button(operations_frame, width=5, height=1, text='Stop', fg='black', command=self.stop_event, bg="light grey") add_button.pack(side=LEFT) add_button = Button(operations_frame, width=5, height=1, text='Up', fg='black', command=self.up_event, bg="light grey") add_button.pack(side=LEFT) add_button = Button(operations_frame, width=5, height=1, text='Down', fg='black', command=self.down_event, bg="light grey") add_button.pack(side=LEFT) # animate buttons others_label_frame = Frame(right_frame, pady=5) others_label_frame.pack(side=TOP) others_label = Label(others_label_frame, text="Others", font="arial 12 bold") others_label.pack() others_frame = Frame(right_frame, pady=5) others_frame.pack(side=TOP) add_button = Button(others_frame, width=5, height=1, text='Animate', fg='black', command=self.animate, bg="light grey") add_button.pack(side=LEFT) add_button = Button(others_frame, width=8, height=1, text='Monitor on/off', fg='black', command=self.display_control, bg="light grey") add_button.pack(side=LEFT) # system buttons systemlabel_frame = Frame(right_frame, pady=5) systemlabel_frame.pack(side=TOP) system_label = Label(systemlabel_frame, text="System", font="arial 12 bold") system_label.pack() system_frame = Frame(right_frame, pady=5) system_frame.pack(side=TOP) add_button = Button(system_frame, width=5, height=1, text='Loopback', fg='black', command=self.loopback, bg="light grey") add_button.pack(side=LEFT) add_button = Button(system_frame, width=10, height=1, text='Server Info', fg='black', command=self.server_info, bg="light grey") add_button.pack(side=LEFT) # define display of showlist shows_title_frame = Frame(left_frame) shows_title_frame.pack(side=TOP) shows_label = Label(shows_title_frame, text="Shows") shows_label.pack() shows_frame = Frame(left_frame) shows_frame.pack(side=TOP) scrollbar = Scrollbar(shows_frame, orient=VERTICAL) self.shows_display = Listbox(shows_frame, selectmode=SINGLE, height=12, width=40, bg="white", activestyle=NONE, fg="black", yscrollcommand=scrollbar.set) scrollbar.config(command=self.shows_display.yview) scrollbar.pack(side=RIGHT, fill=Y) self.shows_display.pack(side=LEFT, fill=BOTH, expand=1) self.shows_display.bind("<ButtonRelease-1>", self.e_select_show) # status_frame status_frame = Frame(self.root, padx=5, pady=5) status_frame.pack(side=TOP, fill=BOTH, expand=1) status_label = Label(status_frame, text="Status", font="arial 12 bold") status_label.pack(side=LEFT) scrollbar = Scrollbar(status_frame, orient=VERTICAL) self.status_display = Text(status_frame, height=10, yscrollcommand=scrollbar.set) scrollbar.config(command=self.status_display.yview) scrollbar.pack(side=RIGHT, fill=Y) self.status_display.pack(side=LEFT, fill=BOTH, expand=1) # *************************************** # SHOWLIST # *************************************** def open_existing_profile(self): initial_dir = self.pp_home_dir + os.sep + "pp_profiles" if os.path.exists(initial_dir) is False: self.mon.err( self, "Profiles directory not found: " + initial_dir + "\n\nHint: Data Home option must end in pp_home") return dir_path = tkinter.filedialog.askdirectory(initialdir=initial_dir) # dir_path="C:\Users\Ken\pp_home\pp_profiles\\ttt" if len(dir_path) > 0: self.open_profile(dir_path) def open_profile(self, dir_path): showlist_file = dir_path + os.sep + "pp_showlist.json" if os.path.exists(showlist_file) is False: self.mon.err( self, "Not a Profile: " + dir_path + "\n\nHint: Have you opened the profile directory?") return self.pp_profile_dir = dir_path self.root.title("Remote for Pi Presents - " + self.pp_profile_dir) self.open_showlist(self.pp_profile_dir) def open_showlist(self, profile_dir): showlist_file = profile_dir + os.sep + "pp_showlist.json" if os.path.exists(showlist_file) is False: self.mon.err( self, "showlist file not found at " + profile_dir + "\n\nHint: Have you opened the profile directory?") self.app_exit() self.current_showlist = ShowList() self.current_showlist.open_json(showlist_file) # if float(self.current_showlist.sissue()) != float(self.editor_issue): # self.mon.err(self,"Version of profile does not match Remote: "+self.editor_issue) # self.app_exit() self.refresh_shows_display() def refresh_shows_display(self): self.shows_display.delete(0, self.shows_display.size()) for index in range(self.current_showlist.length()): self.shows_display.insert( END, self.current_showlist.show(index)['title'] + " [" + self.current_showlist.show(index)['show-ref'] + "]") if self.current_showlist.show_is_selected(): self.shows_display.itemconfig( self.current_showlist.selected_show_index(), fg='red') self.shows_display.see(self.current_showlist.selected_show_index()) def e_select_show(self, event): # print 'select show', self.current_showlist.length() if self.current_showlist is not None and self.current_showlist.length( ) > 0: mouse_item_index = int(event.widget.curselection()[0]) self.current_showlist.select(mouse_item_index) self.current_show_ref = self.current_showlist.selected_show( )['show-ref'] self.refresh_shows_display() else: self.current_show_ref = '' # *************************************** # OSC CONFIGURATION # *************************************** def read_create_osc(self): if self.osc_config.read(self.osc_config_file) is False: self.osc_config.create(self.osc_config_file, 'master') eosc = OSCEditor(self.root, self.osc_config_file, 'remote', 'Create OSC Remote Configuration') self.osc_config.read(self.osc_config_file) def edit_osc(self): if self.osc_config.read(self.osc_config_file) is False: self.osc_config.create(self.osc_config_file) eosc = OSCEditor(self.root, self.osc_config_file, 'remote', 'Edit OSC Reomote Configuration')
class InputDevice(object): def __init__(self): # root is the Tkinter root widget self.root = Tk() self.root.title("Input Device Utility") # self.root.configure(background='grey') self.root.resizable(False, False) # define response to main window closing self.root.protocol("WM_DELETE_WINDOW", self.app_exit) self.my_device = '' self.my_device_display = StringVar() self.device_list = [] self.matches = 0 # overall display root_frame = Frame(self.root) root_frame.pack(side=LEFT) devices_frame = Frame(root_frame, padx=5, pady=10) devices_frame.pack(side=LEFT) devices_label = Label(devices_frame, text="Devices in dev/input") devices_label.pack(side=TOP) devices_list_frame = Frame(devices_frame, padx=5, pady=10) devices_list_frame.pack(side=TOP) selected_device_title = Label(devices_frame, text='Selected device') selected_device_title.pack(side=TOP) self.selected_device_var = StringVar() selected_device = Label(devices_frame, textvariable=self.selected_device_var, fg="red") selected_device.pack(side=TOP) events_frame = Frame(root_frame, padx=5, pady=10) events_frame.pack(side=LEFT) events_title = Label(events_frame, text='Received Events') events_title.pack(side=TOP) events_list_frame = Frame(events_frame, padx=5, pady=10) events_list_frame.pack(side=TOP) # list of devices scrollbar = Scrollbar(devices_list_frame, orient=VERTICAL) self.devices_display = Listbox(devices_list_frame, selectmode=SINGLE, height=20, width=60, bg="white", activestyle=NONE, fg="black", yscrollcommand=scrollbar.set) scrollbar.config(command=self.devices_display.yview) scrollbar.pack(side=RIGHT, fill=Y) self.devices_display.pack(side=LEFT, fill=BOTH, expand=1) self.devices_display.bind("<ButtonRelease-1>", self.e_select_device) # events display scrollbar = Scrollbar(events_list_frame, orient=VERTICAL) self.events_display = Text(events_list_frame, width=40, height=20, wrap='word', font="arial 11", padx=5, yscrollcommand=scrollbar.set) scrollbar.config(command=self.events_display.yview) scrollbar.pack(side=RIGHT, fill=Y) self.events_display.pack(side=LEFT, fill=BOTH, expand=1) self.events_display.config(state=NORMAL) self.events_display.delete(1.0, END) self.events_display.config(state=DISABLED) self.selected_device_index = -1 self.matches = 0 self.get_all_devices() self.refresh_devices_display() self.root.after(10, self.event_loop) # and enter Tkinter event loop self.root.mainloop() # *************************************** # INIT AND EXIT # *************************************** def app_exit(self): self.root.destroy() exit() def event_loop(self): if self.matches > 0: self.get_events() self.root.after(10, self.event_loop) def refresh_devices_display(self): self.devices_display.delete(0, self.devices_display.size()) for device in self.all_devices: self.devices_display.insert(END, device[0] + ' ' + device[1]) if self.selected_device_index >= 0: self.devices_display.itemconfig(self.selected_device_index, fg='red') self.devices_display.see(self.selected_device_index) def e_select_device(self, event): self.selected_device_index = -1 if len(self.all_devices) > 0: self.selected_device_index = int(event.widget.curselection()[0]) selected_device = self.all_devices[self.selected_device_index] self.selected_device_name = selected_device[0] self.selected_device_var.set(self.selected_device_name) self.get_matching_devices() self.refresh_devices_display() def get_all_devices(self): self.all_devices = [] devices = [evdev.InputDevice(fn) for fn in evdev.list_devices()] for device in devices: self.all_devices.append([device.name, device.path]) def get_matching_devices(self): self.matches = 0 self.matching_devices = [] devices = [evdev.InputDevice(fn) for fn in evdev.list_devices()] for device in devices: if self.selected_device_name in device.name: device_ref = evdev.InputDevice(device.path) self.matching_devices.append(device_ref) self.matches += 1 def get_events(self): r, w, x = select(self.matching_devices, [], [], 0) if r == []: return for event in r[0].read(): if event.type == evdev.ecodes.EV_KEY: key_event = evdev.categorize(event) if key_event.keystate == 1: key_text = 'Down' else: key_text = 'Up' # print key_event.keycode,key_text if type(key_event.keycode) is list: code_text = ', '.join(key_event.keycode) else: code_text = key_event.keycode self.events_display.config(state=NORMAL) self.events_display.insert(END, '\n' + code_text + ' ' + key_text) self.events_display.config(state=DISABLED) self.events_display.see(END)
class Application(Frame): """Container class, encapsulates app""" # this class inherits from Tkinter.parent def __init__(self, path, master=None): """Constructor""" # call parent class constructor Frame.__init__(self, master) self.path = path self.image_filenames = find_image_files(path) # necessary to make the application actually appear on the screen self.grid(sticky=N + S + E + W) # PIL image, for loading from file and for resizing self.image_pil = None # Tk photoimage, for display self.image_tk = None # list of coords (2-element lists) of current selection's pts self.points_orig = [] # points_canvas is 'points_orig', in canvas coordinates self.points_canvas = [] # x- and y-coords of displayed image in canvas coordinate frame self.x_offset = -1 self.y_offset = -1 # font in listbox text self.font = Font(family='Helvetica', size=10, weight='normal') # crosshair line size , as fraction of # min(displayed-imagewidth, displayed-image-height) self.crosshair_fraction = 0.05 # color for drawing first crosshair - the origin self.crosshair1_color = 'red' # color for drawing second crosshair - (together with the first # crosshair, these two points define a coordinate frame) - self.crosshair2_color = 'green' # color for drawing third and on crosshairs - all points other # than the first and second, have this color self.crosshair3_color = 'cyan' # the width, in pixels, of crosshairs self.crosshair_thickness = 2 # length of crosshair (updated upon display) self.crosshair_radius = -1 # the scale of currently displayed image (updated upon display) self.image_scaling = 1.0 # create all widges and set their initial conditions self.create_widgets() def create_widgets(self): """Set up all application graphics""" # get the top level winddow top = self.winfo_toplevel() # set the title of the top level window top.title('Image Point Tagging Tool') # make row 0 of the top level window's grid stretchable top.rowconfigure(0, weight=1) # make column 0 of the top level window's grid stretchable top.columnconfigure(0, weight=1) # bind keys for entire app top.bind_all('<Up>', self.select_prev) top.bind_all('<Down>', self.select_next) # make row 0 of Application's widget's grid stretchable self.rowconfigure(0, weight=1) # make column 0 of Application's widget's grid stretchable self.columnconfigure(0, weight=1) self.canvas = Canvas(self, bg='gray') self.canvas.grid(row=0, column=0, rowspan=2, sticky=N + S + E + W) self.canvas.rowconfigure(0, weight=1) self.canvas.columnconfigure(0, weight=1) # bind resize events (need -4 here bec. event gives 4+(real_size)) self.canvas.bind( '<Configure>', lambda e, s=self: s.on_resize_canvas(e.width - 2, e.height - 2)) # bind canvas mouse clicks self.canvas.bind('<Button-1>', self.on_click_button1) self.canvas.bind('<Button-3>', self.on_click_button3) # create scrollbars self.scrollbar_x = Scrollbar(self, orient=HORIZONTAL, width=10) self.scrollbar_y = Scrollbar(self, orient=VERTICAL, width=10) self.scrollbar_x.grid(row=1, column=1, columnspan=2, sticky=E + W) self.scrollbar_y.grid(row=0, column=3, sticky=N + S) # create lb for showing labeled/not-labeled images self.listbox_marks = Listbox(self, width=1, takefocus=0, exportselection=0, font=self.font) self.listbox_marks.grid(row=0, column=1, sticky=N + S + E + W) # create lb for showing image filenames self.lisbox_filenames = Listbox(self, width=30, selectmode=SINGLE, xscrollcommand=self.scrollbar_x.set, yscrollcommand=self.scrollbar_y.set, exportselection=0, font=self.font) self.lisbox_filenames.grid(row=0, column=2, sticky=N + S + E + W) # bind scrollbar movement self.scrollbar_x['command'] = self.lisbox_filenames.xview self.scrollbar_y['command'] = self.on_scrollbar_y # bind left mouse click selection self.lisbox_filenames.bind( '<Button-1>', lambda e, s=self: s.select(self.lisbox_filenames.nearest(e.y))) self.listbox_marks.bind( '<Button-1>', lambda e, s=self: s.select(self.lisbox_filenames.nearest(e.y))) # bind wheel scroll self.lisbox_filenames.bind( '<Button-4>', lambda e, s=self: on_mousewheel(self.listbox_marks, 4)) self.lisbox_filenames.bind( '<Button-5>', lambda e, s=self: on_mousewheel(self.listbox_marks, 5)) self.listbox_marks.bind( '<Button-4>', lambda e, s=self: on_mousewheel(self.lisbox_filenames, 4)) self.listbox_marks.bind( '<Button-5>', lambda e, s=self: on_mousewheel(self.lisbox_filenames, 5)) # skip is # of chars to skip in path string so that only the # part of the path that was not supplied is displayed skip = len(self.path) if self.path[skip - 1] != '/': skip += 1 # insert image filenames plus marks into lists and # select first image that does not have pts file i = 0 index_of_image_with_no_pts_file = -1 for image_filename in self.image_filenames: self.lisbox_filenames.insert(END, image_filename[skip:]) if self.has_pts_file(i): self.listbox_marks.insert(END, '+') else: self.listbox_marks.insert(END, '') if index_of_image_with_no_pts_file < 0: index_of_image_with_no_pts_file = i i += 1 if index_of_image_with_no_pts_file < 0: self.select(0) else: self.select(index_of_image_with_no_pts_file) def on_scrollbar_y(self, *args): """Vertical scrollbar motion callback""" apply(self.lisbox_filenames.yview, args) apply(self.listbox_marks.yview, args) def on_click_button1(self, event): """Button 1 click callback: adds a crosshair at click location""" if self.coord_in_img(event.x, event.y): point = [(event.x - self.x_offset) / self.image_scaling, (event.y - self.y_offset) / self.image_scaling] point_scaled = [float(event.x), float(event.y)] self.points_orig.append(point) self.points_canvas.append(point_scaled) if len(self.points_orig) == 1: self.mark_labeled() self.on_resize_canvas(int(self.canvas['width']), int(self.canvas['height'])) self.save_points() def on_click_button3(self, event): """Button 3 click callback: deletes landmark near click location""" if not self.coord_in_img(event.x, event.y): return i = self.find_point_near_crosshair(event.x, event.y) if i >= 0: del self.points_orig[i] del self.points_canvas[i] if len(self.points_orig) == 0: self.mark_unlabeled() self.on_resize_canvas(int(self.canvas['width']), int(self.canvas['height'])) self.save_points() def select(self, i): """Select the i'th image to work with - make current selection = i""" # uncomment the following line if you are only dealing with # faces that have three points labeled on them and you want to # automatically reorder a previously tagged database so that # the person's right eye is the first point, left eye is # second point and mouth is third point self.sort_points() self.lisbox_filenames.selection_clear(0, END) self.listbox_marks.selection_clear(0, END) self.lisbox_filenames.selection_set(i) self.listbox_marks.selection_set(i) self.lisbox_filenames.see(i) self.listbox_marks.see(i) self.image_pil = PIL.Image.open(self.get_image_filename()) self.points_orig = self.read_pts_file() self.on_resize_canvas(int(self.canvas['width']), int(self.canvas['height'])) def select_prev(self, *args): #pylint: disable=unused-argument """Select entry that comes before current selection""" i = self.get_selected_index() if i > 0: self.select(i - 1) def select_next(self, *args): #pylint: disable=unused-argument """Select entry that comes after current selection""" i = self.get_selected_index() if i < len(self.image_filenames) - 1: self.select(i + 1) def on_resize_canvas(self, width, height): """Called when canvas is resized""" if width <= 0 or height <= 0: return # maximize image width or height depending on aspect ratios image_width = self.image_pil.size[0] image_height = self.image_pil.size[1] image_aspect_ratio = float(image_width) / float(image_height) self.canvas['width'] = width self.canvas['height'] = height canvas_width = int(self.canvas['width']) canvas_height = int(self.canvas['height']) canvas_aspect_ratio = float(canvas_width) / float(canvas_height) if image_aspect_ratio < canvas_aspect_ratio: new_image_width = int(image_aspect_ratio * float(canvas_height)) new_image_height = canvas_height else: new_image_width = canvas_width new_image_height = int(float(canvas_width) / image_aspect_ratio) self.image_tk = PhotoImage( self.image_pil.resize((new_image_width, new_image_height), PIL.Image.BILINEAR)) self.x_offset = 0.5 * (float(canvas_width) - float(new_image_width)) self.y_offset = 0.5 * (float(canvas_height) - float(new_image_height)) self.crosshair_radius = 0.5 * self.crosshair_fraction * float( min(new_image_width, new_image_height)) self.canvas.delete('image') self.canvas.create_image(self.x_offset, self.y_offset, anchor=NW, image=self.image_tk, tags='image') width_scale = float(new_image_width) / float(image_width) height_scale = float(new_image_height) / float(image_height) self.image_scaling = 0.5 * (width_scale + height_scale) self.points_canvas = [[ x[0] * self.image_scaling + self.x_offset, x[1] * self.image_scaling + self.y_offset ] for x in self.points_orig] self.redraw_points() def redraw_points(self): """redraw points in current entry's .pts file""" self.canvas.delete('line') # draw first crosshair in color1 if len(self.points_canvas) > 0: point1 = self.points_canvas[0] self.draw_crosshair(point1[0], point1[1], self.crosshair1_color) # draw second crosshair in color2 if len(self.points_canvas) > 1: point2 = self.points_canvas[1] self.draw_crosshair(point2[0], point2[1], self.crosshair2_color) # draw third or higher crosshair in color3 if len(self.points_canvas) > 2: for point in self.points_canvas[2:]: self.draw_crosshair(point[0], point[1], self.crosshair3_color) def draw_crosshair(self, x_coord, y_coord, fill_color): """Draw a cross at (x_coord, y_coord) in the currently selected image""" start_x = x_coord - self.crosshair_radius start_y = y_coord - self.crosshair_radius end_x = x_coord + self.crosshair_radius end_y = y_coord + self.crosshair_radius min_x = self.x_offset min_y = self.y_offset max_x = self.x_offset + self.image_tk.width() - 1 max_y = self.y_offset + self.image_tk.height() - 1 if start_x < min_x: start_x = min_x if start_y < min_y: start_y = min_y if end_x > max_x: end_x = max_x if end_y > max_y: end_y = max_y self.canvas.create_line(x_coord, start_y, x_coord, end_y, width=self.crosshair_thickness, tags='line', fill=fill_color) self.canvas.create_line(start_x, y_coord, end_x, y_coord, width=self.crosshair_thickness, tags='line', fill=fill_color) def get_selected_index(self): """Returns index of current selection""" return int(self.lisbox_filenames.curselection()[0]) def coord_in_img(self, x_coord, y_coord): """Returns whether (x_coord, y_coord) is inside the shown image""" return (x_coord >= self.x_offset and y_coord >= self.y_offset and x_coord < self.x_offset + self.image_tk.width() and y_coord < self.y_offset + self.image_tk.height()) def find_point_near_crosshair(self, x_coord, y_coord): """Returns index of landmark point near (x_coord, y_coord), or -1""" i = 0 i_min = -1 min_dist = self.image_tk.width() + self.image_tk.height() for pair in self.points_canvas: x_dist = x_coord - pair[0] y_dist = y_coord - pair[1] dist = sqrt(x_dist * x_dist + y_dist * y_dist) if dist <= self.crosshair_radius and dist < min_dist: i_min = i i += 1 return i_min def save_points(self): """Save current points to pts file""" # remove whatever was there before if self.has_pts_file(): os.remove(self.get_pts_filename()) # save current result if len(self.points_orig) > 0: filehandle = open(self.get_pts_filename(), 'w') for pair in self.points_orig: message = str(pair[0]) + ', ' + str(pair[1]) + '\n' filehandle.write(message) filehandle.close() def sort_points(self): """ Reorder points, assuming face labeling, so that the first point is always the person's right eye, the second point is the person's left eye and the third point is the mouth. NB: this function only (destructively) works on self.points_orig """ if len(self.points_orig) != 3: return # step 1 sort the points according to y-value self.points_orig.sort(key=lambda pt: pt[1]) # step 2: from the top-most two points, call the leftmost one # the person's right eye and call the other the person's left eye if self.points_orig[0][0] > self.points_orig[1][0]: # swap first and second points' x-coordinate tmp = self.points_orig[0][0] self.points_orig[0][0] = self.points_orig[1][0] self.points_orig[1][0] = tmp # swap first and second points' y-coordinate tmp = self.points_orig[0][1] self.points_orig[0][1] = self.points_orig[1][1] self.points_orig[1][1] = tmp # order changed, so re-save self.save_points() def has_pts_file(self, i=None): """Returns whether (i'th) selection has a pts file with landmarks""" if i is None: i = self.get_selected_index() return os.path.exists(self.get_pts_filename(i)) def get_pts_filename(self, i=None): """Returns filename of selected (or i'th) .pts file""" if i is None: i = self.get_selected_index() image_filename = self.image_filenames[i] return os.path.splitext(image_filename)[0] + '.pts' def get_image_filename(self, i=None): """Returns filename of (i'th) selection's image""" if i is None: i = self.get_selected_index() return self.image_filenames[i] def read_pts_file(self, i=None): """Returns list of points (lists) in (i'th) selection's .pts file""" if i is None: i = self.get_selected_index() if self.has_pts_file(i): filehandle = open(self.get_pts_filename(i), 'r') lines = filehandle.readlines() filehandle.close() return [[float(pair[0]), float(pair[1])] for pair in [line.split(',') for line in lines]] else: return [] def mark_labeled(self, i=None): """Mark (i'th) selection as having a .pts file""" if i is None: i = self.get_selected_index() self.listbox_marks.insert(i, '+') self.listbox_marks.delete(i + 1) self.listbox_marks.selection_set(i) def mark_unlabeled(self, i=None): """Unmark (i'th) selection as having a .pts file""" if i is None: i = self.get_selected_index() self.listbox_marks.insert(i, '') self.listbox_marks.delete(i + 1) self.listbox_marks.selection_set(i)
class Combobox_Autocomplete(Entry, object): def __init__(self, master, list_of_items=None, autocomplete_function=None, listbox_width=None, listbox_height=7, ignorecase_match=False, startswith_match=True, vscrollbar=True, hscrollbar=True, **kwargs): if hasattr(self, "autocomplete_function"): if autocomplete_function is not None: raise ValueError( "Combobox_Autocomplete subclass has 'autocomplete_function' implemented") else: if autocomplete_function is not None: self.autocomplete_function = autocomplete_function else: if list_of_items is None: raise ValueError( "If not given complete function, list_of_items can't be 'None'") elif ignorecase_match: if startswith_match: def matches_function(entry_data, item): return item.startswith(entry_data) else: def matches_function(entry_data, item): return item in entry_data self.autocomplete_function = lambda entry_data: [ item for item in self.list_of_items if matches_function(entry_data, item)] else: if startswith_match: def matches_function(escaped_entry_data, item): if re.match(escaped_entry_data, item, re.IGNORECASE): return True else: return False else: def matches_function(escaped_entry_data, item): if re.search(escaped_entry_data, item, re.IGNORECASE): return True else: return False def autocomplete_function2(entry_data): escaped_entry_data = re.escape(entry_data) return [item for item in self.list_of_items if matches_function(escaped_entry_data, item)] self.autocomplete_function = autocomplete_function2 self._listbox_height = int(listbox_height) self._listbox_width = listbox_width self.list_of_items = list_of_items self._use_vscrollbar = vscrollbar self._use_hscrollbar = hscrollbar kwargs.setdefault("background", "white") if "textvariable" in kwargs: self._entry_var = kwargs["textvariable"] else: self._entry_var = kwargs["textvariable"] = StringVar() Entry.__init__(self, master, **kwargs) self._trace_id = self._entry_var.trace('w', self._on_change_entry_var) self._listbox = None self.bind("<Tab>", self._on_tab) self.bind("<Up>", self._previous) self.bind("<Down>", self._next) self.bind('<Control-n>', self._next) self.bind('<Control-p>', self._previous) self.bind("<Return>", self._update_entry_from_listbox) self.bind("<Escape>", lambda event: self.unpost_listbox()) def _on_tab(self, event): self.post_listbox() return "break" def _on_change_entry_var(self, name, index, mode): entry_data = self._entry_var.get() if entry_data == '': self.unpost_listbox() self.focus() else: values = self.autocomplete_function(entry_data) if values: if self._listbox is None: self._build_listbox(values) else: self._listbox.delete(0, END) height = min(self._listbox_height, len(values)) self._listbox.configure(height=height) for item in values: self._listbox.insert(END, item) else: self.unpost_listbox() self.focus() def _build_listbox(self, values): listbox_frame = Frame() self._listbox = Listbox(listbox_frame, background="white", selectmode=SINGLE, activestyle="none", exportselection=False) self._listbox.grid(row=0, column=0, sticky=N+E+W+S) self._listbox.bind("<ButtonRelease-1>", self._update_entry_from_listbox) self._listbox.bind("<Return>", self._update_entry_from_listbox) self._listbox.bind("<Escape>", lambda event: self.unpost_listbox()) self._listbox.bind('<Control-n>', self._next) self._listbox.bind('<Control-p>', self._previous) if self._use_vscrollbar: vbar = Scrollbar(listbox_frame, orient=VERTICAL, command=self._listbox.yview) vbar.grid(row=0, column=1, sticky=N+S) self._listbox.configure( yscrollcommand=lambda f, l: autoscroll(vbar, f, l)) elif self._use_hscrollbar: hbar = Scrollbar(listbox_frame, orient=HORIZONTAL, command=self._listbox.xview) hbar.grid(row=1, column=0, sticky=E+W) self._listbox.configure( xscrollcommand=lambda f, l: autoscroll(hbar, f, l)) listbox_frame.grid_columnconfigure(0, weight=1) listbox_frame.grid_rowconfigure(0, weight=1) x = -self.cget("borderwidth") - self.cget("highlightthickness") y = self.winfo_height()-self.cget("borderwidth") - \ self.cget("highlightthickness") elif self._listbox_width: width = self._listbox_width else: width = self.winfo_width() listbox_frame.place(in_=self, x=x, y=y, width=width) height = min(self._listbox_height, len(values)) self._listbox.configure(height=height) for item in values: self._listbox.insert(END, item) def post_listbox(self): if self._listbox is not None: return entry_data = self._entry_var.get() if entry_data == '': return values = self.autocomplete_function(entry_data) if values: self._build_listbox(values) def unpost_listbox(self): if self._listbox is not None: self._listbox.master.destroy() self._listbox = None def get_value(self): return self._entry_var.get() def set_value(self, text, close_dialog=False): self._set_var(text) if close_dialog: self.unpost_listbox() self.icursor(END) self.xview_moveto(1.0) def _set_var(self, text): self._entry_var.trace_vdelete("w", self._trace_id) self._entry_var.set(text) self._trace_id = self._entry_var.trace('w', self._on_change_entry_var) def _update_entry_from_listbox(self, event): if self._listbox is not None: current_selection = self._listbox.curselection() if current_selection: text = self._listbox.get(current_selection) self._set_var(text) self._listbox.master.destroy() self._listbox = None self.focus() self.icursor(END) self.xview_moveto(1.0) return "break" def _previous(self, event): if self._listbox is not None: current_selection = self._listbox.curselection() if len(current_selection) == 0: self._listbox.selection_set(0) self._listbox.activate(0) else: index = int(current_selection[0]) self._listbox.selection_clear(index) if index == 0: index = END else: index -= 1 self._listbox.see(index) self._listbox.selection_set(first=index) self._listbox.activate(index) return "break" def _next(self, event): if self._listbox is not None: current_selection = self._listbox.curselection() if len(current_selection) == 0: self._listbox.selection_set(0) self._listbox.activate(0) else: index = int(current_selection[0]) self._listbox.selection_clear(index) if index == self._listbox.size() - 1: index = 0 else: index += 1 self._listbox.see(index) self._listbox.selection_set(index) self._listbox.activate(index) return "break"
class FontChooser(Toplevel): """ Font chooser toplevel """ def __init__(self, master, font_dict={}, text="Abcd", title="Font Chooser", **kwargs): """ Create a new FontChooser instance. font: dictionnary, like the one returned by the .actual method of a Font object {'family': 'DejaVu Sans', 'overstrike':False, 'size': 12, 'slant': 'italic' or 'roman', 'underline': False, 'weight': 'bold' or 'normal'} text: text to be displayed in the preview label title: window title **kwargs: additional keyword arguments to be passed to Toplevel.__init__ """ Toplevel.__init__(self, master, **kwargs) self.title(title) self.resizable(False, False) self.protocol("WM_DELETE_WINDOW", self.quit) self._validate_family = self.register(self.validate_font_family) self._validate_size = self.register(self.validate_font_size) # variable storing the chosen font self.res = "" style = Style(self) style.configure("prev.TLabel", background="white") bg = style.lookup("TLabel", "background") self.configure(bg=bg) # family list self.fonts = list(set(families())) self.fonts.append("TkDefaultFont") self.fonts.sort() for i in range(len(self.fonts)): self.fonts[i] = self.fonts[i].replace(" ", "\ ") max_length = int(2.5 * max([len(font) for font in self.fonts])) // 3 self.sizes = [ "%i" % i for i in (list(range(6, 17)) + list(range(18, 32, 2))) ] # font default font_dict["weight"] = font_dict.get("weight", "normal") font_dict["slant"] = font_dict.get("slant", "roman") font_dict["family"] = font_dict.get("family", self.fonts[0].replace('\ ', ' ')) font_dict["size"] = font_dict.get("size", 10) # Widgets creation options_frame = Frame(self, relief='groove', borderwidth=2) self.font_family = StringVar(self, " ".join(self.fonts)) self.font_size = StringVar(self, " ".join(self.sizes)) self.var_bold = BooleanVar(self, font_dict["weight"] == "bold") b_bold = Checkbutton(options_frame, text=TR["Bold"], command=self.toggle_bold, variable=self.var_bold) b_bold.grid(row=0, sticky="w", padx=4, pady=(4, 2)) self.var_italic = BooleanVar(self, font_dict["slant"] == "italic") b_italic = Checkbutton(options_frame, text=TR["Italic"], command=self.toggle_italic, variable=self.var_italic) b_italic.grid(row=1, sticky="w", padx=4, pady=2) self.var_size = StringVar(self) self.entry_family = Entry(self, width=max_length, validate="key", validatecommand=(self._validate_family, "%d", "%S", "%i", "%s", "%V")) entry_size = Entry(self, width=4, validate="key", textvariable=self.var_size, validatecommand=(self._validate_size, "%d", "%P", "%V")) self.list_family = Listbox(self, selectmode="browse", listvariable=self.font_family, highlightthickness=0, exportselection=False, width=max_length) self.list_size = Listbox(self, selectmode="browse", listvariable=self.font_size, highlightthickness=0, exportselection=False, width=4) scroll_family = Scrollbar(self, orient='vertical', command=self.list_family.yview) scroll_size = Scrollbar(self, orient='vertical', command=self.list_size.yview) self.preview_font = Font(self, **font_dict) if len(text) > 30: text = text[:30] self.preview = Label(self, relief="groove", style="prev.TLabel", text=text, font=self.preview_font, anchor="center") # Widget configuration self.list_family.configure(yscrollcommand=scroll_family.set) self.list_size.configure(yscrollcommand=scroll_size.set) self.entry_family.insert(0, font_dict["family"]) self.entry_family.selection_clear() self.entry_family.icursor("end") entry_size.insert(0, font_dict["size"]) i = self.fonts.index(self.entry_family.get().replace(" ", "\ ")) self.list_family.selection_clear(0, "end") self.list_family.selection_set(i) self.list_family.see(i) i = self.sizes.index(entry_size.get()) self.list_size.selection_clear(0, "end") self.list_size.selection_set(i) self.list_size.see(i) self.entry_family.grid(row=0, column=0, sticky="ew", pady=(10, 1), padx=(10, 0)) entry_size.grid(row=0, column=2, sticky="ew", pady=(10, 1), padx=(10, 0)) self.list_family.grid(row=1, column=0, sticky="nsew", pady=(1, 10), padx=(10, 0)) self.list_size.grid(row=1, column=2, sticky="nsew", pady=(1, 10), padx=(10, 0)) scroll_family.grid(row=1, column=1, sticky='ns', pady=(1, 10)) scroll_size.grid(row=1, column=3, sticky='ns', pady=(1, 10)) options_frame.grid(row=0, column=4, rowspan=2, padx=10, pady=10, ipadx=10) self.preview.grid(row=2, column=0, columnspan=5, sticky="eswn", padx=10, pady=(0, 10), ipadx=4, ipady=4) button_frame = Frame(self) button_frame.grid(row=3, column=0, columnspan=5, pady=(0, 10), padx=10) Button(button_frame, text="Ok", command=self.ok).grid(row=0, column=0, padx=4, sticky='ew') Button(button_frame, text=TR["Cancel"], command=self.quit).grid(row=0, column=1, padx=4, sticky='ew') self.list_family.bind('<<ListboxSelect>>', self.update_entry_family) self.list_size.bind('<<ListboxSelect>>', self.update_entry_size, add=True) self.list_family.bind("<KeyPress>", self.keypress) self.entry_family.bind("<Return>", self.change_font_family) self.entry_family.bind("<Tab>", self.tab) entry_size.bind("<Return>", self.change_font_size) self.entry_family.bind("<Down>", self.down_family) entry_size.bind("<Down>", self.down_size) self.entry_family.bind("<Up>", self.up_family) entry_size.bind("<Up>", self.up_size) # bind Ctrl+A to select all instead of go to beginning self.bind_class("TEntry", "<Control-a>", self.select_all) self.update_idletasks() self.grab_set() self.entry_family.focus_set() self.lift() def select_all(self, event): event.widget.selection_range(0, "end") def keypress(self, event): key = event.char.lower() l = [i for i in self.fonts if i[0].lower() == key] if l: i = self.fonts.index(l[0]) self.list_family.selection_clear(0, "end") self.list_family.selection_set(i) self.list_family.see(i) self.update_entry_family() def up_family(self, event): try: txt = self.entry_family.get().replace(" ", "\ ") l = [i for i in self.fonts if i[:len(txt)] == txt] if l: self.list_family.selection_clear(0, "end") i = self.fonts.index(l[0]) if i > 0: self.list_family.selection_set(i - 1) self.list_family.see(i - 1) else: i = len(self.fonts) self.list_family.see(i - 1) self.list_family.select_set(i - 1) except TclError: i = len(self.fonts) self.list_family.see(i - 1) self.list_family.select_set(i - 1) self.list_family.event_generate('<<ListboxSelect>>') def up_size(self, event): try: s = self.var_size.get() i = self.sizes.index(s) self.list_size.selection_clear(0, "end") if i > 0: self.list_size.selection_set(i - 1) self.list_size.see(i - 1) else: i = len(self.sizes) self.list_size.see(i - 1) self.list_size.select_set(i - 1) except TclError: i = len(self.sizes) self.list_size.see(i - 1) self.list_size.select_set(i - 1) self.list_size.event_generate('<<ListboxSelect>>') def down_family(self, event): try: txt = self.entry_family.get().replace(" ", "\ ") l = [i for i in self.fonts if i[:len(txt)] == txt] if l: self.list_family.selection_clear(0, "end") i = self.fonts.index(l[0]) if i < len(self.fonts) - 1: self.list_family.selection_set(i + 1) self.list_family.see(i + 1) else: self.list_family.see(0) self.list_family.select_set(0) except TclError: self.list_family.selection_set(0) self.list_family.event_generate('<<ListboxSelect>>') def down_size(self, event): try: s = self.var_size.get() i = self.sizes.index(s) self.list_size.selection_clear(0, "end") if i < len(self.sizes) - 1: self.list_size.selection_set(i + 1) self.list_size.see(i + 1) else: self.list_size.see(0) self.list_size.select_set(0) except TclError: self.list_size.selection_set(0) self.list_size.event_generate('<<ListboxSelect>>') def toggle_bold(self): b = self.var_bold.get() self.preview_font.configure(weight=["normal", "bold"][b]) def toggle_italic(self): b = self.var_italic.get() self.preview_font.configure(slant=["roman", "italic"][b]) def toggle_underline(self): b = self.var_underline.get() self.preview_font.configure(underline=b) def toggle_overstrike(self): b = self.var_overstrike.get() self.preview_font.configure(overstrike=b) def change_font_family(self, event=None): family = self.entry_family.get() if family.replace(" ", "\ ") in self.fonts: self.preview_font.configure(family=family) def change_font_size(self, event=None): size = int(self.var_size.get()) self.preview_font.configure(size=size) def validate_font_size(self, d, ch, V): ''' Validation of the size entry content ''' l = [i for i in self.sizes if i[:len(ch)] == ch] if l: i = self.sizes.index(l[0]) self.list_size.selection_clear(0, "end") self.list_size.selection_set(i) deb = self.list_size.nearest(0) fin = self.list_size.nearest(self.list_size.winfo_height()) if V != "forced": if i < deb or i > fin: self.list_size.see(i) return True if d == '1': return ch.isdigit() else: return True def tab(self, event): self.entry_family = event.widget self.entry_family.selection_clear() self.entry_family.icursor("end") return "break" def validate_font_family(self, action, modif, pos, prev_txt, V): """ completion of the text in the path entry with existing folder/file names """ if self.entry_family.selection_present(): sel = self.entry_family.selection_get() txt = prev_txt.replace(sel, '') else: txt = prev_txt if action == "0": txt = txt[:int(pos)] + txt[int(pos) + 1:] return True else: txt = txt[:int(pos)] + modif + txt[int(pos):] ch = txt.replace(" ", "\ ") l = [i for i in self.fonts if i[:len(ch)] == ch] if l: i = self.fonts.index(l[0]) self.list_family.selection_clear(0, "end") self.list_family.selection_set(i) deb = self.list_family.nearest(0) fin = self.list_family.nearest(self.list_family.winfo_height()) index = self.entry_family.index("insert") self.entry_family.delete(0, "end") self.entry_family.insert(0, l[0].replace("\ ", " ")) self.entry_family.selection_range(index + 1, "end") self.entry_family.icursor(index + 1) if V != "forced": if i < deb or i > fin: self.list_family.see(i) return True else: return False def update_entry_family(self, event=None): # family = self.list_family.get("@%i,%i" % (event.x , event.y)) family = self.list_family.get(self.list_family.curselection()[0]) self.entry_family.delete(0, "end") self.entry_family.insert(0, family) self.entry_family.selection_clear() self.entry_family.icursor("end") self.change_font_family() def update_entry_size(self, event): # size = self.list_size.get("@%i,%i" % (event.x , event.y)) size = self.list_size.get(self.list_size.curselection()[0]) self.var_size.set(size) self.change_font_size() def ok(self): self.res = self.preview_font.actual() self.quit() def get_res(self): return self.res def quit(self): self.destroy()
class ListboxVidget(Vidget, Eventor): """ ListboxVidget contains a Listbox widget. It adds the following abilities: - Store items of any type, unlike Listbox widget that only stores texts. - Remember selected item even if the listbox widget lost focus. - Notify pre-change and post-change events. """ # Error raised when trying to change the listbox while a change is going on class CircularCallError(ValueError): pass # Error raised when trying to change the listbox while it is disabled class DisabledError(ValueError): pass # Event notified when the listbox's items are to be changed ITEMS_CHANGE_SOON = 'ITEMS_CHANGE_SOON' # Event notified when the listbox's items are changed ITEMS_CHANGE_DONE = 'ITEMS_CHANGE_DONE' # Event notified when the listbox's active item is to be changed ITEMCUR_CHANGE_SOON = 'ITEMCUR_CHANGE_SOON' # Event notified when the listbox's active item is changed ITEMCUR_CHANGE_DONE = 'ITEMCUR_CHANGE_DONE' # Events list EVENTS = ( ITEMS_CHANGE_SOON, ITEMS_CHANGE_DONE, ITEMCUR_CHANGE_SOON, ITEMCUR_CHANGE_DONE, ) def __init__( self, items=None, item_to_text=None, normal_bg='', normal_fg='', active_bg='sky blue', active_fg='white', selected_bg='steel blue', selected_fg='white', master=None, ): """ Initialize object. @param items: Items list. @param item_to_text: Item-to-text function. Default is `str`. @param normal_bg: Unselected item background color. @param normal_fg: Unselected item foreground color. @param active_bg: Active item background color. `Active` means the item is selected (in general meaning) but the listbox has no focus. @param active_fg: Active item foreground color. `Active` means the item is selected (in general meaning) but the listbox has no focus. @param selected_bg: Selected item background color. `Selected` means the item is selected (in general meaning) and the listbox has focus. @param selected_fg: Selected item foreground color. `Selected` means the item is selected (in general meaning) and the listbox has focus. @param master: Master widget. @return: None. """ # Initialize Vidget. # Create main frame widget. Vidget.__init__( self, master=master, ) # Initialize Eventor Eventor.__init__(self) # If items list is given if items is not None: # If items list is not list if not isinstance(items, list): # Raise error raise TypeError(items) # If items list is list. # If items list is not given, or items list is given and is list # Items list self._items = items if items is not None else [] # Item-to-text function. Default is `str`. self._item_to_text = item_to_text if item_to_text is not None else str # Unselected item background color self._normal_fg = normal_fg # Unselected item foreground color self._normal_bg = normal_bg # Active item background color self._active_fg = active_fg # Active item foreground color self._active_bg = active_bg # Selected item background color self._selected_fg = selected_fg # Selected item foreground color self._selected_bg = selected_bg # Whether the listbox is changing self._is_changing = False # Active index. `-1` means void, i.e. no item is active. self._indexcur = -1 # Whether active index is being reset to same value self._is_resetting = False # Create listbox widget self._listbox = Listbox( master=self.widget(), relief='groove', activestyle='none', highlightthickness=0, # Active index cache only supports single-selection mode for now. # See 2N6OR. selectmode='single', ) # Set the listbox widget as config target self.config_target_set(self._listbox) # Create x-axis scrollbar self._scrollbar_xview = _HiddenScrollbar( self.widget(), orient=HORIZONTAL, ) # Create y-axis scrollbar self._scrollbar_yview = _HiddenScrollbar( self.widget(), orient=VERTICAL, ) # Mount scrollbars self._listbox.config(xscrollcommand=self._scrollbar_xview.set) self._listbox.config(yscrollcommand=self._scrollbar_yview.set) self._scrollbar_xview.config(command=self._listbox.xview) self._scrollbar_yview.config(command=self._listbox.yview) # Bind single-click event handler self._listbox.bind('<Button-1>', self._on_single_click) # Bind double-click event handler self._listbox.bind('<Double-Button-1>', self._on_double_click) # Update listbox widget self._listbox_widget_update(keep_active=False) # Update widget self._widget_update() def _widget_update(self): """ Update widget. @return: None. """ # Row 0 for listbox and y-axis scrollbar self.widget().rowconfigure(0, weight=1) # Row 1 for x-axis scrollbar self.widget().rowconfigure(1, weight=0) # Column 0 for listbox and x-axis scrollbar self.widget().columnconfigure(0, weight=1) # Column 1 for y-axis scrollbar self.widget().columnconfigure(1, weight=0) # Lay out listbox self._listbox.grid(row=0, column=0, sticky='NSEW') # Lay out x-axis scrollbar self._scrollbar_xview.grid(row=1, column=0, sticky='EW') # Lay out y-axis scrollbar self._scrollbar_yview.grid(row=0, column=1, sticky='NS') def is_enabled(self): """ Test whether the listbox is enabled. @return: Boolean. """ # Return whether the listbox is enabled return self._listbox.config('state')[4] != DISABLED def is_changing(self): """ Test whether the listbox is changing. @return: Boolean. """ # Return whether the listbox is changing return self._is_changing def is_resetting(self): """ Test whether the listbox is setting active index to the same value. @return: Boolean. """ # Return whether the listbox is setting active index to the same value return self._is_resetting def size(self): """ Get number of items. @return: Number of items. """ # Return number of items return len(self._items) def items(self): """ Get items list. Notice do not change the list outside. @return: Items list. """ # Return items list return self._items def items_set( self, items, notify=True, keep_active=False, ): """ Set items list. Notice do not change the list outside. @param items: Items list. @param notify: Whether notify pre-change and post-change events. @param keep_active: Whether keep or clear active index. @return: None. """ # If the items is not list if not isinstance(items, list): # Raise error raise TypeError(items) # If the items is list. # If the listbox is disabled if not self.is_enabled(): # Raise error raise ListboxVidget.DisabledError() # If the listbox is not disabled. # If the listbox is changing if self._is_changing: # Raise error raise ListboxVidget.CircularCallError() # If the listbox is not changing. # Set changing flag on self._is_changing = True # If notify events if notify: # Notify pre-change event self.handler_notify(self.ITEMS_CHANGE_SOON) # Store the new items self._items = items # Update listbox widget self._listbox_widget_update( keep_active=keep_active ) # If notify events if notify: # Notify post-change event self.handler_notify(self.ITEMS_CHANGE_DONE) # Set changing flag off self._is_changing = False def index_is_valid(self, index): """ Test whether given index is valid. Notice -1 is not valid. @param index: Index to test. @return: Boolean. """ # Test whether given index is valid return 0 <= index and index < self.size() def index_is_valid_or_void(self, index): """ Test whether given index is valid or is -1. @param index: Index to test. @return: Boolean. """ # Test whether given index is valid or is -1 return index == -1 or self.index_is_valid(index) def index_first(self): """ Get the first item's index. @return: First item's index, or -1 if the listbox is empty. """ # Return the first item's index return 0 if self.size() > 0 else -1 def index_last(self): """ Get the last item's index. @return: Last item's index, or -1 if the listbox is empty. """ # Return the last item's index return self.size() - 1 def indexcur(self, internal=False, raise_error=False): """ Get the active index. @param internal: See 2N6OR. @return: The active index. If no active active, either return -1, or raise IndexError if `raise_error` is True. """ # Get active indexes indexcurs = self._indexcurs(internal=internal) # If have active indexes if indexcurs: # Return the first active index return indexcurs[0] # If no active indexes else: # If raise error if raise_error: # Raise error raise IndexError(-1) # If not raise error else: # Return -1 return -1 def _indexcurs(self, internal=False): """ Get active indexes list. 2N6OR @param internal: Whether use listbox widget's selected indexes, instead of cached active index. Notice listbox widget has no selected indexes if it has no focus. Notice using cached active index only supports single-selection mode, which means the result list has at most one index. @return: Active indexes list. """ # If use listbox widget's selected indexes if internal: # Return listbox widget's selected indexes list return [int(x) for x in self._listbox.curselection()] # If not use listbox widget's selected indexes else: # If cached active index is valid if self.index_is_valid(self._indexcur): # Return a list with the cached active index return [self._indexcur] # If cached active index is not valid else: # Return empty list return [] def indexcur_set( self, index, focus=False, notify=True, notify_arg=None, ): """ Set active index. @param index: The index to set. @param focus: Whether set focus on the listbox widget. @param notify: Whether notify pre-change and post-change events. @param notify_arg: Event argument. @return: None. """ # If the index is not valid or -1 if not self.index_is_valid_or_void(index): # Raise error raise IndexError(index) # If the index is valid or is -1. # If the listbox is not enabled if not self.is_enabled(): # Raise error raise ListboxVidget.DisabledError() # If the listbox is enabled. # If the listbox is changing if self._is_changing: # Raise error raise ListboxVidget.CircularCallError() # If the listbox is not changing. # Set changing flag on self._is_changing = True # Get old active index old_indexcur = self._indexcur # Set resetting flag on if new and old indexes are equal self._is_resetting = (index == old_indexcur) # If notify events if notify: # Notify pre-change event self.handler_notify(self.ITEMCUR_CHANGE_SOON, notify_arg) # If old active index is valid if self.index_is_valid(old_indexcur): # Set old active item's background color to normal color self._listbox.itemconfig(old_indexcur, background=self._normal_bg) # Set old active item's foreground color to normal color self._listbox.itemconfig(old_indexcur, foreground=self._normal_fg) # Cache new active index self._indexcur = index # Clear listbox widget's selection self._listbox.selection_clear(0, END) # Set listbox widget's selection self._listbox.selection_set(index) # Set listbox widget's activated index self._listbox.activate(index) # If new active index is valid if index != -1: # Set new active item's background color to active color self._listbox.itemconfig(index, background=self._active_bg) # Set new active item's foreground color to active color self._listbox.itemconfig(index, foreground=self._active_fg) # If set focus if focus: # Set focus on the listbox widget self._listbox.focus_set() # If new active index is valid if index != -1: # Make the active item visible self._listbox.see(index) # If notify events if notify: # Notify post-change event self.handler_notify(self.ITEMCUR_CHANGE_DONE, notify_arg) # Set resetting flag off self._is_resetting = False # Set changing flag off self._is_changing = False def indexcur_set_by_event( self, event, focus=False, notify=True, notify_arg=None, ): """ Set active index using a Tkinter event object that contains coordinates of the active item. @param event: Tkinter event object. @param focus: Whether set focus on the listbox widget. @param notify: Whether notify pre-change and post-change events. @param notify_arg: Event argument. @return: None. """ # Get the event's y co-ordinate's nearest listbox item index index = self._listbox.nearest(event.y) # If the index is not valid if not self.index_is_valid_or_void(index): # Ignore the event return # If the index is valid else: # Set the index as active index self.indexcur_set( index=index, focus=focus, notify=notify, notify_arg=notify_arg, ) def item(self, index): """ Get item at given index. @return: Item at given index, or IndexError if the index is not valid. """ return self.items()[index] def itemcur(self, internal=False, raise_error=False): """ Get the active item. @param internal: See 2N6OR. @param raise_error: Whether raise error if no active item. @return: The active item. If no active item, if `raise_error` is True, raise IndexError, otherwise return None. """ # Get active index. # May raise IndexError if `raise_error` is True. indexcur = self.indexcur( internal=internal, raise_error=raise_error, ) # If no active index if indexcur == -1: # Return None return None # If have active index else: # Return the active item return self.items()[indexcur] def item_insert( self, item, index=None, notify=True, keep_active=True, ): """ Insert item at given index. @param item: Item to insert. @param index: Index to insert. `None` means active index, and if no active index, insert at the end. @param notify: Whether notify pre-change and post-change events. @param keep_active: Whether keep or clear active index. @return: None. """ # If notify events if notify: # Notify pre-change events self.handler_notify(self.ITEMCUR_CHANGE_SOON) self.handler_notify(self.ITEMS_CHANGE_SOON) # Get old active index active_index = self.indexcur() # If the index is None, # it means use active index. if index is None: # Use active index. # `-1` works and means appending. index = active_index # Insert the item to the items list self._items.insert(index, item) # If old active index is valid if active_index != -1: # If old active index is GE the inserted index if active_index >= index: # Shift active index by one active_index += 1 # If old active index is not GE the inserted index, use it as-is. # Set new active index self.indexcur_set(index=active_index, notify=False) # Update listbox widget self._listbox_widget_update( keep_active=keep_active ) # If notify events if notify: # Notify post-change events self.handler_notify(self.ITEMS_CHANGE_DONE) self.handler_notify(self.ITEMCUR_CHANGE_DONE) def item_remove( self, index, notify=True, keep_active=True, ): """ Remove item at given index. @param index: Index to remove. @param notify: Whether notify pre-change and post-change events. @param keep_active: Whether keep or clear active index. @return: None. """ # If the index is not valid if not self.index_is_valid(index): # Raise error raise ValueError(index) # If the index is valid. # If notify events if notify: # Notify pre-change events self.handler_notify(self.ITEMCUR_CHANGE_SOON) self.handler_notify(self.ITEMS_CHANGE_SOON) # Get old active index active_index = self.indexcur() # Remove item at the index del self._items[index] # If old active index is valid if active_index != -1: # Get the last index index_last = self.index_last() # If old active index is GT the last index if active_index > index_last: # Use the last index as new active index active_index = index_last # If old active index is not GT the last index, use it as-is. # Set new active index self.indexcur_set(index=active_index, notify=False) # Update listbox widget self._listbox_widget_update( keep_active=keep_active ) # If notify events if notify: # Notify post-change events self.handler_notify(self.ITEMS_CHANGE_DONE) self.handler_notify(self.ITEMCUR_CHANGE_DONE) def handler_add( self, event, handler, need_arg=False, ): """ Add event handler for an event. If the event is ListboxVidget event, add the event handler to Eventor. If the event is not ListboxVidget event, add the event handler to listbox widget. Notice this method overrides `Eventor.handler_add` in order to add non-ListboxVidget event handler to listbox widget. @param event: Event name. @param handler: Event handler. @param need_arg: Whether the event handler needs event argument. @return: None. """ # If the event is ListboxVidget event if event in self.EVENTS: # Add the event handler to Eventor return Eventor.handler_add( self, event=event, handler=handler, need_arg=need_arg, ) # If the event is not ListboxVidget event, # it is assumed to be Tkinter widget event. else: # Add the event handler to listbox widget return self.bind( event=event, handler=handler, ) def bind( self, event, handler, ): """ Add event handler to listbox widget. ListboxVidget internally uses `<Button-1>` and `<Double-Button-1>` to capture active index changes. So if the given event is `<Button-1>` or `<Double-Button-1>`, the given handler will be wrapped. @param event: Event name. @param handler: Event handler. @return: None. """ # If the event is not `<Button-1>` or `<Double-Button-1>` if event not in ['<Button-1>', '<Double-Button-1>']: # Add the event handler to listbox widget self._listbox.bind(event, handler) # If the event is `<Button-1>` or `<Double-Button-1>` else: # Create event handler wrapper def handler_wrapper(e): """ Event handler wrapper that sets new active index and then calls the wrapped event handler. Setting new active index is needed because when this handler is called by Tkinter, the active index of the listbox is still old. @param e: Tkinter event object. @return: None. """ # Set new active index self.indexcur_set_by_event(e, notify=True) # Call the wrapped event handler handler(e) # Add the event handler wrapper to the listbox widget self._listbox.bind(event, handler_wrapper) def _on_single_click(self, event): """ `<Button-1>` event handler that updates active index. @param event: Tkinter event object. @return: None. """ # Updates active index self.indexcur_set_by_event(event, notify=True) def _on_double_click(self, event): """ `<Double-Button-1>` event handler that updates active index. @param event: Tkinter event object. @return: None. """ # Updates active index self.indexcur_set_by_event(event, notify=True) def _listbox_widget_update( self, keep_active, ): """ Update listbox widget's items and selection. @param keep_active: Whether keep or clear active index. @return: None. """ # Remove old items from listbox widget self._listbox.delete(0, END) # Insert new items into listbox widget. # For each ListboxVidget items. for index, item in enumerate(self.items()): # Get item text item_text = self._item_to_text(item) # Insert the item text into listbox widget self._listbox.insert(index, item_text) # Set the item's normal background color self._listbox.itemconfig(index, background=self._normal_bg) # Set the item's normal foreground color self._listbox.itemconfig(index, foreground=self._normal_fg) # Set the item's selected background color self._listbox.itemconfig(index, selectbackground=self._selected_bg) # Set the item's selected foreground color self._listbox.itemconfig(index, selectforeground=self._selected_fg) # If keep active index if keep_active: # Use old active index indexcur = self._indexcur # If not keep active index else: # Set active index to -1 indexcur = self._indexcur = -1 # Clear old selection self._listbox.selection_clear(0, END) # Set new selection. # `-1` works. self._listbox.selection_set(indexcur) # Set new active index. # `-1` works. self._listbox.activate(indexcur) # If new active index is valid if indexcur != -1: # Set active background color self._listbox.itemconfig(indexcur, background=self._active_bg) # Set active foreground color self._listbox.itemconfig(indexcur, foreground=self._active_fg) # Make the active item visible self._listbox.see(indexcur)
class FileChoice(object): def __init__(self, master=None, title=None, prefix=None, list=[], start='', ext="txt", new=False): self.master = master self.value = None self.prefix = prefix self.list = list if prefix and start: self.start = relpath(start, prefix) else: self.start = start self.ext = ext self.new = new self.modalPane = Toplevel(self.master, highlightbackground=BGCOLOR, background=BGCOLOR) if master: logger.debug('winfo: {0}, {1}; {2}, {3}'.format( master.winfo_rootx(), type(master.winfo_rootx()), master.winfo_rooty(), type(master.winfo_rooty()))) self.modalPane.geometry( "+%d+%d" % (master.winfo_rootx() + 50, master.winfo_rooty() + 50)) self.modalPane.transient(self.master) self.modalPane.grab_set() self.modalPane.bind("<Return>", self._choose) self.modalPane.bind("<Escape>", self._cancel) if title: self.modalPane.title(title) if new: nameFrame = Frame(self.modalPane, highlightbackground=BGCOLOR, background=BGCOLOR) nameFrame.pack(side="top", padx=18, pady=2, fill="x") nameLabel = Label(nameFrame, text=_("file:"), bd=1, relief="flat", anchor="w", padx=0, pady=0, highlightbackground=BGCOLOR, background=BGCOLOR) nameLabel.pack(side="left") self.fileName = StringVar(self.modalPane) self.fileName.set("untitled.{0}".format(ext)) self.fileName.trace_variable("w", self.onSelect) self.fname = Entry(nameFrame, textvariable=self.fileName, bd=1, highlightbackground=BGCOLOR) self.fname.pack(side="left", fill="x", expand=1, padx=0, pady=0) self.fname.icursor(END) self.fname.bind("<Up>", self.cursorUp) self.fname.bind("<Down>", self.cursorDown) filterFrame = Frame(self.modalPane, highlightbackground=BGCOLOR, background=BGCOLOR) filterFrame.pack(side="top", padx=18, pady=4, fill="x") filterLabel = Label(filterFrame, text=_("filter:"), bd=1, relief="flat", anchor="w", padx=0, pady=0, highlightbackground=BGCOLOR, background=BGCOLOR) filterLabel.pack(side="left") self.filterValue = StringVar(self.modalPane) self.filterValue.set("") self.filterValue.trace_variable("w", self.setMatching) self.fltr = Entry(filterFrame, textvariable=self.filterValue, bd=1, highlightbackground=BGCOLOR) self.fltr.pack(side="left", fill="x", expand=1, padx=0, pady=0) self.fltr.icursor(END) prefixFrame = Frame(self.modalPane, highlightbackground=BGCOLOR, background=BGCOLOR) prefixFrame.pack(side="top", padx=8, pady=2, fill="x") self.prefixLabel = Label(prefixFrame, text=_("{0}:").format(prefix), bd=1, highlightbackground=BGCOLOR, background=BGCOLOR) self.prefixLabel.pack(side="left", expand=0, padx=0, pady=0) buttonFrame = Frame(self.modalPane, highlightbackground=BGCOLOR, background=BGCOLOR) buttonFrame.pack(side="bottom", padx=10, pady=2) chooseButton = Button(buttonFrame, text="Choose", command=self._choose, highlightbackground=BGCOLOR, background=BGCOLOR, pady=2) chooseButton.pack(side="right", padx=10) cancelButton = Button(buttonFrame, text="Cancel", command=self._cancel, highlightbackground=BGCOLOR, background=BGCOLOR, pady=2) cancelButton.pack(side="left") selectionFrame = Frame(self.modalPane, highlightbackground=BGCOLOR, background=BGCOLOR) selectionFrame.pack(side="bottom", padx=8, pady=2, fill="x") self.selectionValue = StringVar(self.modalPane) self.selectionValue.set("") self.selection = Label(selectionFrame, textvariable=self.selectionValue, bd=1, highlightbackground=BGCOLOR, background=BGCOLOR) self.selection.pack(side="left", fill="x", expand=1, padx=0, pady=0) listFrame = Frame(self.modalPane, highlightbackground=BGCOLOR, background=BGCOLOR, width=40) listFrame.pack(side="top", fill="both", expand=1, padx=5, pady=2) scrollBar = Scrollbar(listFrame, width=8) scrollBar.pack(side="right", fill="y") self.listBox = Listbox(listFrame, selectmode=BROWSE, width=36) self.listBox.pack(side="left", fill="both", expand=1, ipadx=4, padx=2, pady=0) self.listBox.bind('<<ListboxSelect>>', self.onSelect) self.listBox.bind("<Double-1>", self._choose) self.modalPane.bind("<Return>", self._choose) self.modalPane.bind("<Escape>", self._cancel) # self.modalPane.bind("<Up>", self.cursorUp) # self.modalPane.bind("<Down>", self.cursorDown) self.fltr.bind("<Up>", self.cursorUp) self.fltr.bind("<Down>", self.cursorDown) scrollBar.config(command=self.listBox.yview) self.listBox.config(yscrollcommand=scrollBar.set) self.setMatching() def ignore(self, e=None): return "break" def onSelect(self, *args): # Note here that Tkinter passes an event object to onselect() if self.listBox.curselection(): firstIndex = self.listBox.curselection()[0] value = self.matches[int(firstIndex)] r = value[1] p = os.path.join(self.prefix, r) if self.new: if os.path.isfile(p): p = os.path.split(p)[0] r = os.path.split(r)[0] f = self.fileName.get() r = os.path.join(r, f) p = os.path.join(p, f) self.selectionValue.set(r) self.value = p return "break" def cursorUp(self, event=None): cursel = int(self.listBox.curselection()[0]) newsel = max(0, cursel - 1) self.listBox.select_clear(cursel) self.listBox.select_set(newsel) self.listBox.see(newsel) self.onSelect() return "break" def cursorDown(self, event=None): cursel = int(self.listBox.curselection()[0]) newsel = min(len(self.list) - 1, cursel + 1) self.listBox.select_clear(cursel) self.listBox.select_set(newsel) self.listBox.see(newsel) self.onSelect() return "break" def setMatching(self, *args): # disabled = "#BADEC3" # disabled = "#91CC9E" disabled = "#62B374" match = self.filterValue.get() if match: self.matches = matches = [ x for x in self.list if x and match.lower() in x[1].lower() ] else: self.matches = matches = self.list self.listBox.delete(0, END) index = 0 init_index = 0 for item in matches: if type(item) is tuple: # only show the label # (label, value, disabled)FF self.listBox.insert(END, item[0]) if self.new: if not item[-1]: self.listBox.itemconfig(index, fg=disabled) else: self.listBox.itemconfig(index, fg="blue") if self.start and item[1] == self.start: init_index = index else: if item[-1]: self.listBox.itemconfig(index, fg=disabled) else: self.listBox.itemconfig(index, fg="blue") if self.start and item[1] == self.start: init_index = index # elif files: else: self.listBox.insert(END, item) index += 1 self.listBox.select_set(init_index) self.listBox.see(init_index) self.fltr.focus_set() self.onSelect() def _choose(self, event=None): try: if self.listBox.curselection(): firstIndex = self.listBox.curselection()[0] if self.new: if not self.value or os.path.isfile(self.value): return else: tup = self.matches[int(firstIndex)] if tup[-1]: return self.value = os.path.join(self.prefix, tup[1]) else: return except IndexError: self.value = None self.modalPane.destroy() def _cancel(self, event=None): self.value = None self.modalPane.destroy() def returnValue(self): self.master.wait_window(self.modalPane) return self.value
class MyGui(ttk.Frame): controller = None tabs = None _log = None _screen_width = None _screen_height = None COLOR_buttons = '#FFCB6B' COLOR_frames = '#333333' COLOR_foreground = '#D9C7B3' COLOR_log = '#1E1E1E' def __init__(self, master, controller): super().__init__() self.controller = controller self.initUI(master) def initUI(self, master): self.master.title("Corinne") self.master.grid_columnconfigure(0, weight=1) self.master.grid_rowconfigure(2, weight=1) self.master.option_add('*foreground', 'black') self.master.option_add('*background', 'white') # Style for ttk widgets style = ttk.Style() style.configure("TNotebook", background=self.COLOR_frames, borderwidth=1, highlightthickness=1) style.configure("TNotebook.Tab", background=self.COLOR_frames, foreground="black", lightcolor=self.COLOR_frames, borderwidth=0) style.map("TNotebook.Tab", background=[("selected", self.COLOR_buttons)], foreground=[("selected", 'black')]) style.configure("TFrame", background=self.COLOR_frames, foreground="black") # get screen resolution self._screen_width, self._screen_height = master.winfo_screenwidth(), master.winfo_screenheight() start_x = int((self._screen_width / 4)) start_y = int((self._screen_height / 4)) # fit the guy at screen resolution master.geometry('%dx%d+%d+%d' % (self._screen_width / 2, self._screen_height / 2, start_x, start_y)) # create all of the containers top_frame = Frame(master) top_frame.grid(row=1, column=0, sticky=(tk.N, tk.S, tk.E, tk.W)) top_frame.configure(bg=self.COLOR_frames) top_frame.grid_columnconfigure(0, weight=1) property_frame = Frame(master) property_frame.grid(row=2, column=0, sticky=(tk.N, tk.S, tk.E, tk.W)) property_frame.configure(bg=self.COLOR_frames) property_frame.grid_columnconfigure(0, weight=1) property_frame.grid_rowconfigure(0, weight=1) buttons_frame = Frame(master, padx=10, pady=10) buttons_frame.grid(row=0, column=0, sticky=(tk.N, tk.S, tk.E, tk.W)) buttons_frame.configure(bg=self.COLOR_frames) buttons_frame.grid_columnconfigure(0, weight=1) buttons_frame.grid_columnconfigure(1, weight=1) buttons_frame.grid_columnconfigure(2, weight=1) buttons_frame.grid_columnconfigure(3, weight=1) buttons_frame.grid_columnconfigure(4, weight=1) open_icon = PhotoImage(file="icons/open.png") open_button = Button(buttons_frame, text=" Open", image=open_icon, compound=tk.LEFT, padx=1, highlightthickness=0, command=self.open_file) open_button.configure(borderwidth=0, background=self.COLOR_buttons) open_button.image = open_icon open_button.grid(row=0, column=0, padx=5, sticky=(tk.N, tk.S, tk.E, tk.W)) render_icon = PhotoImage(file="icons/render.png") render_button = Button(buttons_frame, text=" Render", image=render_icon, compound=tk.LEFT, padx=1, highlightthickness=0, command=self.open_render_view) render_button.configure(borderwidth=0, background=self.COLOR_buttons) render_button.image = render_icon render_button.grid(row=0, column=1, padx=5, sticky=(tk.N, tk.S, tk.E, tk.W)) prod_icon = PhotoImage(file="icons/product.png") prod_button = Button(buttons_frame, text=" Product", image=prod_icon, compound=tk.LEFT, highlightthickness=0, command=self.open_product_view) prod_button.configure(borderwidth=0, background=self.COLOR_buttons) prod_button.image = prod_icon prod_button.grid(row=0, column=2, padx=5, sticky=(tk.N, tk.S, tk.E, tk.W)) sync_icon = PhotoImage(file="icons/sync.png") sync_button = Button(buttons_frame, text=" Sync", image=sync_icon, compound=tk.LEFT, highlightthickness=0, command=self.open_sync_view) sync_button.configure(borderwidth=0, background=self.COLOR_buttons) sync_button.image = sync_icon sync_button.grid(row=0, column=3, padx=5, sticky=(tk.N, tk.S, tk.E, tk.W)) proj_icon = PhotoImage(file="icons/projection.png") proj_button = Button(buttons_frame, text=" Projection", image=proj_icon, compound=tk.LEFT, highlightthickness=0, command=self.open_proj_view) proj_button.configure(borderwidth=0, background=self.COLOR_buttons) proj_button.image = proj_icon proj_button.grid(row=0, column=4, padx=5, sticky=(tk.N, tk.S, tk.E, tk.W)) # create the log box self._log = Listbox(top_frame, highlightthickness=0, height=5, background=self.COLOR_log, foreground=self.COLOR_foreground) #_scrollb = Scrollbar(top_frame, orient=tk.VERTICAL) #self._log.configure(yscrollcommand=_scrollb.set) #_scrollb.config(command=self._log.yview) self._log.grid(column=0, row=0, padx=10, sticky=(tk.N, tk.S, tk.E, tk.W)) # _scrollb.grid(column=1, row=0, sticky=tk.S + tk.N) # create the tab manager for property self.tabs = ttk.Notebook(property_frame) def open_file(self): path = filedialog.askopenfilename(initialdir=".", filetypes=(("DOT graph", "*.gv *.dot"), ("Chorgram grammar", "*.txt"), ("all files", "*.*")), title="Choose a file." ) msg_result = [] # Check in case user enter an unknown file # or closes without choosing a file. try: path_splitted = os.path.split(path) ext = path_splitted[1].split('.') # Chorgram file if ext[1] == 'txt': msg_result = self.__open_chorgram_file(path) # DOT files elif ext[1] == 'dot' or ext[1] == 'gv': msg_result = self.__open_dot_file__(path) else: self.popupmsg("Unknown extension file") # update log box self.log(msg_result) except: pass def __open_chorgram_file(self, path): path_splitted = os.path.split(path) # ask where store the converted dot file ask_for_path: bool = messagebox.askyesno("Chorgram", "A Chorgram file was inserted\n" + "Do you wish to save the converted dot file in " + path_splitted[0] + "?\n" + "(Click NO to choose a new path)") if ask_for_path: # Yes, use same path # (input path, path to store) msg_result, graph_name = self.controller.GGparser(path, path_splitted[0]) else: new_folder = filedialog.askdirectory() msg_result, graph_name = self.controller.GGparser(path, new_folder) self.__add_new_tab__(graph_name) return msg_result def __open_dot_file__(self, path): # result[0] domitilla boolean # result[1] a message # result[2] graph name result = self.controller.DOTparser(path) msg_result = result[1] if result[0]: # if a domitilla graph was founded # ask where store the converted dot file path_splitted = os.path.split(path) ask_for_path: bool = messagebox.askyesno("Domitilla", "A Domitilla file was inserted\n" "Do you wish to store the converted file in " + path_splitted[0] + "?\n" "(Click NO to choose a new path)") if ask_for_path: # Yes, use same path msg_result.append( self.controller.DomitillaConverter(result[2], path, path_splitted[0])) # (graph name, input path, path to store) else: new_folder = filedialog.askdirectory() msg_result.append(self.controller.DomitillaConverter(result[2], path, new_folder)) if len(result) > 2: # case NO-errors detected # add a new tab for the new graph just opened self.__add_new_tab__(result[2]) return msg_result def open_render_view(self): try: path = filedialog.askopenfilename(initialdir=".", filetypes=(("DOT graph", "*.gv *.dot"), ("all files", "*.*")), title="Choose a file.") path_splitted = os.path.split(path) ext = path_splitted[1].split('.') # Check in case user enter an unknown file # or closes without choosing a file. if ext[1] != 'dot' and ext[1] != 'gv': self.popupmsg("Wrong extension file inserted!\n" "Please insert a DOT file") else: # define the frame and its geometry r_window = tk.Toplevel(padx=20, pady=20, bg=self.COLOR_frames) r_window.wm_title("Render") r_window.resizable(False, False) self.__set_window_dimension__(r_window) label_format = tk.Label(r_window, text="Choose a file format for render:", fg=self.COLOR_foreground, bg=self.COLOR_frames, wraplength=500) label_format.grid(row=0, column=0) # Initialize file format variable for radiobutton option = tk.StringVar() # Radiobutton rb1 = Radiobutton(r_window, text='png', value="png", var=option, bg=self.COLOR_frames) rb2 = Radiobutton(r_window, text='pdf', value="pdf", var=option, bg=self.COLOR_frames) rb1.grid(row=1, column=0) rb2.grid(row=1, column=1) # TODO try except for wrong dot files b = Button(r_window, text='Render', bg=self.COLOR_buttons, command=lambda: (self.log(["[RENDER] " + self.controller.render(path, option.get())]), r_window.destroy())) b.grid(row=2, column=1) except: pass def open_product_view(self): # define the frame and its geometry p_window = tk.Toplevel(padx=20, pady=20, bg=self.COLOR_frames) p_window.wm_title("Product") p_window.resizable(False, False) # set window dimension self.__set_window_dimension__(p_window) # label and combo for 1st graph lbl1 = tk.Label(p_window, text="Choose 1st Graph", bg=self.COLOR_frames, fg=self.COLOR_foreground) lbl1.grid(row=0, column=0, pady=10) combo1 = ttk.Combobox(p_window, values=list(self.controller.get_all_ca().keys())) combo1.grid(row=0, column=1, pady=10) # label and combo for 2st graph lbl2 = tk.Label(p_window, text="Choose 2st Graph", bg=self.COLOR_frames, fg='white') lbl2.grid(row=1, column=0, pady=10) combo2 = ttk.Combobox(p_window, values=list(self.controller.get_all_ca().keys())) combo2.grid(row=1, column=1, pady=10) make_button = Button(p_window, text='Make product', bg=self.COLOR_buttons, command=lambda: (self.__exec_product_button__(combo1.get(), combo2.get()), p_window.destroy())) make_button.grid(row=2, column=0, pady=10) def open_sync_view(self): s_window = tk.Toplevel(padx=20, pady=20, bg=self.COLOR_frames) s_window.wm_title("Synchronisation") s_window.resizable(False, False) # set window dimension self.__set_window_dimension__(s_window) # label and combo for the graph to synchronize lbl1 = tk.Label(s_window, text="Choose Graph", fg='white', bg=self.COLOR_frames) lbl1.grid(row=0, column=0, padx=10, pady=10) option_v1 = tk.StringVar() option_v2 = tk.StringVar() combo = ttk.Combobox(s_window, values=list(self.controller.get_all_ca().keys())) combo.bind("<<ComboboxSelected>>", lambda event: self.__make_sync_interface_menu__(s_window, list( self.controller.get_participants(combo.get())), option_v1, option_v2)) combo.grid(row=1, column=0, padx=10, pady=10) sync_button = Button(s_window, text='Synchronize', bg=self.COLOR_buttons, command=lambda: ( self.__exec_sync_button__(combo.get(), option_v1.get(), option_v2.get()), s_window.destroy())) sync_button.grid(row=4, column=0) def open_proj_view(self): proj_window = tk.Toplevel(padx=20, pady=20, bg=self.COLOR_frames) proj_window.wm_title("Projection") proj_window.resizable(False, False) # set window dimension self.__set_window_dimension__(proj_window) # label and combo for the graph to synchronize lbl1 = tk.Label(proj_window, text="Choose Graph", bg=self.COLOR_frames, fg='white') lbl1.grid(row=0, column=0, padx=10, pady=10) option = tk.StringVar() combo = ttk.Combobox(proj_window, values=list(self.controller.get_all_ca().keys())) combo.bind("<<ComboboxSelected>>", lambda event: self.__make_proj_participant_menu__(proj_window, list( self.controller.get_participants(combo.get())), option)) combo.grid(row=1, column=0, padx=10, pady=10) proj_button = Button(proj_window, text='Project', bg=self.COLOR_buttons, command=lambda: ( self.__exec_proj_button__(combo.get(), option.get()), proj_window.destroy())) proj_button.grid(row=4, column=0) def __add_new_tab__(self, graph_name): self.tabs.grid(row=0, column=0, sticky=(tk.N, tk.S, tk.E, tk.W), padx=10, pady=5) frame = ttk.Frame(self.tabs) frame.grid_columnconfigure(5, weight=1) #frame.grid_columnconfigure(0, weight=2) #frame.grid_rowconfigure(1, weight=2) # Add the tab self.tabs.add(frame, text=graph_name) # -------- LEFT widgets --------- # # create N.states label and textbox label_s = tk.Label(frame, text="N° States :", wraplength=500, bg=self.COLOR_frames, fg=self.COLOR_foreground) label_s.grid(row=0, column=0, pady=10, padx=40) entry_s = Entry(frame, justify=tk.CENTER, width=5, fg='black', highlightthickness=0) entry_s.grid(row=0, column=1, sticky=tk.W) entry_s.insert(tk.END, str(len(self.controller.get_states(graph_name)))) # create N.edges label and textbox label_e = tk.Label(frame, text="N° Edges :", wraplength=500, bg=self.COLOR_frames, fg=self.COLOR_foreground) label_e.grid(row=1, column=0, pady=30, padx=10) entry_e = Entry(frame, justify=tk.CENTER, width=5, fg='black', highlightthickness=0) entry_e.grid(row=1, column=1, sticky=tk.W) entry_e.insert(tk.END, str(len(self.controller.get_edges(graph_name)))) # create Start Node label and textbox label_sn = tk.Label(frame, text="Start Node :", wraplength=500, bg=self.COLOR_frames, fg=self.COLOR_foreground) label_sn.grid(row=2, column=0, pady=10, padx=10) entry_sn = Entry(frame, justify=tk.CENTER, width=5, fg='black', highlightthickness=0) entry_sn.grid(row=2, column=1, sticky=tk.W) entry_sn.insert(tk.END, str(self.controller.get_start_node(graph_name))) # ------- RIGHT widgets ----------- # # create N.Labels label, textbox and Optionmenu label_l = tk.Label(frame, text="N° Labels :", wraplength=500, bg=self.COLOR_frames, fg=self.COLOR_foreground) label_l.grid(row=0, column=2, pady=10, padx=40) entry_l = Entry(frame, justify=tk.CENTER, width=5, fg='black', highlightthickness=0) entry_l.grid(row=0, column=3, sticky=tk.W) option_l = tk.StringVar() elements_l = list(self.controller.get_labels(graph_name)) entry_l.insert(tk.END, len(elements_l)) option_l.set(elements_l[0]) label_menu = ttk.OptionMenu(frame, option_l, elements_l[0], *elements_l) label_menu.grid(row=0, column=4, pady=10, padx=40, sticky=tk.W) # create N.participants label, textbox and Optionmenu label_p = tk.Label(frame, text="N° Participants :", wraplength=500, bg=self.COLOR_frames, fg=self.COLOR_foreground) label_p.grid(row=1, column=2, pady=10, padx=10) entry_p = Entry(frame, justify=tk.CENTER, width=5, fg='black', highlightthickness=0) entry_p.grid(row=1, column=3, sticky=tk.W) option_p = tk.StringVar() elements_p = list(self.controller.get_participants(graph_name)) entry_p.insert(tk.END, len(elements_p)) option_p.set(elements_p[0]) part_menu = ttk.OptionMenu(frame, option_p, elements_p[0], *elements_p) part_menu.grid(row=1, column=4, pady=10, padx=40, sticky=tk.W) # create epsilon moves label and textbox label_eps = tk.Label(frame, text="Epsilon moves :", bg=self.COLOR_frames, fg=self.COLOR_foreground) label_eps.grid(row=2, column=2, pady=10, padx=10) entry_eps = Entry(frame, justify=tk.CENTER, width=5, fg='black', highlightthickness=0) entry_eps.grid(row=2, column=3, sticky=tk.W) entry_eps.insert(tk.END, self.controller.check_for_epsilon_moves(graph_name)) # create close button close_button = Button(frame, text='X', bg=self.COLOR_frames, highlightthickness=0, borderwidth=0, command=lambda: ( self.controller.remove_record(self.tabs.tab(self.tabs.select(), "text")), # remove the record from opened graphs struct self.tabs.forget(self.tabs.select()))) # delete the tab close_button.grid(row=0, column=5, sticky=tk.E + tk.N) # once created, select the tab self.tabs.select(frame) def __exec_sync_button__(self, combo_value, interface1, interface2): path_to_store = filedialog.asksaveasfilename(initialdir=".", title="Save as", filetypes=("DOT graph", "*.gv *.dot")) result = self.controller.synchronize(combo_value, interface1, interface2, path_to_store) # print the log message self.log(result[0]) # create a new tab for the product graph self.__add_new_tab__(result[1]) def __exec_product_button__(self, combo_value1, combo_value2): path_to_store = filedialog.asksaveasfilename(initialdir=".", title="Save as", filetypes=("DOT graph", "*.gv *.dot")) result = self.controller.make_product(combo_value1, combo_value2, path_to_store) # print the log message self.log(result[0]) # create a new tab for the product graph self.__add_new_tab__(result[1]) def __exec_proj_button__(self, combo_value, participant): path_to_store = filedialog.asksaveasfilename(initialdir=".", title="Save as", filetypes=("DOT graph", "*.gv *.dot")) result = self.controller.projection(combo_value, participant, path_to_store) # print the log message self.log(result) def __make_sync_interface_menu__(self, frame, elements, option_v1, option_v2): # label and optionMenu for the 1st interface option_v1.set(elements[0]) lbl2 = tk.Label(frame, text="Select 1st participant", bg=self.COLOR_frames, fg=self.COLOR_foreground) lbl2.grid(row=2, column=0, padx=10, pady=10) op_menu_1 = ttk.OptionMenu(frame, option_v1, elements[0], *elements) op_menu_1.grid(row=2, column=1, padx=10, pady=10) # label and optionMenu for the 2st interface option_v2.set(elements[0]) lbl3 = tk.Label(frame, text='Select 2st participant', bg=self.COLOR_frames, fg=self.COLOR_foreground) lbl3.grid(row=3, column=0, pady=10) op_menu_2 = ttk.OptionMenu(frame, option_v2, elements[0], *elements) op_menu_2.grid(row=3, column=1, padx=10, pady=10) # update window dimension self.__set_window_dimension__(frame) def __make_proj_participant_menu__(self, frame, elements, option): option.set(elements[0]) lbl = tk.Label(frame, text='Select participant to project', bg=self.COLOR_frames, fg=self.COLOR_foreground) lbl.grid(row=2, column=0, padx=10, pady=10) op_menu = ttk.OptionMenu(frame, option, elements[0], *elements) op_menu.grid(row=2, column=1, padx=10, pady=10) self.__set_window_dimension__(frame) def __set_window_dimension__(self, frame): # set window dimension width, height = frame.winfo_reqwidth(), frame.winfo_reqheight() frame.geometry('+%d+%d' % (self._screen_width / 2 - width / 2, self._screen_height / 2 - height / 2)) def log(self, msg): # Write a message in the log box for line in msg: self._log.insert(tk.END, line) self._log.see(tk.END) # make the last item background red #self._log.itemconfig(tk.END, {'bg': 'red'}) def popupmsg(self, msg): popup = tk.Toplevel(padx=20, pady=20) popup.wm_title("!") popup.resizable(False, False) screen_width, screen_height = popup.winfo_screenwidth(), popup.winfo_screenheight() width, height = popup.winfo_reqwidth(), popup.winfo_reqheight() popup.geometry('+%d+%d' % (screen_width / 2 - width / 2, screen_height / 2 - height / 2)) max_size = popup.winfo_screenwidth() / 3 label = tk.Label(popup, text=msg, wraplength=max_size) label.grid(row=0, column=0) b = ttk.Button(popup, text="Okay", command=popup.destroy) b.grid(row=1, column=0)
class TextSelect(Frame): def __init__(self, client, anchor, items, destroyAnchor=False): """ Args: client: [SelectionClient] The window that text is returned to. anchor: A window that the text selection popup is created relative to. items: [str], items to display in the listbox. destroyAnchor: [bool] if true, destroy the anchor after positioning the window. """ self.top = Toplevel() self.anchor = anchor self.top.overrideredirect(1) self.top.wm_geometry('+%s+%s' % (anchor.winfo_rootx() + anchor.winfo_x(), anchor.winfo_rooty() + anchor.winfo_y() ) ) super(TextSelect, self).__init__(self.top) self.entry = Entry(self) self.client = client self.items = items self.place(x = 0.5, y = 0.5, height = 100, width = 100) self.entry.bind('<Return>', self.close) self.entry.bind('<KeyPress>', self.filter) self.entry.bind('<Escape>', self.abort) self.entry.bind('<Up>', self.up) self.entry.bind('<Down>', self.down) self.entry.pack() # Create the list of items. self.list = Listbox(self) for item in self.items: self.list.insert('end', item) self.list.pack() self.grid() self.entry.focus() # Reposition the select button against the anchor. We defer this # until after idle so that the anchor has a chance to get rendered. def reposition(*args): self.top.wm_geometry('+%s+%s' % ( anchor.winfo_rootx(), anchor.winfo_rooty()) ) if destroyAnchor: anchor.destroy() self.after_idle(reposition) def close(self, event): sel = self.list.curselection() if sel: item = self.list.get(sel[0]) else: item = self.entry.get() # Note that the order of this appears to be significant: destroying # before selecting leaves the focus in a weird state. self.client.selected(item) self.top.destroy() return 'braek' def abort(self, event): self.top.destroy() self.client.aborted() return 'break' def up(self, event): sel = self.list.curselection() if not sel: self.list.selection_set(0) return 'break' sel = sel[0] print('sel is %s size is %s' % (sel, self.list.size())) if sel > 0: print('setting selection to %s' % sel) self.list.selection_clear(sel) self.list.selection_set(sel - 1) self.list.see(sel) return 'break' def down(self, event): sel = self.list.curselection() if not sel: self.list.selection_set(0) return 'break' sel = sel[0] print('sel is %s size is %s' % (sel, self.list.size())) if sel < self.list.size() - 1: print('setting selection to %s' % (sel + 1)) self.list.selection_clear(sel) self.list.selection_set(sel + 1) self.list.see(sel) return 'break' def filter(self, event): """Filter the listbox based on the contents of the entryfield.""" # first add the character to the entry. currentText = self.entry.get() print(event.keysym) if event.keysym == 'BackSpace': # Handle backspace specially. if currentText: currentText = currentText[:-1] self.entry.delete(0, 'end') self.entry.insert(0, currentText) else: return 'break' else: # Assume normal character. Insert it. self.entry.insert('insert', event.char) currentText += event.char self.list.delete(0, 'end') pattern = currentText.upper() for item in self.items: if pattern in item.upper(): self.list.insert('end', item) return 'break'
class Combobox_Autocomplete(Entry, object): def __init__(self, master, list_of_items=None, autocomplete_function=None, listbox_width=None, listbox_height=7, ignorecase_match=False, startswith_match=True, vscrollbar=True, hscrollbar=True, **kwargs): if hasattr(self, "autocomplete_function"): if autocomplete_function is not None: raise ValueError( "Combobox_Autocomplete subclass has 'autocomplete_function' implemented" ) else: if autocomplete_function is not None: self.autocomplete_function = autocomplete_function else: if list_of_items is None: raise ValueError( "If not guiven complete function, list_of_items can't be 'None'" ) if ignorecase_match: if startswith_match: def matches_function(entry_data, item): return item.startswith(entry_data) else: def matches_function(entry_data, item): return item in entry_data self.autocomplete_function = lambda entry_data: [ item for item in self.list_of_items if matches_function(entry_data, item) ] else: if startswith_match: def matches_function(escaped_entry_data, item): if re.match(escaped_entry_data, item, re.IGNORECASE): return True else: return False else: def matches_function(escaped_entry_data, item): if re.search(escaped_entry_data, item, re.IGNORECASE): return True else: return False def autocomplete_function(entry_data): escaped_entry_data = re.escape(entry_data) return [ item for item in self.list_of_items if matches_function(escaped_entry_data, item) ] self.autocomplete_function = autocomplete_function self._listbox_height = int(listbox_height) self._listbox_width = listbox_width self.list_of_items = list_of_items self._use_vscrollbar = vscrollbar self._use_hscrollbar = hscrollbar kwargs.setdefault("background", "white") if "textvariable" in kwargs: self._entry_var = kwargs["textvariable"] else: self._entry_var = kwargs["textvariable"] = StringVar() Entry.__init__(self, master, **kwargs) self._trace_id = self._entry_var.trace('w', self._on_change_entry_var) self._listbox = None self.bind("<Tab>", self._on_tab) self.bind("<Up>", self._previous) self.bind("<Down>", self._next) self.bind('<Control-n>', self._next) self.bind('<Control-p>', self._previous) self.bind("<Return>", self._update_entry_from_listbox) self.bind("<Escape>", lambda event: self.unpost_listbox()) def _on_tab(self, event): self.post_listbox() return "break" def _on_change_entry_var(self, name, index, mode): entry_data = self._entry_var.get() if entry_data == '': self.unpost_listbox() self.focus() else: values = self.autocomplete_function(entry_data) if values: if self._listbox is None: self._build_listbox(values) else: self._listbox.delete(0, END) height = min(self._listbox_height, len(values)) self._listbox.configure(height=height) for item in values: self._listbox.insert(END, item) else: self.unpost_listbox() self.focus() def _build_listbox(self, values): listbox_frame = Frame() self._listbox = Listbox(listbox_frame, background="white", selectmode=SINGLE, activestyle="none", exportselection=False) self._listbox.grid(row=0, column=0, sticky=N + E + W + S) self._listbox.bind("<ButtonRelease-1>", self._update_entry_from_listbox) self._listbox.bind("<Return>", self._update_entry_from_listbox) self._listbox.bind("<Escape>", lambda event: self.unpost_listbox()) self._listbox.bind('<Control-n>', self._next) self._listbox.bind('<Control-p>', self._previous) if self._use_vscrollbar: vbar = Scrollbar(listbox_frame, orient=VERTICAL, command=self._listbox.yview) vbar.grid(row=0, column=1, sticky=N + S) self._listbox.configure( yscrollcommand=lambda f, l: autoscroll(vbar, f, l)) if self._use_hscrollbar: hbar = Scrollbar(listbox_frame, orient=HORIZONTAL, command=self._listbox.xview) hbar.grid(row=1, column=0, sticky=E + W) self._listbox.configure( xscrollcommand=lambda f, l: autoscroll(hbar, f, l)) listbox_frame.grid_columnconfigure(0, weight=1) listbox_frame.grid_rowconfigure(0, weight=1) x = -self.cget("borderwidth") - self.cget("highlightthickness") y = self.winfo_height() - self.cget("borderwidth") - self.cget( "highlightthickness") if self._listbox_width: width = self._listbox_width else: width = self.winfo_width() listbox_frame.place(in_=self, x=x, y=y, width=width) height = min(self._listbox_height, len(values)) self._listbox.configure(height=height) for item in values: self._listbox.insert(END, item) def post_listbox(self): if self._listbox is not None: return entry_data = self._entry_var.get() if entry_data == '': return values = self.autocomplete_function(entry_data) if values: self._build_listbox(values) def unpost_listbox(self): if self._listbox is not None: self._listbox.master.destroy() self._listbox = None def get_value(self): return self._entry_var.get() def set_value(self, text, close_dialog=False): self._set_var(text) if close_dialog: self.unpost_listbox() self.icursor(END) self.xview_moveto(1.0) def _set_var(self, text): self._entry_var.trace_vdelete("w", self._trace_id) self._entry_var.set(text) self._trace_id = self._entry_var.trace('w', self._on_change_entry_var) def _update_entry_from_listbox(self, event): if self._listbox is not None: current_selection = self._listbox.curselection() if current_selection: text = self._listbox.get(current_selection) self._set_var(text) self._listbox.master.destroy() self._listbox = None self.focus() self.icursor(END) self.xview_moveto(1.0) return "break" def _previous(self, event): if self._listbox is not None: current_selection = self._listbox.curselection() if len(current_selection) == 0: self._listbox.selection_set(0) self._listbox.activate(0) else: index = int(current_selection[0]) self._listbox.selection_clear(index) if index == 0: index = END else: index -= 1 self._listbox.see(index) self._listbox.selection_set(first=index) self._listbox.activate(index) return "break" def _next(self, event): if self._listbox is not None: current_selection = self._listbox.curselection() if len(current_selection) == 0: self._listbox.selection_set(0) self._listbox.activate(0) else: index = int(current_selection[0]) self._listbox.selection_clear(index) if index == self._listbox.size() - 1: index = 0 else: index += 1 self._listbox.see(index) self._listbox.selection_set(index) self._listbox.activate(index) return "break" # if __name__ == '__main__': # try: # from Tkinter import Tk # except ImportError: # from tkinter import Tk # # list_of_items = ["Cordell Cannata", "Lacey Naples", "Zachery Manigault", "Regan Brunt", "Mario Hilgefort", "Austin Phong", "Moises Saum", "Willy Neill", "Rosendo Sokoloff", "Salley Christenberry", "Toby Schneller", "Angel Buchwald", "Nestor Criger", "Arie Jozwiak", "Nita Montelongo", "Clemencia Okane", "Alison Scaggs", "Von Petrella", "Glennie Gurley", "Jamar Callender", "Titus Wenrich", "Chadwick Liedtke", "Sharlene Yochum", "Leonida Mutchler", "Duane Pickett", "Morton Brackins", "Ervin Trundy", "Antony Orwig", "Audrea Yutzy", "Michal Hepp", "Annelle Hoadley", "Hank Wyman", "Mika Fernandez", "Elisa Legendre", "Sade Nicolson", "Jessie Yi", "Forrest Mooneyhan", "Alvin Widell", "Lizette Ruppe", "Marguerita Pilarski", "Merna Argento", "Jess Daquila", "Breann Bevans", "Melvin Guidry", "Jacelyn Vanleer", "Jerome Riendeau", "Iraida Nyquist", "Micah Glantz", "Dorene Waldrip", "Fidel Garey", "Vertie Deady", "Rosalinda Odegaard", "Chong Hayner", "Candida Palazzolo", "Bennie Faison", "Nova Bunkley", "Francis Buckwalter", "Georgianne Espinal", "Karleen Dockins", "Hertha Lucus", "Ike Alberty", "Deangelo Revelle", "Juli Gallup", "Wendie Eisner", "Khalilah Travers", "Rex Outman", "Anabel King", "Lorelei Tardiff", "Pablo Berkey", "Mariel Tutino", "Leigh Marciano", "Ok Nadeau", "Zachary Antrim", "Chun Matthew", "Golden Keniston", "Anthony Johson", "Rossana Ahlstrom", "Amado Schluter", "Delila Lovelady", "Josef Belle", "Leif Negrete", "Alec Doss", "Darryl Stryker", "Michael Cagley", "Sabina Alejo", "Delana Mewborn", "Aurelio Crouch", "Ashlie Shulman", "Danielle Conlan", "Randal Donnell", "Rheba Anzalone", "Lilian Truax", "Weston Quarterman", "Britt Brunt", "Leonie Corbett", "Monika Gamet", "Ingeborg Bello", "Angelique Zhang", "Santiago Thibeau", "Eliseo Helmuth"] # # root = Tk() # root.geometry("300x200") # # combobox_autocomplete = Combobox_Autocomplete(root, list_of_items, highlightthickness=1) # combobox_autocomplete.pack() # # combobox_autocomplete.focus() # # root.mainloop()
class _cross_platform_fonts(_Dialog): def body(self, master): dialogframe = Frame(master, width=523, height=346) self.dialogframe = dialogframe dialogframe.pack() self.RadioGroup_1_StringVar = StringVar() self.make_Entry_2(self.dialogframe) # Entry: at Main(1,2) self.make_LabelFrame_1( self.dialogframe) # LabelFrame: Attributes : at Main(2,4) self.make_Label_3( self.dialogframe ) # Label: (see sample text above) : at Main(9,1) self.make_Label_4( self.dialogframe) # Label: ABCD efg 123.0 : at Main(8,1) self.make_Label_6( self.dialogframe ) # Label: Courier 10 normal italic underline overstrike : at Main(0,1) self.make_Label_7(self.dialogframe) # Label: at Main(2,3) self.make_Label_8( self.dialogframe) # Label: System Fonts : at Main(0,5) self.make_Listbox_1(self.dialogframe) # Listbox: at Main(2,2) self.make_Listbox_2(self.dialogframe) # Listbox: at Main(1,5) self.make_RadioGroup_1( self.dialogframe ) # RadioGroup: Cross Platform Fonts : at Main(2,1) self.make_Checkbutton_1( self.LabelFrame_1) # Checkbutton: Bold : at LabelFrame_1(1,1) self.make_Checkbutton_2( self.LabelFrame_1) # Checkbutton: Italic : at LabelFrame_1(2,1) self.make_Checkbutton_3( self.LabelFrame_1) # Checkbutton: Underline : at LabelFrame_1(3,1) self.make_Checkbutton_4( self.LabelFrame_1 ) # Checkbutton: Overstrike : at LabelFrame_1(4,1) self.make_Radiobutton_1( self.RadioGroup_1) # Radiobutton: Courier : at RadioGroup_1(1,0) self.make_Radiobutton_2( self.RadioGroup_1) # Radiobutton: Helvetica : at RadioGroup_1(2,0) self.make_Radiobutton_3( self.RadioGroup_1) # Radiobutton: Times : at RadioGroup_1(3,0) self.make_Radiobutton_4( self.RadioGroup_1 ) # Radiobutton: TkDefaultFont : at RadioGroup_1(4,0) self.make_Radiobutton_5( self.RadioGroup_1 ) # Radiobutton: Platform Specific : at RadioGroup_1(6,0) self.make_Radiobutton_6( self.RadioGroup_1) # Radiobutton: Symbol : at RadioGroup_1(5,0) self.RadioGroup_1_StringVar.set("1") self.RadioGroup_1_StringVar_traceName = self.RadioGroup_1_StringVar.trace_variable( "w", self.RadioGroup_1_StringVar_Callback) # >>>>>>insert any user code below this comment for section "top_of_init" self.RadioGroup_1_StringVar.set("2") # make Helvetica the default self.current_font_name = 'Helvetica' self.ignore_entry_change = False # used when Listbox sets Entry self.Entry_2_StringVar.set('10') def set_current_state(self): sL = [self.current_font_name] points = self.Entry_2_StringVar.get().strip() try: points = int(points.strip()) except: points = 10 if points: sL.append(points) else: sL.append(10) if self.Checkbutton_1_StringVar.get() == 'yes': sL.append('bold') else: sL.append('normal') if self.Checkbutton_2_StringVar.get() == 'yes': sL.append('italic') else: sL.append('roman') if self.Checkbutton_3_StringVar.get() == 'yes': sL.append('underline') if self.Checkbutton_4_StringVar.get() == 'yes': sL.append('overstrike') self.full_font_desc = tuple(sL) self.Label_6.configure(text=self.full_font_desc) self.Label_4.configure(font=self.full_font_desc) # TkGridGUI generated code. DO NOT EDIT THE FOLLOWING. section "make_Entry_2" def make_Entry_2(self, frame): """ Entry: at Main(1,2)""" self.Entry_2 = Entry(frame, width="4") self.Entry_2.grid(row=1, column=2, sticky="w") self.Entry_2_StringVar = StringVar() # >>>>>>insert any user code below this comment for section "make_Entry_2" self.Entry_2.configure(textvariable=self.Entry_2_StringVar) self.Entry_2_StringVar_traceName = self.Entry_2_StringVar.trace_variable( "w", self.Entry_2_StringVar_Callback) # TkGridGUI generated code. DO NOT EDIT THE FOLLOWING. section "make_LabelFrame_1" def make_LabelFrame_1(self, frame): """ LabelFrame: Attributes : at Main(2,4)""" self.LabelFrame_1 = LabelFrame(frame, text="Attributes", width="60", height="50") self.LabelFrame_1.grid(row=2, column=4, sticky="n", rowspan="2") # >>>>>>insert any user code below this comment for section "make_LabelFrame_1" # TkGridGUI generated code. DO NOT EDIT THE FOLLOWING. section "make_Label_3" def make_Label_3(self, frame): """ Label: (see sample text above) : at Main(9,1)""" self.Label_3 = Label(frame, text="(see sample text above)", width="30") self.Label_3.grid(row=9, column=1, columnspan="5") # >>>>>>insert any user code below this comment for section "make_Label_3" # TkGridGUI generated code. DO NOT EDIT THE FOLLOWING. section "make_Label_4" def make_Label_4(self, frame): """ Label: ABCD efg 123.0 : at Main(8,1)""" self.Label_4 = Label(frame, text="ABCD efg 123.0", width="14") self.Label_4.grid(row=8, column=1, sticky="ew", columnspan="5") # >>>>>>insert any user code below this comment for section "make_Label_4" # TkGridGUI generated code. DO NOT EDIT THE FOLLOWING. section "make_Label_6" def make_Label_6(self, frame): """ Label: Courier 10 normal italic underline overstrike : at Main(0,1)""" self.Label_6 = Label( frame, text="Courier 10 normal italic underline overstrike", width="30", font="TkDefaultFont 12") self.Label_6.grid(row=0, column=1, sticky="ew", columnspan="4") # >>>>>>insert any user code below this comment for section "make_Label_6" # TkGridGUI generated code. DO NOT EDIT THE FOLLOWING. section "make_Label_7" def make_Label_7(self, frame): """ Label: at Main(2,3)""" self.Label_7 = Label(frame, text="", width="3") self.Label_7.grid(row=2, column=3) # >>>>>>insert any user code below this comment for section "make_Label_7" # TkGridGUI generated code. DO NOT EDIT THE FOLLOWING. section "make_Label_8" def make_Label_8(self, frame): """ Label: System Fonts : at Main(0,5)""" self.Label_8 = Label(frame, text="System Fonts", width="15") self.Label_8.grid(row=0, column=5, sticky="s") # >>>>>>insert any user code below this comment for section "make_Label_8" # TkGridGUI generated code. DO NOT EDIT THE FOLLOWING. section "make_Listbox_1" def make_Listbox_1(self, frame): """ Listbox: at Main(2,2)""" lbframe = Frame(frame) self.Listbox_1_frame = lbframe vbar = Scrollbar(lbframe, orient=VERTICAL) self.Listbox_1 = Listbox(lbframe, width="4", borderwidth="4", height="10", yscrollcommand=vbar.set) vbar.config(command=self.Listbox_1.yview) vbar.grid(row=0, column=1, sticky='ns') self.Listbox_1.grid(row=0, column=0) self.Listbox_1_frame.grid(row=2, column=2, sticky="nsw") # >>>>>>insert any user code below this comment for section "make_Listbox_1" self.Listbox_1.configure( exportselection=False) # stay highlighted after focus leaves # Edit the Listbox Entries self.font_sizeL = [ "%i" % i for i in (list(range(6, 17)) + list(range(18, 32, 2)) + [42, 48, 54, 60, 72]) ] for s in self.font_sizeL: self.Listbox_1.insert(END, s) self.Listbox_1.bind("<ButtonRelease-1>", self.Listbox_1_Click) self.Listbox_1.bind('<<ListboxSelect>>', self.Listbox_1_Click) # TkGridGUI generated code. DO NOT EDIT THE FOLLOWING. section "make_Listbox_2" def make_Listbox_2(self, frame): """ Listbox: at Main(1,5)""" lbframe = Frame(frame) self.Listbox_2_frame = lbframe vbar = Scrollbar(lbframe, orient=VERTICAL) self.Listbox_2 = Listbox(lbframe, width="25", height="15", yscrollcommand=vbar.set) vbar.config(command=self.Listbox_2.yview) vbar.grid(row=0, column=1, sticky='ns') self.Listbox_2.grid(row=0, column=0) self.Listbox_2_frame.grid(row=1, column=5, sticky="ns", rowspan="7") # >>>>>>insert any user code below this comment for section "make_Listbox_2" self.Listbox_2.configure( exportselection=False) # stay highlighted after focus leaves self.sys_fonts = list(set(families())) self.sys_fonts.sort() for s in self.sys_fonts: self.Listbox_2.insert(END, s) self.Listbox_2.bind("<ButtonRelease-1>", self.Listbox_2_Click) self.Listbox_2.bind('<<ListboxSelect>>', self.Listbox_2_Click) # TkGridGUI generated code. DO NOT EDIT THE FOLLOWING. section "make_RadioGroup_1" def make_RadioGroup_1(self, frame): """ RadioGroup: Cross Platform Fonts : at Main(2,1)""" self.RadioGroup_1 = LabelFrame(frame, text="Cross Platform Fonts", width="60", height="50") self.RadioGroup_1.grid(row=2, column=1, sticky="n", rowspan="2") # >>>>>>insert any user code below this comment for section "make_RadioGroup_1" # TkGridGUI generated code. DO NOT EDIT THE FOLLOWING. section "make_Checkbutton_1" def make_Checkbutton_1(self, frame): """ Checkbutton: Bold : at LabelFrame_1(1,1)""" self.Checkbutton_1 = Checkbutton(frame, text="Bold", width="15", anchor="w") self.Checkbutton_1.grid(row=1, column=1) self.Checkbutton_1_StringVar = StringVar() # >>>>>>insert any user code below this comment for section "make_Checkbutton_1" self.Checkbutton_1.configure(variable=self.Checkbutton_1_StringVar, onvalue="yes", offvalue="no") self.Checkbutton_1_StringVar.set("no") self.Checkbutton_1_StringVar_traceName = self.Checkbutton_1_StringVar.trace_variable( "w", self.Checkbutton_1_StringVar_Callback) # TkGridGUI generated code. DO NOT EDIT THE FOLLOWING. section "make_Checkbutton_2" def make_Checkbutton_2(self, frame): """ Checkbutton: Italic : at LabelFrame_1(2,1)""" self.Checkbutton_2 = Checkbutton(frame, text="Italic", width="15", anchor="w") self.Checkbutton_2.grid(row=2, column=1) self.Checkbutton_2_StringVar = StringVar() # >>>>>>insert any user code below this comment for section "make_Checkbutton_2" self.Checkbutton_2.configure(variable=self.Checkbutton_2_StringVar, onvalue="yes", offvalue="no") self.Checkbutton_2_StringVar.set("no") self.Checkbutton_2_StringVar_traceName = self.Checkbutton_2_StringVar.trace_variable( "w", self.Checkbutton_2_StringVar_Callback) # TkGridGUI generated code. DO NOT EDIT THE FOLLOWING. section "make_Checkbutton_3" def make_Checkbutton_3(self, frame): """ Checkbutton: Underline : at LabelFrame_1(3,1)""" self.Checkbutton_3 = Checkbutton(frame, text="Underline", width="15", anchor="w") self.Checkbutton_3.grid(row=3, column=1) self.Checkbutton_3_StringVar = StringVar() # >>>>>>insert any user code below this comment for section "make_Checkbutton_3" self.Checkbutton_3.configure(variable=self.Checkbutton_3_StringVar, onvalue="yes", offvalue="no") self.Checkbutton_3_StringVar.set("no") self.Checkbutton_3_StringVar_traceName = self.Checkbutton_3_StringVar.trace_variable( "w", self.Checkbutton_3_StringVar_Callback) # TkGridGUI generated code. DO NOT EDIT THE FOLLOWING. section "make_Checkbutton_4" def make_Checkbutton_4(self, frame): """ Checkbutton: Overstrike : at LabelFrame_1(4,1)""" self.Checkbutton_4 = Checkbutton(frame, text="Overstrike", width="15", anchor="w") self.Checkbutton_4.grid(row=4, column=1) self.Checkbutton_4_StringVar = StringVar() # >>>>>>insert any user code below this comment for section "make_Checkbutton_4" self.Checkbutton_4.configure(variable=self.Checkbutton_4_StringVar, onvalue="yes", offvalue="no") self.Checkbutton_4_StringVar.set("no") self.Checkbutton_4_StringVar_traceName = self.Checkbutton_4_StringVar.trace_variable( "w", self.Checkbutton_4_StringVar_Callback) # TkGridGUI generated code. DO NOT EDIT THE FOLLOWING. section "make_Radiobutton_1" def make_Radiobutton_1(self, frame): """ Radiobutton: Courier : at RadioGroup_1(1,0)""" self.Radiobutton_1 = Radiobutton(frame, text="Courier", value="1", width="15", anchor="w") self.Radiobutton_1.grid(row=1, column=0) # >>>>>>insert any user code below this comment for section "make_Radiobutton_1" self.Radiobutton_1.configure(variable=self.RadioGroup_1_StringVar) # TkGridGUI generated code. DO NOT EDIT THE FOLLOWING. section "make_Radiobutton_2" def make_Radiobutton_2(self, frame): """ Radiobutton: Helvetica : at RadioGroup_1(2,0)""" self.Radiobutton_2 = Radiobutton(frame, text="Helvetica", value="2", width="15", anchor="w") self.Radiobutton_2.grid(row=2, column=0) # >>>>>>insert any user code below this comment for section "make_Radiobutton_2" self.Radiobutton_2.configure(variable=self.RadioGroup_1_StringVar) # TkGridGUI generated code. DO NOT EDIT THE FOLLOWING. section "make_Radiobutton_3" def make_Radiobutton_3(self, frame): """ Radiobutton: Times : at RadioGroup_1(3,0)""" self.Radiobutton_3 = Radiobutton(frame, text="Times", value="3", width="15", anchor="w") self.Radiobutton_3.grid(row=3, column=0) # >>>>>>insert any user code below this comment for section "make_Radiobutton_3" self.Radiobutton_3.configure(variable=self.RadioGroup_1_StringVar) # TkGridGUI generated code. DO NOT EDIT THE FOLLOWING. section "make_Radiobutton_4" def make_Radiobutton_4(self, frame): """ Radiobutton: TkDefaultFont : at RadioGroup_1(4,0)""" self.Radiobutton_4 = Radiobutton(frame, text="TkDefaultFont", value="4", width="15", anchor="w") self.Radiobutton_4.grid(row=4, column=0) # >>>>>>insert any user code below this comment for section "make_Radiobutton_4" self.Radiobutton_4.configure(variable=self.RadioGroup_1_StringVar) # TkGridGUI generated code. DO NOT EDIT THE FOLLOWING. section "make_Radiobutton_5" def make_Radiobutton_5(self, frame): """ Radiobutton: Platform Specific : at RadioGroup_1(6,0)""" self.Radiobutton_5 = Radiobutton(frame, text="Platform Specific", value="6", width="15", anchor="e") self.Radiobutton_5.grid(row=6, column=0) # >>>>>>insert any user code below this comment for section "make_Radiobutton_5" self.Radiobutton_5.configure(variable=self.RadioGroup_1_StringVar) # TkGridGUI generated code. DO NOT EDIT THE FOLLOWING. section "make_Radiobutton_6" def make_Radiobutton_6(self, frame): """ Radiobutton: Symbol : at RadioGroup_1(5,0)""" self.Radiobutton_6 = Radiobutton(frame, text="Symbol", value="5", width="15", anchor="w") self.Radiobutton_6.grid(row=5, column=0) # >>>>>>insert any user code below this comment for section "make_Radiobutton_6" self.Radiobutton_6.configure(variable=self.RadioGroup_1_StringVar) # TkGridGUI generated code. DO NOT EDIT THE FOLLOWING. section "Listbox_1_Click" def Listbox_1_Click(self, event): #bind method for component ID=Listbox_1 """ Listbox: at Main(2,2)""" pass # >>>>>>insert any user code below this comment for section "Listbox_1_Click" # replace, delete, or comment-out the following #print( "executed method Listbox_1_Click" ) #print( "current selection(s) =",self.Listbox_1.curselection() ) labelL = [] for i in self.Listbox_1.curselection(): labelL.append(self.Listbox_1.get(i)) #print( "current label(s) =",labelL ) if labelL: self.ignore_entry_change = True self.Entry_2_StringVar.set(labelL[0]) self.ignore_entry_change = False # use self.Listbox_1.insert(0, "item zero") # self.Listbox_1.insert(index, "item i") # OR # self.Listbox_1.insert(END, "item end") # to insert items into the list box # TkGridGUI generated code. DO NOT EDIT THE FOLLOWING. section "Listbox_2_Click" def Listbox_2_Click(self, event): #bind method for component ID=Listbox_2 """ Listbox: at Main(1,5)""" pass # >>>>>>insert any user code below this comment for section "Listbox_2_Click" # replace, delete, or comment-out the following #print( "executed method Listbox_2_Click" ) #print( "current selection(s) =",self.Listbox_2.curselection() ) labelL = [] for i in self.Listbox_2.curselection(): labelL.append(self.Listbox_2.get(i)) #print( "current label(s) =",labelL ) # use self.Listbox_2.insert(0, "item zero") # self.Listbox_2.insert(index, "item i") # OR # self.Listbox_2.insert(END, "item end") # to insert items into the list box self.RadioGroup_1_StringVar.set("6") # make Helvetica the default if labelL: self.current_font_name = labelL[0] self.set_current_state() # TkGridGUI generated code. DO NOT EDIT THE FOLLOWING. section "Entry_2_StringVar_traceName" def Entry_2_StringVar_Callback(self, varName, index, mode): """ Entry: at Main(1,2)""" pass # >>>>>>insert any user code below this comment for section "Entry_2_StringVar_traceName" # replace, delete, or comment-out the following #print( "Entry_2_StringVar_Callback varName, index, mode",varName, index, mode ) #print( " new StringVar value =",self.Entry_2_StringVar.get() ) self.set_current_state() if self.ignore_entry_change: return # Looks like a manual change, so try to change Listbox sval = self.Entry_2_StringVar.get().strip() try: ival = int(sval) except: ival = 0 if ival and (sval in self.font_sizeL): index = self.font_sizeL.index(sval) self.Listbox_1.selection_clear(0, "end") self.Listbox_1.select_set(index) self.Listbox_1.see(index) # TkGridGUI generated code. DO NOT EDIT THE FOLLOWING. section "Checkbutton_1_StringVar_traceName" def Checkbutton_1_StringVar_Callback(self, varName, index, mode): """ Checkbutton: Bold : at LabelFrame_1(1,1)""" pass # >>>>>>insert any user code below this comment for section "Checkbutton_1_StringVar_traceName" # replace, delete, or comment-out the following #print( "Checkbutton_1_StringVar_Callback varName, index, mode",varName, index, mode ) #print( " new StringVar value =",self.Checkbutton_1_StringVar.get() ) self.set_current_state() # TkGridGUI generated code. DO NOT EDIT THE FOLLOWING. section "Checkbutton_2_StringVar_traceName" def Checkbutton_2_StringVar_Callback(self, varName, index, mode): """ Checkbutton: Italic : at LabelFrame_1(2,1)""" pass # >>>>>>insert any user code below this comment for section "Checkbutton_2_StringVar_traceName" # replace, delete, or comment-out the following #print( "Checkbutton_2_StringVar_Callback varName, index, mode",varName, index, mode ) #print( " new StringVar value =",self.Checkbutton_2_StringVar.get() ) self.set_current_state() # TkGridGUI generated code. DO NOT EDIT THE FOLLOWING. section "Checkbutton_3_StringVar_traceName" def Checkbutton_3_StringVar_Callback(self, varName, index, mode): """ Checkbutton: Underline : at LabelFrame_1(3,1)""" pass # >>>>>>insert any user code below this comment for section "Checkbutton_3_StringVar_traceName" # replace, delete, or comment-out the following #print( "Checkbutton_3_StringVar_Callback varName, index, mode",varName, index, mode ) #print( " new StringVar value =",self.Checkbutton_3_StringVar.get() ) self.set_current_state() # TkGridGUI generated code. DO NOT EDIT THE FOLLOWING. section "Checkbutton_4_StringVar_traceName" def Checkbutton_4_StringVar_Callback(self, varName, index, mode): """ Checkbutton: Overstrike : at LabelFrame_1(4,1)""" pass # >>>>>>insert any user code below this comment for section "Checkbutton_4_StringVar_traceName" # replace, delete, or comment-out the following #print( "Checkbutton_4_StringVar_Callback varName, index, mode",varName, index, mode ) #print( " new StringVar value =",self.Checkbutton_4_StringVar.get() ) self.set_current_state() # TkGridGUI generated code. DO NOT EDIT THE FOLLOWING. section "RadioGroup_1_StringVar_traceName" def RadioGroup_1_StringVar_Callback(self, varName, index, mode): """ RadioGroup: Cross Platform Fonts : at Main(2,1)""" pass # >>>>>>insert any user code below this comment for section "RadioGroup_1_StringVar_traceName" # replace, delete, or comment-out the following #print( "RadioGroup_1_StringVar_Callback varName, index, mode",varName, index, mode ) #print( " new StringVar value =",self.RadioGroup_1_StringVar.get() ) svar = self.RadioGroup_1_StringVar.get() if svar == '1': self.current_font_name = 'Courier' elif svar == '2': self.current_font_name = 'Helvetica' elif svar == '3': self.current_font_name = 'Times' elif svar == '4': self.current_font_name = 'TkDefaultFont' elif svar == '5': self.current_font_name = 'Symbol' elif svar == '6': labelL = [] for i in self.Listbox_2.curselection(): labelL.append(self.Listbox_2.get(i)) if labelL: self.current_font_name = labelL[0] self.set_current_state() # TkGridGUI generated code. DO NOT EDIT THE FOLLOWING. section "dialog_validate" def validate(self): self.result = {} # return a dictionary of results self.result["Entry_2"] = self.Entry_2_StringVar.get() self.result["Checkbutton_1"] = self.Checkbutton_1_StringVar.get() self.result["Checkbutton_2"] = self.Checkbutton_2_StringVar.get() self.result["Checkbutton_3"] = self.Checkbutton_3_StringVar.get() self.result["Checkbutton_4"] = self.Checkbutton_4_StringVar.get() self.result["RadioGroup_1"] = self.RadioGroup_1_StringVar.get() # >>>>>>insert any user code below this comment for section "dialog_validate" # set values in "self.result" dictionary for return # for example... # self.result["age"] = self.Entry_2_StringVar.get() self.result = {} t = self.full_font_desc self.result["full_font_desc"] = t # return the tuple self.result["full_font_str"] = t[0].replace( ' ', '\ ') + ' %i ' % t[1] + ' '.join(t[2:]) return 1 # TkGridGUI generated code. DO NOT EDIT THE FOLLOWING. section "end" def apply(self): pass
class AutoCompleteEntryListbox(Frame): def __init__(self, master=None, completevalues=[], allow_other_values=False, **kwargs): """ Create a Entry + Listbox with autocompletion. Keyword arguments: - allow_other_values (boolean): whether the user is allowed to enter values not in the list """ exportselection = kwargs.pop('exportselection', False) width = kwargs.pop('width', None) justify = kwargs.pop('justify', None) font = kwargs.pop('font', None) Frame.__init__(self, master, padding=4, **kwargs) self.columnconfigure(0, weight=1) self.rowconfigure(1, weight=1) self._allow_other_values = allow_other_values self._completevalues = completevalues self._validate = self.register(self.validate) self.entry = Entry(self, width=width, justify=justify, font=font, validate='key', exportselection=exportselection, validatecommand=(self._validate, "%d", "%S", "%i", "%s", "%P")) f = Frame(self, style='border.TFrame', padding=1) self.listbox = Listbox(f, width=width, justify=justify, font=font, exportselection=exportselection, selectmode="browse", highlightthickness=0, relief='flat') self.listbox.pack(fill='both', expand=True) scroll = AutoHideScrollbar(self, orient='vertical', command=self.listbox.yview) self.listbox.configure(yscrollcommand=scroll.set) self.entry.grid(sticky='ew') f.grid(sticky='nsew') scroll.grid(row=1, column=1, sticky='ns') for c in self._completevalues: self.listbox.insert('end', c) self.listbox.bind('<<ListboxSelect>>', self.update_entry) self.listbox.bind("<KeyPress>", self.keypress) self.entry.bind("<Tab>", self.tab) self.entry.bind("<Down>", self.down) self.entry.bind("<Up>", self.up) self.entry.focus_set() def tab(self, event): """Move at the end of selected text on tab press.""" self.entry = event.widget self.entry.selection_clear() self.entry.icursor("end") return "break" def keypress(self, event): """Select the first item which name begin by the key pressed.""" key = event.char.lower() l = [i for i in self._completevalues if i[0].lower() == key] if l: i = self._completevalues.index(l[0]) self.listbox.selection_clear(0, "end") self.listbox.selection_set(i) self.listbox.see(i) self.update_entry() def up(self, event): """Navigate in the listbox with up key.""" try: i = self.listbox.curselection()[0] self.listbox.selection_clear(0, "end") if i <= 0: i = len(self._completevalues) self.listbox.see(i - 1) self.listbox.select_set(i - 1) except (TclError, IndexError): self.listbox.selection_clear(0, "end") i = len(self._completevalues) self.listbox.see(i - 1) self.listbox.select_set(i - 1) self.listbox.event_generate('<<ListboxSelect>>') def down(self, event): """Navigate in the listbox with down key.""" try: i = self.listbox.curselection()[0] self.listbox.selection_clear(0, "end") if i >= len(self._completevalues): i = -1 self.listbox.see(i + 1) self.listbox.select_set(i + 1) except (TclError, IndexError): self.listbox.selection_clear(0, "end") self.listbox.see(0) self.listbox.select_set(0) self.listbox.event_generate('<<ListboxSelect>>') def validate(self, action, modif, pos, prev_txt, new_txt): """Complete the text in the entry with values.""" try: sel = self.entry.selection_get() txt = prev_txt.replace(sel, '') except TclError: txt = prev_txt if action == "0": txt = txt[:int(pos)] + txt[int(pos) + 1:] return True else: txt = txt[:int(pos)] + modif + txt[int(pos):] l = [i for i in self._completevalues if i[:len(txt)] == txt] if l: i = self._completevalues.index(l[0]) self.listbox.selection_clear(0, "end") self.listbox.selection_set(i) self.listbox.see(i) index = self.entry.index("insert") self.entry.delete(0, "end") self.entry.insert(0, l[0].replace("\ ", " ")) self.entry.selection_range(index + 1, "end") self.entry.icursor(index + 1) return True else: return self._allow_other_values def __getitem__(self, key): return self.cget(key) def update_entry(self, event=None): """Update entry when an item is selected in the listbox.""" try: sel = self.listbox.get(self.listbox.curselection()[0]) except (TclError, IndexError): return self.entry.delete(0, "end") self.entry.insert(0, sel) self.entry.selection_clear() self.entry.icursor("end") self.event_generate('<<ItemSelect>>') def keys(self): keys = Combobox.keys(self) keys.append('allow_other_values') return keys def get(self): return self.entry.get() def cget(self, key): if key == 'allow_other_values': return self._allow_other_values elif key == 'completevalues': return self._completevalues else: return self.cget(self, key) def config(self, dic={}, **kwargs): self.configure(dic={}, **kwargs) def configure(self, dic={}, **kwargs): dic2 = {} dic2.update(dic) dic2.update(kwargs) self._allow_other_values = dic2.pop('allow_other_values', self._allow_other_values) self._completevalues = dic2.pop('completevalues', self._completevalues) self.config(self, dic2)
class FontChooser(tkfontchooser.FontChooser): def __init__(self, master, font_dict=None, text='AaBbYyZz', title='Font', **kwargs): Toplevel.__init__(self, master, **kwargs) self.title(title) try: self.wm_iconbitmap('transparent.ico') except TclError: pass self.resizable(False, False) self.protocol('WM_DELETE_WINDOW', self.quit) self._validate_family = self.register(self.validate_font_family) self._validate_size = self.register(self.validate_font_size) # --- variable storing the chosen font self.res = '' style = Style(self) style.configure('prev.TLabel') bg = style.lookup('TLabel', 'background') self.configure(bg=bg) # --- family list self.fonts = list(set(families())) self.fonts.append('TkDefaultFont') self.fonts.sort() for i in range(len(self.fonts)): self.fonts[i] = self.fonts[i].replace(' ', '\\ ') max_length = int(2.5 * max([len(font) for font in self.fonts])) // 3 - 2 self.sizes = ['%i' % i for i in (list(range(6, 17)) + list(range(18, 32, 2)) + list(range(36, 48, 4)))] # --- font default font_dict['weight'] = font_dict.get('weight', 'normal') font_dict['slant'] = font_dict.get('slant', 'roman') font_dict['underline'] = font_dict.get('underline', False) font_dict['overstrike'] = font_dict.get('overstrike', False) font_dict['family'] = font_dict.get('family', self.fonts[0].replace('\\ ', ' ')) font_dict['size'] = font_dict.get('size', 10) # --- format list self.formats = ['Regular', 'Italic', 'Bold', 'Bold Italic'] # --- creation of the widgets self.font_family = StringVar(self, ' '.join(self.fonts)) self.font_size = StringVar(self, ' '.join(self.sizes)) self.format_type = StringVar(self, self.formats) self.var_bold = BooleanVar(self, font_dict['weight'] == 'bold') self.var_italic = BooleanVar(self, font_dict['slant'] == 'italic') # ------ Size and family self.var_size = StringVar(self) self.entry_family = Entry(self, width=max_length, validate='key', validatecommand=(self._validate_family, '%d', '%S', '%i', '%s', '%V')) self.entry_size = Entry(self, width=8, validate='key', textvariable=self.var_size, validatecommand=(self._validate_size, '%d', '%P', '%V')) self.entry_format = Entry(self) self.list_family = Listbox(self, selectmode='browse', listvariable=self.font_family, highlightthickness=0, exportselection=False, width=max_length, height=6) self.list_size = Listbox(self, selectmode='browse', listvariable=self.font_size, highlightthickness=0, exportselection=False, width=6, height=6) self.list_format = Listbox(self, selectmode='browse', listvariable=self.format_type, highlightthickness=0, exportselection=False, width=12, height=6) self.scroll_family = Scrollbar(self, orient='vertical', command=self.list_family.yview) self.scroll_size = Scrollbar(self, orient='vertical', command=self.list_size.yview) self.scroll_format = Scrollbar(self, orient='vertical', command=self.list_format.yview) self.family_label = Label(self, text='Font:') self.style_label = Label(self, text='Font style:') self.size_label = Label(self, text='Size:') self.script_label = Label(self, text='Script:') self.script_box = Combobox(self, values=['Western']) self.more_fonts_label = Lbl(self, text='Show more fonts', underline=1, fg='blue', cursor='hand2') f = Font(self.more_fonts_label, self.more_fonts_label.cget("font")) f.configure(underline=True) self.more_fonts_label.configure(font=f) self.preview_font = Font(self, **font_dict) if len(text) > 30: text = text[:30] self.preview_window = LabelFrame(self, relief='groove', text='Sample', bd=1) # --- widget configuration self.list_family.configure(yscrollcommand=self.scroll_family.set) self.list_size.configure(yscrollcommand=self.scroll_size.set) self.list_format.configure(yscrollcommand=self.scroll_format.set) self.entry_family.insert(0, font_dict['family']) self.entry_family.selection_clear() self.entry_family.icursor('end') self.entry_format.insert(0, self.formats[1]) self.entry_format.selection_clear() self.entry_format.icursor('end') self.entry_size.insert(0, font_dict['size']) try: i = self.fonts.index(self.entry_family.get().replace(' ', '\\ ')) except ValueError: # unknown font i = 0 self.list_family.selection_clear(0, 'end') self.list_family.selection_set(i) self.list_family.see(i) try: i = self.sizes.index(self.entry_size.get()) self.list_size.selection_clear(0, 'end') self.list_size.selection_set(i) self.list_size.see(i) except ValueError: # size not in listtsg pass # font family location config self.family_label.grid(row=0, column=0, sticky='nsew', pady=(10, 1), padx=(10, 1)) self.entry_family.grid(row=1, column=0, sticky='nsew', pady=(1, 1), padx=(10, 0), columnspan=2) self.list_family.grid(row=2, column=0, sticky='nsew', pady=(1, 10), padx=(10, 0)) self.scroll_family.grid(row=2, column=1, sticky='ns', pady=(1, 10)) # font style/format location config self.style_label.grid(row=0, column=2, sticky='nsew', pady=(10, 1), padx=(15, 1)) self.entry_format.grid(row=1, column=2, sticky='nsew', pady=(1, 1), padx=(15, 0), columnspan=2) self.list_format.grid(row=2, column=2, sticky='nsew', pady=(1, 10), padx=(15, 0)) self.scroll_format.grid(row=2, column=3, sticky='ns', pady=(1, 10)) # font size location config self.size_label.grid(row=0, column=4, sticky='nsew', pady=(10, 1), padx=(15, 1)) self.entry_size.grid(row=1, column=4, sticky='nsew', pady=(1, 1), padx=(15, 10), columnspan=2) self.list_size.grid(row=2, column=4, sticky='nsew', pady=(1, 10), padx=(15, 0)) self.scroll_size.grid(row=2, column=5, sticky='nsew', pady=(1, 10), padx=(0, 10)) # font preview location config self.preview_window.grid(row=4, column=2, columnspan=4, sticky='nsew', rowspan=2, padx=15, pady=(0, 10), ipadx=10, ipady=10) self.preview_window.config(height=75) preview = Label(self.preview_window, text=text, font=self.preview_font, anchor='center') preview.place(relx=0.5, rely=0.5, anchor='center') self.script_label.grid(row=6, column=2, sticky='nsw', padx=(15, 0)) self.script_box.grid(row=7, column=2, sticky='nsw', pady=(1, 30), padx=(15, 0)) self.script_box.current(0) self.more_fonts_label.grid(row=8, column=0, pady=(35, 20), padx=(15, 0), sticky='nsw') button_frame = Frame(self) button_frame.grid(row=9, column=2, columnspan=4, pady=(0, 10), padx=(10, 0)) Button(button_frame, text='Ok', command=self.ok).grid(row=0, column=0, padx=4, sticky='ew') Button(button_frame, text='Cancel', command=self.quit).grid(row=0, column=1, padx=4, sticky='ew') self.list_family.bind('<<ListboxSelect>>', self.update_entry_family) self.list_format.bind('<<ListboxSelect>>', self.update_entry_format) self.list_size.bind('<<ListboxSelect>>', self.update_entry_size, add=True) self.list_family.bind('<KeyPress>', self.keypress) self.entry_family.bind('<Return>', self.change_font_family) self.entry_family.bind('<Tab>', self.tab) self.entry_size.bind('<Return>', self.change_font_size) self.more_fonts_label.bind('<Button-1>', search_fonts) self.entry_family.bind('<Down>', self.down_family) self.entry_size.bind('<Down>', self.down_size) self.entry_family.bind('<Up>', self.up_family) self.entry_size.bind('<Up>', self.up_size) self.bind_class('TEntry', '<Control-a>', self.select_all) self.wait_visibility(self) self.grab_set() self.entry_family.focus_set() self.lift() def update_entry_format(self, _): style = self.list_format.get(self.list_format.curselection()[0]) self.entry_format.delete(0, 'end') self.entry_format.insert(0, style) self.entry_format.selection_clear() self.entry_format.icursor('end') if style == self.formats[0]: self.var_italic = FALSE self.var_bold = FALSE elif style == self.formats[1]: self.var_italic = TRUE self.var_bold = FALSE elif style == self.formats[2]: self.var_italic = FALSE self.var_bold = TRUE elif style == self.formats[3]: self.var_italic = TRUE self.var_bold = TRUE else: log.error('invalid style') self.preview_font.configure(weight=['normal', 'bold'][self.var_bold], slant=['roman', 'italic'][self.var_italic])
class GUI(Frame): def __init__(self, resources, master=None): Frame.__init__(self, master) self.resources = resources # Tkinter objects self.model_initial_names = None self.model_final_names = None self.widget_initial_names = None self.widget_final_names = None self.canvas_view = None self.model_height = None self.model_width = None self.vcmd = None self.selected_format = None self.validate = None # Initialize tkinter self.grid() self.create_objects() self.last_directory = "" # Lists for image name handling self.image_files = list() self.image_names = list() self.new_image_names = list() self.directory = "" # Thumbnails self.last_view = "" # Rules self.rule = None self.user_rule = "" def create_objects(self): top = self.winfo_toplevel() # Menu menu_top = Menu(top) top["menu"] = menu_top menu_top.add_command(label="About...") # Image line form_general = Frame(self, padx=10, pady=10) form_general.grid(row=0, column=0) # Original names form_initial_data = LabelFrame(form_general, text="Images", padx=10, pady=5) form_initial_data.grid(column=0, row=0, rowspan=2, sticky=N + S) initial_scroll_y = Scrollbar(form_initial_data, orient=VERTICAL) initial_scroll_y.grid(column=2, row=0, rowspan=5, sticky=N + S) self.model_initial_names = StringVar(self) self.widget_initial_names = Listbox( form_initial_data, height=27, width=35, activestyle="dotbox", listvariable=self.model_initial_names, selectmode=EXTENDED, yscrollcommand=initial_scroll_y.set) self.widget_initial_names.grid(column=0, row=0, rowspan=5, columnspan=2, sticky=N + S + W + E) initial_scroll_y["command"] = self.widget_initial_names.yview self.widget_initial_names.bind("<Double-Button-1>", self.select_name) self.widget_initial_names.bind("<<ListboxSelect>>", self.preview) button_up = Button(form_initial_data, image=self.resources["up"], command=self.name_up) button_up.grid(column=3, row=1, sticky=N + S) button_down = Button(form_initial_data, image=self.resources["down"], command=self.name_down) button_down.grid(column=3, row=3, sticky=N + S) button_add = Button(form_initial_data, image=self.resources["add"], command=self.add_file_names) button_add.grid(column=0, row=5, sticky=W + E) button_delete = Button(form_initial_data, image=self.resources["delete"], command=self.remove_file_names) button_delete.grid(column=1, row=5, sticky=W + E) # Preview form_preview = LabelFrame(form_general, text="Preview", padx=10, pady=5) form_preview.grid(column=1, row=0, columnspan=2, sticky=N + S + W + E, padx=10) form_preliminary = Frame(form_preview) form_preliminary.grid(column=1, row=0, sticky=N + S + W + E) self.canvas_view = Canvas(form_preliminary, width=256, height=256, bg="white", cursor="crosshair") self.canvas_view.grid(sticky=N + S + W + E) # Final names form_final_names = Frame(form_preview) form_final_names.grid(column=2, row=0, sticky=N + S + W + E) final_scroll_y = Scrollbar(form_final_names) final_scroll_y.grid(column=1, row=0, sticky=N + S) self.model_final_names = StringVar() self.widget_final_names = Listbox(form_final_names, height=18, width=35, activestyle="dotbox", listvariable=self.model_final_names, selectmode=SINGLE, yscrollcommand=final_scroll_y.set) self.widget_final_names.grid(column=0, row=0, sticky=N + W + E) final_scroll_y["command"] = self.widget_final_names.yview self.widget_final_names.bind("<Double-Button-1>", self.select_name) self.widget_final_names.bind("<<ListboxSelect>>", self.preview) # Options line form_options = Frame(form_general) form_options.grid(row=1, column=1, columnspan=2, sticky=E + W, padx=10) # ..dimensions self.model_width = StringVar() self.model_height = StringVar() form_dimensions = LabelFrame(form_options, text="Dimensions (0 = no change)", padx=10, pady=10) form_dimensions.grid(row=0, column=1, sticky=N + S + W + E, padx=5) self.vcmd = (form_dimensions.register(self.event_on_validate), "%S", "%P") label_width = Label(form_dimensions, text="Width") label_width.grid(column=0, row=1, sticky=W) entry_width = Entry(form_dimensions, validate="key", validatecommand=self.vcmd, textvariable=self.model_width, justify=RIGHT) entry_width.grid(column=1, row=1, sticky=E + W) entry_width.insert(END, "0") label_height = Label(form_dimensions, text="Height") label_height.grid(column=0, row=2, sticky=W) entry_height = Entry(form_dimensions, validate="key", validatecommand=self.vcmd, textvariable=self.model_height, justify=RIGHT) entry_height.grid(column=1, row=2, sticky=E + W) entry_height.insert(END, "0") # .. formats form_formats = LabelFrame(form_options, text="Formats", padx=10, pady=10) form_formats.grid(row=0, column=0, rowspan=2, sticky=N + S + E + W) formats = ["No change", "JPG", "GIF", "BMP", "PNG", "TIFF"] self.selected_format = StringVar(value="No change") for n in range(len(formats)): radio_format = Radiobutton(form_formats, text=formats[n], value=formats[n], variable=self.selected_format) radio_format.grid(row=n + 1, column=0, sticky=W) # .. name change self.validate = (form_dimensions.register(self.event_validate_rule), "%P") form_names = LabelFrame(form_options, text="Names", padx=10, pady=10) form_names.grid(row=1, column=1, sticky=N + S + E + W, padx=5) self.selected_name = IntVar(value=0) radio_nochange = Radiobutton(form_names, text="No change", value=0, variable=self.selected_name, command=self.rule_change) radio_nochange.grid(row=1, column=0, columnspan=2, sticky=W) radio_name_only = Radiobutton(form_names, text="Name + N", value=1, variable=self.selected_name, command=self.rule_change) radio_name_only.grid(row=2, column=0, sticky=W) self.entry_name_only = Entry(form_names, width=10, validate="key", validatecommand=self.validate) self.entry_name_only.grid(row=2, column=1, sticky=W + E) radio_rule = Radiobutton(form_names, text="Rule", value=2, variable=self.selected_name, command=self.rule_change) radio_rule.grid(row=3, column=0, sticky=W) self.button_change = Button(form_names, text="Change", command=self.custom_rule) self.button_change.grid(row=3, column=1, sticky=W + E) self.entry_name_only["state"] = DISABLED self.button_change["state"] = DISABLED # ..directory form_directory = LabelFrame(form_options, text="Destination", padx=10, pady=10) form_directory.grid(row=0, column=2, sticky=N + S + W) button_directory = Button(form_directory, image=self.resources["dir"], command=self.choose_directory) button_directory.grid(row=0, column=0, sticky=N + S + E + W) self.label_selected_directory = Label(form_directory, text="<Directory not chosen>", width=25, anchor=W) self.label_selected_directory.grid(row=0, column=1, stick=W + E) # .. convert self.button_convert = Button(form_options, image=self.resources["convert_d"], state=DISABLED, height=91, command=self.convert_images) self.button_convert.grid(row=1, column=2, sticky=E + W + S) #### EVENTS ############################################ def add_file_names(self): new_names = get_image_file_names(self, self.last_directory) if len(new_names) > 0: names = get_names_in_path(new_names) self.image_files.extend(new_names) self.image_names.extend(names) self.change_names() self.refresh_names() self.last_directory = os.path.split(new_names[0])[0] def remove_file_names(self): indices = self.widget_initial_names.curselection() while len(indices) > 0: ind = indices[0] self.widget_initial_names.delete(ind) self.image_files.pop(int(ind)) self.image_names.pop(int(ind)) indices = self.widget_initial_names.curselection() if self.last_view not in self.image_files: self.canvas_view.delete("Image") self.change_names() self.refresh_names(True) def name_up(self): indices = list(self.widget_initial_names.curselection()) for n in range(len(indices)): indices[n] = int(indices[n]) indices.sort() for n in range(len(indices)): idx = indices[n] if idx != 0 and idx - 1 not in indices: x = self.image_files.pop(idx) self.image_files.insert(idx - 1, x) indices[n] -= 1 self.image_names = get_names_in_path(self.image_files) self.change_names() self.refresh_names() for n in indices: self.widget_initial_names.selection_set(n) def name_down(self): indices = list(self.widget_initial_names.curselection()) for n in range(len(indices)): indices[n] = int(indices[n]) indices.sort() indices = indices[::-1] total = len(self.image_files) for n in range(len(indices)): indices[n] = int(indices[n]) idx = indices[n] if idx != total - 1 and idx + 1 not in indices: x = self.image_files.pop(idx) self.image_files.insert(idx + 1, x) indices[n] += 1 self.image_names = get_names_in_path(self.image_files) self.change_names() self.refresh_names() for n in indices: self.widget_initial_names.selection_set(n) def change_names(self): # Apply name rule self.new_image_names = self.image_names[:] for n in range(len(self.new_image_names)): self.new_image_names[n] = os.path.splitext( self.new_image_names[n])[0] if self.rule is not None: self.new_image_names = change_names_with_rule( self.new_image_names, self.rule)[0] return True def select_name(self, e): indices = e.widget.curselection() if len(indices) > 0: ind = int(indices[-1]) if e.widget == self.widget_initial_names: self.widget_final_names.selection_clear(0, END) self.widget_final_names.selection_set(ind) self.widget_final_names.see(ind) else: self.widget_initial_names.selection_clear(0, END) self.widget_initial_names.selection_set(ind) self.widget_initial_names.see(ind) def refresh_names(self, only_last=False): if not only_last: self.model_initial_names.set('') for n in self.image_names: self.widget_initial_names.insert(END, n) self.model_final_names.set('') for n in self.new_image_names: self.widget_final_names.insert(END, n) def preview(self, e): indices = e.widget.curselection() if len(indices) > 0: idx = int(indices[-1]) if self.last_view == self.image_files[idx]: return else: self.last_view = self.image_files[idx] try: current_image = Image.open(self.image_files[idx]) current_image.thumbnail((256, 256)) self.photo = ImageTk.PhotoImage(current_image) self.canvas_view.delete("Image") self.canvas_view.create_image(128, 128, image=self.photo) self.canvas_view.addtag_all("Image") except: showerror( "Error in preview", "It was not possible to preview image: " + self.image_files[idx]) def rule_change(self): n = self.selected_name.get() if n == 0: self.entry_name_only["state"] = DISABLED self.button_change["state"] = DISABLED self.rule = None elif n == 1: self.entry_name_only["state"] = NORMAL self.button_change["state"] = DISABLED self.rule = make_naming_rule(self.entry_name_only.get()) elif n == 2: self.entry_name_only["state"] = DISABLED self.button_change["state"] = NORMAL self.custom_rule() self.change_names() self.refresh_names(True) def custom_rule(self): input_rule = AskRule(self, self.user_rule) if input_rule.result is not None: self.user_rule = input_rule.result rule = compile_rule(self.user_rule) if rule is None: showerror("Error - Rule", "Compilation error") else: self.rule = rule self.change_names() self.refresh_names(True) def event_validate_rule(self, text): self.rule = make_naming_rule(text) self.change_names() self.refresh_names(True) return True def choose_directory(self): directory = get_save_directory() if directory != "": self.directory = directory if len(directory) > 25: directory = directory[0:5] + "..." + directory[-16:] self.label_selected_directory["text"] = directory if self.directory != "": self.button_convert["state"] = NORMAL self.button_convert["image"] = self.resources["convert"] else: self.button_convert["state"] = DISABLED self.button_convert["image"] = self.resources["convert_d"] def convert_images(self): if len(self.image_files) == 0: showinfo("No images", "No images were selected.") return selected_format = self.selected_format.get() width = self.model_width.get() if width == "": width = 0 else: width = int(width) height = self.model_height.get() if height == "": height = 0 else: height = int(height) current_directory = self.directory for n in range(len(self.image_files)): path = self.image_files[n] name = self.new_image_names[n] name = os.path.splitext(name)[0] current_image = Images(path) if not current_image.is_valid(): showerror("Error while converting", "Unable to open image: " + path) else: if not (width == 0 and height == 0): current_image.change_size(width, height) if selected_format != "No change": current_image.change_format(selected_format) current_image.save(current_directory, name) def event_on_validate(self, modification, text): try: if len(text) > 5: return False n = int(modification) return True except ValueError: return False
class MyGui(ttk.Frame): controller = None tabs = None tabs_history = [] _log = None _screen_width = None _screen_height = None COLOR_buttons = '#FFCB6B' COLOR_frames = '#333333' COLOR_foreground = '#D9C7B3' COLOR_log = '#1E1E1E' def v_restoreSession(self): with open("saved_sessions/saved_tabs.pkl", "rb") as f: history = pickle.load(f) for i in history: self.open_file(i) def v_splashscreen(self, master, height, width): image_splash = "icons/logo-gif.gif" image = tk.PhotoImage(file=image_splash) canvas = tk.Canvas(master, height=height * 0.8, width=width) canvas.create_image(width * 0.8 / 2, height * 0.8 / 2, image=image) canvas.grid() master.after(5000, master.destroy) def __init__(self, master, controller): super().__init__() master.protocol("WM_DELETE_WINDOW", self.v_on_closing) self.controller = controller self.v_initUi(master) if os.path.isfile("saved_sessions/saved_tabs.pkl"): self.v_restoreSession() def v_on_closing(self): close = messagebox.askyesnocancel( title="Warning", message="Do you want to save the session?") if close == True: picklefile = open("saved_sessions/saved_tabs.pkl", "wb") pickle.dump(self.tabs_history, picklefile) picklefile.close() self.master.destroy() elif close == False: if os.path.isfile("saved_sessions/saved_tabs.pkl"): os.remove("saved_sessions/saved_tabs.pkl") self.master.destroy() def dos2unix(self, file_path): # replacement strings WINDOWS_LINE_ENDING = b'\r\n' UNIX_LINE_ENDING = b'\n' with open(file_path, 'rb') as open_file: content = open_file.read() content = content.replace(WINDOWS_LINE_ENDING, UNIX_LINE_ENDING) with open(file_path, 'wb') as open_file: open_file.write(content) def help_window(self): self.top = tk.Toplevel() self.top.title("Usage") label = tk.Label( self.top, text="Open: takes in input DOT files,but can also it can get:\n" + " - Chorgram file (.txt), a grammar used for Global Graph\n" + " - DOT files (.gv) generated by Domitilla and converte them into DOT files with CA sintax\n\n\n" + "Once taken one or more files as input, Corinne can apply some functions on it:\n\n" + " - Product: a cartesian product of two CA\n" + " - Synchronization: given a CA, it can synchronize two participants of its\n" + " - Projection: given a CA, you can select one participant from it and get the relative CFSM", justify=tk.LEFT, padx=15, pady=15).pack() def v_initUi(self, master): self.master.title("Corinne 2.0") self.master.grid_columnconfigure(0, weight=1) self.master.grid_rowconfigure(2, weight=1) self.master.option_add('*foreground', 'black') self.master.option_add('*background', 'white') # Style for ttk widgets style = ttk.Style() style.configure("TNotebook", background=self.COLOR_frames, borderwidth=1, highlightthickness=1) style.configure("TNotebook.Tab", background=self.COLOR_frames, foreground="black", lightcolor=self.COLOR_frames, borderwidth=0) style.map("TNotebook.Tab", background=[("selected", self.COLOR_buttons)], foreground=[("selected", 'black')]) style.configure("TFrame", background=self.COLOR_frames, foreground="black") # get screen resolution self._screen_width, self._screen_height = master.winfo_screenwidth( ), master.winfo_screenheight() start_x = int((self._screen_width / 4)) start_y = int((self._screen_height / 4)) # fit the guy at screen resolution master.geometry('%dx%d+%d+%d' % (self._screen_width / 2, self._screen_height / 2, start_x, start_y)) #self.v_splashscreen(master,start_x,start_y) #create all the containers menu_frame = Frame(master, padx=10, pady=10) menu_frame.grid(row=0, column=0, sticky=(tk.N, tk.S, tk.E, tk.W)) menu_frame.configure(bg=self.COLOR_frames) menu_frame.grid_columnconfigure(0, weight=1) menu_frame.grid_columnconfigure(1, weight=1) tab_frame = Frame(master, pady=15) tab_frame.grid(row=1, column=0, sticky=(tk.N, tk.S, tk.E, tk.W)) tab_frame.configure(bg=self.COLOR_frames) tab_frame.grid_columnconfigure(0, weight=1) tab_frame.grid_rowconfigure(0, weight=1) log_frame = Frame(master) log_frame.grid(row=2, column=0, sticky=(tk.N, tk.S, tk.E, tk.W)) log_frame.configure(bg=self.COLOR_frames) log_frame.grid_columnconfigure(0, weight=1) prova = Image.open("icons/open.png") open_icon = ImageTk.PhotoImage(prova) render_icon = PhotoImage(file="icons/render.png") prod_icon = PhotoImage(file="icons/product.png") sync_icon = PhotoImage(file="icons/sync.png") proj_icon = PhotoImage(file="icons/projection.png") menu_bar = Menu(master) file_menu = Menu(menu_bar, tearoff=False) file_menu.add_command(label="Open", compound=tk.LEFT, command=lambda: self.open_file(None)) file_menu.add_command(label="Open Saved Tabs", command=self.v_restoreSession) menu_bar.add_cascade(label="File", menu=file_menu) trasformation_menu = Menu(menu_bar, tearoff=False) trasformation_menu.add_command(label="Product", compound=tk.LEFT, command=self.open_product_view) trasformation_menu.add_command(label="Sync", compound=tk.LEFT, command=self.open_sync_view) trasformation_menu.add_command(label="Projection", compound=tk.LEFT, command=self.open_proj_view) menu_bar.add_cascade(label="Trasformations", menu=trasformation_menu) demonstration_menu = Menu(menu_bar, tearoff=False) demonstration_menu.add_command(label="Well-formedness", compound=tk.LEFT, command=self.open_well_formedness) demonstration_menu.add_separator() demonstration_menu.add_command(label="Well-branchedness", compound=tk.LEFT, command=self.open_well_branchedness) demonstration_menu.add_command(label="Well-sequencedness", compound=tk.LEFT, command=self.open_well_sequencedness) menu_bar.add_cascade(label="Properties", menu=demonstration_menu) help_menu = Menu(menu_bar, tearoff=False) help_menu.add_command(label="ReadMe", compound=tk.LEFT, command=self.help_window) menu_bar.add_cascade(label="Help", menu=help_menu) self.master.config(menu=menu_bar) # create the log box self._log = Listbox(log_frame, highlightthickness=0, height=1, background=self.COLOR_log, foreground=self.COLOR_foreground) self._log.pack(side="bottom", fill="x", padx=5, pady=5) self.tabs = ttk.Notebook(tab_frame) def open_file(self, path): if path == None: path = filedialog.askopenfilename(initialdir=".", filetypes=(("DOT graph", "*.gv *.dot"), ("Chorgram grammar", "*.txt"), ("all files", "*.*")), title="Choose a file.") self.dos2unix(path) msg_result = [] # Check in case user enter an unknown file # or closes without choosing a file. try: path_splitted = os.path.split(path) ext = path_splitted[1].split('.') # Chorgram file if ext[1] == 'txt': msg_result = self.__open_chorgram_file(path) # DOT files elif ext[1] == 'dot' or ext[1] == 'gv': msg_result = self.__open_dot_file__(path) else: self.popupmsg("Unknown extension file") # update log box self.log(msg_result) except: pass def __open_chorgram_file(self, path): path_splitted = os.path.split(path) # ask where store the converted dot file ask_for_path: bool = messagebox.askyesno( "Chorgram", "A Chorgram file was inserted\n" + "Do you wish to save the converted dot file in " + path_splitted[0] + "?\n" + "(Click NO to choose a new path)") if ask_for_path: # Yes, use same path # (input path, path to store) msg_result, graph_name = self.controller.GGparser( path, path_splitted[0]) else: new_folder = filedialog.askdirectory() msg_result, graph_name = self.controller.GGparser(path, new_folder) self.__add_new_tab__(graph_name, path) return msg_result def __open_dot_file__(self, path): # result[0] domitilla boolean # result[1] a message # result[2] graph name result = self.controller.DOTparser(path) msg_result = result[1] if result[0]: # if a domitilla graph was founded # ask where store the converted dot file path_splitted = os.path.split(path) ask_for_path: bool = messagebox.askyesno( "Domitilla", "A Domitilla file was inserted\n" "Do you wish to store the converted file in " + path_splitted[0] + "?\n" "(Click NO to choose a new path)") if ask_for_path: # Yes, use same path msg_result.append( self.controller.DomitillaConverter(result[2], path, path_splitted[0]) ) # (graph name, input path, path to store) else: new_folder = filedialog.askdirectory() msg_result.append( self.controller.DomitillaConverter(result[2], path, new_folder)) if len(result) > 2: # case NO-errors detected # add a new tab for the new graph just opened self.__add_new_tab__(result[2], path) return msg_result def open_render_view(self): try: path = filedialog.askopenfilename(initialdir=".", filetypes=(("DOT graph", "*.gv *.dot"), ("all files", "*.*")), title="Choose a file.") path_splitted = os.path.split(path) ext = path_splitted[1].split('.') # Check in case user enter an unknown file # or closes without choosing a file. if ext[1] != 'dot' and ext[1] != 'gv': self.popupmsg("Wrong extension file inserted!\n" "Please insert a DOT file") else: # define the frame and its geometry r_window = tk.Toplevel(padx=20, pady=20, bg=self.COLOR_frames) r_window.wm_title("Render") r_window.resizable(False, False) self.__set_window_dimension__(r_window) label_format = tk.Label( r_window, text="Choose a file format for render:", fg=self.COLOR_foreground, bg=self.COLOR_frames, wraplength=500) label_format.grid(row=0, column=0) # Initialize file format variable for radiobutton option = tk.StringVar() # Radiobutton rb1 = Radiobutton(r_window, text='png', value="png", var=option, bg=self.COLOR_frames) rb2 = Radiobutton(r_window, text='pdf', value="pdf", var=option, bg=self.COLOR_frames) rb1.grid(row=1, column=0) rb2.grid(row=1, column=1) # TODO try except for wrong dot files b = Button(r_window, text='Render', bg=self.COLOR_buttons, command=lambda: (self.log([ "[RENDER] " + self.controller.render( path, option.get(), True) ]), r_window.destroy())) b.grid(row=2, column=1) except: pass def open_product_view(self): # define the frame and its geometry p_window = tk.Toplevel(padx=20, pady=20, bg=self.COLOR_frames) p_window.wm_title("Product") p_window.resizable(False, False) # set window dimension self.__set_window_dimension__(p_window) # label and combo for 1st graph lbl1 = tk.Label(p_window, text="Choose 1st Graph", bg=self.COLOR_frames, fg=self.COLOR_foreground) lbl1.grid(row=0, column=0, pady=10) combo1 = ttk.Combobox(p_window, values=list(self.controller.get_all_ca().keys())) combo1.grid(row=0, column=1, pady=10) # label and combo for 2st graph lbl2 = tk.Label(p_window, text="Choose 2st Graph", bg=self.COLOR_frames, fg='white') lbl2.grid(row=1, column=0, pady=10) combo2 = ttk.Combobox(p_window, values=list(self.controller.get_all_ca().keys())) combo2.grid(row=1, column=1, pady=10) make_button = Button( p_window, text='Make product', bg=self.COLOR_buttons, command=lambda: (self.__exec_product_button__( combo1.get(), combo2.get()), p_window.destroy())) make_button.grid(row=2, column=0, pady=10) def open_sync_view(self): s_window = tk.Toplevel(padx=20, pady=20, bg=self.COLOR_frames) s_window.wm_title("Synchronisation") s_window.resizable(False, False) # set window dimension self.__set_window_dimension__(s_window) # label and combo for the graph to synchronize lbl1 = tk.Label(s_window, text="Choose Graph", fg='white', bg=self.COLOR_frames) lbl1.grid(row=0, column=0, padx=10, pady=10) option_v1 = tk.StringVar() option_v2 = tk.StringVar() combo = ttk.Combobox(s_window, values=list(self.controller.get_all_ca().keys())) combo.bind( "<<ComboboxSelected>>", lambda event: self.__make_sync_interface_menu__( s_window, list(self.controller.get_participants(combo.get())), option_v1, option_v2)) combo.grid(row=1, column=0, padx=10, pady=10) sync_button = Button( s_window, text='Synchronize', bg=self.COLOR_buttons, command=lambda: (self.__exec_sync_button__(combo.get( ), option_v1.get(), option_v2.get()), s_window.destroy())) sync_button.grid(row=4, column=0) def open_proj_view(self): proj_window = tk.Toplevel(padx=20, pady=20, bg=self.COLOR_frames) proj_window.wm_title("Projection") proj_window.resizable(False, False) # set window dimension self.__set_window_dimension__(proj_window) # label and combo for the graph to synchronize lbl1 = tk.Label(proj_window, text="Choose Graph", bg=self.COLOR_frames, fg='white') lbl1.grid(row=0, column=0, padx=10, pady=10) option = tk.StringVar() combo = ttk.Combobox(proj_window, values=list(self.controller.get_all_ca().keys())) combo.bind( "<<ComboboxSelected>>", lambda event: self.__make_proj_participant_menu__( proj_window, list(self.controller.get_participants(combo.get()) ), option)) combo.grid(row=1, column=0, padx=10, pady=10) proj_button = Button( proj_window, text='Project', bg=self.COLOR_buttons, command=lambda: (self.__exec_proj_button__(combo.get(), option.get( )), proj_window.destroy())) proj_button.grid(row=4, column=0) def open_well_formedness(self): p_window = tk.Toplevel(padx=20, pady=20, bg=self.COLOR_frames) p_window.wm_title("Well-formedness") p_window.resizable(False, False) # set window dimension self.__set_window_dimension__(p_window) # label and combo for 1st graph lbl1 = tk.Label(p_window, text="Choose Graph", bg=self.COLOR_frames, fg=self.COLOR_foreground) lbl1.grid(row=0, column=0, pady=10) combo1 = ttk.Combobox(p_window, values=list(self.controller.get_all_ca().keys())) combo1.grid(row=0, column=1, pady=10) verify_button = Button( p_window, text='Verify', bg=self.COLOR_buttons, command=lambda: (self.__exec_well_formedness__(combo1.get()), p_window.destroy())) verify_button.grid(row=2, column=0, pady=10) def open_well_branchedness(self): p_window = tk.Toplevel(padx=20, pady=20, bg=self.COLOR_frames) p_window.wm_title("Well-branchedness") p_window.resizable(False, False) # set window dimension self.__set_window_dimension__(p_window) # label and combo for 1st graph lbl1 = tk.Label(p_window, text="Choose Graph", bg=self.COLOR_frames, fg=self.COLOR_foreground) lbl1.grid(row=0, column=0, pady=10) combo1 = ttk.Combobox(p_window, values=list(self.controller.get_all_ca().keys())) combo1.grid(row=0, column=1, pady=10) verify_button = Button(p_window, text='Verify', bg=self.COLOR_buttons, command=lambda: (self.__exec_well_branchedness__(combo1.get()), p_window.destroy())) verify_button.grid(row=2, column=0, pady=10) def open_well_sequencedness(self): p_window = tk.Toplevel(padx=20, pady=20, bg=self.COLOR_frames) p_window.wm_title("Well-sequencedness") p_window.resizable(False, False) # set window dimension self.__set_window_dimension__(p_window) # label and combo for 1st graph lbl1 = tk.Label(p_window, text="Choose Graph", bg=self.COLOR_frames, fg=self.COLOR_foreground) lbl1.grid(row=0, column=0, pady=10) combo1 = ttk.Combobox(p_window, values=list(self.controller.get_all_ca().keys())) combo1.grid(row=0, column=1, pady=10) verify_button = Button(p_window, text='Verify', bg=self.COLOR_buttons, command=lambda: (self.__exec_well_sequencedness__(combo1.get()), p_window.destroy())) verify_button.grid(row=2, column=0, pady=10) def __add_new_tab__(self, graph_name, v_path): self.tabs.grid(row=0, column=0, sticky=(tk.N, tk.S, tk.E, tk.W), padx=10, pady=5) frame = ttk.Frame(self.tabs) frame.grid_columnconfigure(4, weight=1) # Add the tab self.tabs.add(frame, text=graph_name) label_s = tk.Label(frame, text="N° States:", wraplength=500, bg=self.COLOR_frames, fg=self.COLOR_foreground) label_s.grid(row=0, column=0, pady=10, padx=20) entry_s = tk.Label(frame, text=(str( len(self.controller.get_states(graph_name)))), wraplength=500, bg=self.COLOR_frames, fg=self.COLOR_foreground) entry_s.grid(row=0, column=1, pady=10, padx=20) label_e = tk.Label(frame, text="N° Edges:", wraplength=500, bg=self.COLOR_frames, fg=self.COLOR_foreground) label_e.grid(row=1, column=0, pady=10, padx=20) entry_e = tk.Label(frame, text=str(len( self.controller.get_edges(graph_name))), wraplength=500, bg=self.COLOR_frames, fg=self.COLOR_foreground) entry_e.grid(row=1, column=1, pady=10, padx=20) label_sn = tk.Label(frame, text="Start Node:", wraplength=500, bg=self.COLOR_frames, fg=self.COLOR_foreground) label_sn.grid(row=2, column=0, pady=10, padx=20) entry_sn = tk.Label(frame, text=str( self.controller.get_start_node(graph_name)), wraplength=500, bg=self.COLOR_frames, fg=self.COLOR_foreground) entry_sn.grid(row=2, column=1, pady=10, padx=20) label_eps = tk.Label(frame, text="Epsilon moves:", wraplength=500, bg=self.COLOR_frames, fg=self.COLOR_foreground) label_eps.grid(row=5, column=0, pady=10, padx=20) entry_eps = tk.Label( frame, text=self.controller.check_for_epsilon_moves(graph_name), wraplength=500, bg=self.COLOR_frames, fg=self.COLOR_foreground) entry_eps.grid(row=5, column=1, pady=10, padx=20) label_l = tk.Label(frame, text="N° Labels:", wraplength=500, bg=self.COLOR_frames, fg=self.COLOR_foreground) label_l.grid(row=3, column=0, pady=10, padx=20) elements_l = list(self.controller.get_labels(graph_name)) option_l = tk.StringVar() if not elements_l: print("lista vuota") else: option_l.set(elements_l[0]) label_menu = ttk.OptionMenu(frame, option_l, elements_l[0], *elements_l) label_menu.grid(row=3, column=2, pady=10, padx=20) entry_l = tk.Label(frame, text=len(elements_l), wraplength=500, bg=self.COLOR_frames, fg=self.COLOR_foreground) entry_l.grid(row=3, column=1, pady=10, padx=20) label_p = tk.Label(frame, text="N° Participants:", wraplength=500, bg=self.COLOR_frames, fg=self.COLOR_foreground) label_p.grid(row=4, column=0, pady=10, padx=20) elements_p = list(self.controller.get_participants(graph_name)) option_p = tk.StringVar() if not elements_p: print("seconda lista vuota") else: option_p.set(elements_p[0]) part_menu = ttk.OptionMenu(frame, option_p, elements_p[0], *elements_p) part_menu.grid(row=4, column=2, pady=10, padx=20) entry_p = tk.Label(frame, text=len(elements_p), wraplength=500, bg=self.COLOR_frames, fg=self.COLOR_foreground) entry_p.grid(row=4, column=1, pady=10, padx=20) self.controller.render(v_path, 'png', False) new_path = v_path + ".png" image = Image.open(new_path) image = image.resize((150, 200), Image.ANTIALIAS) img = ImageTk.PhotoImage(image) labelprova = tk.Label(frame, image=img) labelprova.photo = img labelprova.grid(row=0, column=3, rowspan=5, padx=(150, 0), pady=10) # create close button close_button = Button( frame, text='X', bg=self.COLOR_frames, highlightthickness=0, borderwidth=0, command=lambda: ( self.controller.remove_record( self.tabs.tab(self.tabs.select(), "text")), # remove the record from opened graphs struct self.tabs.forget(self.tabs.select()), self.tabs_history.remove(v_path))) # delete the tab close_button.grid(row=0, column=4, sticky=tk.E + tk.N) #update tab in tab history self.tabs_history.append(v_path) # once created, select the tab self.tabs.select(frame) def __exec_sync_button__(self, combo_value, interface1, interface2): path_to_store = filedialog.asksaveasfilename(initialdir=".", title="Save as", filetypes=("DOT graph", "*.gv *.dot")) result = self.controller.synchronize(combo_value, interface1, interface2, path_to_store) # print the log message self.log(result[0]) # create a new tab for the product graph self.open_file(path_to_store + ".gv") def __exec_product_button__(self, combo_value1, combo_value2): path_to_store = filedialog.asksaveasfilename(initialdir=".", title="Save as", filetypes=("DOT graph", "*.gv *.dot")) result = self.controller.make_product(combo_value1, combo_value2, path_to_store) # print the log message self.log(result[0]) # create a new tab for the product graph self.open_file(path_to_store + ".gv") def __exec_proj_button__(self, combo_value, participant): path_to_store = filedialog.asksaveasfilename(initialdir=".", title="Save as", filetypes=("DOT graph", "*.gv *.dot")) result = self.controller.projection(combo_value, participant, path_to_store) self.open_file(path_to_store + ".gv") # print the log message self.log(result[0]) def __make_sync_interface_menu__(self, frame, elements, option_v1, option_v2): # label and optionMenu for the 1st interface option_v1.set(elements[0]) lbl2 = tk.Label(frame, text="Select 1st participant", bg=self.COLOR_frames, fg=self.COLOR_foreground) lbl2.grid(row=2, column=0, padx=10, pady=10) op_menu_1 = ttk.OptionMenu(frame, option_v1, elements[0], *elements) op_menu_1.grid(row=2, column=1, padx=10, pady=10) # label and optionMenu for the 2st interface option_v2.set(elements[0]) lbl3 = tk.Label(frame, text='Select 2st participant', bg=self.COLOR_frames, fg=self.COLOR_foreground) lbl3.grid(row=3, column=0, pady=10) op_menu_2 = ttk.OptionMenu(frame, option_v2, elements[0], *elements) op_menu_2.grid(row=3, column=1, padx=10, pady=10) # update window dimension self.__set_window_dimension__(frame) def __make_proj_participant_menu__(self, frame, elements, option): option.set(elements[0]) lbl = tk.Label(frame, text='Select participant to project', bg=self.COLOR_frames, fg=self.COLOR_foreground) lbl.grid(row=2, column=0, padx=10, pady=10) op_menu = ttk.OptionMenu(frame, option, elements[0], *elements) op_menu.grid(row=2, column=1, padx=10, pady=10) self.__set_window_dimension__(frame) def __exec_well_formedness__(self, combo_value): result = self.controller.make_well_formedness(combo_value) self.log(result[0]) return def __exec_well_branchedness__(self, combo_value): result = self.controller.make_well_branchedness(combo_value) self.log(result[0]) return def __exec_well_sequencedness__(self, combo_value): result = self.controller.make_well_sequencedness(combo_value) self.log(result[0]) return def __set_window_dimension__(self, frame): # set window dimension width, height = frame.winfo_reqwidth(), frame.winfo_reqheight() frame.geometry('+%d+%d' % (self._screen_width / 2 - width / 2, self._screen_height / 2 - height / 2)) def log(self, msg): print(msg) # Write a message in the log box for line in msg: self._log.insert(tk.END, line) self._log.see(tk.END) # make the last item background red #self._log.itemconfig(tk.END, {'bg': 'red'}) def popupmsg(self, msg): popup = tk.Toplevel(padx=20, pady=20) popup.wm_title("!") popup.resizable(False, False) screen_width, screen_height = popup.winfo_screenwidth( ), popup.winfo_screenheight() width, height = popup.winfo_reqwidth(), popup.winfo_reqheight() popup.geometry( '+%d+%d' % (screen_width / 2 - width / 2, screen_height / 2 - height / 2)) max_size = popup.winfo_screenwidth() / 3 label = tk.Label(popup, text=msg, wraplength=max_size) label.grid(row=0, column=0) b = ttk.Button(popup, text="Okay", command=popup.destroy) b.grid(row=1, column=0)
class FontChooser(Toplevel): """Font chooser dialog.""" def __init__(self, master, font_dict={}, text="Abcd", title="Font Chooser", **kwargs): """ Create a new FontChooser instance. Arguments: master : Tk or Toplevel instance master window font_dict : dict dictionnary, like the one returned by the ``actual`` method of a ``Font`` object: :: {'family': str, 'size': int, 'weight': 'bold'/'normal', 'slant': 'italic'/'roman', 'underline': bool, 'overstrike': bool} text : str text to be displayed in the preview label title : str window title kwargs : dict additional keyword arguments to be passed to ``Toplevel.__init__`` """ Toplevel.__init__(self, master, **kwargs) self.title(title) self.resizable(False, False) self.protocol("WM_DELETE_WINDOW", self.quit) self._validate_family = self.register(self.validate_font_family) self._validate_size = self.register(self.validate_font_size) # --- variable storing the chosen font self.res = "" style = Style(self) style.configure("prev.TLabel", background="white") bg = style.lookup("TLabel", "background") self.configure(bg=bg) # --- family list self.fonts = list(set(families())) self.fonts.append("TkDefaultFont") self.fonts.sort() for i in range(len(self.fonts)): self.fonts[i] = self.fonts[i].replace(" ", "\ ") max_length = int(2.5 * max([len(font) for font in self.fonts])) // 3 self.sizes = ["%i" % i for i in (list(range(6, 17)) + list(range(18, 32, 2)))] # --- font default font_dict["weight"] = font_dict.get("weight", "normal") font_dict["slant"] = font_dict.get("slant", "roman") font_dict["underline"] = font_dict.get("underline", False) font_dict["overstrike"] = font_dict.get("overstrike", False) font_dict["family"] = font_dict.get("family", self.fonts[0].replace('\ ', ' ')) font_dict["size"] = font_dict.get("size", 10) # --- creation of the widgets # ------ style parameters (bold, italic ...) options_frame = Frame(self, relief='groove', borderwidth=2) self.font_family = StringVar(self, " ".join(self.fonts)) self.font_size = StringVar(self, " ".join(self.sizes)) self.var_bold = BooleanVar(self, font_dict["weight"] == "bold") b_bold = Checkbutton(options_frame, text=TR["Bold"], command=self.toggle_bold, variable=self.var_bold) b_bold.grid(row=0, sticky="w", padx=4, pady=(4, 2)) self.var_italic = BooleanVar(self, font_dict["slant"] == "italic") b_italic = Checkbutton(options_frame, text=TR["Italic"], command=self.toggle_italic, variable=self.var_italic) b_italic.grid(row=1, sticky="w", padx=4, pady=2) self.var_underline = BooleanVar(self, font_dict["underline"]) b_underline = Checkbutton(options_frame, text=TR["Underline"], command=self.toggle_underline, variable=self.var_underline) b_underline.grid(row=2, sticky="w", padx=4, pady=2) self.var_overstrike = BooleanVar(self, font_dict["overstrike"]) b_overstrike = Checkbutton(options_frame, text=TR["Overstrike"], variable=self.var_overstrike, command=self.toggle_overstrike) b_overstrike.grid(row=3, sticky="w", padx=4, pady=(2, 4)) # ------ Size and family self.var_size = StringVar(self) self.entry_family = Entry(self, width=max_length, validate="key", validatecommand=(self._validate_family, "%d", "%S", "%i", "%s", "%V")) self.entry_size = Entry(self, width=4, validate="key", textvariable=self.var_size, validatecommand=(self._validate_size, "%d", "%P", "%V")) self.list_family = Listbox(self, selectmode="browse", listvariable=self.font_family, highlightthickness=0, exportselection=False, width=max_length) self.list_size = Listbox(self, selectmode="browse", listvariable=self.font_size, highlightthickness=0, exportselection=False, width=4) scroll_family = Scrollbar(self, orient='vertical', command=self.list_family.yview) scroll_size = Scrollbar(self, orient='vertical', command=self.list_size.yview) self.preview_font = Font(self, **font_dict) if len(text) > 30: text = text[:30] self.preview = Label(self, relief="groove", style="prev.TLabel", text=text, font=self.preview_font, anchor="center") # --- widget configuration self.list_family.configure(yscrollcommand=scroll_family.set) self.list_size.configure(yscrollcommand=scroll_size.set) self.entry_family.insert(0, font_dict["family"]) self.entry_family.selection_clear() self.entry_family.icursor("end") self.entry_size.insert(0, font_dict["size"]) try: i = self.fonts.index(self.entry_family.get().replace(" ", "\ ")) except ValueError: # unknown font i = 0 self.list_family.selection_clear(0, "end") self.list_family.selection_set(i) self.list_family.see(i) try: i = self.sizes.index(self.entry_size.get()) self.list_size.selection_clear(0, "end") self.list_size.selection_set(i) self.list_size.see(i) except ValueError: # size not in list pass self.entry_family.grid(row=0, column=0, sticky="ew", pady=(10, 1), padx=(10, 0)) self.entry_size.grid(row=0, column=2, sticky="ew", pady=(10, 1), padx=(10, 0)) self.list_family.grid(row=1, column=0, sticky="nsew", pady=(1, 10), padx=(10, 0)) self.list_size.grid(row=1, column=2, sticky="nsew", pady=(1, 10), padx=(10, 0)) scroll_family.grid(row=1, column=1, sticky='ns', pady=(1, 10)) scroll_size.grid(row=1, column=3, sticky='ns', pady=(1, 10)) options_frame.grid(row=0, column=4, rowspan=2, padx=10, pady=10, ipadx=10) self.preview.grid(row=2, column=0, columnspan=5, sticky="eswn", padx=10, pady=(0, 10), ipadx=4, ipady=4) button_frame = Frame(self) button_frame.grid(row=3, column=0, columnspan=5, pady=(0, 10), padx=10) Button(button_frame, text="Ok", command=self.ok).grid(row=0, column=0, padx=4, sticky='ew') Button(button_frame, text=TR["Cancel"], command=self.quit).grid(row=0, column=1, padx=4, sticky='ew') self.list_family.bind('<<ListboxSelect>>', self.update_entry_family) self.list_size.bind('<<ListboxSelect>>', self.update_entry_size, add=True) self.list_family.bind("<KeyPress>", self.keypress) self.entry_family.bind("<Return>", self.change_font_family) self.entry_family.bind("<Tab>", self.tab) self.entry_size.bind("<Return>", self.change_font_size) self.entry_family.bind("<Down>", self.down_family) self.entry_size.bind("<Down>", self.down_size) self.entry_family.bind("<Up>", self.up_family) self.entry_size.bind("<Up>", self.up_size) # bind Ctrl+A to select all instead of go to beginning self.bind_class("TEntry", "<Control-a>", self.select_all) self.wait_visibility(self) self.grab_set() self.entry_family.focus_set() self.lift() def select_all(self, event): """Select all entry content.""" event.widget.selection_range(0, "end") def keypress(self, event): """Select the first font whose name begin by the key pressed.""" key = event.char.lower() l = [i for i in self.fonts if i[0].lower() == key] if l: i = self.fonts.index(l[0]) self.list_family.selection_clear(0, "end") self.list_family.selection_set(i) self.list_family.see(i) self.update_entry_family() def up_family(self, event): """Navigate in the family listbox with up key.""" try: i = self.list_family.curselection()[0] self.list_family.selection_clear(0, "end") if i <= 0: i = len(self.fonts) self.list_family.see(i - 1) self.list_family.select_set(i - 1) except TclError: self.list_family.selection_clear(0, "end") i = len(self.fonts) self.list_family.see(i - 1) self.list_family.select_set(i - 1) self.list_family.event_generate('<<ListboxSelect>>') def up_size(self, event): """Navigate in the size listbox with up key.""" try: s = self.var_size.get() if s in self.sizes: i = self.sizes.index(s) elif s: sizes = list(self.sizes) sizes.append(s) sizes.sort(key=lambda x: int(x)) i = sizes.index(s) else: i = 0 self.list_size.selection_clear(0, "end") if i <= 0: i = len(self.sizes) self.list_size.see(i - 1) self.list_size.select_set(i - 1) except TclError: i = len(self.sizes) self.list_size.see(i - 1) self.list_size.select_set(i - 1) self.list_size.event_generate('<<ListboxSelect>>') def down_family(self, event): """Navigate in the family listbox with down key.""" try: i = self.list_family.curselection()[0] self.list_family.selection_clear(0, "end") if i >= len(self.fonts): i = -1 self.list_family.see(i + 1) self.list_family.select_set(i + 1) except TclError: self.list_family.selection_clear(0, "end") self.list_family.see(0) self.list_family.select_set(0) self.list_family.event_generate('<<ListboxSelect>>') def down_size(self, event): """Navigate in the size listbox with down key.""" try: s = self.var_size.get() if s in self.sizes: i = self.sizes.index(s) elif s: sizes = list(self.sizes) sizes.append(s) sizes.sort(key=lambda x: int(x)) i = sizes.index(s) - 1 else: s = len(self.sizes) - 1 self.list_size.selection_clear(0, "end") if i < len(self.sizes) - 1: self.list_size.selection_set(i + 1) self.list_size.see(i + 1) else: self.list_size.see(0) self.list_size.select_set(0) except TclError: self.list_size.selection_set(0) self.list_size.event_generate('<<ListboxSelect>>') def toggle_bold(self): """Update font preview weight.""" b = self.var_bold.get() self.preview_font.configure(weight=["normal", "bold"][b]) def toggle_italic(self): """Update font preview slant.""" b = self.var_italic.get() self.preview_font.configure(slant=["roman", "italic"][b]) def toggle_underline(self): """Update font preview underline.""" b = self.var_underline.get() self.preview_font.configure(underline=b) def toggle_overstrike(self): """Update font preview overstrike.""" b = self.var_overstrike.get() self.preview_font.configure(overstrike=b) def change_font_family(self, event=None): """Update font preview family.""" family = self.entry_family.get() if family.replace(" ", "\ ") in self.fonts: self.preview_font.configure(family=family) def change_font_size(self, event=None): """Update font preview size.""" size = int(self.var_size.get()) self.preview_font.configure(size=size) def validate_font_size(self, d, ch, V): """Validation of the size entry content.""" l = [i for i in self.sizes if i[:len(ch)] == ch] i = None if l: i = self.sizes.index(l[0]) elif ch.isdigit(): sizes = list(self.sizes) sizes.append(ch) sizes.sort(key=lambda x: int(x)) i = min(sizes.index(ch), len(self.sizes)) if i is not None: self.list_size.selection_clear(0, "end") self.list_size.selection_set(i) deb = self.list_size.nearest(0) fin = self.list_size.nearest(self.list_size.winfo_height()) if V != "forced": if i < deb or i > fin: self.list_size.see(i) return True if d == '1': return ch.isdigit() else: return True def tab(self, event): """Move at the end of selected text on tab press.""" self.entry_family = event.widget self.entry_family.selection_clear() self.entry_family.icursor("end") return "break" def validate_font_family(self, action, modif, pos, prev_txt, V): """Completion of the text in the entry with existing font names.""" if self.entry_family.selection_present(): sel = self.entry_family.selection_get() txt = prev_txt.replace(sel, '') else: txt = prev_txt if action == "0": txt = txt[:int(pos)] + txt[int(pos) + 1:] return True else: txt = txt[:int(pos)] + modif + txt[int(pos):] ch = txt.replace(" ", "\ ") l = [i for i in self.fonts if i[:len(ch)] == ch] if l: i = self.fonts.index(l[0]) self.list_family.selection_clear(0, "end") self.list_family.selection_set(i) deb = self.list_family.nearest(0) fin = self.list_family.nearest(self.list_family.winfo_height()) index = self.entry_family.index("insert") self.entry_family.delete(0, "end") self.entry_family.insert(0, l[0].replace("\ ", " ")) self.entry_family.selection_range(index + 1, "end") self.entry_family.icursor(index + 1) if V != "forced": if i < deb or i > fin: self.list_family.see(i) return True else: return False def update_entry_family(self, event=None): """Update family entry when an item is selected in the family listbox.""" # family = self.list_family.get("@%i,%i" % (event.x , event.y)) family = self.list_family.get(self.list_family.curselection()[0]) self.entry_family.delete(0, "end") self.entry_family.insert(0, family) self.entry_family.selection_clear() self.entry_family.icursor("end") self.change_font_family() def update_entry_size(self, event): """Update size entry when an item is selected in the size listbox.""" # size = self.list_size.get("@%i,%i" % (event.x , event.y)) size = self.list_size.get(self.list_size.curselection()[0]) self.var_size.set(size) self.change_font_size() def ok(self): """Validate choice.""" self.res = self.preview_font.actual() self.quit() def get_res(self): """Return chosen font.""" return self.res def quit(self): self.destroy()
class ContactsPage(Frame): """ Contacts Page: Contains the list of currently added contacts === Public Attributes === master: The frame containing all information on this page controller: Reference to the Controller Class page_name: String containing a name for this page; used for setting the tabs in the containing notebook contacts_list: Dictionary of contacts, each contact is a Contact class instance, each key is the name of the contact current_contact: Contains the contact that was selected the last time we clicked on show info. scroll_bar: Scroll bar that controls what is viewable in the contacts list; won't scroll if nothing is in the list, or everything is already shown. contacts_field: Area where contacts are shown; 10 at a time letters_field: Listbox that shows each letter of the alphabet to help the user find contact they're looking for show_info: Button that updates the info_field with the information of the currently selected contact wheel_spin: WheelSpinner object for the Show Contact Button. delete: Button that deletes the selected contact info_field: Listbox that contains the information of the currently selected contact info_scroll: Scrollbar that controls what is viewable in the info_field; won't scroll if nothing is in the list, or everything is already shown self.load: Button to load contacts self.save: Button to save contacts === Methods === create: Initializes objects & places them on the page insert_contact: Adds a contact's name to the end of the contacts field show_contact_info: Shows the information of the selected contact in the info listbox delete_contact: Deletes the selected contact & reloads the contacts Listbox clear_fields: Clears both fields on the contacts page load_contacts: Loads contacts in from a file save_contacts: Saves contacts as a file yview: Adjusts the view of contacts_field & letters_field at the same time on_mouse_wheel: Adjusts the view of contacts_field and letters_field at the same time, for the mouse wheel """ def __init__(self, master, controller, **kw): super().__init__(master, **kw) self.master = master self.controller = controller self.page_name = "View Contacts" # Initialize object names self.contacts_list = {} self.current_contact = None self.alphabetical_order = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'] self.scroll_bar = None self.contacts_field = None self.letters_field = None self.show_info = None self.wheel_spin = None self.delete = None self.info_field = None self.info_scroll = None self.bind("<<Finish Spinning Wheel>>", self.show_winning_info) self.bind("<Visibility>", self.__on_visibility) self.load = None self.save = None self.create() def create(self) -> None: self.info_scroll = Scrollbar(self, orient=VERTICAL) self.info_field = Listbox( self, yscrollcommand=self.info_scroll.set ) self.delete = Button(self, text="Delete", command=lambda: self.delete_contact()) self.delete.grid(row=2, column=3, columnspan=3, sticky=N + S + E + W) self.show_info = Button(self, text="Show Info", command=lambda: self.show_contact_info()) self.show_info.grid(row=2, column=0, columnspan=3, sticky=N + S + E + W) wheel_spin_options = ['Name', 'Home Phone Numbers', 'Work Phone Numbers', 'Personal Phone Numbers', 'Emails', 'Home Addresses', 'Notes'] self.wheel_spin = WheelSpinner(self, wheel_spin_options, width=50, height=200, radius=60) self.wheel_spin.grid(row=3, column=0, columnspan=5) self.scroll_bar = Scrollbar(self) self.contacts_field = Listbox( self, yscrollcommand=self.scroll_bar.set, selectmode=SINGLE, exportselection=0 ) self.letters_field = Listbox( self, width=2, selectmode=NONE, exportselection=0 ) self.letters_field.bind('<<ListboxSelect>>', self.scroll_to_letter) self.contacts_field.grid(row=1, column=0, columnspan=5, sticky=N + S + E + W) self.letters_field.grid(row=1, column=4, sticky=N + S + E + W) self.scroll_bar.grid(row=1, column=5, sticky=N + S + E + W) self.scroll_bar.config(command=self.yview) self.contacts_field.bind("<MouseWheel>", self.on_mouse_wheel) self.letters_field.bind("<MouseWheel>", self.on_letter_mouse_wheel) self.save = Button( self, text="Save Contacts", command=lambda: self.save_contacts() ) self.save.grid(row=0, column=3, columnspan=4, sticky=N + S + E + W) self.load = Button( self, text="Load Contacts", command=lambda: self.load_contacts() ) self.load.grid(row=0, column=0, columnspan=3, sticky=N + S + E + W) for i in range(3): self.grid_rowconfigure(i, weight=1) for i in range(4): self.grid_columnconfigure(i, weight=1) def scroll_to_letter(self, event): id = 0 for contact in self.order_contact(): if contact[0] == self.letters_field.get(self.letters_field.curselection()[0]): self.contacts_field.see(id) self.contacts_field.selection_clear(0, END) self.contacts_field.selection_set(id) self.letters_field.selection_clear(0, END) return id += 1 self.letters_field.selection_clear(0, END) def on_mouse_wheel(self, event) -> str: self.contacts_field.yview("scroll", int(-event.delta / 80), "units") return "break" def on_letter_mouse_wheel(self, event) -> str: self.letters_field.yview("scroll", int(-event.delta / 80), "units") return "break" def yview(self, *args) -> None: self.contacts_field.yview(*args) self.letters_field.yview(*args) def delete_contact(self) -> None: name = self.contacts_field.get(self.contacts_field.curselection()[0]) del self.contacts_list[name] self.clear_fields() for contact in sorted(self.contacts_list): self.insert_contact(contact) def clear_fields(self) -> None: for field in [self.contacts_field, self.info_field, self.letters_field]: field.delete(0, END) def refresh_fields(self) -> None: self.clear_fields() for contact in self.order_contact(): self.contacts_field.insert(END, contact) for letter in self.alphabetical_order: self.letters_field.insert(END, letter.upper()) def load_contacts(self) -> None: self.randomize_alphabetical_order() with open("project/contacts_pickle", 'rb') as infile: self.contacts_list = pickle.load(infile) self.refresh_fields() def save_contacts(self) -> None: with open("project/contacts_pickle", 'wb') as outfile: pickle.dump(self.contacts_list, outfile) def insert_contact(self, contact) -> None: self.contacts_field.insert(END, contact) def show_contact_info(self) -> None: """ This method shows the spinning wheel if a contact is selected and if the wheel isn't already rotating It is called on the Show Contact Info button. :return: None """ if len(self.contacts_field.curselection()) == 0 or self.wheel_spin.is_rotating: return name = self.contacts_field.get(self.contacts_field.curselection()[0]) self.current_contact = self.contacts_list[name] self.wheel_spin.draw() def show_winning_info(self, event) -> None: """ This method is called when the event <<Finish Spinning Wheel>> is invoked. It displays the current contact information that was selected by the spinning wheel. :return: None """ self.randomize_alphabetical_order() self.refresh_fields() winner = self.wheel_spin.winner label = self.wheel_spin.display_label text0 = self.current_contact.name + "'s " + winner.lower() + ':\n' text1 = '' if winner == 'Name': text1 = self.current_contact.name elif winner == 'Home Phone Numbers': for elem in self.current_contact.phone_numbers["Home"]: text1 = text1 + elem + ", " elif winner == 'Work Phone Numbers': for elem in self.current_contact.phone_numbers["Work"]: text1 = text1 + elem + ', ' elif winner == 'Personal Phone Numbers': for elem in self.current_contact.phone_numbers["Personal"]: text1 = text1 + elem + ', ' elif winner == 'Emails': for elem in self.current_contact.email_addresses: text1 = text1 + elem + ', ' elif winner == 'Home Addresses': for elem in self.current_contact.addresses: text1 = text1 + elem + ', ' elif winner == 'Notes': for elem in self.current_contact.notes: text1 = text1 + elem + ', ' label['text'] = text0 + text1 def randomize_alphabetical_order(self): random.shuffle(self.alphabetical_order) def order_contact(self) -> list: """ This function takes all the contacts and order them in the order stored in self.alphabetical order :return: The ordered list """ i = 0 order = self.alphabetical_order contacts = list(self.contacts_list) ordered_list = [] # We loop until we have all the contact ordered. while i < len(self.contacts_list): current_next_contact = None for contact in contacts: if current_next_contact is None: current_next_contact = contact continue # If the first letter is higher in the order than the current next contact, we # change the contact. if order.index(contact[0].lower()) < order.index(current_next_contact[0].lower()): current_next_contact = contact continue # If the first character is the same, we loop through the other character to find # which on should be # added first. if order.index(contact[0].lower()) == order.index(current_next_contact[0].lower()): for current_character in range(1, min(len(contact), len(current_next_contact))): if order.index(contact[current_character].lower()) < \ order.index(current_next_contact[current_character].lower()): current_next_contact = contact break if order.index(contact[current_character].lower()) > \ order.index(current_next_contact[current_character].lower()): break # we append the contact to the list and remove it from the remaining contact to order. contacts.remove(current_next_contact) ordered_list.append(current_next_contact) i += 1 return ordered_list def __on_visibility(self, event) -> None: """ This function is called when the user click on the contact tab. It randomizes the alphabetical order :param event: :return: None """ self.randomize_alphabetical_order() self.refresh_fields()
class Player: def __init__(self, master): self.master = master pygame.init() pygame.mixer.init() #===Empty thread list=====# self.threads = [] #=====show an icon for the player===# def get_icon(): self.winicon = PhotoImage(file="best (2).png") master.iconphoto(False, self.winicon) #=====run the get_icon on a different thread from the gui=====# def icon(): mythreads = threading.Thread(target=get_icon) self.threads.append(mythreads) mythreads.start() icon() #=======all Button symbols and variables======# PLAY = "►" PAUSE = "║║" RWD = "⏮" FWD = "⏭" STOP = "■" UNPAUSE = "||" mute = "🔇" unmute = u"\U0001F50A" vol_mute = 0.0 vol_unmute = 1 #==========music playlist listbox=========# self.scroll = Scrollbar(master) self.play_list = Listbox(master, font="Sansarif 12 bold", bd=5, bg="white", width=37, height=19, selectbackground="black") self.play_list.place(x=600, y=77) self.scroll.place(x=946, y=80, height=389, width=15) self.scroll.config(command=self.play_list.yview) self.play_list.config(yscrollcommand=self.scroll.set) files = 'best (2).png' self.img1 = Image.open(files) self.img1 = self.img1.resize((600, 470), Image.ANTIALIAS) self.img = ImageTk.PhotoImage(self.img1) self.lab = Label(master) self.lab.grid(row=0, column=0) self.lab["compound"] = LEFT self.lab["image"] = self.img #=====show the song playing==========# self.var = StringVar() self.var.set( ".............................................................................." ) self.song_title = Label(master, font="Helvetica 12 bold", bg="black", fg="white", width=60, textvariable=self.var) self.song_title.place(x=3, y=0) # =====add a music list to the listbox======" def append_listbox(): global song_list try: directory = askdirectory() os.chdir(directory) # it permits to change the current dir song_list = os.listdir() song_list.reverse() for item in song_list: # it returns the list of files song pos = 0 self.play_list.insert(pos, item) pos += 1 global size index = 0 size = len(song_list) self.play_list.selection_set(index) self.play_list.see(index) self.play_list.activate(index) self.play_list.selection_anchor(index) except: showerror("File selected error", "Please choose a file correctly") # =====run the append_listbox function on separate thread====== # def add_songs_playlist(): mythreads = threading.Thread(target=append_listbox) self.threads.append(mythreads) mythreads.start() #=====show music time=========# def get_time(): current_time = pygame.mixer.music.get_pos() / 1000 formated_time = time.strftime("%H:%M:%S", time.gmtime(current_time)) next_one = self.play_list.curselection() song = self.play_list.get(next_one) song_timer = MP3(song) song_length = int(song_timer.info.length) format_for_length = time.strftime("%H:%M:%S", time.gmtime(song_length)) self.label_time.config( text=f"{ format_for_length} / {formated_time}") self.progress["maximum"] = song_length self.progress["value"] = int(current_time) master.after(100, get_time) #=====play the music====# def Play_music(): try: track = self.play_list.get(ACTIVE) pygame.mixer.music.load(track) self.var.set(track) pygame.mixer.music.play() get_time() # iterate through all the songs in the playlist # there is a bug when closing the window except: showerror("No Music", "Please load the music you want to play") def playAll(): try: index = 0 for i in range(size): self.play_list.select_clear(0, END) self.play_list.selection_set(index, last=None) self.play_list.see(index) self.play_list.activate(index) self.play_list.selection_anchor(index) track = self.play_list.get(index) pygame.mixer.music.load(track) self.var.set(track) pygame.mixer.music.play() current_song = self.play_list.curselection() song = self.play_list.get(current_song) song_timer = MP3(song) song_length = int(song_timer.info.length) * 1000 get_time() index += 1 except: showerror("No songs in playlist", "Please add music") def play_all(): mythreads = threading.Thread(target=playAll) self.threads.append(mythreads) mythreads.start() # ===pause and unpause == # def pause_unpause(): if self.button_pause['text'] == PAUSE: pygame.mixer.music.pause() self.button_pause['text'] = UNPAUSE elif self.button_pause['text'] == UNPAUSE: pygame.mixer.music.unpause() self.button_pause['text'] = PAUSE # ==play the music on a diffent thread from the gui == # def play_thread(): mythreads = threading.Thread(target=Play_music) self.threads.append(mythreads) mythreads.start() master.bind("<space>", lambda x: play_thread()) # ===stop=== def stop(): pygame.mixer.music.stop() #====increase and decrease volume when slider is moved()==# def volume(x): pygame.mixer.music.set_volume(self.volume_slider.get()) # ====mute and unmute the song while the song plays== # def muted(): if self.button_mute['text'] == unmute: pygame.mixer.music.set_volume(vol_mute) self.volume_slider.set(vol_mute) self.button_mute['fg'] = "red" self.button_mute['text'] = mute elif self.button_mute['text'] == mute: pygame.mixer.music.set_volume(vol_unmute) self.volume_slider.set(vol_unmute) self.button_mute['fg'] = "white" self.button_mute['text'] = unmute #===move to the next song===# def nextSong(): try: next_one = self.play_list.curselection() next_one = next_one[0] + 1 song = self.play_list.get(next_one) pygame.mixer.music.load(song) pygame.mixer.music.play() self.play_list.select_clear(0, END) self.play_list.activate(next_one) self.play_list.selection_set(next_one, last=None) self.var.set(song) get_time() self.play_list.see(next_one) except: showerror("No Next Song", "Please press the previous button") def next(): mythreads = threading.Thread(target=nextSong) self.threads.append(mythreads) mythreads.start() #===move to the previous song===# def prevSong(): try: next_one = self.play_list.curselection() next_one = next_one[0] - 1 song = self.play_list.get(next_one) pygame.mixer.music.load(song) pygame.mixer.music.play() self.play_list.select_clear(0, END) self.play_list.activate(next_one) self.play_list.selection_set(next_one, last=None) self.var.set(song) get_time() self.play_list.see(next_one) except: showerror("No previous Song", "Please press the Next button") def prev(): mythreads = threading.Thread(target=prevSong) self.threads.append(mythreads) mythreads.start() self.master.bind('<Left>', lambda x: prev()) self.master.bind('<Right>', lambda x: next()) #=====exit the application=====# def exit(): MsgBox = askquestion( 'Exit Application', 'Are you sure you want to exit the music player.', icon='warning') if MsgBox == 'yes': master.quit() master.after(100, exit) else: showinfo('Return', 'Continue playing your awesome music') return #=====Help window=====# def help(): top = Toplevel() top.title("Help") top.geometry("350x554+500+80") top.resizable(width=0, height=0) user_manual = [ " MUSIC PLAYER USER MANUAL: \n", "1. play button = ( ► )", "2. pause button = ║║ ", "3. unpause symbol = ||", "4. next button = ⏭ ", "5. previous button = ⏮", "6. mute button = '\U0001F50A' ", "7. unmute symbol = 🔇", "8. stop button = ■ ", "\n\n| Made by manucho | Copyright @ 2021 |\n" ] for i in user_manual: manual = Label(top, text=i, width=50, height=3, font="Helvetica, 11", bg="black", fg="white") manual.pack(side=TOP, fill=BOTH) #==============================================================================================# # THis part contains the menu, volume slider , music playlist label and the volume slider # #===============================================================================================# self.menu = Menu( self.lab, font="helvetica, 3", ) master.config(menu=self.menu) self.menu.add_command(label="HELP", command=help) self.menu.add_command(label="EXIT", command=exit) self.separator = ttk.Separator(self.lab, orient='horizontal') self.separator.place(relx=0, rely=0.87, relwidth=1, relheight=1) self.button_play = Button(master, text=PLAY, width=5, bd=5, bg="black", fg="white", font="Helvetica, 15", command=play_thread) self.button_play.place(x=150, y=415) self.button_stop = Button(master, text=STOP, width=5, bd=5, font="Helvetica, 15", bg="black", fg="white", command=stop) self.button_stop.place(x=225, y=415) self.button_prev = Button(master, text=FWD, width=5, bd=5, font="Helvetica, 15", bg="black", fg="white", command=next) self.button_prev.place(x=300, y=415) self.buttonPlayall = Button(self.master, text='\U0001F500', bg='black', fg='white', font='Helvetica, 15', bd=5, width=3, command=play_all) self.buttonPlayall.place(x=375, y=415) self.button_next = Button(master, text=RWD, width=5, bd=5, bg="black", fg="white", font="Helvetica, 15", command=prev) self.button_next.place(x=10, y=415) self.button_pause = Button(master, text=PAUSE, width=4, bd=5, font="Helvetica, 15", bg="black", fg="white", command=pause_unpause) self.button_pause.place(x=85, y=415) self.button_mute = Button(master, text=unmute, width=2, bd=5, font="Helvetica, 15", bg="black", fg="white", command=muted) self.button_mute.place(x=430, y=415) self.label_playlist = Label(master, text=u"♫ Music Playlist ♫ ", width=31, font="Helvetica, 15") self.label_playlist.place(x=610, y=5) self.button_load_music = Button( master, text="♫ Click Here To Load The Music ♫", width=43, bd=5, font="Helvetica, 10", bg="black", fg="white", command=add_songs_playlist) self.button_load_music.place(x=605, y=45) self.style = ttk.Style() self.style.configure("myStyle.Horizontal.TScale", background="#505050") self.volume_slider = ttk.Scale(self.lab, from_=0, to=1, orient=HORIZONTAL, value=1, length=120, command=volume, style="myStyle.Horizontal.TScale") self.volume_slider.place(x=475, y=424) self.progress = ttk.Progressbar(self.lab, orient=HORIZONTAL, value=0, length=453, mode='determinate') self.progress.place(x=0, y=385) self.label_time = Label(master, text="00:00:00 / 00:00:00", width=17, font="Helvetica, 10", bg="black", fg="white") self.label_time.place(x=460, y=387)
class SettingsFrame(Frame): """ Frame inheritance class for application settings and controls. """ def __init__(self, app, *args, **kwargs): """ Constructor """ self.__app = app # Reference to main application class self.__master = self.__app.get_master() # Reference to root class (Tk) Frame.__init__(self, self.__master, *args, **kwargs) # Initialise up key variables self._image_num = 0 self._image_name = "image" self._settype = StringVar() self._zoom = DoubleVar() self._radius = DoubleVar() self._exponent = IntVar() self._zx_off = DoubleVar() self._zy_off = DoubleVar() self._cx_off = DoubleVar() self._cy_off = DoubleVar() self._maxiter = IntVar() self._zx_coord = DoubleVar() self._zy_coord = DoubleVar() self._theme = StringVar() self._shift = IntVar() self._themes = None self._filename = StringVar() self._filepath = None self._frames = IntVar() self._zoominc = DoubleVar() self._autoiter = IntVar() self._autosave = IntVar() self._validsettings = True self.body() def body(self): """ Set up frame and widgets. """ # Create settings panel widgets # pylint: disable=W0108 self.lbl_settype = Label(self, text=LBLMODE) self.spn_settype = Spinbox( self, values=("BurningShip", "Tricorn", "Julia", "Mandelbrot"), width=8, bg=GOOD, wrap=True, textvariable=self._settype, ) self.lbl_zoom = Label(self, text=LBLZOOM) self.ent_zoom = Entry( self, border=2, relief="sunken", width=12, bg=GOOD, justify=RIGHT, textvariable=self._zoom, ) self.lbl_zoominc = Label(self, text=LBLZOOMINC, justify=LEFT) self.ent_zoominc = Entry(self, width=5, border=2, bg=GOOD, justify=RIGHT, textvariable=self._zoominc) self.lbl_zx_off = Label(self, text=LBLZXOFF) self.ent_zx_off = Entry( self, border=2, relief="sunken", width=12, bg=GOOD, justify=RIGHT, textvariable=self._zx_off, ) self.lbl_zy_off = Label(self, text=LBLZYOFF) self.ent_zy_off = Entry( self, border=2, relief="sunken", width=12, bg=GOOD, justify=RIGHT, textvariable=self._zy_off, ) self.lbl_cx_off = Label(self, text=LBLCX) self.ent_cx_off = Entry( self, border=2, relief="sunken", width=12, bg=GOOD, justify=RIGHT, textvariable=self._cx_off, state=DISABLED, ) self.lbl_cy_off = Label(self, text=LBLCY) self.ent_cy_off = Entry( self, border=2, relief="sunken", width=12, bg=GOOD, justify=RIGHT, textvariable=self._cy_off, state=DISABLED, ) self.lbl_niter = Label(self, text=LBLITER, justify=LEFT) self.ent_maxiter = Entry( self, border=2, relief="sunken", bg=GOOD, width=8, justify=RIGHT, textvariable=self._maxiter, ) self.chk_autoiter = Checkbutton(self, text="Auto", variable=self._autoiter, onvalue=1, offvalue=0) self.lbl_theme = Label(self, text=LBLTHEME, justify=LEFT) self.lbl_radius = Label(self, text=LBLRAD, justify=LEFT) self.ent_radius = Entry( self, border=2, relief="sunken", bg=GOOD, width=8, justify=RIGHT, textvariable=self._radius, ) self.lbl_exp = Label(self, text=LBLEXP) self.spn_exp = Spinbox( self, border=2, relief="sunken", bg=GOOD, width=4, from_=2, to=20, wrap=True, textvariable=self._exponent, ) self.sep_1 = ttk.Separator( self, orient=HORIZONTAL, ) self.lbx_theme = Listbox( self, border=2, relief="sunken", bg=GOOD, width=6, height=5, justify=LEFT, exportselection=False, ) self.lbl_shift = Label(self, text=LBLSHIFT, justify=LEFT) self.scl_shift = Scale( self, from_=0, to=100, orient=HORIZONTAL, variable=self._shift, border=2, relief="sunken", sliderlength=20, troughcolor=GOOD, ) self.scrollbar = Scrollbar(self, orient=VERTICAL) self.lbx_theme.config(yscrollcommand=self.scrollbar.set) self.scrollbar.config(command=self.lbx_theme.yview) self.lbx_theme.select_set(0) self.lbx_theme.event_generate("<<ListboxSelect>>") self.lbl_coords = Label(self, text="Re, Im", fg="grey") self.btn_plot = Button( self, text=BTNPLOT, width=8, fg="green", command=lambda: self.__app.frm_fractal.plot(), ) self.btn_cancel = Button( self, text=BTNCAN, width=8, command=lambda: self.__app.frm_fractal.cancel_press(), ) self.btn_reset = Button(self, text=BTNRST, width=8, command=self.reset) self.ent_save = Entry( self, textvariable=self._filename, width=6, border=2, relief="sunken", bg=GOOD, justify=LEFT, ) self._filename.set(self._image_name + str(self._image_num)) self.btn_save = Button(self, text=BTNSAVE, width=8, command=self.save_image) self.lbl_auto = Label(self, text=LBLAUTO, justify=LEFT) self.btn_autozoom = Button( self, text=BTNZOOM, width=8, command=lambda: self.__app.frm_fractal.animate_zoom(), ) self.btn_autospin = Button( self, text=BTNSPIN, width=8, command=lambda: self.__app.frm_fractal.animate_spin(), state=DISABLED, ) self.chk_autosave = Checkbutton(self, text=BTNSAVE, variable=self._autosave, onvalue=1, offvalue=0) self.lbl_frames = Label(self, text=FRMSTXT) self.ent_frames = Entry(self, width=5, border=2, bg=GOOD, justify=RIGHT, textvariable=self._frames) # Get list of available themes for idx, theme in enumerate(THEMES): self.lbx_theme.insert(idx, theme) self.body_arrange() # Position all widgets in frame self.reset() # Reset all settings to their defaults self.set_traces( ) # Trace entry variables for validation and event handling def body_arrange(self): """ Position widgets in frame """ # Position all widgets in their parent frames self.btn_plot.grid(column=0, row=1, ipadx=3, ipady=3, sticky=(W), padx=3, pady=3) self.btn_cancel.grid(column=1, row=1, ipadx=3, ipady=3, sticky=(W), padx=3, pady=3) self.btn_reset.grid(column=2, row=1, ipadx=3, ipady=3, sticky=(W), padx=3, pady=3) self.ent_save.grid(column=0, row=2, columnspan=2, sticky=(W, E), padx=3, pady=3) self.btn_save.grid(column=2, row=2, ipadx=3, ipady=3, sticky=(W), padx=3, pady=3) self.lbl_auto.grid(column=0, row=3, sticky=(W)) self.btn_autozoom.grid(column=1, row=3, ipadx=3, ipady=3, sticky=(W), padx=3, pady=3) self.btn_autospin.grid(column=2, row=3, ipadx=3, ipady=3, sticky=(W), padx=3, pady=3) self.lbl_frames.grid(column=0, row=4, sticky=(W)) self.ent_frames.grid(column=1, row=4, sticky=(W), padx=3, pady=3) self.chk_autosave.grid(column=2, row=4, sticky=(W), padx=3, pady=3) self.sep_1.grid(column=0, row=5, columnspan=3, pady=5, sticky=(W, E)) self.lbl_settype.grid(column=0, row=6, sticky=(W)) self.spn_settype.grid(column=1, row=6, columnspan=2, sticky=(W, E), padx=3, pady=3) self.lbl_zoom.grid(column=0, row=7, sticky=(W)) self.ent_zoom.grid(column=1, row=7, columnspan=2, sticky=(W, E), padx=3, pady=3) self.lbl_zoominc.grid(column=0, row=8, sticky=(W)) self.ent_zoominc.grid(column=1, row=8, sticky=(W), padx=3, pady=3) self.lbl_zx_off.grid(column=0, row=9, sticky=(W)) self.ent_zx_off.grid(column=1, row=9, columnspan=2, sticky=(W, E), padx=3, pady=3) self.lbl_zy_off.grid(column=0, row=10, sticky=(W)) self.ent_zy_off.grid(column=1, row=10, columnspan=2, sticky=(W, E), padx=3, pady=3) self.lbl_cx_off.grid(column=0, row=11, sticky=(W)) self.ent_cx_off.grid(column=1, row=11, columnspan=2, sticky=(W, E), padx=3, pady=3) self.lbl_cy_off.grid(column=0, row=12, sticky=(W)) self.ent_cy_off.grid(column=1, row=12, columnspan=2, sticky=(W, E), padx=3, pady=3) self.lbl_niter.grid(column=0, row=13, sticky=(W)) self.ent_maxiter.grid(column=1, row=13, sticky=(W), padx=3, pady=3) self.chk_autoiter.grid(column=2, row=13, sticky=(W), padx=3, pady=3) self.lbl_radius.grid(column=0, row=14, sticky=(W)) self.ent_radius.grid(column=1, row=14, sticky=(W), padx=3, pady=3) self.lbl_exp.grid(column=0, row=15, sticky=(W)) self.spn_exp.grid(column=1, row=15, sticky=(W), padx=3, pady=3) self.lbl_theme.grid(column=0, row=16, sticky=(W)) self.lbx_theme.grid(column=1, row=16, padx=3, pady=3, columnspan=2, sticky=(N, S, W, E)) self.scrollbar.grid(column=2, row=16, sticky=(N, S, E)) self.lbl_shift.grid(column=0, row=17, sticky=(W)) self.scl_shift.grid(column=1, row=17, columnspan=2, padx=3, pady=3, sticky=(W, E)) self.lbx_theme.bind("<<ListboxSelect>>", self.get_sel_theme) def set_traces(self): """ Set up entry variable traces for validation and event handling """ self._validsettings = True self._settype.trace("w", self.val_settings) self._zoom.trace("w", self.val_settings) self._zx_off.trace("w", self.val_settings) self._zy_off.trace("w", self.val_settings) self._cx_off.trace("w", self.val_settings) self._cy_off.trace("w", self.val_settings) self._maxiter.trace("w", self.val_settings) self._radius.trace("w", self.val_settings) self._exponent.trace("w", self.val_settings) self._filename.trace("w", self.val_settings) self._frames.trace("w", self.val_settings) self._zoominc.trace("w", self.val_settings) def val_settings(self, *args, **kwargs): """ Validate all user-entered settings. (A personal choice but I find this user experience more intuitive than the standard validatecommand method for Entry widgets) """ self._validsettings = True self.__app.set_status("") if self.is_float(self.ent_zoom.get()) and self._zoom.get() > 0: flg = GOOD else: flg = BAD self.flag_entry(self.ent_zoom, flg) if self.is_float(self.ent_zx_off.get()): flg = GOOD else: flg = BAD self.flag_entry(self.ent_zx_off, flg) if self.is_float(self.ent_zy_off.get()): flg = GOOD else: flg = BAD self.flag_entry(self.ent_zy_off, flg) if self.is_float(self.ent_cx_off.get()): flg = GOOD else: flg = BAD self.flag_entry(self.ent_cx_off, flg) if self.is_float(self.ent_cy_off.get()): flg = GOOD else: flg = BAD self.flag_entry(self.ent_cy_off, flg) if self.is_integer(self.ent_maxiter.get()) and self._maxiter.get() > 0: flg = GOOD else: flg = BAD self.flag_entry(self.ent_maxiter, flg) if self.is_float(self.ent_radius.get()) and self._radius.get() > 0: flg = GOOD else: flg = BAD self.flag_entry(self.ent_radius, flg) if self.is_integer(self.spn_exp.get()) and self._exponent.get() > 1: flg = GOOD else: flg = BAD self.flag_entry(self.spn_exp, flg) if self.is_integer(self.ent_frames.get()) and self._frames.get() > 0: flg = GOOD else: flg = BAD self.flag_entry(self.ent_frames, flg) if self.is_float(self.ent_zoominc.get()) and self._zoominc.get() > 0: flg = GOOD else: flg = BAD self.flag_entry(self.ent_zoominc, flg) if self.is_filename(self.ent_save.get()): flg = GOOD else: flg = BAD self.flag_entry(self.ent_save, flg) if self.spn_settype.get() in {"Mandelbrot", "Tricorn", "BurningShip"}: self.btn_autospin.config(state=DISABLED) self.ent_cx_off.config(state=DISABLED) self.ent_cy_off.config(state=DISABLED) self._cx_off.set(0) self._cy_off.set(0) flg = GOOD elif self.spn_settype.get() == "Julia": self.btn_autospin.config(state=NORMAL) self.ent_cx_off.config(state=NORMAL) self.ent_cy_off.config(state=NORMAL) flg = GOOD else: flg = BAD self.flag_entry(self.spn_settype, flg) def flag_entry(self, ent, flag): """ Flag entry field as valid or invalid and set global validity status flag. This flag is used throughout to determine whether functions can proceed or not. """ ent.config(bg=flag) if flag == BAD: self._validsettings = False self.__app.set_status(VALERROR, "red") def is_float(self, flag): """ Validate if entry is a float. """ try: float(flag) return True except ValueError: return False def is_integer(self, flag): """ Validate if entry is a positive integer. """ try: int(flag) if int(flag) > 0: return True return False except ValueError: return False def is_filename(self, flag): """ Validate if entry represents a valid filename using a regexp. """ return match("^[\w\-. ]+$", flag) and flag != "" def reset(self): """ Reset settings to defaults. """ self._settype.set("Mandelbrot") self._zoom.set(0.75) self._zx_off.set(-0.5) self._zy_off.set(0.0) self._cx_off.set(0.0) self._cy_off.set(0.0) self._maxiter.set(128) self._radius.set(2.0) self._exponent.set(2) self._frames.set(10) self._zoominc.set(2.0) self._autoiter.set(1) self._autosave.set(0) self._theme.set("Default") self._filename.set("image0") self._shift.set(0) self.set_sel_theme() self.__app.set_status(SETINITTXT) def get_sel_theme(self, *args, **kwargs): """ Get selected theme from listbox and set global variable. """ idx = self.lbx_theme.curselection() if idx == "": idx = 0 self._theme.set(self.lbx_theme.get(idx)) def set_sel_theme(self): """ Lookup index of selected theme and highlight that listbox position. NB: this requires 'exportselection=False' option to be set on listbox to work properly. """ for idx, theme in enumerate(THEMES): if theme == self._theme.get(): self.lbx_theme.activate(idx) self.lbx_theme.see(idx) break def get_settings(self): """ Return all current settings as a dict. """ if not self._validsettings: settings = {"valid": self._validsettings} return settings settings = { "settype": self._settype.get(), "zoom": self._zoom.get(), "zxoffset": self._zx_off.get(), "zyoffset": self._zy_off.get(), "cxoffset": self._cx_off.get(), "cyoffset": self._cy_off.get(), "maxiter": self._maxiter.get(), "autoiter": self._autoiter.get(), "autosave": self._autosave.get(), "radius": self._radius.get(), "exponent": self._exponent.get(), "theme": self._theme.get(), "shift": self._shift.get(), "filepath": self._filepath, "filename": self._filename.get(), "frames": self._frames.get(), "zoominc": self._zoominc.get(), "valid": self._validsettings, } return settings def update_settings(self, **kwargs): """ Update settings from keyword parms. """ if "settype" in kwargs: self._settype.set(kwargs["settype"]) if "zoom" in kwargs: self._zoom.set(kwargs["zoom"]) if "zxoffset" in kwargs: self._zx_off.set(kwargs["zxoffset"]) if "zyoffset" in kwargs: self._zy_off.set(kwargs["zyoffset"]) if "cxoffset" in kwargs: self._cx_off.set(kwargs["cxoffset"]) if "cyoffset" in kwargs: self._cy_off.set(kwargs["cyoffset"]) if "maxiter" in kwargs: self._maxiter.set(kwargs["maxiter"]) if "autoiter" in kwargs: self._autoiter.set(kwargs["autoiter"]) if "autosave" in kwargs: self._autosave.set(kwargs["autosave"]) if "radius" in kwargs: self._radius.set(kwargs["radius"]) if "exponent" in kwargs: self._exponent.set(kwargs["exponent"]) if "filepath" in kwargs: self._filepath.set(kwargs["filepath"]) if "filename" in kwargs: self._filename.set(kwargs["filename"]) if "frames" in kwargs: self._frames.set(kwargs["frames"]) if "zoominc" in kwargs: self._zoominc.set(kwargs["zoominc"]) if "theme" in kwargs: self._theme.set(kwargs["theme"]) self.set_sel_theme() if "shift" in kwargs: self._shift.set(kwargs["shift"]) def set_filepath(self): """ Sets filepath for saved files for the duration of this session. """ default = os.getcwd() # Default _filepath is current working directory if self._filepath is None: self._filepath = filedialog.askdirectory(title=SAVETITLE, initialdir=default, mustexist=True) if self._filepath == "": self._filepath = None # User cancelled return self._filepath def save_image(self): """ Save image as PNG file to selected filepath and automatically increment default image name. NB: currently this will overwrite any existing file of the same name without warning. """ # Bug out if the settings are invalid settings = self.__app.frm_settings.get_settings() if not settings["valid"]: return # Check if image has been created image = self.__app.frm_fractal.mandelbrot.get_image() if image is None: self.__app.set_status(NOIMGERROR, "red") return # Set _filename and path if self.set_filepath() is None: # User cancelled return fname = self._filename.get() fqname = self._filepath + "/" + fname # Save the image along with its metadata try: # image.write(fqname + ".png", format="png") image.save(fqname + ".png", format="png") self.save_metadata() except OSError: self.__app.set_status(SAVEERROR, "red") self._filepath = None return self._image_num += 1 self._filename.set(self._image_name + str(self._image_num)) self.__app.set_status(IMGSAVETXT + fqname + ".png", "green") # Return focus to image frame self.__app.frm_fractal.focus_set() def save_metadata(self): """ Save json file containing meta data associated with image, allowing it to be imported and reproduced. """ if self._filepath is None: if self.set_filepath() is None: # User cancelled return fname = self._filename.get() fqname = self._filepath + "/" + fname filename = fqname + ".json" createtime = strftime("%b %d %Y %H:%M:%S %Z", gmtime()) jsondata = { MODULENAME: { "filename": fqname + ".png", "created": createtime, "settype": self._settype.get(), "zoom": self._zoom.get(), "zoominc": self._zoominc.get(), "frames": self._frames.get(), "escradius": self._radius.get(), "exponent": self._exponent.get(), "maxiter": self._maxiter.get(), "zxoffset": self._zx_off.get(), "zyoffset": self._zy_off.get(), "cxoffset": self._cx_off.get(), "cyoffset": self._cy_off.get(), "theme": self._theme.get(), "shift": self._shift.get(), } } try: with open(filename, "w") as outfile: dump(jsondata, outfile) except OSError: self.__app.set_status(METASAVEERROR, "red") self._filepath = None # Return focus to image frame self.__app.frm_fractal.focus_set() def import_metadata(self): """ Update settings from imported json metadata file. """ # Select and read file try: default = os.getcwd() filepath = filedialog.askopenfilename( initialdir=default, title=SELTITLE, filetypes=(("json files", "*.json"), ("all files", "*.*")), ) if filepath == "": # User cancelled return with open(filepath, "r") as infile: jsondata = infile.read() except OSError: self.__app.set_status(OPENFILEERROR, "red") return # Parse file try: settings = loads(jsondata).get(MODULENAME) # Set plot parameters self._settype.set(settings.get("settype", "Mandelbrot")) self._zoom.set(settings.get("zoom", 1)) self._zoominc.set(settings.get("zoominc", 2.0)) self._frames.set(settings.get("frames", 10)) self._radius.set(settings.get("escradius", 2.0)) self._exponent.set(settings.get("exponent", 2)) self._maxiter.set(settings.get("maxiter", 256)) self._zx_off.set(settings.get("zxoffset", 0)) self._zy_off.set(settings.get("zyoffset", 0)) self._cx_off.set(settings.get("cxoffset", 0)) self._cy_off.set(settings.get("cyoffset", 0)) self._theme.set(settings.get("theme", "Default")) self._shift.set(settings.get("shift", 0)) except OSError: self.__app.set_status(BADJSONERROR, "red") return self.set_sel_theme() fbase = os.path.basename(filepath) filename, fileext = os.path.splitext(fbase) self.__app.set_status( "Metadata file " + filename + fileext + METAPROMPTTXT, "green") # Return focus to image frame self.__app.frm_fractal.focus_set()
class MyClient: def __init__(self): self.__ini_data() print('Data prepared...') self.__ini_widget() self.__exit() def __ini_data(self): self.c_data = {'msg': '', 'release': False, 's_time': ''} self.INI_SERVER = False self.INI_CLIENT = False self.EXIT=False self.HOST = '127.0.0.1' # self.HOST = '192.168.1.101' self.PORT = 54321 self.client = socket.socket() def __ini_widget(self): self.root_window = Tk() self.root_window.title('Client') # pre connection settings widgets group frame_address = Frame(self.root_window) frame_address.pack(side=TOP, fill=X, padx=5, pady=5) label_ip = Label(frame_address, text='IP') label_ip.grid(row=0, sticky=EW) self.ip_text = StringVar() self.ip_text.set(self.HOST) self.ip_texter = Entry(frame_address, textvariable=self.ip_text, width=30) self.ip_texter.grid(row=0, column=1, sticky=EW) label_port = Label(frame_address, text='Port') label_port.grid(row=1, sticky=EW) self.port_text = StringVar() self.port_text.set(self.PORT) self.port_texter = Entry(frame_address, textvariable=self.port_text) self.port_texter.grid(row=1, column=1, sticky=EW) self.btn_open = Button(frame_address, text='Connect', command=self.__connect) self.btn_open.grid(row=2, columnspan=2, sticky=NSEW) self.conn_stat = Label(self.root_window, text="Please click 'Connect'") self.conn_stat.pack(side=TOP, fill=X) # talk widgets group self.frame_talk = Frame(self.root_window) self.list_box_data = Listbox(self.frame_talk) self.list_box_data.pack(expand=True, fill=BOTH) self.s_text = Label(self.frame_talk, text="Server's message'") self.s_text.pack(side=TOP, fill=X) self.frame_client_input = Frame(self.frame_talk) self.frame_client_input.pack(fill=X) self.c_text = StringVar() self.c_texter = Entry(self.frame_client_input, textvariable=self.c_text) self.c_texter.bind("<Key>", self.on_texter_key) self.c_texter.pack(side=LEFT, fill=X, expand=True) btn_send = Button(self.frame_client_input, text='Send', command=self.on_click_btn_send) btn_send.pack(side=RIGHT) btn_exit = Button(self.root_window, text='Exit', command=self.on_btn_exit) btn_exit.pack(side=BOTTOM, fill=X, padx=5, pady=5) # open window self.root_window.mainloop() def on_click_btn_send(self): try: self.c_data['s_time'] = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') self.c_data['msg'] = self.c_text.get() self.client.send(json.dumps(self.c_data).encode('utf-8')) self.c_text.set('') self.list_box_data.insert(END, 'Server %s :' % (self.c_data['s_time'])) self.list_box_data.insert(END, ' %s' % (self.c_data['msg'])) self.list_box_data.see(END) except Exception as e: traceback.print_exc() def __exit(self): self.client.close() def __start_listen(self): while True: try: s_data = json.loads(self.client.recv(1024).decode('utf8')) if s_data['release']: # self.CONN=False break print('server:', s_data['msg']) self.s_text['text'] = s_data['msg'] self.list_box_data.insert(END, 'Server %s :' % (s_data['s_time'])) self.list_box_data.insert(END, ' %s' % (s_data['msg'])) self.list_box_data.see(END) self.root_window.update() except Exception as e: traceback.print_exc() if not self.EXIT: self.conn_stat['text'] = 'Connection released' self.frame_client_input.destroy() self.s_text['text'] = "Server is offline" def __connect(self): self.HOST = self.ip_text.get() self.PORT = int(self.port_text.get()) try: self.client.connect((self.HOST, self.PORT)) except ConnectionRefusedError: traceback.print_exc() print('目标ip:port可能没在监听') return self.frame_talk.pack(expand=True, fill=BOTH) self.conn_stat['text'] = "Connected" self.INI_SERVER=True self.INI_CLIENT=True self.btn_open.destroy() self.root_window.update() self.thread_listen = threading.Thread(target=self.__start_listen) self.thread_listen.start() def on_btn_exit(self): self.EXIT=True self.frame_client_input.destroy() self.conn_stat['text'] = "Waiting for server's exit" self.c_data['release'] = True self.client.send(json.dumps(self.c_data).encode('utf-8')) print('exit signal sent...') self.root_window.update() self.thread_listen.join() self.root_window.destroy() def on_texter_key(self, event): if event.keysym == 'Return': # 按下了回车 self.on_click_btn_send()
class Editor: # Info Defaults LOG_FILE = 'editor.log' devices = [] vidPK = [] vidPATH = [] vidNAME = [] uuid = [] uuidFK = [] def __init__(self, master, soundGenerator, rfidScanner): self.environment = Environment() self.soundProvider = SoundProvider(soundGenerator) self.configureScannerProvider(rfidScanner) self.load() frame = Frame(master) frame.pack() self.activeCardNumber = StringVar() self.usbSpin = StringVar() self.usbSpin.set(self.environment.Usb) Label(frame, text='RFID Card').grid(row=0, column=0) Label(frame, text='Video').grid(row=0, column=2) self.ee = Entry(frame, textvariable=self.activeCardNumber, state=DISABLED, disabledforeground='black') self.ee.grid(row=1, column=0) self.r = Button(frame, text='Read Card', command=self.startCardProcess) self.r.grid(row=1, column=1) self.box = Listbox(frame) for entry in self.vidNAME: self.box.insert(END, entry) self.box.bind("<<ListboxSelect>>", self.newselection) self.box.grid(row=1, rowspan=5, column=2, columnspan=2) self.scroll = Scrollbar(self.box, orient=VERTICAL) self.box.config(yscrollcommand=self.scroll.set) self.scroll.config(command=self.box.yview) Button(frame, text='Assign Kill Code', command=self.createKiller).grid(row=2, column=0) Label(frame, text='Source USB').grid(row=4, column=0) self.spin = Spinbox(frame, values=self.devices) self.spin.delete(0, END) self.spin.insert(0, self.environment.Usb) self.spin.grid(row=5, column=0) self.status = Button(frame, text='Update Device Repository', command=self.updateDevice, disabledforeground='blue') self.status.grid(row=6, column=0) Button(frame, text='Save', command=self.save).grid(row=6, column=2) Button(frame, text='Quit', command=self.closeWithSavePrompt).grid( row=6, column=3) def configureScannerProvider(self, rfidScanner): provider = RFIDScannerProvider(rfidScanner) self.RFIDScannerProvider = provider.PN532( int(self.environment.CHIP_SELECT_PIN), int(self.environment.MASTER_OUTPUT_SLAVE_INPUT_PIN), int(self.environment.MASTER_INPUT_SLAVE_OUTPUT_PIN), int(self.environment.SERIAL_CLOCK_PIN)) def closeWithSavePrompt(self): ans = messagebox.askquestion( 'Save And Quit', 'Would you like to save your changes?') if ans == 'yes': self.save() sys.exit(0) def startCardProcess(self): # Disable button to prevent event stacking self.r.config(state=DISABLED) self.processCard() self.r.config(state=NORMAL) def processCard(self): # Scans RFID cards and sets them to text box try: self.processCardUnchecked() except Exception as e: self.displayScanError(e) def processCardUnchecked(self): cardScan = CardScanWrapper(self.soundS, self.RFIDScannerProvider) cardScan.runScan() self.processResult(cardScan.getFormattedResult()) def processResult(self, scanResult): if scanResult == None: return self.activeCardNumber.set(scanResult) # Populate text box self.deselectActiveListboxItems() self.linkCardWithListbox(scanResult) def deselectActiveListboxItems(self): # De-select any active items in listbox self.box.selection_clear(0, END) def linkCardWithListbox(self, scanResult): index = self.verifyCard(scanResult) if str(self.uuidFK[index]) == self.environment.KillCommand: messagebox.showinfo( 'Kill Card', 'This card is currently assigned to kill the application.') return self.highlightItemInListbox(index) def highlightItemInListbox(self, index): try: i = self.vidPK.index(self.uuidFK[index]) except: messagebox.showinfo('Card Unassigned', 'Card is not currently assigned to a video') else: self.box.see(i) self.box.selection_clear(0, END) self.box.selection_set(i) self.box.activate(i) def verifyCard(self, uidt): try: uuidIndex = self.uuid.index(uidt) except: uuidIndex = self.addNewCard(uidt) return uuidIndex def addNewCard(self, uidt): self.uuid.append(uidt) self.uuidFK.append(-1) newIndex = len(self.uuid) - 1 return newIndex def displayScanError(self, e): messagebox.showerror('Error Occurred', 'Error: ' + str(e)) logging.error('Scan Failed: ' + str(e)) def save(self): toSaveList = self.makePairedList( self.vidPK, self.vidNAME, self.vidPATH) self.safeSaveToFile(self.environment.VideoList, toSaveList) toSaveList = self.makePairedList(self.uuid, self.uuidFK) self.safeSaveToFile(self.environment.UuidTable, toSaveList) def makePairedList(self, *itemLists): stop = len(itemLists) subList = [] listAll = [] for listIndex in range(len(itemLists[0])): del subList[:] for indice in range(stop): subList.append(itemLists[indice][listIndex]) listAll.append(list(subList)) return listAll def safeSaveToFile(self, fileName, pairList): try: self.writePairedListToTempFile(fileName, pairList) except Exception as e: logging.error('Failed to create video list: ' + str(e)) else: self.replaceOriginalFileWithItsTemp(fileName) def replaceOriginalFileWithItsTemp(self, fileName): try: if os.path.isfile(fileName): os.remove(fileName) os.rename(fileName + '.temp', fileName) except Exception as e: logging.error('Failed to replace old video list: ' + str(e)) def writePairedListToTempFile(self, fileName, pairedList): f = open(fileName + '.temp', 'w') self.writePairedListGivenFile(f, pairedList) f.close() def writePairedListGivenFile(self, f, pairedList): i = 0 while(i < len(pairedList) - 1): self.writeSingleLineOfPairedListToOpenFile(f, pairedList, i) f.write('\n') i = i+1 self.writeSingleLineOfPairedListToOpenFile(f, pairedList, i) def writeSingleLineOfPairedListToOpenFile(self, f, pairedList, itemIndex): fLine = "" for item in range(len(pairedList[itemIndex])): fLine = fLine + str(pairedList[itemIndex][item]) + ',' f.write(fLine[:-1]) def updateDevice(self): scan = self.safeScan() if scan != None: self.safeProcessScan(scan) self.status.config(text='Update Device Repository', state=NORMAL) self.status.update_idletasks() def safeScan(self): scan = None try: scan = self.runScannerWithNotification() except Exception as e: self.showScanErrorMessage(e) return scan def runScannerWithNotification(self): self.status.config(text='Scanning...', state=DISABLED) self.status.update_idletasks() scan = ScriptedFileSearch(subprocess) scan.scan("scanner.sh") return scan def showScanErrorMessage(self, e): messagebox.showerror('Scan Failed', 'Scan error: ' + str(e)) logging.error(str(e)) def safeProcessScan(self, scan): try: self.processScan(scan) except Exception as e: self.showErrorMessage(e) def showErrorMessage(self, e): messagebox.showerror('Error', 'Error: ' + str(e)) logging.error(str(e)) def refreshListBox(self): self.box.delete(0, END) for entry in self.vidNAME: self.box.insert(END, entry) def processScan(self, scan): # Check if a scan turned up any results if self.scanIsEmpty(scan): self.showAbortScanMessage() return self.verifyUSB() self.processScanFiles(scan) self.refreshListBox() def showAbortScanMessage(self): messagebox.showwarning( 'No Files Found', 'A scan failed to find any files.') logging.warning('Empty Scan occurred when attempting a merge') def scanIsEmpty(self, scan): return len(scan.NAME) == 0 def verifyUSB(self): if self.environment.Usb != self.spin.get(): self.Usb = self.spin.get() self.environment.update() def processScanFiles(self, scan): i = 0 j = 0 newPK = [] newName = [] newPath = [] self.status.config(text='Reading Files...') self.status.update_idletasks() # Iterate through list while i < len(scan.NAME): # Verifiy File try: if scan.PATH[i].find(self.environment.Usb) >= 0: # File resides on repository - update FK try: # Locate matching entry fkk = self.vidNAME.index(scan.NAME[i]) except Exception as e: # No matching entry logging.info('New file found in repository: ' + str(e)) pass else: # Update FK on uuid table for uu in self.uuidFK: if uu == self.vidPK[fkk]: uu = scan.PK[i] # Store entry in new Tables newPK.append(scan.PK[i]) newName.append(scan.NAME[i]) newPath.append(scan.PATH[i]) else: # Video resides on updating device - check if file already copied found = False while j < len(scan.NAME): if str(scan.NAME[i]) == str(scan.NAME[j]) and scan.PATH[j].find(self.environment.Usb) >= 0: found = True break j = j + 1 if not found: # Copy file and append try: # Get device name device = scan.PATH[i].replace('/media/pi/', '') device = device[0:device.find('/')] # Copy self.status.config( text='Copying ' + scan.NAME[i] + '...') self.status.update_idletasks() shutil.copyfile( scan.PATH[i], scan.PATH[i].replace(device, self.environment.Usb)) except Exception as e: logging.error('Failed to copy' + scan.NAME[i] + ': ' + str(e)) else: # Add to new array newPK.append(scan.PK[i]) newName.append(scan.NAME[i]) newPath.append( scan.PATH[i].replace(device, self.environment.Usb)) except Exception as e: logging.error(str(e)) i = i+1 del self.vidNAME[:] del self.vidPATH[:] del self.vidPK[:] self.vidNAME = newName self.vidPATH = newPath self.vidPK = newPK def newselection(self, event): # Fires when a new item is selected in the listbox selection = event.widget.curselection() try: txt = self.ee.get() if txt == '': return i = self.uuid.index(txt) self.uuidFK[i] = self.vidPK[selection[0]] except Exception as e: messagebox.showerror('Error During Set', 'Error: ' + str(e)) logging.error(str(e)) def createKiller(self): try: self.assignCurrentCardAsKiller() self.box.selection_clear(0, END) except Exception as e: self.handleCardNotScannedError(e) def assignCurrentCardAsKiller(self): i = self.uuid.index(self.ee.get()) self.uuidFK[i] = int(self.environment.KillCommand) def handleCardNotScannedError(self, e): messagebox.showinfo( 'Card Not Scanned', 'Please scan a card to assign it a [Kill Application] code.' + str(e)) logging.error(str(e)) def load(self): # Generate Log logging.basicConfig(filename=self.LOG_FILE, level=logging.INFO) # Load Sound file self.soundProvider.init() self.soundProvider.mixer.pre_init(44100, -16, 12, 512) # pygame.init() IS this only for linux distribution? self.soundS = self.soundProvider.mixer.Sound(self.environment.SCAN_SOUND) self.soundS.set_volume(1) # Create an instance of the PN532 class. self.RFIDScannerProvider.begin()) # Configure PN532 to communicate with MiFare cards. self.RFIDScannerProvider.SAM_configuration() self.loadFiles() self.locateDevices() self.loadDevice() def loadFiles(self): self.readCSV(self.environment.VideoList, (int, self.vidPK), (str, self.vidNAME), (str, self.vidPATH)) self.readCSV(self.environment.UuidTable, (str, self.uuid), (int, self.uuidFK))
class AutocompleteEntry(Entry): def __init__(self, autocompleteList, *args, **kwargs): # Listbox length if 'listboxLength' in kwargs: self.listboxLength = kwargs['listboxLength'] del kwargs['listboxLength'] else: self.listboxLength = 8 # Custom matches function if 'matchesFunction' in kwargs: self.matchesFunction = kwargs['matchesFunction'] del kwargs['matchesFunction'] else: def matches(fieldValue, acListEntry): pattern = re.compile('.*' + re.escape(fieldValue) + '.*', re.IGNORECASE) return re.match(pattern, acListEntry) self.matchesFunction = matches Entry.__init__(self, *args, **kwargs) self.focus() self.autocompleteList = autocompleteList self.var = self["textvariable"] if self.var == '': self.var = self["textvariable"] = StringVar() self.var.trace('w', self.changed) self.bind("<Right>", self.selection) self.bind("<Up>", self.moveUp) self.bind("<Down>", self.moveDown) self.listboxUp = False def changed(self, name, index, mode): if self.var.get() == '': if self.listboxUp: self.listbox.destroy() self.listboxUp = False else: words = self.comparison() if words: if not self.listboxUp: self.listbox = Listbox(width=self["width"], height=self.listboxLength) self.listbox.bind("<Button-1>", self.selection) self.listbox.bind("<Right>", self.selection) self.listbox.place(x=self.winfo_x(), y=self.winfo_y() + self.winfo_height()) self.listboxUp = True self.listbox.delete(0, END) for w in words: self.listbox.insert(END, w) else: if self.listboxUp: self.listbox.destroy() self.listboxUp = False def selection(self, event): if self.listboxUp: self.var.set(self.listbox.get(ACTIVE)) self.listbox.destroy() self.listboxUp = False self.icursor(END) def moveUp(self, event): if self.listboxUp: if self.listbox.curselection() == (): index = '0' else: index = self.listbox.curselection()[0] if index != '0': self.listbox.selection_clear(first=index) index = str(int(index) - 1) self.listbox.see(index) # Scroll! self.listbox.selection_set(first=index) self.listbox.activate(index) def moveDown(self, event): if self.listboxUp: if self.listbox.curselection() == (): index = '0' else: index = self.listbox.curselection()[0] if index != END: self.listbox.selection_clear(first=index) index = str(int(index) + 1) self.listbox.see(index) # Scroll! self.listbox.selection_set(first=index) self.listbox.activate(index) def comparison(self): return [ w for w in self.autocompleteList if self.matchesFunction(self.var.get(), w) ]
class MyServer: def __init__(self): self.__ini_data() print('Data prepared...') # self.__ini_socket() self.__ini_widget() self.__exit() def __ini_widget(self): self.root_window = Tk() self.root_window.title('Server') # pre connection settings widgets group frame_address = Frame(self.root_window) frame_address.pack(side=TOP, fill=X, padx=5, pady=5) label_ip = Label(frame_address, text='IP') label_ip.grid(row=0, sticky=EW) self.ip_text = StringVar() self.ip_text.set(self.HOST) self.ip_texter = Entry(frame_address, textvariable=self.ip_text, width=30) self.ip_texter.grid(row=0, column=1, sticky=EW) label_port = Label(frame_address, text='Port') label_port.grid(row=1, sticky=EW) self.port_text = StringVar() self.port_text.set(self.PORT) self.port_texter = Entry(frame_address, textvariable=self.port_text) self.port_texter.grid(row=1, column=1, sticky=EW) self.btn_open = Button(frame_address, text='Open Port', command=self.__open_port) self.btn_open.grid(row=2, columnspan=2, sticky=NSEW) # talk widgets group self.frame_talk = Frame(self.root_window) self.c_info = Label(self.frame_talk, text='Client info - -\nWating for connection') self.c_info.pack(side=TOP, fill=X) self.list_box_data = Listbox(self.frame_talk) self.list_box_data.pack(expand=True, fill=BOTH) self.c_text = Label(self.frame_talk, text="Client's message") self.c_text.pack(side=TOP, fill=X) self.frame_server_input = Frame(self.frame_talk) self.frame_server_input.pack(fill=X) self.s_text = StringVar() self.s_texter = Entry(self.frame_server_input, textvariable=self.s_text) self.s_texter.bind("<Key>", self.on_texter_key) self.s_texter.pack(side=LEFT, fill=X, expand=True) btn_send = Button(self.frame_server_input, text='Send', command=self.on_click_btn_send, width=15) btn_send.pack(side=RIGHT) # btn exit btn_exit = Button(self.root_window, text='Exit', command=self.on_btn_exit) btn_exit.pack(side=BOTTOM, fill=X, padx=5, pady=5) # open window self.root_window.mainloop() def __exit(self): if self.INI_SERVER: self.server.close() print('Byebye') # if self.CONN: # self.client.close() def __start_listen(self): print('Start listen') try: self.client, self.c_add = self.server.accept() t_conn_establish = datetime.datetime.now() print('Connection established') self.c_info['text'] = 'Client info: host = %s , port = %d\nConnected at %s' % ( self.c_add[0], self.c_add[1], t_conn_establish.strftime('%Y-%m-%d %H:%M:%S')) self.INI_CLIENT = True # self.CONN = True self.root_window.update() while True: try: c_data = json.loads(self.client.recv(1024).decode('utf8')) if c_data['release']: # 放在后面会卡死 # self.CONN = False break print('%s\nClient:%s' % (c_data['s_time'], c_data['msg'])) self.c_text['text'] = c_data['msg'] self.list_box_data.insert(END, 'Client %s :' % (c_data['s_time'])) self.list_box_data.insert(END, ' %s' % (c_data['msg'])) self.list_box_data.see(END) self.root_window.update() except Exception as e: traceback.print_exc() if not self.EXIT: self.c_info['text'] = 'Connection released' self.frame_server_input.destroy() self.c_text['text'] = "Client is offline" except OSError as e: traceback.print_exc() pass def on_click_btn_send(self): try: self.s_data['msg'] = self.s_text.get() self.s_data['s_time'] = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') self.client.send(json.dumps(self.s_data).encode('utf-8')) self.s_text.set('') self.list_box_data.insert(END, 'Server %s :' % (self.s_data['s_time'])) self.list_box_data.insert(END, ' %s' % (self.s_data['msg'])) self.list_box_data.see(END) except Exception as e: traceback.print_exc() def on_texter_key(self, event): if event.keysym == 'Return': # 按下了回车 self.on_click_btn_send() def __open_port(self): self.btn_open.destroy() self.frame_talk.pack(expand=True, fill=BOTH) self.HOST = self.ip_text.get() self.PORT = int(self.port_text.get()) self.server.bind((self.HOST, self.PORT)) self.INI_SERVER = True print('Server socket bind successfully\nListening...') self.c_info['text'] = "Waiting for client's connection" self.server.listen(3) self.root_window.update() self.thread_listen = threading.Thread(target=self.__start_listen) self.thread_listen.start() def on_btn_exit(self): self.EXIT=True self.frame_server_input.destroy() self.c_text['text'] = "Waiting for client's exit" if self.INI_SERVER and not self.INI_CLIENT: self.server.close() if self.INI_CLIENT: self.s_data['release'] = True self.client.send(json.dumps(self.s_data).encode('utf-8')) print('Exit signal sent...') # self.client.close() self.root_window.update() self.thread_listen.join() self.root_window.destroy() def __ini_data(self): self.INI_SERVER = False self.INI_CLIENT = False # self.CONN=False self.EXIT=False self.HOST = '127.0.0.1' # self.HOST = '192.168.1.101' self.PORT = 54321 self.s_data = {'msg': '', 'release': False, 's_time': ''} self.server = socket.socket()
class MessageMakerListFrame: def __init__(self, messageMakerPartFrame): self.frame = Frame(messageMakerPartFrame.frame) self.scrollbar = Scrollbar(self.frame, orient=VERTICAL) self.listBox = Listbox(self.frame, activestyle="none", width=40, height=12, yscrollcommand=self.scrollbar.set) self.scrollbar.config(command=self.listBox.yview) self.scrollbar.grid(row=0, column=1, sticky=NS) self.populateListbox(messageMakerPartFrame.parent.message.parts) self.window = messageMakerPartFrame.parent self.messageMakerPartFrame = messageMakerPartFrame self.listBox.grid(row=0, column=0) def moveSelectedUp(self): current = self.listBox.curselection() if current and current[0] != 0: index = current[0] text = self.listBox.get(index) self.listBox.delete(index) self.listBox.insert(index - 1, text) self.listBox.selection_set(index - 1) self.listBox.see(index - 2) self.window.message.parts[index], self.window.message.parts[ index - 1] = self.window.message.parts[ index - 1], self.window.message.parts[index] def moveSelectedDown(self): current = self.listBox.curselection() if current and current[0] != self.listBox.size() - 1: index = current[0] text = self.listBox.get(index) self.listBox.delete(index) self.listBox.insert(index + 1, text) self.listBox.selection_set(index + 1) self.listBox.see(index + 2) self.window.message.parts[index], self.window.message.parts[ index + 1] = self.window.message.parts[ index + 1], self.window.message.parts[index] def deleteSelected(self): current = self.listBox.curselection() if current: index = current[0] self.listBox.delete(index) self.listBox.selection_set(index) self.listBox.see(index) del self.window.message.parts[index] for part in self.window.message.parts: part.sortOrder = self.window.message.parts.index(part) + 1 def copySelected(self): current = self.listBox.curselection() if current: index = current[0] copiedPart = deepcopy(self.window.message.parts[index]) copiedPart.sortOrder = len(self.window.message.parts) self.window.message.parts.append(copiedPart) self.listBox.select_clear(0, END) self.listBox.insert(END, copiedPart.partType + ": " + copiedPart.value) self.listBox.selection_set(END) self.listBox.see(END) def populateListbox(self, parts): self.listBox.delete(0, END) index = 1 for part in parts: self.listBox.insert(index, part.partType + ": " + part.value) index += 1 def getMessageComponentWindow(self, isEditButton): if isEditButton: current = self.listBox.curselection() if current: index = current[0] MessageComponentWindow(self, self.window.message.parts[index], index) else: return else: MessageComponentWindow(self, None, len(self.window.message.parts) + 1)