Esempio n. 1
0
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)
Esempio n. 3
0
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"
Esempio n. 5
0
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()
Esempio n. 6
0
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)
Esempio n. 7
0
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
Esempio n. 8
0
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)
Esempio n. 9
0
File: tkui.py Progetto: mindhog/mawb
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'
Esempio n. 10
0
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
Esempio n. 12
0
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)
Esempio n. 13
0
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])
Esempio n. 14
0
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
Esempio n. 15
0
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)
Esempio n. 16
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()
Esempio n. 17
0
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()
Esempio n. 18
0
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)
Esempio n. 19
0
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()
Esempio n. 20
0
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()
Esempio n. 21
0
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)
        ]
Esempio n. 23
0
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()
Esempio n. 24
0
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)