Example #1
0
class EpFpFrame(ttk.LabelFrame):
    """
    Part of characters panel. Contains
    ep and fp fields with labels.
    """
    def __init__(self, character_panel):
        ttk.LabelFrame.__init__(self, character_panel, text=EP_FRAME_TITLE)

        self.ep_field = CharacterValueField(self, validate_integer,
                                            width=3, name=mgc.MAX_EP)
        self.akt_ep_field = CharacterValueField(self, validate_integer,
                                                width=3, name=mgc.ACT_EP,
                                                to_trace=self.ep_field.value)

        self.fp_field = CharacterValueField(self, validate_integer,
                                            width=3, name=mgc.MAX_FP)
        self.akt_fp_field = CharacterValueField(self, validate_integer,
                                                width=3, name=mgc.ACT_FP,
                                                to_trace=self.fp_field.value)

        self.is_not_living_state = IntVar()
        self.is_not_living_state.set(0)
        self.is_not_living = Checkbutton(self, text='Elettelen',
                                         variable=self.is_not_living_state)

        self.is_not_living_state.trace('w', self._set_living_state)

        ep_last_column = place_fields_separator(
            self, self.ep_field, self.akt_ep_field, label_text=EP_LABEL)

        place_fields_separator(
            self, self.fp_field, self.akt_fp_field,
            label_text=FP_LABEL, start_row=1)

        self.is_not_living.grid(column=ep_last_column, row=0)

    def reset_frame(self):
        """
        Resets all the child widgets of the frame.
        """
        on_all_children('reset_fld', self)

    def _set_living_state(self, *_args):
        if self.is_not_living_state.get():
            self.fp_field.disable()
            self.akt_fp_field.disable()

        else:
            self.fp_field.enable()
            self.akt_fp_field.enable()
Example #2
0
File: viz.py Project: rdslade/xPico
    def __init__(self, parent):
        global loaded, devicesLoaded, deviceChosen
        loaded = IntVar()
        loaded.set(getNumDevicesLoaded())
        loaded.trace("w", updateDevicesLoaded)
        s = ttk.Style()
        s.theme_use('default')
        s.configure("red.Horizontal.TProgressbar", foreground='red', background='red')
        s.configure("green.Horizontal.TProgressbar", foreground='green', background='green')
        self.parent = parent
        self.parent.title("NET232Plus Programmer")
        self.stations = []
        self.frame = tk.Frame(self.parent)
        # self.configureMenu()
        self.titleLabel = tk.Label(self.frame, text = 'Details/Instructions', font = 10)
        self.instructions = tk.Label(self.frame, text = '- Programming stations \
are labelled with both COM ports listed in config.txt\n \
            - Click START to begin the upload and verification', pady = 5)
        devices = getCOMPorts()
        # Size of window based on how many stations are present
        root_width = max(700, (len(devices) - 1) * 205)
        self.parent.geometry(str(root_width) + "x900+0+0")
        long_len = 10
        devicesLoaded = tk.Label(self.frame, text = ("Devices Loaded: " + str(loaded.get())).ljust(long_len), pady = 10)
        self.buttonFrame = tk.Frame(self.frame)
        self.clearCounter = tk.Button(self.buttonFrame, text = "Clear Counter", width = 10, bg = gridColor, height = 2, command = clearDevCounter)
        self.start = tk.Button(self.buttonFrame, text = "START", width = 22, bg = gridColor, height = 3, command = self.startUpload)

        OPTIONS = []
        for option in deviceOptions:
            OPTIONS.append(option)
        # print(OPTIONS)

        def callback(*args):
            device_details = deviceOptions[deviceChosen.get()]
            # print(device_details.web_files)
            # print(device_details.firmware_files)

        deviceChosen = StringVar(self.parent)
        deviceChosen.set(OPTIONS[0]) # default value
        deviceChosen.trace("w", callback)
        self.optionMenu = tk.OptionMenu(self.frame, deviceChosen, *OPTIONS)

        self.packObjects()
        for d in range(0, len(devices)):
            self.stations.append(Station(root, devices[d], d))
Example #3
0
class ResizeImageDialog(CustomDialog):
    def __init__(self,
                 root,
                 width,
                 height,
                 maintain_aspect_ratio=False,
                 primary_dimension='width',
                 title="Resize",
                 on_change=list(),
                 on_cancel=list(),
                 on_confirm=list(),
                 **kwargs):
        self.init_width = width
        self.init_height = height
        self.init_maintain_aspect_ratio = maintain_aspect_ratio
        self.init_primary_dimension = primary_dimension
        self.on_change = on_change
        self.on_confirm = on_confirm
        self.on_cancel = on_cancel

        self.window = Toplevel()
        self.window.title(title)
        self.window.transient(root)
        self.window.grab_set()

        self.primary_dimension = StringVar()
        self.primary_dimension.set(primary_dimension)

        self.resize_mode = StringVar()
        self.resize_mode.set('percentage')

        self.resize_percentage = IntVar()
        self.resize_percentage.set(100.0)

        self.resize_width = IntVar()
        self.resize_width.set(width)
        self.resize_height = IntVar()
        self.resize_height.set(height)

        self.maintain_aspect_ratio = IntVar()

        # See https://stackoverflow.com/a/4140988/11628429
        self.vcmd_is_float = (self.window.register(self.is_float), '%P')
        self.vcmd_is_int = (self.window.register(self.is_int), '%P')

        self.percentage_radiobutton = Radiobutton(self.window,
                                                  text='By percentage',
                                                  value='percentage',
                                                  variable=self.resize_mode,
                                                  command=self.set_mode)
        self.percentage_radiobutton.grid(row=0, column=0)

        self.percentage_entry = Spinbox(self.window,
                                        from_=0,
                                        to=float('inf'),
                                        textvariable=self.resize_percentage,
                                        validate='all',
                                        validatecommand=self.vcmd_is_float)
        self.percentage_entry.grid(row=0, column=1)

        self.absolute_radiobutton = Radiobutton(self.window,
                                                text='By absolute value',
                                                value='absolute',
                                                variable=self.resize_mode,
                                                command=self.set_mode)
        self.absolute_radiobutton.grid(row=2, column=0)

        self.ratio_checkbox = Checkbutton(self.window,
                                          text='Maintain aspect ratio',
                                          variable=self.maintain_aspect_ratio,
                                          command=self.ratio_change)
        self.ratio_checkbox.grid(row=3, column=0)

        self.width_label = Label(self.window, text='Width')
        self.width_label.grid(row=5, column=0)

        self.width_entry = Spinbox(
            self.window,
            from_=0,
            to=float('inf'),
            textvariable=self.resize_width,
            validate='all',
            validatecommand=self.vcmd_is_int
        )  # needs a command to respect aspect ratio on change
        self.width_entry.grid(row=5, column=1)

        self.height_label = Label(self.window, text='Height')
        self.height_label.grid(row=6, column=0)

        self.height_entry = Spinbox(self.window,
                                    from_=0,
                                    to=float('inf'),
                                    textvariable=self.resize_height,
                                    validate='all',
                                    validatecommand=self.vcmd_is_int)
        self.height_entry.grid(row=6, column=1)

        self.cancel_button = Button(self.window,
                                    text='Cancel',
                                    command=self.cancel)
        self.cancel_button.grid(row=9, column=0)

        self.reset_button = Button(self.window,
                                   text='Reset',
                                   command=self.reset)
        self.reset_button.grid(row=9, column=1)

        self.confirm_button = Button(self.window,
                                     text='Confirm',
                                     command=self.confirm)
        self.confirm_button.grid(row=9, column=2)

        self.resize_percentage.trace('w', self.on_percentage_change)
        self.resize_width_trace_id = self.resize_width.trace(
            'w', self.on_width_change)
        self.resize_height_trace_id = self.resize_height.trace(
            'w', self.on_height_change)
        self.set_mode()

    def set_resize_width_without_trace(self, value):
        if not isinstance(value, int):
            raise TypeError('height should be an int')
        self.resize_width.trace_vdelete("w", self.resize_width_trace_id)
        self.resize_width.set(value)
        self.resize_width_trace_id = self.resize_width.trace(
            'w', self.on_width_change)

    def set_resize_height_without_trace(self, value):
        if not isinstance(value, int):
            raise TypeError('width should be an int')
        self.resize_height.trace_vdelete("w", self.resize_height_trace_id)
        self.resize_height.set(value)
        self.resize_height_trace_id = self.resize_height.trace(
            'w', self.on_height_change)

    def on_update(self, *args):
        mode = self.resize_mode.get()
        for callback in self.on_change:
            if mode == 'percentage':
                percentage = self.resize_percentage.get() / 100
                callback(percentage, percentage,
                         self.maintain_aspect_ratio.get(),
                         self.primary_dimension.get())
            else:
                callback(self.resize_width.get(), self.resize_height.get(),
                         self.maintain_aspect_ratio.get(),
                         self.primary_dimension.get())

    def on_percentage_change(self, *args):
        self.on_update()

    def on_width_change(self, *args):
        self.primary_dimension.set('width')
        if self.maintain_aspect_ratio.get():
            self.set_resize_height_without_trace(
                round(self.init_height *
                      (self.resize_width.get() / self.init_width)))
        self.on_update()

    def on_height_change(self, *args):
        self.primary_dimension.set('height')
        if self.maintain_aspect_ratio.get():
            self.set_resize_width_without_trace(
                round(self.init_width *
                      (self.resize_height.get() / self.init_height)))
        self.on_update()

    def reset(self):
        self.resize_percentage.set(100.0)
        self.set_resize_width_without_trace(self.init_width)
        self.set_resize_height_without_trace(self.init_height)
        self.maintain_aspect_ratio.set(self.init_maintain_aspect_ratio)
        self.primary_dimension.set(self.init_primary_dimension)
        self.on_update()

    def set_mode(self):
        mode = self.resize_mode.get()
        if mode == 'percentage':
            self.percentage_entry.config(state='normal')
            self.ratio_checkbox.config(state='disabled')
            self.width_label.config(state='disabled')
            self.width_entry.config(state='disabled')
            self.height_label.config(state='disabled')
            self.height_entry.config(state='disabled')
        else:
            self.percentage_entry.config(state='disabled')
            self.ratio_checkbox.config(state='normal')
            self.width_label.config(state='normal')
            self.width_entry.config(state='normal')
            self.height_label.config(state='normal')
            self.height_entry.config(state='normal')

    def is_float(self, P):
        try:
            float(P)
            return True
        except ValueError:
            return False

    def is_int(self, P):
        return str.isdecimal(P)

    def ratio_change(self):
        if self.maintain_aspect_ratio.get():
            if self.primary_dimension.get() == 'width':
                self.set_resize_height_without_trace(
                    round(self.init_height *
                          (self.resize_width.get() / self.init_width)))
            else:
                self.set_resize_width_without_trace(
                    round(self.init_width *
                          (self.resize_height.get() / self.init_height)))
            self.on_update()
        print(self.maintain_aspect_ratio.get())

        print('w', self.resize_width.get())
        print('h', self.resize_height.get())
Example #4
0
class MyApp(Tk):
    """
    This class serves as a central control.
    Where are all process are launched and variables are set and shared.
    """

    __title__ = "Senior"

    def __init__(self, *args, **kwargs):
        Tk.__init__(self, *args, **kwargs)

        container = Frame(self)
        container.grid()

        self.serial = SerialComm()

        self.frames = {}
        self.q = LifoQueue()
        self.eye_tracker = EyeTracker(self.q)

        self.variables = {}

        self.fps = IntVar()
        self.fps.set(20)

        self.temp = IntVar()
        self.temp.set(20)
        self.temp.trace("w", self.send_command)
        self.variables[self.temp.__str__()] = 't', self.temp

        self.temp_current = StringVar()
        self.temp_current.set('Temperature')

        self.tts = StringVar()
        self.tts.set('Type and Play')

        self.temp_offset = IntVar()
        self.temp_offset.set(5)
        self.temp_offset.trace("w", self.send_command)
        self.variables[self.temp_offset.__str__()] = 'o', self.temp_offset

        self.samples = 9

        self.window = IntVar()
        self.window.trace("w", self.send_command)
        self.variables[self.window.__str__()] = 'w', self.window

        self.mouse_control = BooleanVar()
        self.mouse_control.set(False)

        self.talk = BooleanVar()
        self.talk.set(True)

        self.alarm = BooleanVar()
        self.alarm.set(False)
        self.alarm.trace("w", self.send_command)
        self.variables[self.alarm.__str__()] = 'a', self.alarm

        self.light = BooleanVar()
        self.light.set(False)
        self.light.trace("w", self.send_command)
        self.variables[self.light.__str__()] = 'l', self.light

        self.heater = BooleanVar()
        self.heater.set(False)
        self.heater.trace("w", self.send_command)
        self.variables[self.heater.__str__()] = 'h', self.heater

        self.ac = BooleanVar()
        self.ac.set(False)
        self.ac.trace("w", self.send_command)
        self.variables[self.ac.__str__()] = 'f', self.ac

        self.move = BooleanVar()
        self.move.set(False)

        self.w, self.h = pyautogui.size()

        self.hor_div = DoubleVar()
        self.hor_div.set(5)
        self.hor_div.trace("w", self.send_command)
        self.variables[self.hor_div.__str__()] = 'hor', self.hor_div

        self.ver_div = DoubleVar()
        self.ver_div.set(5)
        self.ver_div.trace("w", self.send_command)
        self.variables[self.ver_div.__str__()] = 'ver', self.ver_div

        self.mouse_directions = []

        self.mouse = MouseAndSpeech(self)
        self.t = Thread(target=self.mouse.process)
        self.t.start()

        self.frame = None
        self.draw = False

        frame = Preview(container, self)
        self.frames[Preview] = frame
        frame.grid(row=0, column=1, sticky="nsew", rowspan=100)
        self.current_frame = frame

        frame = Settings(container, self)
        self.frames[Settings] = frame
        frame.grid(row=0, column=0, sticky="nsew", pady=10, padx=10)
        frame.grid_remove()

        frame = Applications(container, self)
        self.frames[Applications] = frame
        frame.grid(row=0, column=0, sticky="nsew", pady=10, padx=10)
        frame.grid_remove()

        # Menu Bar
        menu = MyMenu(self)
        self.config(menu=menu)

        Tk.iconbitmap(self, default=resource_path('icon.ico'))
        Tk.wm_title(self, "Senior")
        w = (self.winfo_screenwidth() - self.eye_tracker.window_size) // 2
        self.geometry('+{}+{}'.format(w, 0))
        self.protocol("WM_DELETE_WINDOW", lambda: self.close())

    def show_frame(self, cont):
        """
        This method is used to switch betwen views.
        """
        if self.current_frame is not None:
            self.current_frame.grid_remove()
        frame = self.frames[cont]
        frame.grid()
        self.current_frame = frame

        if cont is Applications:
            frame.button_alarm.focus()

    def send_command(self, widget, *args):
        """
        This method send data to the Arduino whenever a button is clicked.
        """
        w = self.variables[widget]
        try:
            indicator = w[0]
            value = str(int(w[1].get()))
            if indicator == 'f' and value == '1':
                self.heater.set(False)
                self.serial.send_serial('h0')
            elif indicator == 'h' and value == '1':
                self.ac.set(False)
                self.serial.send_serial('f0')
            elif indicator == 't':
                self.heater.set(False)
                self.ac.set(False)

            s = indicator + value
            self.serial.send_serial(s)

            if len(value) > 0:
                value = float(w[1].get())
                if indicator in ['ver', 'hor']:
                    x_offset = self.ver_div.get()
                    y_offset = self.hor_div.get()
                    if indicator == 'ver':
                        y_offset = value * (self.h // 100)
                    elif indicator == 'hor':
                        x_offset = value * (self.w // 100)

                    self.mouse_directions = [(-x_offset, -y_offset),
                                             (0, -y_offset),
                                             (x_offset, -y_offset),
                                             (-x_offset, 0), 0, (x_offset, 0),
                                             (-x_offset, y_offset),
                                             (0, y_offset),
                                             (x_offset, y_offset), 0]
        except:
            pass

    def close(self):
        """
        Method used to to close the program orderly so no threads are left hanging.
        """

        print(self.__title__)
        while self.mouse.isTalking:
            print('Is Talking')

        self.eye_tracker.video_capture.release()
        clear_queue(self.q)
        self.q.put(False)
        self.q.join()
        self.t.join()
        self.destroy()
Example #5
0
class UserInterface(UIfunctions):
    def __init__(self, filehandler, databasehandler, path=None):
        self.title = "OpenKeynote (BETA)"
        self._filehandler = filehandler
        self._databasehandler = databasehandler
        self.path = path
        self.itemlist = []
        self.root = Tk()
        self.previeweditem = ""
        self.editeditem = ""
        self.case_selected = IntVar()
        self.parentname = ""
        self.autosave = IntVar()
        self._html_path = None

        self._default_title = self.title
        self.main_window()
        self.tree_view()
        self.frame_vertical_bar()
        self.bindings_and_menu()
        self.frame_setup()

        self.update_status()
        self.root.mainloop()

    def main_window(self, *args):
        self.mainframe = Frame(self.root)
        self.mainframe.grid(column=0, row=0, sticky=E+W+N+S)
        self.bottomframe = Frame(self.root)
        self.bottomframe.grid(column=0, row=1, sticky=E+W)
        self.statusbar = Label(
            self.bottomframe, text=self._filehandler.statustext, anchor=W)
        self.statusbar.pack(fill=BOTH, padx=0, pady=0)
        self.root.columnconfigure(0, weight=1)
        self.root.rowconfigure(0, weight=1)
        self.root.rowconfigure(1, pad=10)
        self.pw = PanedWindow(self.mainframe, orient=HORIZONTAL)
        self.pw.pack(fill=BOTH, expand=1)
        self.pane_left = Frame(self.root)
        self.pw.add(self.pane_left)
        self.pane_right = Frame(self.root)
        self.pw.add(self.pane_right)

        self.frame_left = Frame(self.pane_left)
        self.frame_left.pack(fill=BOTH, expand=1, padx=3, pady=3)
        self.frame_center = Frame(self.pane_right)
        self.frame_center.grid(row=0, column=0, sticky=W+N, rowspan=4)
        self.frame_right = Frame(self.pane_right)
        self.frame_right.grid(row=0, column=1, sticky=W+E+N+S, padx=3, pady=3)
        self.pane_right.columnconfigure(1, weight=1)
        self.pane_right.rowconfigure(0, weight=1)
        self.sf1 = Text(self.frame_left, height=1, width=25, borderwidth=1,
                        relief="solid", highlightthickness=0)
        self.sf1.insert(1.0, "TODO: Searchbar")
        self.sf1.grid(row=0, column=0, sticky=W+E+N+S, pady=5)
        self.sf1.config(state=DISABLED)
        self.cs = Button(self.frame_left, text="X", width=1)
        self.cs.grid(row=0, column=1)
        self.frame_left.columnconfigure(0, weight=1)

    def tree_view(self, *args):
        """
        Tree view
        """
        self.l1 = ttk.Treeview(self.frame_left, columns=["stuff"], show="tree")
        self.yscroll = Scrollbar(self.frame_left, orient=VERTICAL)
        self.yscroll.config(width=10)
        self.l1['yscrollcommand'] = self.yscroll.set
        self.yscroll['command'] = self.l1.yview
        self.l1.grid(row=1, column=0, columnspan=3,  padx=30,
                     pady=10, sticky=N+S+E+W)
        self.yscroll.grid(row=1, column=0, columnspan=3, sticky=N+S+E)
        self.l1.bind("<ButtonRelease-1>", self.change_selection)

        self.frame_left.rowconfigure(1, weight=1)

    def frame_vertical_bar(self, *args):
        self.vbs = []
        middlebuttons = ("New Item", "New Subitem", "Delete",
                         "Rename", "Change Parent","- Descriptions (BETA) -")
        middlefunctions = (
            lambda: self.add_item(parent=self.parentname),
            lambda: self.add_item(parent=self.previeweditem),
            lambda: self.delete_item_dialog(),
            self.rename_item_dialog,
            self.change_parent_dialog,
            lambda: self.description_window(database_rows=self._databasehandler.view())
            #lambda: self.save_keynote_to_database(title="title",keynote="KN10", entreprise="min entreprise", category="")
            #self.save_all_keynotes_to_database
            )

        for a, button_text in enumerate(middlebuttons):
            self.vbs.append(ttk.Button(self.frame_center, text=button_text))
            self.vbs[a].pack(fill=BOTH)
            self.vbs[a].config(command=middlefunctions[a], width=10)
        for x in [2, 3, 4]:
            self.vbs[x].config(state=DISABLED)
        self.tx1 = Label(self.frame_right, text="Preview", anchor=W)
        self.tx1.grid(row=0, column=0, columnspan=3, sticky=W+E)
        self.tx2 = Label(self.frame_right, text="Editing", anchor=W)
        self.tx2.grid(row=2, column=0, sticky=W+E)

        self.e1 = scrolledtext.ScrolledText(self.frame_right,
                                            fg="#555", font=("Courier", 13),
                                            padx=10, pady=10,
                                            highlightthickness=0,
                                            borderwidth=1, relief="solid")
        self.e1.grid(row=1, column=0, columnspan=3, sticky=N+S+E+W)
        # was Text before

        self.e2 = scrolledtext.ScrolledText(self.frame_right,
                                            font=("Courier", 13), borderwidth=1,
                                            relief="solid", padx=10, pady=10,
                                            highlightthickness=0)
        self.e2.grid(row=3, column=0, columnspan=3, sticky=E+W+S+N)
        # AUTOSAVE
        self.autosaveFrame = LabelFrame(self.frame_center, text=' Autosave ')
        self.autosaveFrame.pack(fill=BOTH)
        self.autosave.trace(
            'w', lambda *args: print(f"Autosave: {self.autosave.get()}"))
        self.autosaveCheck = Checkbutton(
            self.autosaveFrame, text="Enabled", variable=self.autosave, anchor=W)
        self.autosaveCheck.select()
        self.autosaveCheck.pack(fill=BOTH)
        self.labelsFrame = LabelFrame(self.frame_center, text=' Change Case ')
        self.labelsFrame.pack(fill=BOTH)

        # CASE BUTTONS
        self.case_radiobuttons = []
        self.case_selected.set(99)
        rbtns = ['No change', 'UPPERCASE', 'lowercase', 'First Letter']
        for a, button_text in enumerate(rbtns):
            self.case_radiobuttons.append(
                Radiobutton(self.labelsFrame, text=button_text,
                            variable=self.case_selected,
                            value=a, command=self.change_case, width=10, anchor=W))
            self.case_radiobuttons[a].grid(sticky="W", row=a)

    def change_case(self, *args):
        pass

    def bindings_and_menu(self, *args):
        """
        Main key bindings
        """
        if os.name == "nt":
            self.CTRL = "Control"
            self.MBTN = "3"
        else:
            self.CTRL = "Command"
            self.MBTN = "2"

        def bindings_key(event):
            if event.state == 8 or event.state == 12:
                return
            else:
                return("break")

        self.sf1.bind("<Tab>", lambda a: self.focus_on(target=self.l1))
        self.sf1.bind("<Shift-Tab>", lambda a: self.focus_on(target=self.vb2))
        self.e1.bind("<Key>", bindings_key)
        self.e1.bind("<Tab>", lambda a: self.focus_on(target=self.e2))
        self.e1.bind(
            "<Shift-Tab>", lambda a: self.focus_on(target=self.vbs[-1]))

        self.e2.bind("<Tab>", lambda a: self.focus_on(target=self.vb1))
        self.e2.bind("<Shift-Tab>", lambda a: self.focus_on(target=self.e1))

        self.vb1 = ttk.Button(self.frame_right, text="Edit")
        self.vb1.grid(row=2, column=1)
        self.vb1.config(command=self.edit_item)
        self.vb2 = ttk.Button(self.frame_right, text="Save")
        self.vb2.grid(row=2, column=2)
        self.vb2.config(command=self.saveitem)

        self.frame_right.rowconfigure(1, weight=1)
        self.frame_right.rowconfigure(3, weight=1)
        self.frame_right.columnconfigure(0, weight=1)

        self.menu = Menu(self.root)
        self.root.config(menu=self.menu)
        file = Menu(self.menu, tearoff=0)  # TODO is it a win thing?
        file.add_command(label='New File*', command=self.close_file)
        file.add_command(
            label='Open File...', accelerator=f"{self.CTRL}-o",
            command=self.open_file_dialog)
        file.add_command(label='Save File',
                         accelerator=f"{self.CTRL}-s", command=self.save_file)
        file.add_command(label='Save File As...',
                         command=self.save_file_dialog)
        file.add_command(label='Close file', command=self.close_file)
        file.add_command(
            label='Exit', accelerator=f"{self.CTRL}-q", command=self.client_exit)
        self.menu.add_cascade(label='File', menu=file)

        self.clickmenu = Menu(self.root, tearoff=0)
        self.clickmenu.add_command(label="Cut")
        self.clickmenu.add_command(label="Copy")
        self.clickmenu.add_command(label="Paste")
        self.root.bind_class(
            "Text", f"<Button-{self.MBTN}><ButtonRelease-{self.MBTN}>",
            lambda event=None: self.right_click_menu())

        menu_edit = Menu(self.menu, tearoff=0)
        menu_edit.add_command(label='Select All', accelerator=f"{self.CTRL}-a",
                              command=self.select_all)
        self.root.bind(f"<{self.CTRL}-a>", self.select_all)
        self.e1.bind(f"<{self.CTRL}-a>", self.select_all)

        self.e1.bind(f"<{self.CTRL}-c>", self.e1.event_generate("<<Copy>>"))
        self.e2.bind(f"<{self.CTRL}-a>", self.select_all)
        menu_edit.add_command(label='Cut', accelerator=f"{self.CTRL}-x",
                              command=lambda: self.root.event_generate("<<Cut>>"))
        menu_edit.add_command(label='Copy', accelerator=f"{self.CTRL}-c",
                              command=lambda: self.copy_text())
        menu_edit.add_command(label='Paste', accelerator=f"{self.CTRL}-v",
                              command=lambda: self.root.event_generate("<<Paste>>"))
        self.menu.add_cascade(label='Edit', menu=menu_edit)

        menu_help = Menu(self.menu, tearoff=0)
        menu_help.add_command(label='About', command=self.about)
        self.menu.add_cascade(label='Help', menu=menu_help)

        for i in "Up,Down,Right,Return,Left".split(","):
            self.root.bind("<"+i+">", self.change_selection)

        self.e1.bind("<F2>", self.rename_item_dialog)
        self.e1.bind(f"<{self.CTRL}-s>", None)
        self.e1.bind(f"<{self.CTRL}-o>", None)
        self.root.bind("<F2>", self.rename_item_dialog)
        self.root.bind(f"<{self.CTRL}-s>", self.save_file)
        self.root.bind(f"<{self.CTRL}-o>", self.open_file_dialog)

    def copy_text(self, event=None):
        w = self.root.focus_get()
        w.event_generate("<<Copy>>")

    def update_title(self, title=""):
        self.root.title(title)

    def frame_setup(self, *args):
        """
        Misc UI functions
        """

        # sharp fonts in high res (https://stackoverflow.com/questions/41315873/
        # attempting-to-resolve-blurred-tkinter-text-scaling-on-windows-10-high-dpi-disp)
        if os.name == "nt":
            # TODO
            # self.root.protocol("WM_DELETE_WINDOW", self.client_exit)
            from ctypes import windll, pointer, wintypes
            try:
                windll.shcore.SetProcessDpiAwareness(1)
            except Exception:
                pass  # this will fail on Windows Server and maybe early Windows
            # TODO: Put link to ico file on windows.
            try:
                iconpath = Path("icon.ico")
                self.root.iconbitmap(Path())
            except:
                print("error with icon")
            else:  # mac?
                self.root.createcommand('exit', self.client_exit)

        self.root.title(self.title)
        if self.path:
            self.open_file(path=self.path)

        """ TODO: ICON /Windows

        self.root.iconbitmap("/Users/msn/Dropbox/py/Git/OpenKeynote/images/ico.icns")

        img = Image(
            "photo", file="/Users/msn/Dropbox/py/Git/OpenKeynote/images/large.gif")
        self.root.iconphoto(True, img) # you may also want to try this.
        self.root.call('wm','iconphoto', self.root._w, img)
        """
        self.width = min(int(self.root.winfo_screenwidth()-500), 1500)
        self.height = int(self.root.winfo_screenheight()-500)
        self.root.winfo_width()
        self.root.winfo_height()
        self.x = (self.root.winfo_screenwidth() // 2) - (self.width // 2)
        # self.x = 0
        self.y = (self.root.winfo_screenheight() // 2) - (self.height // 2)
        # self.y = 50
        self.root.geometry(f"{self.width}x{self.height}+{self.x}+{self.y}")
        self.root.update()
        self.root.after(0, self.fixUI)

    def right_click_menu(self, event=None):
        x, y = self.root.winfo_pointerxy()
        w = self.root.winfo_containing(x, y)
        # https://stackoverflow.com/a/8476726/11514850
        # w = self.root
        self.clickmenu.entryconfigure("Cut",
                                      command=lambda: w.event_generate("<<Cut>>"))
        self.clickmenu.entryconfigure("Copy",
                                      command=lambda: w.event_generate("<<Copy>>"))
        self.clickmenu.entryconfigure("Paste",
                                      command=lambda: w.event_generate("<<Paste>>"))
        self.clickmenu.tk.call("tk_popup", self.clickmenu,
                               w.winfo_pointerx(), w.winfo_pointery())

    def update_status(self, event=None):
        """
        Set statusbar in bottom of the window
        """
        self.statusbar.config(text=self._filehandler.refresh_status())
        self.root.after(100, self.update_status)

    def client_exit(self, *args):
        answer = messagebox.askyesnocancel('quit?', 'Save file first?')
        if answer == True:
            self.save_file()
            sys.exit()
        if answer == None:
            return
        if answer == False:
            exit()
Example #6
0
class App(Tk):
    STEP_MODE: IntVar  # Пошаговое проигрывание анимации.
    SHOW_INFLECTION_POINTS: IntVar

    started = False  # Мы начали искать корни, в это время нельзя менять менять уравнение.
    paused = False  # Пауза для анимации.
    done: bool = False
    st: float
    en: float
    div: int
    eps: float

    lin_x: any  # Множество точек для построения графика.
    lin_space_size: int = 400  # Кол-во точек для построения графика

    solver: HordEquationSolver

    expr: StringVar  # Введенное пользователем выражение

    # График
    ax: plt.Axes
    plots: List[Line2D] = []
    h_lines: List[Line2D] = []  # Горизонтальные линии
    main_plot: any = None
    deriv_plot: any = None
    inflection_points: any = None

    # Список решений
    solutions: List[Solution] = []
    solution_ids: List[str] = []

    tree: Treeview  # Таблица результатов
    b_start: Button  # Кнопка начала/остановки
    lin_space_label: Label  # Надпись о точности графика

    cached_function: any  # Декодированная функция, что бы каждый раз не вызывать eval
    after_job_id: any = None  # id отложенного вызова функции для её отмены

    def f(self, x):
        return self.cached_function(x)

    def __init__(self, st: float, en: float, div: int, eps: float):
        super().__init__()
        self.st = st
        self.en = en
        self.div = div
        self.eps = eps
        self.lin_x = np.linspace(
            st, en,
            self.lin_space_size)  # Множество точек для построения графика.

        fig = plt.Figure(tight_layout=True)
        self.ax = fig.add_subplot()

        self.ax.axhline(0, color='black')

        self.grid_columnconfigure(0, weight=2)
        self.grid_columnconfigure(1, weight=1)
        self.grid_rowconfigure(0, weight=1)
        self.fagg = FigureCanvasTkAgg(fig, self)
        self.fagg.get_tk_widget().grid(row=0, column=0, sticky='WNSE')
        self.frame = Frame(self)
        self.frame.grid(row=0, column=1, sticky='WNSE', rowspan=2)

        self.init_sidebar()

        self.solver = HordEquationSolver(st, en, div, eps, self.f)

        self.prepare()

        self.fagg.draw()
        button_frame = Frame(self)
        button_frame.grid(row=1, column=0, sticky='WE')
        self.b_start = Button(button_frame, text='start')
        self.b_start.pack(side='left', anchor='center')
        self.b_start.bind('<Button>', self.start)
        Button(button_frame, text='reset',
               command=self.reset).pack(side='left', anchor='center')

    def init_sidebar(self):
        self.cached_function = eval('lambda x: ' + DEFAULT_EXPRESSION)
        self.expr = StringVar(self)
        self.expr.set(DEFAULT_EXPRESSION)
        self.expr.trace_variable('w', self.var_debounce(self.expression_input))

        # Динамические переменные для входных полей
        start_v = DoubleVar(self, value=self.st)
        end = DoubleVar(self, value=self.en)
        epsilon = DoubleVar(self, value=self.eps)
        divs = IntVar(self, value=self.div)
        lin_space_var = DoubleVar(self,
                                  value=math.log(self.lin_space_size,
                                                 LOG_BASE))
        variables = ((start_v, 'st'), (end, 'en'), (epsilon, 'eps'), (divs,
                                                                      'div'))

        # Функция обертка для сигнализирования о смене переменной.
        def outer(var, var_name):
            def inner(*_args):
                try:
                    self.params_input(var.get(), var_name)
                except Exception:
                    pass

            return inner

        for (v, name) in variables:
            v.trace('w', self.debounce(outer(v, name), 250))

        lin_debouncer = self.debounce(self.modify_lin_space_size, 150)
        lin_space_var.trace(
            'w', lambda *_args: self.modify_lin_space_size_callback(
                lin_space_var.get(), lin_debouncer))

        self.frame.columnconfigure(1, weight=2)
        Label(self.frame, text='Выражение:').grid(column=0,
                                                  row=0,
                                                  columnspan=2,
                                                  sticky='EW')
        Entry(self.frame, textvariable=self.expr).grid(column=0,
                                                       row=1,
                                                       columnspan=2,
                                                       sticky='EW')

        self.frame.rowconfigure(2, minsize=25)
        Label(self.frame, text='Начало').grid(column=0, row=3, sticky='W')
        Entry(self.frame, textvariable=start_v).grid(column=1,
                                                     row=3,
                                                     sticky='EW')

        Label(self.frame, text='Конец').grid(column=0, row=4, sticky='W')
        Entry(self.frame, textvariable=end).grid(column=1, row=4, sticky='EW')

        Label(self.frame, text='Точность').grid(column=0, row=5, sticky='W')
        Entry(self.frame, textvariable=epsilon).grid(column=1,
                                                     row=5,
                                                     sticky='EW')

        Label(self.frame, text='Разделение').grid(column=0, row=6, sticky='W')
        Entry(self.frame, textvariable=divs).grid(column=1, row=6, sticky='EW')

        self.frame.rowconfigure(7, minsize=25)
        self.lin_space_label = Label(
            self.frame,
            text=f'Количество точек графика: {self.lin_space_size}')
        self.lin_space_label.grid(column=0, row=8, columnspan=2, sticky='W')
        w = Scale(self.frame,
                  from_=math.log(5, LOG_BASE),
                  to=math.log(MAX_LINE_SPACE_SIZE, LOG_BASE),
                  resolution=0.1 / LOG_BASE,
                  orient=HORIZONTAL,
                  variable=lin_space_var)
        w.grid(column=0, row=9, columnspan=2, sticky='EW')

        self.tree = Treeview(self.frame)
        self.tree['columns'] = (1, 2, 3, 4, 5)
        self.tree.column('#0', width=35)
        self.tree.column(1, width=130, anchor='center')
        self.tree.column(2, width=80, anchor='center')
        self.tree.column(3, width=80, anchor='center')
        self.tree.column(4, width=80, anchor='center')
        self.tree.column(5, width=80, anchor='center')
        self.tree.heading('#0', text='№')
        self.tree.heading(1, text='Интервал')
        self.tree.heading(2, text='Корень')
        self.tree.heading(3, text='Значение')
        self.tree.heading(4, text='Итераций')
        self.tree.heading(5, text='Ошибка')

        self.tree.grid(column=0, row=10, columnspan=2, sticky='EWSN')

        self.STEP_MODE = IntVar(self, value=0)
        self.SHOW_INFLECTION_POINTS = IntVar(self, value=1)
        self.SHOW_INFLECTION_POINTS.trace(
            'w', lambda *_args: self.redraw_main_plot(True))
        Checkbutton(self.frame,
                    text='Пошаговый режим',
                    variable=self.STEP_MODE).grid(column=0,
                                                  row=11,
                                                  sticky='WS')
        Checkbutton(self.frame, text='Показывать точка перегиба', variable=self.SHOW_INFLECTION_POINTS)\
            .grid(column=0, row=12, sticky='WS')

    def modify_lin_space_size_callback(self, value, callback):
        self.lin_space_label.configure(
            text=f'Количество точек графика: {round(LOG_BASE**value)}')
        callback(value)

    def modify_lin_space_size(self, size):
        self.lin_space_size = round(LOG_BASE**size)
        self.redraw_main_plot(True)

    def redraw_main_plot(self, draw=False):
        if self.st != self.lin_x.min() or self.en != self.lin_x.max(
        ) or self.lin_space_size != len(self.lin_x):
            self.lin_x = np.linspace(self.st, self.en, self.lin_space_size)

        if self.main_plot:
            self.main_plot.remove()
        if self.deriv_plot:
            self.deriv_plot.remove()
            self.deriv_plot = None
        if self.inflection_points:
            self.inflection_points.remove()
            self.inflection_points = None

        v = self.f(self.lin_x)
        self.main_plot = self.ax.plot(self.lin_x,
                                      v,
                                      label='f(x)',
                                      color='tab:blue')[0]

        if self.SHOW_INFLECTION_POINTS.get():
            v2 = np.diff(v)
            v2 = np.insert(v2, 0, v2[0] - (v2[1] - v2[0]))
            v2 /= ((self.en - self.st) / self.lin_space_size)

            v2 = np.diff(v2)
            second_derivative_is_zero = all([x < self.eps for x in v2])
            if not second_derivative_is_zero:
                v2 = np.append(v2, v2[-1])
                v2 /= ((self.en - self.st) / self.lin_space_size)
                inflection_points_x = []
                inflection_points_y = []
                for i in range(1, len(v2)):
                    if v2[i - 1] * v2[i] <= 0:
                        if v[i - 1] == 0:
                            continue
                        n = i - 1 if v2[i - 1] < v2[i] else i
                        x = self.st + (self.en -
                                       self.st) / self.lin_space_size * n
                        y = self.f(x)

                        inflection_points_x.append(x)
                        inflection_points_y.append(y)

                self.inflection_points = self.ax.scatter(inflection_points_x,
                                                         inflection_points_y,
                                                         80,
                                                         marker='x',
                                                         color='violet')

                self.deriv_plot = self.ax.plot(self.lin_x,
                                               v2,
                                               label="f''(x)",
                                               color='tab:green')[0]

        mx_y = max(v)
        mn_y = min(v)
        m = (mx_y - mn_y) / 50
        dx = abs(self.en - self.st) * 0.05
        self.ax.set_ylim(mn_y - m, mx_y + m)
        self.ax.set_xlim(self.st - dx, self.en + dx)

        if draw:
            self.fagg.draw()

    # Первичное отображение, подготавливает все данные для него.
    def prepare(self):
        step = (self.en - self.st) / self.div

        self.redraw_main_plot()

        for plt in self.plots:
            plt.remove()
        self.plots.clear()
        for line in self.h_lines:
            line.remove()
        self.h_lines.clear()

        for i in range(self.div + 1):
            x = (self.st + step * i)
            self.h_lines.append(self.ax.axvline(x, color='black'))

        self.plots = [
            self.ax.plot(self.lin_x,
                         self.solver.hord.k * self.lin_x + self.solver.hord.b,
                         label='lin',
                         color='tab:orange')[0],
            self.ax.scatter([self.solver.p1.x], [self.solver.p1.y],
                            marker='o',
                            color='red'),
            self.ax.scatter([self.solver.p_fixed.x], [self.solver.p_fixed.y],
                            marker='o',
                            color='blue')
        ]
        self.fagg.draw()

    def var_debounce(self,
                     func: Callable[[str], None],
                     t: int = 500) -> Callable:
        def inner(*args):
            func(self.tk.globalgetvar(args[0]))

        return self.debounce(inner, t)

    def debounce(self, func: Callable[..., None], t: int = 500) -> Callable:
        return Debouncer(self, func, t)

    def expression_input(self, value):
        try:
            self.cached_function = eval('lambda x: ' + value)
            self.reset()
        except Exception as e:
            # traceback.print_exc()
            # print(e)
            pass

    def params_input(self, value, var_name):
        self.__setattr__(var_name, value)
        self.reset()

    def reset(self):
        if self.after_job_id:
            self.after_cancel(self.after_job_id)
        self.started = False
        self.done = False
        self.tree.delete(*self.solution_ids)
        self.solutions.clear()
        self.solution_ids.clear()
        self.solver = HordEquationSolver(self.st, self.en, self.div, self.eps,
                                         self.f)
        self.b_start.configure(text='start')
        self.b_start.bind('<Button>', self.start)
        self.prepare()

    def start(self, event):
        if self.STEP_MODE.get():
            self.step_solve()
        else:
            if not self.started or self.paused:
                self.started = True
                self.paused = False
                self.step_solve()
                event.widget.configure(text='stop')
                event.widget.bind('<Button>', self.stop)

    def stop(self, event):
        self.paused = True
        if self.after_job_id:
            self.after_cancel(self.after_job_id)
        event.widget.configure(text='start')
        event.widget.bind('<Button>', self.start)

    # Перерисовка хорды и точек пересечения.
    def redraw_solution(self):
        for pt in self.plots:
            pt.remove()
        self.plots = [
            self.ax.plot(self.lin_x,
                         self.solver.hord.k * self.lin_x + self.solver.hord.b,
                         label='lin',
                         color='tab:orange')[0],
            self.ax.scatter([self.solver.p1.x], [self.solver.p1.y],
                            marker='o',
                            color='red'),
            self.ax.scatter([self.solver.p_fixed.x], [self.solver.p_fixed.y],
                            marker='o',
                            color='blue')
        ]
        self.fagg.draw()
        self.ax.relim()

    def step_solve(self):
        if self.started and not self.solver.done or self.STEP_MODE.get():
            status = self.solver.next_step()
            self.redraw_solution()
            if not status:
                if self.started and not self.paused and not self.STEP_MODE.get(
                ):
                    self.after_job_id = self.after(100, self.step_solve)
            else:
                self.add_solution(self.solver.get_solution())
                if self.solver.next_segment():
                    print('DONE!!!!')
                    self.b_start.configure(text='DONE!!!!')
                    self.b_start.unbind('<Button>')
                else:
                    self.redraw_solution()
                    if not self.STEP_MODE.get():
                        self.after_job_id = self.after(200, self.step_solve)

    def add_solution(self, sol):
        if sol.err == 0:
            interval = f'({sol.interval[0]:.5} : {sol.interval[1]:.5})'
            iid = self.tree.insert('',
                                   'end',
                                   text=str(len(self.solutions) + 1),
                                   values=(interval, f'{sol.x:.7}',
                                           f'{sol.y:.5}', sol.iter, sol.err))
        else:
            iid = self.tree.insert('',
                                   'end',
                                   text=str(len(self.solutions) + 1),
                                   values=('', '', '', '', sol.err))
        self.solutions.append(sol)
        self.solution_ids.append(iid)
Example #7
0
class PanelFrame(tk.Frame):
    def __init__(self, parent):

        tk.Frame.__init__(self, parent)
        self.parent = parent

        CTRL_BUTTONS = ["TOGGLE BT", "SET PORT", "DELAY (seconds)"]
        #use radiobuttons to set one thing at once? -toggle bt starts the run...
        ENTRIES = ["", str(use_Reader(1)), str(use_Reader(3))]

        for i in range(3):
            print(ENTRIES[i])

        buttons = []
        self.entries = []
        self.e_vars = []
        self.var = IntVar()
        #self.colconfigure(0, weight=1)
        #self.colconfigure(1, weight=0)
        self.prev = 0
        self.active = 0
        self.var.set(0)

        for i in range(len(CTRL_BUTTONS)):
            el = self.create_button_frame(CTRL_BUTTONS[i], ENTRIES[i], i)
            buttons.append(el)
            e_var = StringVar()
            #print(ENTRIES[i])#debug
            e_var.set(ENTRIES[i])
            e = Entry(self, textvariable=e_var)

            self.entries.append(e)
            self.e_vars.append(e_var)

            #self.rowconfigure(1, weight=1)
            el.grid(row=0, column=i, sticky="nsew")
            e.grid(row=1, column=i, sticky="nsew")

        self.prev_vars = []
        for ev in self.e_vars:
            self.prev_vars.append(ev.get())

        #disable useless entry under "toggle BT"
        self.entries[0].config(state=tk.DISABLED)

        self.activate_entries()
        self.var.trace("w", self.activate_entries)

    def create_button_frame(self, b_name, entry_text, index):

        button = Radiobutton(self, text=b_name,indicatoron=False,\
                        variable=self.var, value=index)

        return button

    def activate_entries(self, *args):

        #CTRL_BUTTONS =["TOGGLE BT","SET PORT", "DELAY (seconds)"]

        self.active = self.var.get()
        for i in range(1, len(self.e_vars)):
            if i == self.active:
                #self.entries[i].enable()
                self.prev_vars[i] = self.e_vars[i].get()
                self.entries[i].config(state=tk.NORMAL)

            else:
                #copy the old values (just debug)
                self.prev_vars[i] = self.e_vars[i].get()
                #self.e_vars[i].set(self.prev_vars[self.active])
                #self.entries[i].disable()
                self.entries[i].config(state=tk.DISABLED)

        if self.active == 0:
            manage_Reader(1)  #connect

        else:
            manage_Reader(2)  #disconnect

        if self.active == 1:  #set port
            #print(self.e_vars[1].get())#debug
            if (self.e_vars[1].get().isalnum()):
                setup_Reader(2, self.e_vars[1].get())

        elif self.prev == 2:  #set delay
            #print(self.e_vars[2].get())#debug
            if (self.e_vars[2].get().isdigit()
                    and self.e_vars[2].get().isalnum()):
                setup_Reader(4, int(self.e_vars[2].get()))

        #if self.prev == self.active:
        #   self.var.set(len(self.e_vars))
        self.prev = self.active
class ImageSlider(Frame):
    try:
        NOIMAGE = PILImage.open(
            open(
                os.path.join(os.path.dirname(__file__), 'images/noimages.png'),
                'rb'))
        BUTTONLEFT = PILImage.open(
            open(os.path.join(os.path.dirname(__file__), 'images/left.png'),
                 'rb'))
        BUTTONRIGHT = PILImage.open(
            open(os.path.join(os.path.dirname(__file__), 'images/right.png'),
                 'rb'))
    except FileNotFoundError as e:
        try:
            NOIMAGE = PILImage.open(open('images/noimages.png', 'rb'))
            BUTTONLEFT = PILImage.open(open('images/left.png', 'rb'))
            BUTTONRIGHT = PILImage.open(open('images/right.png', 'rb'))
        except Exception as f:
            raise f

    def __init__(self,
                 parent,
                 directory=os.getcwd(),
                 placeholder=NOIMAGE,
                 leftarrow=BUTTONLEFT,
                 rightarrow=BUTTONRIGHT,
                 *args,
                 **kw):
        Frame.__init__(self, parent, *args, **kw)
        self.directory = directory
        if placeholder != ImageSlider.NOIMAGE:
            placeholder = PILImage.open(open(placeholder, 'rb'))
        if leftarrow != ImageSlider.BUTTONLEFT:
            leftarrow = PILImage.open(open(leftarrow, 'rb'))
        if rightarrow != ImageSlider.BUTTONRIGHT:
            rightarrow = PILImage.open(open(rightarrow, 'rb'))
        self.placeholder = ImageTk.PhotoImage(placeholder)
        self.leftarrow, self.rightarrow = ImageTk.PhotoImage(
            leftarrow), ImageTk.PhotoImage(rightarrow)
        self.pics, self.photoims = None, None
        self.scalevar = IntVar()
        tf = Frame(self, bg=self['bg'])
        tf.pack(fill='both', expand=True)
        t1 = Frame(tf, bg=self['bg'])
        t1.pack(side='left', fill='y')
        Button(
            t1,
            image=self.leftarrow,
            command=lambda: self.scalevar.set(self.scalevar.get() - 1)).pack(
                side='left')
        t2 = Frame(tf, bg=self['bg'])
        t2.pack(side='left', fill='both', expand=True)
        self.imagelabel = Label(t2,
                                image=self.placeholder,
                                text='',
                                compound='top')
        self.imagelabel.pack(side='bottom')
        t3 = Frame(tf, bg=self['bg'])
        t3.pack(side='right', fill='y')
        Button(
            t3,
            image=self.rightarrow,
            command=lambda: self.scalevar.set(self.scalevar.get() + 1)).pack(
                side='right')
        self.scalevar.trace('w', lambda *event: self.changepic())
        self.slider = Scale(self, variable=self.scalevar, orient='horizontal')
        self.slider.pack(side='bottom')
        self.loaddirectory(directory)

    def loaddirectory(self, directory):
        self.directory = directory
        self.pics = [
            directory + '/' + pic for pic in os.listdir(directory)
            if pic.split('.')[-1] in ('gif', 'png', 'jpg')
        ]
        pics = [PILImage.open(open(pic, 'rb')) for pic in self.pics]
        for pic in pics:
            if max(pic.size) > 200:
                pic.thumbnail((200, 200), PILImage.ANTIALIAS)
        self.photoims = [ImageTk.PhotoImage(pic) for pic in pics]
        if not self.photoims:
            self.scalevar.set(0)
            self.slider.configure(from_=0, to_=0)
            self.imagelabel.configure(image=self.placeholder, text='')
        else:
            self.slider.configure(from_=1, to=len(self.photoims))
            self.scalevar.set(1)

    def changepic(self):
        if not self.photoims: return
        index = self.scalevar.get()
        if index == 0: self.scalevar.set(len(self.photoims))
        elif index > len(self.photoims): self.scalevar.set(1)
        else:
            self.imagelabel.configure(image=self.photoims[index - 1],
                                      text=self.pics[index - 1].rsplit(
                                          '/', maxsplit=1)[-1])

    def getpic(self):
        return self.pics[self.scalevar.get() - 1]
Example #9
0
class Roller(Frame):
    def __init__(self, group, index):
        Frame.__init__(self, group)

        self.group = group
        self.index = index
        self.results = [0]
        self.history = []

        self.name = StringVar()
        self.dice_qty = IntVar()
        self.die_faces = IntVar()
        self.modifier = IntVar()
        self.finalmod = IntVar()
        self.results_text = StringVar()

        self.name.trace('w', self.group.mainframe.set_unsaved_title)
        self.dice_qty.trace('w', self.group.mainframe.set_unsaved_title)
        self.die_faces.trace('w', self.group.mainframe.set_unsaved_title)
        self.modifier.trace('w', self.group.mainframe.set_unsaved_title)
        self.finalmod.trace('w', self.group.mainframe.set_unsaved_title)
        self.results_text.trace('w', self.group.mainframe.set_unsaved_title)

        default_font = ('Courier', 14)

        self.menu_btn = Menubutton(self,
                                   bd=1,
                                   relief='solid',
                                   font=('Courier', 8),
                                   text='\u25e2',
                                   takefocus=1,
                                   highlightthickness=1)
        self.name_entry = Entry(self,
                                bd=1,
                                relief='solid',
                                font=('Verdana', 12),
                                width=16,
                                textvariable=self.name)

        self.dice_qty_spin = NumericSpinner(self,
                                            self.dice_qty,
                                            1,
                                            99,
                                            callback=self.reset,
                                            initial=1)
        self.die_faces_spin = NumericSpinner(
            self,
            self.die_faces,
            2,
            100,
            interval=self.group.mainframe.allow_odd.get(),
            initial=10)
        self.modifier_spin = NumericSpinner(self,
                                            self.modifier,
                                            -99,
                                            100,
                                            callback=self.apply_modifiers)
        self.finalmod_spin = NumericSpinner(self,
                                            self.finalmod,
                                            -99,
                                            100,
                                            callback=self.apply_modifiers)
        self.dice_lbl = Label(self, text=' d', font=default_font)
        self.modifier_lbl = Label(self, text='\u002b', font=default_font)
        self.finalmod_lbl = Label(self, text='\u002b', font=default_font)

        self.roll_btn = Button(self,
                               bd=0,
                               image=self.group.roll_img,
                               command=lambda: self.roll(single=True))
        self.results_entry = Entry(self,
                                   bd=0,
                                   relief='solid',
                                   font=default_font,
                                   width=0,
                                   textvariable=self.results_text,
                                   state='readonly',
                                   justify='center')

        self.menu_btn.config(menu=self.create_menu())

        self.menu_btn.grid(row=index, column=0, padx=(4, 0))
        self.name_entry.grid(row=index, column=1, padx=(4, 0))
        self.dice_qty_spin.grid(row=index, column=2, padx=(4, 0))
        self.dice_lbl.grid(row=index, column=3, padx=(0, 0))
        self.die_faces_spin.grid(row=index, column=4, padx=(0, 0))
        self.modifier_lbl.grid(row=index, column=5, padx=(6, 6))
        self.modifier_spin.grid(row=index, column=6, padx=(0, 0))
        self.roll_btn.grid(row=index, column=7, padx=(8, 0))
        self.results_entry.grid(row=index, column=8, padx=(8, 0))
        self.finalmod_lbl.grid(row=index, column=9, padx=(6, 6))
        self.finalmod_spin.grid(row=index, column=10, padx=(0, 4))

        self.name.set('Roller {}'.format(len(self.group.rollers) + 1))
        self.die_faces.set(10)
        self.results_text.set('0 = 0')

        self.grid(row=index, sticky='w', pady=4)

    def create_menu(self):
        menu = Menu(self.menu_btn,
                    tearoff=0,
                    postcommand=self.group.maintain_roller_indices)

        menu.add_command(label='Add', underline=0, command=self.add_roller)
        menu.add_command(label='Clone',
                         underline=0,
                         command=lambda: self.add_roller(clone=True))
        menu.add_command(label='Up',
                         underline=0,
                         command=lambda: self.move_roller(offset=-1))
        menu.add_command(label='Down',
                         underline=0,
                         command=lambda: self.move_roller(offset=1))
        menu.add_separator()  #  ------
        menu.add_command(label='Remove',
                         underline=0,
                         command=self.remove_roller)

        return menu

    def create_hist_record(self):
        record = {
            'dice_qty': self.dice_qty.get(),
            'die_faces': self.die_faces.get(),
            'modifier': self.modifier.get(),
            'results_text': self.results_text.get(),
            'finalmod': self.finalmod.get(),
            'timestamp': str(dt.now().time())[:8],
            'results': self.results
        }
        return record

    def add_roller(self, clone=False):
        destination_index = self.index + 1

        roller = Roller(self.group, destination_index)
        self.group.rollers.insert(roller.index, roller)

        for i in range(destination_index, len(self.group.rollers)):
            self.group.rollers[i].grid(row=i + 1)

        if clone:
            roller.name.set(self.name.get())
            roller.dice_qty.set(self.dice_qty.get())
            roller.die_faces.set(self.die_faces.get())
            roller.modifier.set(self.modifier.get())
            roller.finalmod.set(self.finalmod.get())
            roller.reset()

        for h in self.history:
            record = roller.create_hist_record()
            record['timestamp'] = h['timestamp']
            roller.history.append(record)

        roller.apply_modifiers()

        for r in self.group.rollers:
            r.lift()

        self.group.mainframe.editmenu.entryconfigure(
            self.group.mainframe.editmenu.index('end'),
            command=lambda: self.add_roller(clone=clone))
        self.group.mainframe.bind_all('<Control-r>',
                                      lambda e: self.add_roller(clone=clone))

    def move_roller(self, offset=0, destination_index=0):
        if not destination_index:
            destination_index = self.index + offset

        if destination_index >= 0:
            roller = self.group.rollers.pop(self.index)
            self.group.rollers.insert(destination_index, roller)

        self.group.maintain_roller_indices()
        self.name.set(self.name.get())

        self.group.mainframe.editmenu.entryconfigure(
            self.group.mainframe.editmenu.index('end'),
            command=lambda: self.move_roller(offset=offset))
        self.group.mainframe.bind_all(
            '<Control-r>', lambda e: self.move_roller(offset=offset))

    def remove_roller(self):
        if len(self.group.rollers) > 1:
            self.grid_remove()
            self.group.rollers.remove(self)
            self.name.set('')

    def reset(self, loading=False):
        self.results = [0 for i in range(self.dice_qty.get())]
        self.dice_qty_spin.step(0)
        self.die_faces_spin.step(0)
        self.modifier_spin.step(0)
        self.finalmod_spin.step(0)
        if not loading:
            self.apply_modifiers()
            self.group.maintain_result_widths()

    def roll(self, single=False):
        rolls = self.dice_qty.get()
        sides = self.die_faces.get()

        if self.group.mainframe.allow_odd.get() % 2 == 0 and sides % 2 != 0:
            self.die_faces.set(sides - 1)
            sides -= 1

        mod = self.modifier.get()
        fmod = self.finalmod.get()
        max_roll = sides
        min_roll = 1
        results = []

        if self.group.mainframe.use_random_org.get():
            url = 'https://www.random.org/integers/?col={0}&num={0}&min={1}&max={2}&base=10&format=plain&rnd=new'
            url = url.format(rolls, min_roll, max_roll)
            try:
                resp = urlopen(url)
                results.extend([
                    int(x) for x in str(resp.read().rstrip(),
                                        encoding='utf8').split('\t')
                ])
                sleep(0.1)
            except:
                print('Failed to use random.org, falling back to CSPRNG!')

        if not results:
            csprng = SystemRandom()
            for i in range(rolls):
                roll = csprng.randint(min_roll, sides)
                results.append(roll)

        self.results = []
        for n in results:
            if n == max_roll:
                self.results.append(n * CRIT)
            elif n == min_roll:
                self.results.append(n * FAIL)
            else:
                self.results.append(n)

        self.apply_modifiers(True)

        self.history.append(self.create_hist_record())
        hist_index = len(self.history) - 1
        if single:
            for roller in self.group.rollers:
                if roller is not self:
                    roller.history.append(roller.create_hist_record())
            self.group.navigate_history(desired_index=hist_index)

        self.group.hist_index = hist_index
        self.name.set(self.name.get())

        self.group.mainframe.editmenu.entryconfigure(
            self.group.mainframe.editmenu.index('end'),
            command=lambda: self.roll(single=single))
        self.group.mainframe.bind_all('<Control-r>',
                                      lambda e: self.roll(single=single))

    def apply_modifiers(self, rolling=False):
        fmod = self.finalmod.get()
        dmod = self.modifier.get()
        dqty = self.dice_qty.get()

        formatted_results = []
        total = 0
        for n in self.results:
            if n > CRIT:
                n = int(n / CRIT)
                n = n + dmod
                formatted_results.append('\u25b2{}'.format(str(n)))
            elif 0 < n < 1:
                n = int(n / FAIL)
                n = n + dmod
                formatted_results.append('\u25bc{}'.format(str(n)))
            else:
                n = n + dmod
                formatted_results.append(str(n))
            total += n

        s = ' + '.join(formatted_results)
        s = '{} = {}'.format(total + fmod, s)

        if not rolling and self.history:
            self.history[self.group.hist_index]['modifier'] = dmod
            self.history[self.group.hist_index]['finalmod'] = fmod
            self.history[self.group.hist_index]['results_text'] = s

        self.results_text.set(s)
        self.group.maintain_result_widths()
class Application(Tk):
    def __init__(self, debug_mode=0):
        Tk.__init__(self)
        self.engine = None
        self.language = None
        self.width = 0
        self.height = 0
        self.resolution_code = None
        self.is_full_screen = IntVar()
        self.screen_ratio = None
        self.resolution_list = []
        self.debug_mode = debug_mode
        if self.debug_mode:
            basicConfig(level=DEBUG)
            pil_logger = getLogger("PIL.PngImagePlugin")
            pil_logger.level = WARNING
        self.data_reader = DataReader(self)
        self._process_config()
        self.card_texts = {}
        self.ui_text_variables = {}
        self._load_text_variables()
        self.save_handler = SaveHandler(self)
        self.is_game_setup_in_progress = IntVar(value=0)
        self.is_game_in_progress = IntVar(value=0)
        self.is_turn_in_progress = IntVar(value=1)
        self._render_panes()
        self.is_game_in_progress.trace('w', self._follow_game_progress_change)
        self.is_turn_in_progress.trace('w', self._follow_turn_progress_change)
        self.players = {}
        self._text_placer()
        self.protocol("WM_DELETE_WINDOW", self.shutdown_ttk_repeat_fix)
        self.exit_in_progress = False

    @property
    def board_width(self):
        return self.height - 10

    def _process_config(self):
        settings = self.data_reader.load_settings()
        self.language = Languages.get_language_by_name(settings.language)
        s.language = Languages.get_language_by_name(settings.language)
        s.application_width = self.width = settings.width
        s.application_height = self.height = settings.height
        self.resolution_code = settings.resolution_code
        self.resolution_list = settings.resolution_list
        self.screen_ratio = self.resolution_code[:4]
        self.is_full_screen.set(settings.full_screen)
        self._fix_window_size()

    def _fix_window_size(self):
        self.minsize(self.width, self.height)
        self.maxsize(self.width, self.height)
        if self.is_full_screen.get():
            self._set_full_screen_position()
        else:
            self._set_windowed_position()

    def _set_full_screen_position(self):
        self.overrideredirect(1)
        self.geometry("{}x{}+0+0".format(self.width, self.height))

    def _set_windowed_position(self):
        self.overrideredirect(0)
        self.geometry("{}x{}+{}+{}".format(self.width, self.height, 100, 100))

    def _load_text_variable_values(self):
        text_variable_values = self.data_reader.load_dictionary(
            {
                Languages.HUNGARIAN.value: "hu",
                Languages.ENGLISH.value: "en",
                Languages.PIRATE.value: "arr"
            }[self.language],
            entry_type='textvariable')
        return text_variable_values

    def _load_text_variables(self):
        for term, translation in s.language.var_terms.items():
            self.ui_text_variables.setdefault(term,
                                              StringVar()).set(translation)

    def _text_placer(self):
        picked_nations = []
        self.title(s.language.title)
        self.menu.load_ui_texts()
        if self.is_game_setup_in_progress.get():
            picked_nations = self._save_game_setup_state()
        if self.is_game_setup_in_progress.get():
            self._reload_game_setup_state(picked_nations)
        if self.is_game_in_progress.get():
            self.menu.reset_game_tab()

    def _save_game_setup_state(self):
        picked_nations = []
        for i in range(6):
            empire_name = self.game_board.player_setups[i].empire_picker.get()
            if empire_name != '':
                current_empire = Empires.get_by_name(empire_name)
                picked_nations.append(current_empire)
            else:
                picked_nations.append('')
        return picked_nations

    def _reload_game_setup_state(self, picked_nations: List[Empire]):
        adjectives = [empire.value.adjective for empire in Empires]
        translated_adjectives = [
            s.language.get(adjective) for adjective in adjectives
        ]
        for index, picked_nation in enumerate(picked_nations):
            self.game_board.player_setups[index].empire_picker.config(
                value=translated_adjectives)
            if picked_nation:
                self.game_board.player_setups[index].empire_picker.set(
                    s.language.get(picked_nation.adjective))

    def set_new_language(self, new_language):
        if self.language == new_language:
            return
        self.language = new_language
        s.language = new_language
        self._load_text_variables()
        self._text_placer()
        self.card_texts = self.data_reader.load_cards_text()
        self.status_bar.log(s.language.new_language)
        self.data_reader.save_settings(new_language=new_language)

    def _render_panes(self):
        self.columnconfigure('all', weight=1)
        self.rowconfigure('all', weight=1)
        menu_width = int((self.height / 3) - 10)
        self.status_bar = LogFrame(self, menu_width)
        self.status_bar.grid(row=1, column=0, sticky=S + W, padx=5, pady=5)
        if self.is_game_in_progress.get():
            self._render_game_board()
        else:
            self._render_game_board_placeholder()
        self.menu = Tabs(self, menu_width)
        self.menu.grid(row=0, column=0, sticky=N + W, padx=5, pady=5)
        if self.screen_ratio == 'wide':
            ship_width = self.width - menu_width - self.board_width - 30
            self.ship = Frame(self, width=ship_width)
            self.ship.grid(row=0,
                           column=2,
                           rowspan=2,
                           sticky=W + N + E,
                           padx=5,
                           pady=5)

    def _render_game_board_placeholder(self):
        self.game_board = Frame(self,
                                width=self.board_width,
                                height=self.board_width)
        self.game_board.player_setups = []
        self.game_board.grid(row=0,
                             column=1,
                             rowspan=2,
                             sticky=N + W,
                             padx=5,
                             pady=5)

    def _render_game_board(self):
        self.game_board = Board(self, self.board_width)
        self.game_board.render_board()
        self.game_board.grid(row=0,
                             column=1,
                             rowspan=2,
                             sticky=N + W,
                             padx=5,
                             pady=5)

    def resize(self, new_resolution, is_new_full_screen):
        is_same_resolution = (self.width, self.height) == (new_resolution[0],
                                                           new_resolution[1])
        is_same_full_screen_setting = self.is_full_screen.get(
        ) == is_new_full_screen
        if is_same_resolution and is_same_full_screen_setting:
            return
        self.data_reader.save_settings(new_resolution[2],
                                       str(is_new_full_screen))
        player_data = []
        if self.is_game_setup_in_progress.get():
            player_data = self._save_game_setup_before_resize()
        self._process_config()
        self._remove_everything()
        self._render_panes()
        if self.is_game_setup_in_progress.get():
            self._load_game_setup_before_resize(player_data)
        self._text_placer()
        self.menu.select(self.menu.settings_tab)
        self.status_bar.log(
            '%s %i×%i' % (s.language.new_resolution, self.width, self.height))

    def _save_game_setup_before_resize(self):
        player_data = []
        for i in range(6):
            if self.game_board.player_setups[i].active.get():
                player_data.append(
                    self.game_board.player_setups[i].get_player_state())
        return player_data

    def _load_game_setup_before_resize(self, player_data):
        self.start_game_setup()
        for i, player_state in enumerate(player_data):
            self.game_board.player_setups[i].set_player_state(player_state)

    def _remove_everything(self):
        Gallery.discard_cache()
        self.menu.destroy()
        self.game_board.destroy()
        self.status_bar.destroy()
        try:
            self.ship.destroy()
        except AttributeError:
            pass

    def confirm_discard_game(self):
        is_game_in_progress = self.is_game_in_progress.get()
        if is_game_in_progress and not askokcancel(
                self.ui_text_variables['new_game'].get(),
                s.language.discard_game):
            return False
        elif is_game_in_progress:
            self.is_game_in_progress.set(0)
            return True
        else:
            return True

    def start_game_setup(self):
        confirmed = self.confirm_discard_game()
        if confirmed:
            if self.is_game_setup_in_progress.get():
                self._reset_board()
            self._prepare_game_setup()

    def _reset_board(self):
        self.is_game_setup_in_progress.set(0)
        self.game_board.destroy()
        self._render_game_board_placeholder()
        self.menu.release_new_game_button()

    def _prepare_game_setup(self):
        self.is_game_setup_in_progress.set(1)
        self.menu.push_new_game_button()
        self.game_board.destroy()
        self._render_game_board_placeholder()
        self.game_board = NewGamePanel(self)
        self.game_board.columnconfigure('all', weight=1)
        self.game_board.rowconfigure('all', weight=1)
        self.game_board.grid(row=0,
                             column=1,
                             rowspan=2,
                             sticky=N + E + W + S,
                             padx=5,
                             pady=5)

    def select_file_to_load(self):
        if self.is_game_in_progress.get():
            if not askokcancel(self.ui_text_variables['new_game'].get(),
                               s.language.discard_game_b):
                return
        game_state = self.save_handler.load_saved_state()
        if game_state is None or not game_state.check():
            return
        self.status_bar.log(s.language.loading_game)
        self.load_game(game_state)

    def load_game(self, game_state):
        self._reset_for_game_start()
        for data in game_state.player_data:
            empire_literal = game_state.player_data[data].empire
            game_state.player_data[data].empire = Empires.get_by_adjective(
                empire_literal)
            self.players[data] = Player(self.game_board,
                                        game_state.player_data[data])
        self._prepare_new_ui()
        while self.player_order[0] != game_state.next_player:
            self.player_order.append(self.player_order.pop(0))
        self.engine = Vezerlo(self, game_state.taverns)
        self.game_board.update_wind_direction(game_state.wind_index)
        if game_state.is_lieutenant_found:
            self.engine.set_hadnagyElokerult()
        if game_state.is_grog_lord_defeated:
            self.engine.set_grogbaroLegyozve()
        self.menu.update_developer_tab()
        self.engine.set_paklik(game_state.card_decks)
        self.status_bar.log(s.language.loading_done)
        self.engine.szakasz_0()

    def _reset_for_game_start(self):
        self.is_game_setup_in_progress.set(0)
        self.card_texts = self.data_reader.load_cards_text()
        self.players = {}
        self.update_idletasks()
        self.is_game_in_progress.set(1)
        self.game_board.destroy()
        self.game_board = Board(self, self.board_width)
        self.game_board.grid(row=0,
                             column=1,
                             rowspan=2,
                             sticky=N + W,
                             padx=5,
                             pady=5)

    def _prepare_new_ui(self):
        self.player_order = sorted(self.players.keys())
        self.game_board.render_board()
        self.menu.release_new_game_button()
        self.menu.select(self.menu.game_tab)

    def start_game(self, player_states):
        self._reset_for_game_start()
        for index, player_state in enumerate(player_states):
            self.players['player' + str(index)] = Player(
                self.game_board, player_state)
        self._prepare_new_ui()
        self.engine = Vezerlo(self)
        self.menu.update_developer_tab()
        self.status_bar.log(s.language.start_game_done)
        self.engine.szakasz_0()

    def _follow_game_progress_change(self, *args, **kwargs):
        if self.is_game_in_progress.get():
            self.menu.tab(1, state=NORMAL)
        else:
            self.menu.tab(1, state=DISABLED)

    def _follow_turn_progress_change(self, *args, **kwargs):
        if not self.is_turn_in_progress.get():
            self.menu.enable_save_buttons()
            self.menu.tab(0, state=NORMAL)
            self.menu.tab(2, state=NORMAL)
        else:
            self.menu.disable_save_buttons()
            self.menu.tab(0, state=DISABLED)
            self.menu.tab(2, state=DISABLED)

    def shutdown_ttk_repeat_fix(self):
        self.eval('::ttk::CancelRepeat')
        self.exit_in_progress = True
        self.exit()

    def get_window_position(self):
        info = self.winfo_geometry()
        xpos = info.index('+') + 1
        ypos = info[xpos:].index('+') + xpos
        x = int(info[xpos:ypos])
        y = int(info[ypos:])
        return x, y

    def save_game(self):
        game_state = GameState()
        game_state.next_player = self.player_order[0]
        game_state.wind_index = self.game_board.wind_direction.index(0)
        for player in sorted(list(self.players)):
            game_state.player_data[player] = self.players[player].export()
        for empire in Empires:
            game_state.taverns[empire.value.capital] = self.engine.varostar[
                empire.value.capital].export_matroz()
        game_state.card_decks = [
            self.engine.eventdeck, self.engine.eventstack,
            self.engine.kincspakli, self.engine.treasurestack
        ]
        game_state.is_grog_lord_defeated = self.engine.grogbaroLegyozve.get()
        game_state.is_lieutenant_found = self.engine.hadnagyElokerult.get()
        if game_state.check():
            self.save_handler.write_save(game_state)
        else:
            raise RuntimeError('Invalid game state.')

    def save_and_exit(self):
        self.save_game()
        self.shutdown_ttk_repeat_fix()

    def exit(self):
        if self.is_game_in_progress.get(
        ) and self.game_board.is_field_select_blinking:
            self.game_board.is_field_select_blinking = False
        self.destroy()
Example #11
0
class MainFrame(Frame):
    def set_saved_title(self, fpath):
        fname = split(fpath)[-1].replace('.json', '')
        self.master.title('{}  -  {}'.format(fname, _title))

    def set_unsaved_title(self, *args):
        if len(roller_groups) < 1:
            return
        if self.autosave.get():
            self.save_config(self.fpath)
            return
        title = self.master.title()
        if title == _title:
            title = '{}  -  {}'.format('Unsaved', title)
        if '*' not in title:
            title = '*' + title

        self.master.title(title)

    def __init__(self, master):
        Frame.__init__(self, master)

        self.master = master

        self.use_random_org = BooleanVar()
        self.allow_odd = IntVar()
        self.always_on_top = BooleanVar()
        self.autosave = BooleanVar()

        self.use_random_org.trace('w', self.set_unsaved_title)
        self.allow_odd.trace('w', self.set_unsaved_title)
        self.always_on_top.trace('w', self.set_unsaved_title)

        self.set_defaults()

        self.menubar = Menu(master)

        self.filemenu = Menu(self.menubar,
                             tearoff=0,
                             postcommand=maintain_group_indices)
        self.filemenu.add_command(label='New',
                                  underline=0,
                                  command=self.reset_default_group,
                                  accelerator='Ctrl+N')
        self.filemenu.add_command(label='Load',
                                  underline=3,
                                  command=self.load_config,
                                  accelerator='Ctrl+D')
        self.filemenu.add_command(
            label='Save',
            underline=1,
            command=lambda: self.save_config(fpath=self.fpath),
            accelerator='Ctrl+S')
        self.filemenu.add_command(label='Save as...',
                                  underline=4,
                                  command=self.save_config,
                                  accelerator='Ctrl+Shift+S')

        self.editmenu = Menu(self.menubar, tearoff=0)
        self.editmenu.add_checkbutton(label='Use random.org',
                                      underline=0,
                                      variable=self.use_random_org)
        self.editmenu.add_checkbutton(label='Allow odd dice',
                                      underline=6,
                                      variable=self.allow_odd,
                                      command=self.toggle_odd,
                                      onvalue=1,
                                      offvalue=2)
        self.editmenu.add_separator()  #      ------------------
        self.editmenu.add_checkbutton(label='Always on top',
                                      underline=10,
                                      variable=self.always_on_top,
                                      command=self.pin)
        self.editmenu.add_checkbutton(label='Autosave',
                                      underline=4,
                                      variable=self.autosave,
                                      command=self.toggle_autosave)
        self.editmenu.add_separator()  #      ------------------
        self.editmenu.add_command(label='Repeat last action',
                                  underline=0,
                                  accelerator='Ctrl+R')

        self.menubar.add_cascade(label='File', underline=0, menu=self.filemenu)
        self.menubar.add_cascade(label='Edit', underline=0, menu=self.editmenu)

        self.menubar.config(relief='flat')

        master.config(menu=self.menubar)

        self.reset_default_group()

        self.bind_all('<Control-n>', lambda e: self.reset_default_group())
        self.bind_all('<Control-d>', lambda e: self.load_config())
        self.bind_all('<Control-s>',
                      lambda e: self.save_config(fpath=self.fpath))
        self.bind_all('<Control-Shift-S>', lambda e: self.save_config())

    def ask_proceed(self):
        if '*' in self.master.title():
            if not askyesno(
                    'Unsaved changes!',
                    'There are unsaved changes!\r\nWould you like to proceed anyway?'
            ):
                return False
        return True

    def pin(self):
        self.master.wm_attributes('-topmost', self.always_on_top.get())

    def toggle_odd(self):
        for group in roller_groups:
            for roller in group.rollers:
                roller.die_faces_spin.interval = self.allow_odd.get()
                num = roller.die_faces.get()
                if num % 2 != 0:
                    roller.die_faces.set(num - 1)

    def toggle_autosave(self):
        if self.autosave.get():
            self.save_config(self.fpath)
        else:
            self.set_unsaved_title()

    def set_defaults(self):
        self.master.title(_title)
        self.fpath = ''
        self.use_random_org.set(False)
        self.allow_odd.set(2)
        self.always_on_top.set(False)
        self.autosave.set(False)

    def reset_default_group(self):
        if self.ask_proceed():
            self.autosave.set(False)
            self.clear_groups()
            self.set_defaults()
            self.create_group(0, 1)

    @staticmethod
    def clear_groups():
        temp_groups = list(roller_groups)
        for group in temp_groups:
            group.remove_group(override=True)

    def create_group(self, index, rollers):
        default_group = RollerGroup(self, index)
        for i in range(rollers):
            default_group.rollers.append(Roller(default_group, i))
        roller_groups.append(default_group)

    def load_config(self):
        autosave = False
        self.autosave.set(autosave)
        if not self.ask_proceed():
            return

        fpath = askopenfilename(filetypes=[('JSON', '*.json'), ('All', '*.*')],
                                defaultextension='.json')
        if not fpath or not isfile(fpath):
            return
        self.fpath = fpath

        self.clear_groups()

        with open(fpath, 'r') as f:
            group_dict = load(f)

        try:
            settings_dict = group_dict.pop('settings')
            autosave = (settings_dict['autosave'])
            self.use_random_org.set(settings_dict['use_random_org'])
            self.allow_odd.set(settings_dict['allow_odd'])
            self.always_on_top.set(settings_dict['always_on_top'])
        except KeyError:
            pass

        g = 0
        for group_name, group_settings in group_dict.items():
            self.create_group(g, len(group_settings['rollers']))

            group = roller_groups[g]
            group.name.set(group_name)
            group.index = group_settings['index']

            r = 0
            h = 0
            for roller_name, roller_settings in group_settings[
                    'rollers'].items():
                roller = group.rollers[r]
                roller.name.set(roller_name)
                for attr, value in roller_settings.items():
                    try:
                        getattr(roller, attr).set(value)
                    except AttributeError:
                        setattr(roller, attr, value)
                roller.reset(loading=True)
                h = len(roller.history) - 1
                r += 1

            group.navigate_history(desired_index=h)
            g += 1

        roller_groups.sort(key=lambda x: x.index)

        maintain_group_indices()
        for group in roller_groups:
            group.rollers.sort(key=lambda x: x.index)
            group.maintain_roller_indices()
            for roller in group.rollers:
                roller.apply_modifiers()

        maintain_tabstops()

        self.pin()
        self.autosave.set(autosave)
        self.set_saved_title(fpath)

    def save_config(self, fpath=''):
        if not fpath:
            fpath = asksaveasfilename(filetypes=[('JSON', '*.json'),
                                                 ('All', '*.*')],
                                      defaultextension='.json')
        if not fpath:
            if '*' in self.master.title():
                self.autosave.set(False)
            return
        self.fpath = fpath

        d1 = {}
        d1['settings'] = {
            'use_random_org': self.use_random_org.get(),
            'allow_odd': self.allow_odd.get(),
            'always_on_top': self.always_on_top.get(),
            'autosave': self.autosave.get()
        }
        for group in roller_groups:
            group.maintain_roller_indices()
            d2 = {}
            d2['index'] = group.index
            d2['rollers'] = {}
            for roller in group.rollers:
                name = roller.name.get()
                while name in d2['rollers']:
                    name += '!'
                d2['rollers'][name] = {
                    'index': roller.index,
                    'history': roller.history,
                    'dice_qty': roller.dice_qty.get(),
                    'die_faces': roller.die_faces.get(),
                    'modifier': roller.modifier.get(),
                    'finalmod': roller.finalmod.get()
                }
            name = group.name.get()
            if name in d1:
                name += '!'
            d1[name] = d2

        with open(fpath, 'w') as f:
            f.write(dumps(d1, indent=2, separators=(',', ': ')))

        self.set_saved_title(fpath)
Example #12
0
    def __init__(self, master, par=False):
        """
        GUI for selecting default parameters - will write parameters to file \
        of users choosing.

        :type master: Tk
        :param master: Tkinter window
        :type par: EQcorrscanParameters
        :param par: Default parameters to start-up with.
        """
        from tkinter import Label, Button, Entry, DoubleVar, StringVar, IntVar
        from tkinter import BooleanVar, OptionMenu, Checkbutton
        import tkMessageBox
        from eqcorrscan.utils import parameters
        from obspy import UTCDateTime
        import warnings

        # Set the default par, only if they don't already exist.
        if not par:
            par = parameters.EQcorrscanParameters([''], 2, 10, 4, 100, 2,
                                                  '1900-01-01', '2300-01-01',
                                                  '', 'seishub', 4, False, '',
                                                  'jpg', False, 8, 'MAD', 6)
        # Callback functions for all variables (ugly)

        def update_template_names(*args):
            par.template_names = [name.strip() for name in
                                  template_names.get().split(',')]
            template_names.set(', '.join(par.template_names))

        def update_lowcut(*args):
            par.lowcut = lowcut.get()
            lowcut.set(par.lowcut)

        def update_highcut(*args):
            par.highcut = highcut.get()
            if par.highcut >= 0.5 * par.samp_rate:
                msg = ('Highcut must be less than the Nyquist, setting to ' +
                       str((par.samp_rate / 2.0) - 1))
                tkMessageBox.showwarning(title="Nyquist error",
                                         message=msg)
                par.highcut = (par.samp_rate / 2.0) - 1
            highcut.set(par.highcut)

        def update_filt_order(*args):
            par.filt_order = filt_order.get()
            filt_order.set(par.filt_order)

        def update_samp_rate(*args):
            par.samp_rate = samp_rate.get()
            if par.highcut >= 0.5 * par.samp_rate:
                msg = ('Highcut must be less than the Nyquist, setting to ' +
                       str((par.samp_rate / 2.0) - 1))
                tkMessageBox.showwarning(title="Nyquist error",
                                         message=msg)
                par.highcut = (par.samp_rate / 2.0) - 1
                highcut.set(par.highcut)
            samp_rate.set(par.samp_rate)

        def update_debug(*args):
            par.debug = debug.get()
            debug.set(par.debug)

        def update_startdate(*args):
            par.startdate = UTCDateTime(startdate.get())
            startdate.set(str(par.startdate))

        def update_enddate(*args):
            par.enddate = UTCDateTime(enddate.get())
            enddate.set(str(par.enddate))

        def update_archive(*args):
            par.archive = archive.get()
            archive.set(par.archive)

        def update_arc_type(*args):
            par.arc_type = arc_type.get()
            arc_type.set(par.arc_type)

        def update_cores(*args):
            par.cores = cores.get()
            cores.set(par.cores)

        def update_plotvar(*args):
            par.plotvar = plotvar.get()
            plotvar.set(par.plotvar)

        def update_plot_format(*args):
            par.plot_format = plot_format.get()
            plot_format.set(par.plot_format)

        def update_tempdir(*args):
            par.tempdir = tempdir.get()
            tempdir.set(par.tempdir)

        def update_threshold(*args):
            par.threshold = threshold.get()
            threshold.set(par.threshold)

        def update_threshold_type(*args):
            par.threshold_type = threshold_type.get()
            threshold_type.set(par.threshold_type)

        def update_plotdir(*args):
            par.plotdir = plotdir.get()
            plotdir.set(par.plotdir)

        def update_trigger_interval(*args):
            par.trigger_interval = trigger_interval.get()
            trigger_interval.set(par.trigger_interval)
        # Set some grid parameters
        nrows = 25
        ncolumns = 3
        self.master = master
        master.title("EQcorrscan parameter setup")
        self.label = Label(master, text="Alpha GUI for default setup")
        self.label.grid(column=0, columnspan=ncolumns, row=0)

        # Set up parameter input
        self.t_names_label = Label(master, text="Template names", anchor='e')
        self.t_names_label.grid(column=0, row=1, sticky='e')
        template_names = StringVar()
        template_names.set(', '.join(par.template_names))
        self.t_names_box = Entry(master, bd=2, textvariable=template_names)
        self.t_names_box.grid(column=1, row=1)
        template_names.trace("w", update_template_names)
        self.t_names_lookup = Button(master, text="Lookup",
                                     command=lambda: self.get_template_names(par))
        self.t_names_lookup.grid(column=2, row=1)

        self.lowcut_label = Label(master, text="Lowcut (Hz)", anchor='e')
        self.lowcut_label.grid(column=0, row=2, sticky='e')
        lowcut = DoubleVar()
        lowcut.set(par.lowcut)
        self.lowcut_box = Entry(master, bd=2, textvariable=lowcut)
        self.lowcut_box.grid(column=1, row=2)
        lowcut.trace("w", update_lowcut)

        self.highcut_label = Label(master, text="Highcut (Hz)", anchor='e')
        self.highcut_label.grid(column=0, row=3, sticky='e')
        highcut = DoubleVar()
        highcut.set(par.highcut)
        self.highcut_box = Entry(master, bd=2, textvariable=highcut)
        self.highcut_box.grid(column=1, row=3)
        highcut.trace("w", update_highcut)

        self.filt_order_label = Label(master, text="Filter order")
        self.filt_order_label.grid(column=0, row=4, sticky='e')
        filt_order = DoubleVar()
        filt_order.set(par.filt_order)
        self.filt_order_box = Entry(master, bd=2, textvariable=filt_order)
        self.filt_order_box.grid(column=1, row=4)
        filt_order.trace("w", update_filt_order)

        self.samp_rate_label = Label(master, text="Sample rate (Hz)")
        self.samp_rate_label.grid(column=0, row=5, sticky='e')
        samp_rate = DoubleVar()
        samp_rate.set(par.samp_rate)
        self.samp_rate_box = Entry(master, bd=2, textvariable=samp_rate)
        self.samp_rate_box.grid(column=1, row=5)
        samp_rate.trace("w", update_samp_rate)

        self.debug_label = Label(master, text="Debug")
        self.debug_label.grid(column=0, row=6, sticky='e')
        debug = IntVar()
        debug.set(par.debug)
        self.debug_box = Entry(master, bd=2, textvariable=debug)
        self.debug_box.grid(column=1, row=6)
        debug.trace("w", update_debug)

        self.startdate_label = Label(master, text="Start date (yyyy-mm-dd)")
        self.startdate_label.grid(column=0, row=6, sticky='e')
        startdate = StringVar()
        startdate.set(par.startdate)
        self.startdate_box = Entry(master, bd=2, textvariable=startdate)
        self.startdate_box.grid(column=1, row=6)
        startdate.trace("w", update_startdate)

        self.enddate_label = Label(master, text="End date (yyyy-mm-dd)")
        self.enddate_label.grid(column=0, row=8, sticky='e')
        enddate = StringVar()
        enddate.set(par.enddate)
        self.enddate_box = Entry(master, bd=2, textvariable=enddate)
        self.enddate_box.grid(column=1, row=8)
        enddate.trace("w", update_enddate)

        self.archive_label = Label(master, text="Archive")
        self.archive_label.grid(column=0, row=9, sticky='e')
        archive = StringVar()
        archive.set(par.archive)
        self.archive_box = Entry(master, bd=2, textvariable=archive)
        self.archive_box.grid(column=1, row=9)
        archive.trace("w", update_archive)
        self.archive_lookup = Button(master, text="Lookup",
                                     command=lambda: self.get_archive(par))
        self.archive_lookup.grid(column=2, row=9)


        self.arc_type_label = Label(master, text="Archive type")
        self.arc_type_label.grid(column=0, row=10, sticky='e')
        arc_type = StringVar()
        arc_type.set(par.arc_type)
        self.arc_type_box = OptionMenu(master, arc_type,
                                       "seishub", "fdsn", "day_vols")
        self.arc_type_box.grid(column=1, row=10, sticky='w,e')
        arc_type.trace("w", update_arc_type)

        self.cores_label = Label(master, text="Number of cores")
        self.cores_label.grid(column=0, row=11, sticky='e')
        cores = IntVar()
        cores.set(par.cores)
        self.cores_box = Entry(master, bd=2, textvariable=cores)
        self.cores_box.grid(column=1, row=11)
        cores.trace("w", update_cores)

        self.plotvar_label = Label(master, text="Plotting on/off")
        self.plotvar_label.grid(column=0, row=12, sticky='e')
        plotvar = BooleanVar()
        plotvar.set(par.plotvar)
        self.plotvar_box = Checkbutton(master, text='Plot on', var=plotvar,
                                       onvalue=True, offvalue=False)
        self.plotvar_box.grid(column=1, row=12)
        plotvar.trace("w", update_plotvar)

        self.plotdir_label = Label(master, text="Plot directory")
        self.plotdir_label.grid(column=0, row=13, sticky='e')
        plotdir = StringVar()
        plotdir.set(par.plotdir)
        self.plotdir_box = Entry(master, bd=2, textvariable=plotdir)
        self.plotdir_box.grid(column=1, row=13)
        plotdir.trace("w", update_plotdir)
        self.plotdir_lookup = Button(master, text="Lookup",
                                     command=lambda: self.get_plotdir(par))
        self.plotdir_lookup.grid(column=2, row=13)

        self.plot_format_label = Label(master, text="Plot format")
        self.plot_format_label.grid(column=0, row=14, sticky='e')
        plot_format = StringVar()
        plot_format.set(par.plot_format)
        self.plot_format_box = OptionMenu(master, plot_format,
                                          "jpg", "eps", "pdf", "png")
        self.plot_format_box.grid(column=1, row=14, sticky='w,e')
        plot_format.trace("w", update_plot_format)

        self.tempdir_label = Label(master, text="Temporary directory")
        self.tempdir_label.grid(column=0, row=15, sticky='e')
        tempdir = StringVar()
        tempdir.set(par.tempdir)
        self.tempdir_box = Entry(master, bd=2, textvariable=tempdir)
        self.tempdir_box.grid(column=1, row=15)
        tempdir.trace("w", update_tempdir)
        self.tempdir_lookup = Button(master, text="Lookup",
                                     command=lambda: self.get_tempdir(par))
        self.tempdir_lookup.grid(column=2, row=15)

        self.threshold_label = Label(master, text="Threshold")
        self.threshold_label.grid(column=0, row=16, sticky='e')
        threshold = DoubleVar()
        threshold.set(par.threshold)
        self.threshold_box = Entry(master, bd=2, textvariable=threshold)
        self.threshold_box.grid(column=1, row=16)
        threshold.trace("w", update_threshold)

        self.threshold_type_label = Label(master, text="Threshold type")
        self.threshold_type_label.grid(column=0, row=17, sticky='e')
        threshold_type = StringVar()
        threshold_type.set(par.threshold_type)
        self.threshold_type_box = OptionMenu(master, threshold_type,
                                             "MAD", "absolute", "av_chan_corr")
        self.threshold_type_box.grid(column=1, row=17, sticky='w,e')
        threshold_type.trace("w", update_threshold_type)

        self.trigger_interval_label = Label(master,
                                            text="Minimum trigger " +
                                            "interval (s)")
        self.trigger_interval_label.grid(column=0, row=18, sticky='e')
        trigger_interval = DoubleVar()
        trigger_interval.set(par.trigger_interval)
        self.trigger_interval_box = Entry(master, bd=2,
                                          textvariable=trigger_interval)
        self.trigger_interval_box.grid(column=1, row=18)
        trigger_interval.trace("w", update_trigger_interval)

        # End of user editable section, now we have read/write buttons
        self.read_button = Button(master, text="Read parameters",
                                  command=lambda: self.read_par(master))
        self.read_button.grid(column=0, row=nrows-2, sticky='w,e')

        self.write_button = Button(master, text="Write parameters",
                                   command=lambda: self.write_par(par))
        self.write_button.grid(column=1, row=nrows-2, sticky='w,e')
Example #13
0
class View(object):
    def __init__(self, master=None):
        self.root = master
        self.status_build = False
        self.init_view()

    def init_view(self):
        '''基本框架'''
        self.frm_main = LabelFrame(self.root, borderwidth=0)
        self.frm_main.pack(side='left', fill='y')

        self.frm_advance = LabelFrame(self.root, text='高级选项')
        # self.frm_advance.pack(expand='yes', side='right', fill='both', padx=15, pady=10)
        # self.frm_2 = LabelFrame(self.frm_advance, text='高级配置', width=300)
        # self.frm_2.pack(expand='yes', side='top', fill='both', padx=15, pady=10)

        self.frm_project = LabelFrame(self.frm_main, text='项目信息')
        self.frm_config = LabelFrame(self.frm_main, text='配置信息')
        self.frm_operate = LabelFrame(self.frm_main, text='操作')
        self.frm_status = LabelFrame(self.frm_main, text='状态')

        self.frm_project.pack(expand='yes',
                              side='top',
                              fill='both',
                              padx=15,
                              pady=10)
        self.frm_config.pack(fill='x', padx=15, pady=10)
        self.frm_operate.pack(fill='x', padx=15, pady=10)
        self.frm_status.pack(side='bottom', fill='x', padx=15, pady=10)

        self.init_project()
        self.init_config()
        self.init_operate()
        self.init_status()

    def init_project(self):
        '''项目配置'''
        labels = ['入口文件:', '工作目录:', '目标路径:', '图标路径:']

        self.entry_value_list = list()
        for index, label_text in enumerate(labels):
            temp_strvar = StringVar()
            temp_label = Label(self.frm_project, text=label_text)
            temp_entry = Entry(self.frm_project,
                               textvariable=temp_strvar,
                               width=50)
            self.entry_value_list.append(temp_strvar)
            temp_label.grid(row=index % 4,
                            column=0,
                            padx=5,
                            pady=5,
                            sticky='w')
            temp_entry.grid(row=index % 4,
                            column=1,
                            padx=5,
                            pady=5,
                            sticky='we')

        self.btn_main_path = Button(self.frm_project,
                                    text='选择文件',
                                    command=self.fn_select_main)
        self.btn_work_path = Button(self.frm_project,
                                    text='选择路径',
                                    command=self.fn_work_path)
        self.btn_dist_path = Button(self.frm_project,
                                    text='选择路径',
                                    command=self.fn_dist_path)
        self.btn_ico_path = Button(self.frm_project,
                                   text='选择图标',
                                   command=self.fn_icon_path)
        self.btn_main_path.grid(row=0, column=2, padx=5, pady=5, sticky='we')
        self.btn_work_path.grid(row=1, column=2, padx=5, pady=5, sticky='w')
        self.btn_dist_path.grid(row=2, column=2, padx=5, pady=5, sticky='e')
        self.btn_ico_path.grid(row=3, column=2, padx=5, pady=5, sticky='e')

    def init_config(self):
        '''配置选项'''
        # 定义变量,并初始化
        self.cfg_onefile = IntVar(value=1)
        self.cfg_onedir = IntVar(value=0)
        self.cfg_noconsole = IntVar(value=1)
        self.cfg_clean = IntVar(value=1)
        self.cfg_upx = IntVar(value=1)  # UPX 默认开启
        self.cfg_rename = IntVar()
        self.cfg_exe_name = StringVar()
        # 自定义配置文件
        self.cfg_specfile = StringVar(value='build.spec')
        # 子配置框架
        self.frm_config_base = LabelFrame(self.frm_config,
                                          text='基本',
                                          borderwidth=0)
        self.frm_config_base.pack(fill='x', padx=10, pady=5, ipady=5)
        self.frm_config_exe = LabelFrame(self.frm_config,
                                         text='生成执行文件类型',
                                         borderwidth=0)
        self.frm_config_exe.pack(fill='x', padx=10, pady=5, ipady=5)
        self.frm_config_other = LabelFrame(self.frm_config,
                                           text='其它',
                                           borderwidth=0)
        self.frm_config_other.pack(fill='x', padx=10, pady=5, ipady=5)
        self.frm_config_spec = LabelFrame(self.frm_config,
                                          text='配置文件',
                                          borderwidth=0)
        self.frm_config_spec.pack(fill='x', padx=10, pady=5, ipady=5)

        # 定义按钮
        self.btn_noconsole = Checkbutton(self.frm_config_base,
                                         text='关闭控制台',
                                         variable=self.cfg_noconsole)
        self.btn_clean = Checkbutton(self.frm_config_base,
                                     text='构建前清理',
                                     variable=self.cfg_clean)
        self.btn_upx = Checkbutton(self.frm_config_base,
                                   text='UPX压缩',
                                   variable=self.cfg_upx)
        self.btn_isonefile = Checkbutton(self.frm_config_exe,
                                         text='独立执行文件',
                                         variable=self.cfg_onefile)
        self.btn_isonedir = Checkbutton(self.frm_config_exe,
                                        text='文件夹包含',
                                        variable=self.cfg_onedir)
        self.btn_rename = Checkbutton(self.frm_config_other,
                                      text='修改执行文件名',
                                      variable=self.cfg_rename)
        self.entry_rename = Entry(self.frm_config_other,
                                  textvariable=self.cfg_exe_name)

        # self.btn_rename = Checkbutton(self.frm_config_spec, text='生成配置文件', variable=self.cfg_specfile)
        self.entry_specfile = Entry(self.frm_config_spec,
                                    textvariable=self.cfg_specfile)

        # 放置按钮
        self.btn_isonefile.pack(side='left', fill='x')
        self.btn_isonedir.pack(side='left', fill='x')
        self.btn_noconsole.pack(side='left', fill='x')
        self.btn_clean.pack(side='left', fill='x')
        self.btn_upx.pack(side='left', fill='x')
        self.btn_rename.pack(side='left', fill='x')
        self.entry_rename.pack(fill='x')
        self.entry_specfile.pack(fill='x')

        # 变量自动切换操作
        self.cfg_onefile.trace('w', self.cfg_onefile_trace)
        self.cfg_onedir.trace('w', self.cfg_onedir_trace)

    def cfg_onefile_trace(self, *args):
        '''cfg_onefile 与 cfg_onedir 可以同时不选,但不能同时选中,选中独立执行文件时不能选中文件夹包'''
        if self.cfg_onefile.get() == 1:
            self.cfg_onedir.set(0)

    def cfg_onedir_trace(self, *args):
        '''cfg_onefile 与 cfg_onedir 可以同时不选,但不能同时选中,选中文件夹包含时不能选中独立执行文件'''
        if self.cfg_onedir.get() == 1:
            self.cfg_onefile.set(0)

    def init_operate(self):
        '''操作命令'''
        # 定义按钮
        self.btn_build = Button(self.frm_operate,
                                text='构建生成',
                                command=self.fn_build)
        self.btn_clear = Button(self.frm_operate,
                                text='清理',
                                command=self.fn_clear)
        self.btn_reset = Button(self.frm_operate,
                                text='重置',
                                command=self.fn_reset)
        self.btn_advance = Button(self.frm_operate,
                                  text='高级选项',
                                  command=self.fn_toggle_advance)

        # 放置按钮
        self.btn_build.pack(fill='x', side='left')
        self.btn_clear.pack(fill='x', side='left')
        self.btn_reset.pack(fill='x', side='left')
        self.btn_advance.pack(fill='x', side='right')

    def init_status(self):
        '''状态栏'''
        self.label_status = Label(self.frm_status, text='待命')
        self.label_status.grid(row=1, column=0, padx=5, pady=5, sticky='we')

    def fn_build(self):
        '''生成可执行文件'''
        if len(self.entry_value_list[0].get()) == 0:
            self.label_status['text'] = '请选择源文件'
            return
        if not self.status_build:
            thread_build = Thread(target=self.fn_thread)
            thread_build.setDaemon(True)
            thread_build.start()
        else:
            self.label_status['text'] = '正在打包,请稍后再操作!'

    def fn_thread(self):
        '''线程执行生成动作'''
        self.status_build = True
        self.label_status['text'] = '正在打包,请稍等。。。'
        try:
            cmd = self.fn_build_cmd()
            print(cmd)
            # pirun(cmd)
            system(' '.join(cmd))
            # call(split(' '.join(cmd)), shell=True)
            self.status_build = False
            self.label_status['text'] = '打包成功!'
        except Exception as e:
            self.label_status['text'] = str(e)
            self.status_build = False

    def fn_clear(self):
        '''清理生成文件'''
        pass

    def fn_reset(self):
        '''重置表单内容'''
        for i in range(4):
            self.entry_value_list[i].set('')

        self.cfg_onefile.set(1)
        self.cfg_noconsole.set(1)
        self.cfg_clean.set(1)
        self.cfg_upx.set(1)
        self.cfg_rename.set(0)
        self.cfg_exe_name.set('')

    def fn_toggle_advance(self):
        '''切换高级选项界面'''
        if self.frm_advance.winfo_ismapped():
            set_window_center(self.root, width=(self.root.winfo_width() - 400))
            self.frm_advance.pack_forget()
        else:
            set_window_center(self.root, width=(self.root.winfo_width() + 400))
            self.frm_advance.pack(expand='yes',
                                  side='right',
                                  fill='both',
                                  padx=15,
                                  pady=10)

    def fn_select_main(self):
        '''选择源文件'''
        types = (('py files', '*.py'), ('pyc files', '*.pyc'),
                 ('spec files', '*.spec'), ('All files', '*.*'))
        path = filedialog.askopenfilename(filetypes=types)
        if not path:
            return
        _path = os.path.dirname(path)
        # 主文件
        self.entry_value_list[0].set(path)
        # 工作目录
        self.entry_value_list[1].set(os.path.join(_path, 'build/'))
        # dist目录
        self.entry_value_list[2].set(os.path.join(_path, 'dist/'))

    def fn_work_path(self):
        '''选择工作目录'''
        path = filedialog.askdirectory()
        if not path:
            return
        self.entry_value_list[1].set(path)

    def fn_dist_path(self):
        '''选择生成文件目录'''
        path = filedialog.askdirectory()
        if not path:
            return
        self.entry_value_list[2].set(path)

    def fn_icon_path(self):
        '''选择图标文件'''
        types = (('ico files', '*.ico'), ('icns files', '*.icns'),
                 ('All files', '*.*'))
        path = filedialog.askopenfilename(filetypes=types)
        if not path:
            return
        self.entry_value_list[3].set(path)

    def fn_build_cmd(self, cli=True):
        '''组装命令'''

        cmds = []
        if cli is True:
            # 使用系统命令行
            cmds.append('pyinstaller')
        if len(self.entry_value_list[0].get()) > 0:
            cmds.append(self.entry_value_list[0].get())
        else:
            return cmds
        cmds.append('--windowed')
        cmds.append('-y')
        cmds.append('--noconfirm')
        # cmds.append('--filenames=build.spec')
        # cmds.append('/usr/local/bin/pyinstaller')

        if self.cfg_onefile.get() == 1:
            cmds.append('--onefile')
        elif self.cfg_onedir.get() == 1:
            cmds.append('--onedir')

        if self.cfg_clean.get() == 1:
            cmds.append('--clean')
            cmds.append('--noconfirm')

        if self.cfg_upx.get() == 0:
            cmds.append('--noupx')

        if self.cfg_noconsole.get() == 1:
            cmds.append('--noconsole')

        if len(self.entry_value_list[1].get()) > 0:
            cmds.append('--workpath=' + self.entry_value_list[1].get())

        if len(self.entry_value_list[2].get()) > 0:
            cmds.append('--distpath=' + self.entry_value_list[2].get())

        if len(self.entry_value_list[3].get()) > 0:
            cmds.append('--icon=' + self.entry_value_list[3].get())

        if self.cfg_rename.get() == 1:
            if len(self.cfg_exe_name.get()) > 0:
                cmds.append('--name=' + self.cfg_exe_name.get())

        # print(' '.join(cmds))
        return cmds
Example #14
0
class sa_setting_ui:
    def __init__(self,net_list_path,isrootwindow = True, root = None):
        
        self.net_list_path = net_list_path
        if(isrootwindow):
            self.root = tk.Tk()
        else:
            self.root = tk.Toplevel(root)
            self.root.grab_set()
        

        self.root.title("Simulated Annealing Properties")
        self.root.geometry("500x600")
        self.root.resizable(False, False)

        vcmd = (self.root.register(self.validate_entry), "%d",'%S')

        self.alpha_entry = tk.Entry(self.root,width=10,validate="key", validatecommand=vcmd)
        self.start_temp_entry = tk.Entry(self.root,width=10,validate="key", validatecommand=vcmd)
        self.end_temp_entry = tk.Entry(self.root,width=10,validate="key", validatecommand=vcmd)
        self.kb_entry = tk.Entry(self.root,width=10,validate="key", validatecommand=vcmd)

        text_alpha_entry=tk.StringVar()
        text_alpha_entry.set("\u03B1(%):")
        self.label_alpha_entry = tk.Label(self.root, textvariable=text_alpha_entry, height=1)

        text_start_temp=tk.StringVar()
        text_start_temp.set("T(start):")
        self.label_start_temp_entry = tk.Label(self.root, textvariable=text_start_temp, height=1)

        text_end_temp=tk.StringVar()
        text_end_temp.set("T(stop):")
        self.label_end_temp_entry = tk.Label(self.root, textvariable=text_end_temp, height=1)

        text_kb =tk.StringVar()
        text_kb.set("k:")
        self.label_kb_set = tk.Label(self.root, textvariable=text_kb, height=1)
   
        text_choice =tk.StringVar()
        text_choice.set("Problem:")
        self.label_drop_menu = tk.Label(self.root, textvariable=text_choice, height=1)
           
        text_log_path =tk.StringVar()
        text_log_path.set("Log Path:")
        self.label_log_path = tk.Label(self.root, textvariable=text_log_path, height=1)

        self.log_path_entry = tk.Entry(self.root,width=10)
        
        self.text_label_error =tk.StringVar()
        self.text_label_error.set("")
        self.label_error = tk.Label(self.root, textvariable=self.text_label_error, height=1,fg="red")

        self.run_sim_btn = tk.Button(self.root,text="Run Solver", command=self.runSolver)
        self.sel_path_btn = tk.Button(self.root,text="...",command=self.selPath)
        self.log_path_entry.config(state='disabled') 

        self.kill_sim_btn = tk.Button(self.root,text="Kill Sim", command=self.killSim)
        self.kill_sim_btn.config(state="disabled")

        self.description = ScrolledText(self.root,bg='white',relief="groove",height=10,width=50,font='TkFixedFont')


        self.selected = tk.StringVar(self.root)
        self.selected.trace("w",self.changeChoice)
        self.choices = { 'Partitioning','Travelling Salesman'}
        self.selected.set('Partitioning') # set the default option
        self.drop_menu = tk.OptionMenu(self.root, self.selected, *self.choices)

        self.show_sim_active = IntVar()
        self.show_sim_active.set(1)
        self.show_sim_btn = tk.Checkbutton(self.root,text = "Show Live Sim", variable = self.show_sim_active, \
                 onvalue = 1, offvalue = 0, height=1, \
                 width = 20)

        self.log_active = IntVar()
        self.log_active.trace("w",self.saveLog)

        self.log_active.set(1)
        self.log_active_btn = tk.Checkbutton(self.root,text = "Save Log", variable = self.log_active, \
                 onvalue = 1, offvalue = 0, height=1, \
                 width = 10)


        self.alpha_entry.place(relx=0.5, rely=0.03, anchor="w")
        self.label_alpha_entry.place(relx=0.4, rely=0.03, anchor="e")

        self.start_temp_entry.place(relx=0.5, rely=0.105, anchor="w")
        self.label_start_temp_entry.place(relx=0.4, rely=0.105, anchor="e")
    
        self.end_temp_entry.place(relx=0.5, rely=0.18, anchor="w")
        self.label_end_temp_entry.place(relx=0.4,rely=0.18,anchor="e")

        self.kb_entry.place(relx=0.5, rely=0.255, anchor="w")
        self.label_kb_set.place(relx=0.4, rely=0.255, anchor="e")

        self.drop_menu.place(relx=0.5,rely=0.33,anchor="w")
        self.label_drop_menu.place(relx=0.4,rely=0.33,anchor="e")

        self.sel_path_btn.place(relx=0.62,rely=0.405,anchor="w")
        self.label_log_path.place(relx=0.4,rely=0.405,anchor="e")
        self.log_path_entry.place(relx=0.5,rely=0.405,anchor="w")
        self.log_active_btn.place(relx=0.675,rely=0.405,anchor="w")

        self.description.place(relx=0.5,rely=0.7,anchor="center")


        self.label_error.place(relx=0.5,rely=0.875,anchor="center")
        self.run_sim_btn.place(relx=0.5, rely=0.95, anchor="center")
        self.kill_sim_btn.place(relx=0.25,rely=0.95,anchor="center")

        self.show_sim_btn.place(relx=0.675, rely=0.1425, anchor="w")

        self.alpha_entry.delete(0,"end")
        self.alpha_entry.insert(0,"95")

        self.start_temp_entry.delete(0,"end")
        self.start_temp_entry.insert(0,"100")

        self.end_temp_entry.delete(0,"end")
        self.end_temp_entry.insert(0,"1")

        self.kb_entry.delete(0,"end")
        self.kb_entry.insert(0,"1")

        self.kill_sim = False

        self.root.protocol("WM_DELETE_WINDOW", self.on_closing)

        self.root.update()

    def on_closing(self):
        tmp = "./tmp.dat"
        if (os.path.isfile(self.net_list_path)):
            os.remove(self.net_list_path)

        if (os.path.isfile(tmp)):
            os.remove(tmp)

        self.root.destroy()

    def saveLog(self,*args):
        self.log_path_entry.config(state='normal') 
        self.log_path_entry.delete(0,tk.END)
        self.log_path_entry.config(state='disabled') 
        if(self.log_active.get()==1):
            self.sel_path_btn.config(state='normal')
        else:
            self.sel_path_btn.config(state='disabled')
            

    def selPath(self):
        path = filedialog.askdirectory(initialdir="./")
        self.log_path_entry.config(state='normal') 
        self.log_path_entry.delete(0,tk.END)
        self.log_path_entry.insert(0,path)
        self.log_path_entry.config(state='disabled') 

    def killSim(self):
        self.kill_sim_btn.config(state="disabled")
        self.kill_sim = True
        self.sim_sa.stop = True

    def validate_entry(self,d,S):
        if(d=='1'): #something inserted
            for ch in S:
                if(ch.isdigit()==False):
                    return False
            return True
        else:
            return True

    def runSolver(self):
        self.kill_sim = False
        alpha = float(self.alpha_entry.get())
        t_start = float(self.start_temp_entry.get())
        t_end = float(self.end_temp_entry.get())
        kb = float(self.kb_entry.get())
        log_path = self.log_path_entry.get()

        if(self.log_active.get()):
            if(os.path.isdir(log_path)==False):
                self.text_label_error.set("Log Path is Invalid")
                return
        else:
            log_path = None

        if(alpha >= 100 or alpha <= 0):
            self.text_label_error.set("\u03B1 invalid, must be between (0,100)")
            return
        if(t_end > t_start):
            self.text_label_error.set("T(start) must be greater than T(end)")
            return
        
        tag = ["UI"]
 
        ui_str = utility.returnTagListFromNetlistPath(self.net_list_path,tag)
        canv_width = 500
        canv_height = 500
        for row in ui_str:
            if(row[0]=="UI"):
                if(row[1]=="cwh"): #canvas width,height
                    canv_width = int(row[2])
                    canv_height = int(row[3])

        self.sim_sa_ui = display_ui(self.root,canv_width,canv_height)
        self.run_sim_btn.config(state="disabled")
        self.kill_sim_btn.config(state="normal")

        self.text_label_error.set("")

        if(self.selected.get()=="Partitioning"):
            self.sim_sa = saPartitionSolver(self.net_list_path,(alpha/100),t_start,t_end,kb,log_path)   
            if(self.show_sim_active.get()):     
                self.runSimulatedAnnealingPartitionItr()
            else:
                self.runSimulatedAnnealingPartition()
        else:
            self.sim_sa_ui.root.destroy()
            self.runSimulatedAnnealingTS()

    def runSimulatedAnnealingTS(self):
        self.run_sim_btn.config(state="normal")
        self.kill_sim_btn.config(state="disabled")
        messagebox.showwarning("Warning", "Work in Progress")

    def runSimulatedAnnealingPartitionItr(self):
        if(self.kill_sim ==True):
            self.run_sim_btn.config(state="normal")
            self.sim_sa_ui.root.destroy()
            return
        tmp_file = "./tmp.dat"
        cost,accepted,fin,reason,set1,set2 = self.sim_sa.runNextIteration(tmp_file)
        self.sim_sa_ui.importNetlist(tmp_file)
        self.sim_sa_ui.update()

        setA = "SetA = {"
        for _module in set1:
            setA += _module.name + " , "
        setA += " }"

        setB = "SetB = {"
        for _module in set2:
            setB += _module.name + " , "
        setB += " }"

        if(fin):
            self.run_sim_btn.config(state="normal")
            self.sim_sa_ui.changeTxt("Cost: " + str(cost) + "\n" + "Final Solution" + "\n" + setA + "\n" + setB)
            self.sim_sa_ui.activateMenuBar()
            self.sim_sa_ui.allow_kill = True
            self.kill_sim_btn.config(state="disabled")
            return
        if(accepted):
            self.sim_sa_ui.changeTxt("Cost: " + str(cost) + "\n" + "Solution Accepted" + "\n" + "Reason: " + reason + "\n" + setA + "\n" + setB)
        else:
            self.sim_sa_ui.changeTxt("Cost: " + str(cost) + "\n" + "Solution Rejected" +  "\n" + "Reason: " + reason + "\n" + setA + "\n" + setB )

        self.root.after(1000,self.runSimulatedAnnealingPartitionItr)

    def runSimulatedAnnealingPartition(self):
        tmp_file = "./tmp.dat"
        self.sim_sa_ui.changeTxt("SIM RUNNING")

        cost,set1,set2 = self.sim_sa.runUntilComplete(tmp_file)

        self.run_sim_btn.config(state="normal")
        if(self.kill_sim==True):
            self.sim_sa_ui.root.destroy()
            return

        setA = "SetA = {"
        for _module in set1:
            setA += _module.name + " , "
        setA += " }"

        setB = "SetB = {"
        for _module in set2:
            setB += _module.name + " , "
        setB += " }"

        self.sim_sa_ui.importNetlist(tmp_file)
        self.sim_sa_ui.update()
        self.sim_sa_ui.changeTxt("Cost: " + str(cost) + "\n" + "Final Solution" + "\n" + setA + "\n" + setB)
        self.sim_sa_ui.activateMenuBar()
        self.kill_sim_btn.config(state="disabled")
        self.sim_sa_ui.allow_kill = True

    def changeChoice(self,*args):
        self.description.config(state="normal")
        self.description.delete('1.0',tk.END)
        sa_str = "P = exp(-\u0394/k*T)"
        sa_str += "\nT = \u03B1*T'"
        if(self.selected.get()=="Partitioning"):
            sa_str += "\nIn Partitioning the solver will attempt to evenly divde the modules in the netlist into two seperatesets (setA and setB) by cutting nets. The sets aredecided by minimzing the total cutting cost required. The cost to cut a net is dictated by it's weight."
            self.description.insert(tk.INSERT,sa_str)
        else:
            sa_str += "\nIn Traveling Salesman the solver will attempt to visit all modules in the netlist in a route that minimizes total cost of travel without repeating a visit. The cost to travel from one module to another is dictated by the net's weight that connects the two."
            self.description.insert(tk.INSERT,sa_str)

        self.description.config(state="disabled")
Example #15
0
class PricesEditor(Frame):
    def __init__(self, master, **kwargs):
        super().__init__(master, **kwargs)

        self.create_prices_ui()
        self.initial_values = {}
        self.table_modified = False

    def create_prices_ui(self):
        self.rowconfigure(0, weight=1)
        self.columnconfigure(0, weight=1)
        self.frm_prices = Frame(self)
        self.frm_prices.rowconfigure(2, weight=1)
        self.frm_prices.columnconfigure(0, weight=1)
        self.frm_prices.grid(padx=10, pady=10, sticky='nsew')

        # Button Frame
        frm_btns = Frame(self.frm_prices, relief=SOLID, borderwidth=2)
        frm_btns.grid(row=0, column=0, pady=(0, 5), sticky='nsew')
        frm_btns.columnconfigure(10, weight=1)
        # self.entry_currency = \
        #     Combobox_Autocomplete(frm, list_of_items=['one of many currencies'], startswith_match=False)

        self.search_var = StringVar()
        self.entry_search = PlaceholderEntry(frm_btns,
                                             'Search..',
                                             style='Default.TEntry',
                                             textvariable=self.search_var)

        self.search_var.trace_variable(
            'w',
            lambda a, b, c: self.tree.search(self.entry_search.get_value()))
        self.entry_search.bind(
            '<Return>',
            lambda event: self.tree.search(self.entry_search.get_value(),
                                           find_next=True))
        self.btn_apply = Button(frm_btns,
                                text='Apply',
                                command=self.applyChanges)
        self.btn_reload = Button(
            frm_btns,
            text='Reload',
            command=lambda: self.loadPrices(force_reload=True))

        self.entry_search.grid(row=2, column=0, pady=5, padx=5)
        self.btn_apply.grid(row=2, column=2, pady=5)
        # frm.columnconfigure(3, weight=1)
        self.btn_reload.grid(row=2, column=3, sticky='e', pady=5)

        self.var_advanced = BooleanVar(False)
        self.var_advanced.trace_variable(
            'w', lambda a, b, c: self._on_view_option_change())
        self.cb_advanced = Checkbutton(frm_btns,
                                       text='Advanced',
                                       variable=self.var_advanced)
        self.cb_advanced.grid(row=2, column=10, sticky='e', padx=10)

        frm_border = Frame(self.frm_prices, relief=SOLID, borderwidth=2)
        frm_border.grid(row=2, column=0, sticky='nsew')
        frm_border.rowconfigure(2, weight=1)
        frm_border.columnconfigure(0, weight=1)
        # Tree Frame
        self.frm_tree = Frame(frm_border)
        self.frm_tree.grid(row=2, column=0, sticky='nsew', padx=5, pady=(0, 0))
        self.frm_tree.rowconfigure(0, weight=1)
        self.frm_tree.columnconfigure(0, weight=1)

        self.tree = EditableTreeview(self.frm_tree,
                                     on_cell_update=self.onCellUpdate)
        scrly = AutoScrollbar(self.frm_tree, command=self.tree.yview)
        scrlx = AutoScrollbar(self.frm_tree,
                              command=self.tree.xview,
                              orient=HORIZONTAL)
        self.tree.config(yscrollcommand=scrly.set, xscrollcommand=scrlx.set)
        self.tree.grid(row=0, column=0, sticky='nsew')
        scrly.grid(row=0, column=1, sticky='nsew')
        scrlx.grid(row=1, column=0, sticky='nsew')

        # Button Frame
        frm = Frame(frm_border)  #, relief=SOLID, borderwidth=1)
        # frm = Frame(self.frm_prices)
        frm.grid(row=0, column=0, sticky='nsew')
        # self.entry_currency = \
        #     Combobox_Autocomplete(frm, list_of_items=['one of many currencies'], startswith_match=False)

        lbl = Label(frm, text='Item value threshold:')
        lbl.grid(row=0, column=0, padx=5, pady=5, sticky='w')
        self.var_threshold = StringVar()
        self.entry_threshold = TooltipEntry(frm,
                                            textvariable=self.var_threshold)
        self.entry_threshold.bind(
            '<FocusOut>', lambda event: self._validate_threshold_entry())
        self.entry_threshold.grid(row=0, column=1, padx=5, pady=5)
        self.var_threshold.trace(
            'w', lambda a, b, c: self.on_entry_change(self.entry_threshold))

        lbl = Label(frm, text='Budget:')
        lbl.grid(row=0, column=2, padx=5, pady=5)
        self.var_budget = StringVar()
        self.entry_budget = TooltipEntry(frm, textvariable=self.var_budget)
        self.entry_budget.bind('<FocusOut>',
                               lambda event: self._validate_budget_entry())
        self.entry_budget.grid(row=0, column=3, padx=5, pady=5)
        self.var_budget.trace(
            'w', lambda a, b, c: self.on_entry_change(self.entry_budget))

        lbl = Label(frm, text='Minimum price:')
        lbl.grid(row=0, column=4, padx=5, pady=5)
        self.var_min_price = StringVar()
        self.entry_min_price = TooltipEntry(frm,
                                            textvariable=self.var_min_price)
        self.entry_min_price.bind(
            '<FocusOut>', lambda event: self._validate_min_price_entry())
        self.entry_min_price.grid(row=0, column=5, padx=5, pady=5)
        self.var_min_price.trace(
            'w', lambda a, b, c: self.on_entry_change(self.entry_min_price))

        lbl = Label(frm, text='Default filter override:')
        lbl.grid(row=0, column=6, padx=5, pady=5)
        self.lbl_fprice_override = lbl
        self.var_fprice_override = StringVar()
        self.entry_fprice_override = TooltipEntry(
            frm, textvariable=self.var_fprice_override)
        self.entry_fprice_override.bind(
            '<FocusOut>', lambda event: self._validate_fprice_override_entry())
        self.entry_fprice_override.grid(row=0, column=7, padx=5, pady=5)
        self.var_fprice_override.trace(
            'w',
            lambda a, b, c: self.on_entry_change(self.entry_fprice_override))

        # Advanced

        lbl = Label(frm, text='Default item value override:')
        lbl.grid(row=1, column=0, padx=5, pady=(2, 5), sticky='w')
        self.lbl_price_override = lbl
        self.var_price_override = StringVar()
        self.entry_price_override = TooltipEntry(
            frm, textvariable=self.var_price_override)
        self.entry_price_override.bind(
            '<FocusOut>', lambda event: self._validate_price_override_entry())
        self.entry_price_override.grid(row=1, column=1, padx=5, pady=(2, 5))
        self.var_price_override.trace(
            'w',
            lambda a, b, c: self.on_entry_change(self.entry_price_override))

        # Confidence Level
        lbl = Label(frm, text="Confidence level:")
        lbl.grid(row=1, column=2, padx=5, pady=(2, 5), sticky='w')
        self.lbl_confidence_lvl = lbl
        self.var_confidence_lvl = IntVar()
        self.entry_confidence_lvl = ConfidenceScale(
            frm, variable=self.var_confidence_lvl)
        self.entry_confidence_lvl.grid(row=1, column=3, padx=5, pady=(2, 5))
        self.var_confidence_lvl.trace(
            'w',
            lambda a, b, c: self.on_entry_change(self.entry_confidence_lvl))

        self.var_5l_filters = BooleanVar(False)
        self.cb_5l_filters = VarCheckbutton(frm,
                                            text='Enable 5L filters',
                                            variable=self.var_5l_filters)
        self.cb_5l_filters.var = self.var_5l_filters
        self.cb_5l_filters.grid(row=1,
                                column=4,
                                padx=5,
                                pady=(2, 5),
                                columnspan=1)
        self.var_5l_filters.trace_variable(
            'w', lambda a, b, c: self.on_entry_change(self.cb_5l_filters))

        # Tree Config
        tree = self.tree

        def init_tree_column(col):
            col_name = pricesColumns[0] if col == '#0' else col
            tree.heading(col,
                         text=PricesColumn[col_name].value,
                         anchor=W,
                         command=lambda col=col: tree.sort_col(col))
            tree.column(col, width=140, stretch=False)

        # self.tree['columns'] = ('ID', 'Item Price', 'Override', 'Filter Price', 'Filter Override', 'Effective Filter Price', 'Filter State Override', '')
        self.tree['columns'] = pricesColumns[1:]

        self.tree.register_column(
            PricesColumn.Override.name,
            ColEntry(TooltipEntry(self.tree),
                     func_validate=_validate_price_override))
        self.tree.register_column(
            PricesColumn.FilterOverride.name,
            ColEntry(TooltipEntry(self.tree),
                     func_validate=_validate_price_override))
        self.tree.register_column(
            PricesColumn.FilterStateOverride.name,
            ColEntry(Combobox(self.tree,
                              values=filterStateOptions,
                              state=READONLY),
                     accept_events=('<<ComboboxSelected>>', '<Return>')))

        for col in (('#0', ) + tree['columns']):
            init_tree_column(col)

        tree.heading('#0', anchor=CENTER)
        tree.column('#0', width=200, stretch=False)
        tree.column(PricesColumn.Filler.name, stretch=True)

        tree.heading(PricesColumn.ItemPrice.name,
                     command=lambda col=PricesColumn.ItemPrice.name: tree.
                     sort_col(col, key=self._price_key))
        tree.heading(PricesColumn.Override.name,
                     command=lambda col=PricesColumn.Override.name: tree.
                     sort_col(col, key=self._price_key))
        tree.heading(PricesColumn.FilterOverride.name,
                     command=lambda col=PricesColumn.FilterOverride.name: tree.
                     sort_col(col, key=self._price_key))

        tree.heading(PricesColumn.FilterPrice.name,
                     command=lambda col=PricesColumn.FilterPrice.name: tree.
                     sort_col(col, key=self._rate_key, default=0))
        tree.heading(PricesColumn.EffectiveFilterPrice.name,
                     command=lambda col=PricesColumn.EffectiveFilterPrice.name:
                     tree.sort_col(col, key=self._rate_key, default=0))

        self.bvar_modified = BooleanVar()
        self.bvar_modified.trace('w', lambda a, b, c: self._updateApplyState())
        self.bvar_modified.set(False)
        self.var_advanced.set(False)

    def _rate_key(self, key):
        if key == 'N/A':
            return 0
        return float(key)

    def _price_key(self, key):
        if key == '':
            return None  # this means it will be ignored while sorting
        try:
            return cm.compilePrice(key, base_price=0)
        except Exception:
            return 0

    def on_entry_change(self, entry):
        val = entry.get()

        if self.initial_values[entry] != val:
            self.bvar_modified.set(True)

    # def on_price_entry_focusout(self, widget):
    #     valid = _validate_price(widget, accept_empty=False)
    #     if valid and not self.bvar_modified.get() and self.initial_values[widget] != widget.get():
    #         self.bvar_modified.set(True)
    #     return valid
    #
    # def on_override_entry_focusout(self, widget):
    #     valid = _validate_price_override(widget, accept_empty=False)
    #     if valid and not self.bvar_modified.get() and self.initial_values[widget] != widget.get():
    #         self.bvar_modified.set(True)
    #     return valid

    def _validate_threshold_entry(self):
        return _validate_price(self.entry_threshold, accept_empty=False)

    def _validate_budget_entry(self):
        return _validate_price(self.entry_budget, accept_empty=True)

    def _validate_min_price_entry(self):
        return _validate_price(self.entry_min_price, accept_empty=True)

    def _validate_price_override_entry(self):
        return _validate_price_override(self.entry_price_override,
                                        accept_empty=False)

    def _validate_fprice_override_entry(self):
        return _validate_price_override(self.entry_fprice_override,
                                        accept_empty=False)

    def _update_modified(self):
        modified = any(entry.get() != self.initial_values[entry]
                       for entry in self.initial_values) or self.table_modified

        self.bvar_modified.set(modified)

    def _updateApplyState(self):
        if self.bvar_modified.get():
            self.btn_apply.config(state=NORMAL)
        else:
            self.btn_apply.config(state=DISABLED)

    def _validateForm(self):
        if not self._validate_threshold_entry():
            return False
        if not self._validate_budget_entry():
            return False
        if not self._validate_min_price_entry():
            return False
        if not self._validate_price_override_entry():
            return False
        if not self._validate_fprice_override_entry():
            return False
        return True

    def applyChanges(self, event=None):
        if not self.bvar_modified.get() or not fm.initialized:
            return
        if not self._validateForm():
            return

        price_threshold = self.entry_threshold.get()
        default_price_override = self.entry_price_override.get()
        default_fprice_override = self.entry_fprice_override.get()
        budget = self.entry_budget.get()
        min_price = self.entry_min_price.get()
        confidence_lvl = self.entry_confidence_lvl.get(
        ) or fm.DEFAULT_CONFIDENCE_LEVEL
        enable_5l_filters = self.var_5l_filters.get()

        price_overrides = {}
        filter_price_overrides = {}
        filter_state_overrides = {}

        for iid in self.tree.get_children():
            id = self.tree.set(iid, PricesColumn.ID.name)
            iprice = self.tree.set(iid, PricesColumn.Override.name)

            if iprice:
                price_overrides[id] = iprice

            fprice = self.tree.set(iid, PricesColumn.FilterOverride.name)
            if fprice:
                filter_price_overrides[id] = fprice

            fstate = self.tree.set(iid, PricesColumn.FilterStateOverride.name)
            try:
                filter_state_overrides[id] = FilterStateOption[fstate].value
            except KeyError:
                pass

        ids = set([
            self.tree.set(iid, PricesColumn.ID.name)
            for iid in self.tree.get_children()
        ])

        # preserve unhandled ids configuration
        for key in (set(fm.price_overrides) - ids):
            price_overrides[key] = fm.price_overrides[key]

        for key in (set(fm.filter_price_overrides) - ids):
            filter_price_overrides[key] = fm.filter_price_overrides[key]

        for key in (set(fm.filter_state_overrides) - ids):
            filter_state_overrides[key] = fm.filter_state_overrides[key]

        try:
            fm.updateConfig(default_price_override, default_fprice_override,
                            price_threshold, budget, min_price,
                            price_overrides,
                            filter_price_overrides, filter_state_overrides,
                            int(confidence_lvl), enable_5l_filters)
        except AppException as e:
            messagebox.showerror(
                'Validation error',
                'Failed to update configuration:\n{}'.format(e),
                parent=self.winfo_toplevel())
        except Exception as e:
            logexception()
            messagebox.showerror(
                'Update error',
                'Failed to apply changes, unexpected error:\n{}'.format(e),
                parent=self.winfo_toplevel())
        else:
            # SHOULD always work since config is valid, main console will report any failures
            # background thread because schema validating takes a bit of time
            threading.Thread(target=fm.compileFilters).start()
            self._initFormState()

    def loadPrices(self, force_reload=False):
        if not cm.initialized or not fm.initialized:
            return

        if not force_reload:
            self._update_modified()  # in case of reverted changes
            if self.bvar_modified.get():  # dont interrupt user changes
                return

        tree = self.tree
        tree.clear()

        table = {}
        for fltr in fm.autoFilters:
            # effective_rate = cm.crates.get(curr, '')
            # if effective_rate != '':
            #     effective_rate = round(effective_rate, 3)

            fid = fltr.id

            fstate_override = fm.filter_state_overrides.get(fid, '')
            try:
                fstate_override = FilterStateOption(fstate_override).name
            except ValueError:
                fstate_override = ''

            table[fid] = (fltr.title, fid, fm.item_prices[fid],
                          fm.price_overrides.get(fid, ''),
                          _to_display_rate(
                              fm.compiled_item_prices.get(fid, 'N/A')),
                          fm.filter_price_overrides.get(fid, ''),
                          _to_display_rate(
                              fm.compiled_filter_prices.get(fid, 'N/A')),
                          fstate_override)

        for fid in table:
            tree.insert('', END, '', text=table[fid][0], values=table[fid][1:])

        # tree.sort_by('#0', descending=True)
        tree.sort_col('#0', reverse=False)

        self._initFormState()

    # def onItemPriceUpdate(self, iid, col, old, new):
    #     print('IPrice update: iid {}, col {}'.format(iid, col))

    def onCellUpdate(self, iid, col, old, new):
        if old != new:
            self.table_modified = True
            self.bvar_modified.set(True)
            # self._update_modified()

    def _initFormState(self):
        self.table_modified = False
        self.initial_values[self.entry_threshold] = fm.price_threshold
        self.initial_values[self.entry_budget] = fm.budget
        self.initial_values[self.entry_min_price] = fm.default_min_price
        self.initial_values[
            self.entry_price_override] = fm.default_price_override
        self.initial_values[
            self.entry_fprice_override] = fm.default_fprice_override
        self.initial_values[self.entry_confidence_lvl] = fm.confidence_level
        self.initial_values[self.cb_5l_filters] = fm.enable_5l_filters

        self.var_threshold.set(fm.price_threshold)
        self.var_budget.set(fm.budget)
        self.var_min_price.set(fm.default_min_price)
        self.var_price_override.set(fm.default_price_override)
        self.var_fprice_override.set(fm.default_fprice_override)
        self.var_confidence_lvl.set(fm.confidence_level)
        self.var_5l_filters.set(fm.enable_5l_filters)

        self.bvar_modified.set(False)

    def _on_view_option_change(self):
        advanced_widgets = [
            self.entry_price_override, self.lbl_price_override,
            self.lbl_confidence_lvl, self.entry_confidence_lvl,
            self.cb_5l_filters
        ]
        if not self.var_advanced.get():
            for w in advanced_widgets:
                w.grid_remove()
            self.tree.config(displaycolumn=[
                PricesColumn.FilterPrice.name, PricesColumn.FilterOverride.
                name, PricesColumn.EffectiveFilterPrice.name,
                PricesColumn.Filler.name
            ])
        else:
            for w in advanced_widgets:
                w.grid()
            self.tree.config(displaycolumn='#all')
        self.tree.on_entry_close()
Example #16
0
    def __init__(self, parent, fixNum, mode_):
        global loaded, devicesLoaded, device
        # self.communicationThread = threading.Thread(target = self.testMessages)
        # completeIndSend = IntVar()
        # completeIndSend.set(0)
        # completeIndSend.trace('w', self.updateComVar)
        loaded = IntVar()
        loaded.set(getNumDevicesLoaded())
        loaded.trace("w", updateDevicesLoaded)
        device = StringVar()
        device.set(None)
        s = ttk.Style()
        s.theme_use('default')
        s.configure("red.Horizontal.TProgressbar",
                    foreground='red',
                    background='red')
        s.configure("green.Horizontal.TProgressbar",
                    foreground='green',
                    background='green')
        self.parent = parent
        self.fixNum = fixNum
        self.parent.title("WROOM-xx Programmer")
        self.stations = []
        self.frame = tk.Frame(self.parent)
        self.configureMenu()
        self.titleLabel = tk.Label(self.frame,
                                   text='Details/Instructions',
                                   font=10)
        self.instructions = tk.Label(self.frame,
                                     text='- Programming stations \
are labelled with both COM ports listed in cfg.txt\n \
            - Click START to begin the upload',
                                     pady=5)
        devices = getCOMPorts(int(self.fixNum) - 1)
        self.mode = mode_
        ### Get screen resolution metrics
        user32 = ctypes.windll.user32
        screenWidth = user32.GetSystemMetrics(0)
        screenHeight = user32.GetSystemMetrics(1)
        height = screenHeight
        if self.mode == "prod":
            height /= 2
        else:
            height /= 1.5
        xpos = (self.fixNum - 1) * (screenWidth / 2)
        self.parent.geometry(
            str(int(screenWidth / 2)) + "x" + str(int(height)) + "+" +
            str(int(xpos)) + "+0")
        devicesLoaded = tk.Label(self.frame,
                                 text=("Devices Loaded: " +
                                       str(loaded.get())).ljust(10),
                                 pady=10)
        self.buttonFrame = tk.Frame(self.frame)
        self.clearCounter = tk.Button(self.buttonFrame,
                                      text="Clear Counter",
                                      width=15,
                                      bg=gridColor,
                                      height=2,
                                      command=clearDevCounter)
        # self.start = tk.Button(self.buttonFrame, text = "START", width = 22, bg = gridColor, height = 3, command = self.startUpload, state = tk.DISABLED)
        self.configureFirmwareSelection()
        self.createDeviceOptions()
        self.packObjects()
        # d[0] is common port; begin Station initalization at 1, passing in unique station id
        for d in range(0, len(devices)):
            self.stations.append(Station(parent, devices[d], d))
Example #17
0
class CurrencyEditor(Frame):
    def __init__(self, master, **kwargs):
        super().__init__(master, **kwargs)

        self.bvar_modified = BooleanVar()
        self.create_currency_ui()

    def create_currency_ui(self):
        self.rowconfigure(0, weight=1)
        self.columnconfigure(0, weight=1)
        self.frm_currency = Frame(self)
        self.frm_currency.rowconfigure(2, weight=1)
        self.frm_currency.columnconfigure(0, weight=1)
        self.frm_currency.grid(padx=10, pady=10, sticky='nsew')

        # Tree Frame
        self.frm_tree = Frame(self.frm_currency)
        self.frm_tree.grid(row=2, sticky='nsew')
        self.frm_tree.rowconfigure(0, weight=1)
        self.frm_tree.columnconfigure(0, weight=1)

        self.tree = EditableTreeview(self.frm_tree,
                                     on_cell_update=self.onCellUpdate)
        scrly = AutoScrollbar(self.frm_tree, command=self.tree.yview)
        scrlx = AutoScrollbar(self.frm_tree,
                              command=self.tree.xview,
                              orient=HORIZONTAL)
        self.tree.config(yscrollcommand=scrly.set, xscrollcommand=scrlx.set)
        self.tree.grid(row=0, column=0, sticky='nsew')
        scrly.grid(row=0, column=1, sticky='nsew')
        scrlx.grid(row=1, column=0, sticky='nsew')

        self.tree.insert('', 0, text='Exalted Orb', values=('90', '85'))

        frm = Frame(self.frm_currency, relief=SOLID, borderwidth=2)
        frm.columnconfigure(10, weight=1)
        frm.grid(row=1, column=0, pady=(0, 5), sticky='nsew')
        # self.entry_currency = \
        #     Combobox_Autocomplete(frm, list_of_items=['one of many currencies'], startswith_match=False)

        self.search_var = StringVar()
        self.entry_search = PlaceholderEntry(frm,
                                             'Search..',
                                             style='Default.TEntry',
                                             textvariable=self.search_var)

        self.search_var.trace_variable(
            'w',
            lambda a, b, c: self.tree.search(self.entry_search.get_value()))
        self.entry_search.bind(
            '<Return>',
            lambda event: self.tree.search(self.entry_search.get_value(),
                                           find_next=True))
        # self.btn_currency_search = Button(frm, text='Search', command=lambda event: self.tree_currency.search(self.entry_currency_search.get_value(), find_next=True))
        self.btn_apply = Button(frm, text='Apply', command=self.applyChanges)
        self.btn_reload = Button(
            frm,
            text='Reload',
            command=lambda: self.loadCurrency(force_reload=True))

        self.entry_search.grid(row=2, column=0, pady=5, padx=5)
        # self.btn_currency_search.grid(row=2, column=1, pady=5)
        self.btn_apply.grid(row=2, column=2, pady=5)
        # frm.columnconfigure(3, weight=1)
        self.btn_reload.grid(row=2, column=3, sticky='e', pady=5)

        # Confidence Level
        lbl = Label(frm, text="Confidence level:")
        lbl.grid(row=2, column=10, padx=5, sticky='nse', pady=(3, 5))
        self.lbl_confidence_lvl = lbl
        self.var_confidence_lvl = IntVar()
        self.entry_confidence_lvl = ConfidenceScale(
            frm, variable=self.var_confidence_lvl)
        self.entry_confidence_lvl.grid(row=2, column=11, padx=5, pady=5)
        self.var_confidence_lvl.trace(
            'w',
            lambda a, b, c: self.on_entry_change(self.entry_confidence_lvl))

        # Tree Config
        tree = self.tree
        tree['columns'] = currencyColumns[1:]
        tree.register_column(
            'Override',
            ColEntry(TooltipEntry(tree),
                     func_validate=_validate_price_override))

        def init_tree_column(col):
            col_name = currencyColumns[0] if col == '#0' else col
            tree.heading(col,
                         text=CurrencyColumn[col_name].value,
                         anchor=W,
                         command=lambda col=col: tree.sort_col(col))
            tree.column(col, width=140, stretch=False)

        for col in ('#0', ) + tree['columns']:
            init_tree_column(col)

        tree.heading('#0', anchor=CENTER)
        tree.column('#0', width=250, stretch=False)
        tree.column(CurrencyColumn.Filler.name, stretch=True)

        tree.heading(CurrencyColumn.Rate.name,
                     command=lambda col=CurrencyColumn.Rate.name: tree.
                     sort_col(col, key=float, default=0))
        tree.heading(CurrencyColumn.EffectiveRate.name,
                     command=lambda col=CurrencyColumn.EffectiveRate.name: tree
                     .sort_col(col, key=float, default=0))
        tree.heading(CurrencyColumn.Override.name,
                     command=lambda col=CurrencyColumn.Override.name: tree.
                     sort_col(col, key=self._price_key))

        self.bvar_modified.trace('w', lambda a, b, c: self._updateApplyState())

    def _price_key(self, key):
        if key == '':
            return None  # this means it will be ignored while sorting
        try:
            return cm.compilePrice(key, base_price=0)
        except Exception:
            return 0

    def _updateApplyState(self):
        if self.bvar_modified.get():
            self.btn_apply.config(state=NORMAL)
        else:
            self.btn_apply.config(state=DISABLED)

    def loadCurrency(self, force_reload=False):
        if not cm.initialized:
            return
        if not force_reload and self.bvar_modified.get():
            return

        self.var_confidence_lvl.set(cm.confidence_level)

        tree = self.tree
        tree.clear()

        table = {}
        for curr in cm.shorts:
            effective_rate = cm.crates.get(curr, '0')
            table[curr] = (_to_display_rate(cm.rates.get(curr, '')),
                           cm.overrides.get(curr, ''),
                           _to_display_rate(effective_rate))

        for curr in table:
            tree.insert('', END, '', text=curr, values=table[curr])

        tree.sort_col(CurrencyColumn.EffectiveRate.name, key=float, default=0)

        self.bvar_modified.set(False)

    def applyChanges(self, event=None):
        if not self.bvar_modified.get() or not cm.initialized:
            return

        overrides = {}

        for iid in self.tree.get_children():
            #TODO: hide #0 col and move names to a value column
            currency_name_col = '#0'  # CurrencyColumn.Currency.name
            # id = self.tree.set(iid, currency_name_col)
            id = self.tree.item(iid, 'text')
            override = self.tree.set(iid, CurrencyColumn.Override.name)

            if override:
                overrides[id] = override

        # ids = set([self.tree.set(iid, currency_name_col) for iid in self.tree.get_children()])
        ids = set(
            [self.tree.item(iid, 'text') for iid in self.tree.get_children()])

        # preserve unhandled ids configuration
        for key in (set(cm.overrides) - ids):
            overrides[key] = cm.overrides[key]

        cm.confidence_level = self.entry_confidence_lvl.get()

        try:
            cm.compile(overrides=overrides)
            if fm.initialized:
                threading.Thread(target=fm.compileFilters).start()
            self.bvar_modified.set(False)
        except AppException as e:
            messagebox.showerror('Update error',
                                 e,
                                 parent=self.winfo_toplevel())
        except Exception as e:
            logexception()
            messagebox.showerror(
                'Update error',
                'Failed to apply changes, unexpected error:\n{}'.format(e),
                parent=self.winfo_toplevel())

    def onCellUpdate(self, iid, col, old, new):
        if not self.bvar_modified.get() and old != new:
            self.bvar_modified.set(True)

    def on_entry_change(self, entry):
        self.bvar_modified.set(True)
Example #18
0
class ImageCropper(Frame):
    def __init__(self,
                 parent,
                 image,
                 cropsize,
                 returntype='bboxscale',
                 *args,
                 **kw):
        Frame.__init__(self, parent, *args, **kw)
        self.original = PILImage.open(open(image, 'rb'))
        if self.original.size[0] > 800 or self.original.size[1] > 800:
            self.original.thumbnail((800, 800), PILImage.LANCZOS)
        self.cropsize = cropsize
        self.returntype = returntype
        self.previewlabel = Label(self)
        self.previewlabel.pack()
        self.canvas = Canvas(self,
                             width=self.original.size[0],
                             height=self.original.size[1])
        self.img = ImageTk.PhotoImage(self.original)
        self.image = self.canvas.create_image(self.original.size[0] // 2,
                                              self.original.size[1] // 2,
                                              image=self.img)
        self.canvas.pack(padx=5, pady=10)
        self._drag_data = {}
        self.cropregion = self.canvas.create_rectangle(2,
                                                       2,
                                                       cropsize[0],
                                                       cropsize[1],
                                                       width=2)

        def _handcursor(event):
            self.canvas['cursor'] = 'hand2'

        def _arrowcursor(event):
            self.canvas['cursor'] = 'arrow'

        self.canvas.tag_bind(self.cropregion, '<Enter>', _handcursor)
        self.canvas.tag_bind(self.cropregion, '<Leave>', _arrowcursor)
        self.canvas.tag_bind(self.cropregion, "<ButtonPress-1>",
                             self.OnTokenButtonPress)
        self.canvas.tag_bind(self.cropregion, "<ButtonRelease-1>",
                             self.OnTokenButtonRelease)
        self.canvas.tag_bind(self.cropregion, "<B1-Motion>",
                             self.OnTokenMotion)
        self.scalevar = IntVar()
        maxcrop = min(self.original.size[0] / cropsize[0] * 100,
                      self.original.size[1] / cropsize[1] * 100)
        self.scale = Scale(self,
                           from_=1,
                           to=maxcrop,
                           orient='horizontal',
                           variable=self.scalevar)
        self.scale.set(100)
        self.scalevar.trace('w', self.scalebbox)
        self.scale.pack()
        self.bf = Frame(self, bg=self['bg'])
        self.bf.pack()
        self.updatepreview()

    def gather(self):
        if self.returntype == 'croppedimage': return self.getboundimage()
        else:
            return (self.canvas.bbox(self.cropregion),
                    self.scalevar.get() / 100)

    def getboundimage(self):
        img = self.original.crop(self.canvas.bbox(self.cropregion))
        if img.size[0] < self.cropsize[0]:
            img = img.resize(self.cropsize, PILImage.LANCZOS)
        else:
            img.thumbnail(self.cropsize, PILImage.LANCZOS)
        return img

    def updatepreview(self):
        self.preview = self.getboundimage()
        self.preview = ImageTk.PhotoImage(self.preview)
        self.previewlabel['image'] = self.preview

    def scalebbox(self, *event):
        x1, y1, x2, y2 = self.canvas.coords(self.cropregion)
        centerpoint = (x1 + (x2 - x1) // 2, y1 + (y2 - y1) // 2)
        width, height = self.cropsize[0] * self.scalevar.get(
        ) / 100, self.cropsize[1] * self.scalevar.get() / 100
        self.canvas.coords(
            self.cropregion,
            (centerpoint[0] - width // 2, centerpoint[1] - height // 2,
             centerpoint[0] + width // 2, centerpoint[1] + height // 2))
        self.updatepreview()

    def OnTokenButtonPress(self, event):
        '''Being drag of an object'''
        # record the item and its location
        self._drag_data["x"] = event.x
        self._drag_data["y"] = event.y

    def OnTokenButtonRelease(self, event):
        '''End drag of an object'''
        # reset the drag information
        self._drag_data["x"] = 0
        self._drag_data["y"] = 0

    def OnTokenMotion(self, event):
        '''Handle dragging of an object'''
        # compute how much this object has moved
        delta_x = event.x - self._drag_data["x"]
        delta_y = event.y - self._drag_data["y"]
        ## Keep in screen
        x1, y1, x2, y2 = self.canvas.coords(self.cropregion)
        height, width = int(self.canvas.cget("height")), int(
            self.canvas.cget("width"))
        if x1 + delta_x < 0: delta_x = -x1
        elif x2 + delta_x > width: delta_x = width - x2
        if y1 + delta_y < 0: delta_y = -y1
        elif y2 + delta_y > height: delta_y = height - y2
        # move the object the appropriate amount
        self.canvas.move(self.cropregion, delta_x, delta_y)
        # record the new position
        self._drag_data["x"] = event.x
        self._drag_data["y"] = event.y
        self.updatepreview()
Example #19
0
class SettingsPopup(Frame):
    def __init__(self, parent, controller):
        Frame.__init__(self, parent, background="#bbb", bd=1)
        self.controller = controller
        self.place(anchor="ne", relx=1, y=50)
        # self.geometry(str(400) + "x" + str(400))

        whiteframe = Frame(self)
        whiteframe.pack(fill="both", expand=2, padx=1, pady=1)

        self.closeBtn = ImageTk.PhotoImage(
            PILImage.open("./close_btn.png").resize((8, 8)))
        buttonClose = Button(whiteframe,
                             image=self.closeBtn,
                             command=self.closeSettings)
        buttonClose.pack(anchor="w")
        # buttonClose.place(anchor="nw")

        bottomframe1 = Frame(whiteframe)
        bottomframe1.pack(side=BOTTOM)

        CameraLabel = Label(bottomframe1, text='Camera Number')
        CameraLabel.grid(row=1, column=0)
        self.camNumber = IntVar()
        entryCam = Entry(bottomframe1, textvariable=self.camNumber)
        entryCam.grid(row=1, column=1)
        self.camNumber.set(self.controller.get("camera"))
        self.camNumber.trace("w", self.setCamera)

        ComLabel = Label(bottomframe1, text='COM Port')
        ComLabel.grid(row=2, column=0)
        self.comNumber = IntVar()
        entryCom = Entry(bottomframe1, textvariable=self.comNumber)
        entryCom.grid(row=2, column=1)
        self.comNumber.set(self.controller.get("comPort"))
        self.comNumber.trace("w", self.setComPort)

        labelLat = Label(bottomframe1, text='Latitude (N+)')
        labelLat.grid(row=3, column=0)
        self.latVal = DoubleVar()
        self.latVal.set(self.controller.get("Lat"))
        self.latVal.trace("w", self.setLat)
        entryLat = Entry(bottomframe1, textvariable=self.latVal)
        entryLat.grid(row=3, column=1)

        labelLon = Label(bottomframe1, text='Longitude (E+)')
        labelLon.grid(row=4, column=0)
        self.lonVal = DoubleVar()
        self.lonVal.set(self.controller.get("Lon"))
        self.lonVal.trace("w", self.setLon)
        entryLon = Entry(bottomframe1, textvariable=self.lonVal)
        entryLon.grid(row=4, column=1)

        rotationLabel = Label(bottomframe1, text='Rotation')
        rotationLabel.grid(row=5, column=0)
        self.rotationAngle = IntVar()
        self.rotationAngle.set(self.controller.get("rotate"))
        self.rotationAngle.trace("w", self.setRot)
        entryRot = Entry(bottomframe1, textvariable=self.rotationAngle)
        entryRot.grid(row=5, column=1)

        horFlip = Button(bottomframe1,
                         text='Horizontal Flip',
                         command=self.setHorFlip)
        horFlip.grid(row=6, column=0)

        verFlip = Button(bottomframe1,
                         text='Vertical Flip',
                         command=self.setVerFlip)
        verFlip.grid(row=6, column=1)

        def getExposureStr(strValue):
            value = float(strValue)
            if value > 0:
                return "{:.3f}".format(pow(2, value))
            else:
                return "1/{:.3f}".format(pow(2, abs(value)))

        def setExposureValue(strValue):
            value = float(strValue)
            self.controller.set("exposure", value)
            self.labelExposureVal.config(text=getExposureStr(value))

        labelExposure = Label(bottomframe1, text="Exposure (s)")
        labelExposure.grid(row=7, column=0)
        self.labelExposureVal = Label(bottomframe1,
                                      text=getExposureStr(
                                          self.controller.get("exposure")))
        self.labelExposureVal.grid(row=7, column=1)

        exposure = Scale(bottomframe1,
                         from_=-15,
                         to=5,
                         resolution=.1,
                         orient="horizontal",
                         showvalue=0,
                         command=setExposureValue)
        exposure.grid(row=8, column=0, columnspan=2, sticky="nesw")
        exposure.set(self.controller.get("exposure"))

        def setGainValue(strValue):
            value = int(strValue)
            self.controller.set("gain", value)
            self.labelGainVal.config(text=str(value))

        labelGain = Label(bottomframe1, text="Gain")
        labelGain.grid(row=9, column=0)
        self.labelGainVal = Label(bottomframe1,
                                  text=str(self.controller.get("gain")))
        self.labelGainVal.grid(row=9, column=1)

        gain = Scale(bottomframe1,
                     from_=0,
                     to=300,
                     orient="horizontal",
                     showvalue=0,
                     command=setGainValue)
        gain.grid(row=10, column=0, columnspan=2, sticky="nesw")
        gain.set(self.controller.get("gain"))

        def setRefreshRate(strValue):
            value = int(strValue)
            self.controller.set("refreshRate", value)
            self.labelRefreshRateVal.config(text=str(value))

        labelRefresh = Label(bottomframe1, text="Refresh Rate (FPS)")
        labelRefresh.grid(row=11, column=0)
        self.labelRefreshRateVal = Label(
            bottomframe1, text=str(self.controller.get("refreshRate")))
        self.labelRefreshRateVal.grid(row=11, column=1)

        refreshRate = Scale(bottomframe1,
                            from_=1,
                            to=60,
                            orient="horizontal",
                            showvalue=0,
                            command=setRefreshRate)
        refreshRate.grid(row=12, column=0, columnspan=2, sticky="nesw")
        refreshRate.set(self.controller.get("refreshRate"))

    def closeSettings(self):
        self.destroy()

    # noinspection PyUnusedLocal
    def setLat(self, *args):
        self.controller.set("Lat", varGetOrDefault(self.latVal, 0.0))

    # noinspection PyUnusedLocal
    def setLon(self, *args):
        self.controller.set("Lon", varGetOrDefault(self.lonVal, 0.0))

    # noinspection PyUnusedLocal
    def setComPort(self, *args):
        self.controller.set("comPort", varGetOrDefault(self.comNumber, 0))

    # noinspection PyUnusedLocal
    def setCamera(self, *args):
        self.controller.set("camera", varGetOrDefault(self.camNumber, 0))

    # noinspection PyUnusedLocal
    def setRot(self, *args):
        self.controller.set("rotate", varGetOrDefault(self.rotationAngle, 0))

    # noinspection PyUnusedLocal
    def setHorFlip(self, *args):
        self.controller.set("flipHorizontal",
                            not self.controller.get("flipHorizontal"))

    # noinspection PyUnusedLocal
    def setVerFlip(self, *args):
        self.controller.set("flipVertical",
                            not self.controller.get("flipVertical"))
class InstructorGUI(tk.Frame):
    """Instructor GUI frame to be used in the main GUI
 
    This class contains multiple input widgets for the GUI,
    as well as the Client class used to connect with the
    socket server.
    """

    # Settings
    header_1_style = "TkDefaultFont 18 bold"
    header_2_style = "TkDefaultFont 16 bold"
    default_style  = "TkDefaultFont 14"

    def __init__(self, parent, *args, **kwargs):
        """Constructor
 
        Args:
            parent (tk.widget): parent widget to make the frame a child of
            *args: Variable length argument list
            **kwargs: Arbitrary keyword argument list
        """
        ttk.Frame.__init__(self, parent, *args, **kwargs)
        self.parent = parent

        # Socket client
        self.client = Client(port=25565)

        # ============ GUI Variables ============ 
        self.host = StringVar(self, value=self.client.get_hostname())

        # Instructor realtime setting variables
        self.hr = IntVar(self, value=80)
        self.threshold = StringVar(self, value=20)
        self.hr_paced = IntVar(self, value=80)
        
        self.pathway_1 = IntVar(self, value=0)
        self.pathway_2 = IntVar(self, value=0)

        # Manual Override variables
        self.position = IntVar(self, value=0)
        self.is_pacing = BooleanVar(self, value=False)
        self.is_pos_overriding = BooleanVar(self, value=False)

        # ============ Main Frame Sides ===========
        # Left side frame
        frame_left = Frame(self, bd=1, relief=tk.SUNKEN)
        frame_left.pack(side=tk.LEFT, padx=10, pady=10)
        Label(frame_left, text="Override Settings", font=self.header_1_style).pack()

        # Middle frame
        frame_mid = Frame(self, bd=1, relief=tk.SUNKEN)
        frame_mid.pack(side=tk.LEFT, padx=10, pady=10)
        Label(frame_mid, text="Real-Time Settings", font=self.header_1_style).pack()

        # Right side frame
        frame_right = Frame(self, bd=1, relief=tk.SUNKEN)
        frame_right.pack(side=tk.RIGHT, padx=10, pady=10)
        Label(frame_right, text="Display Preview", font=self.header_1_style).pack()

        # ============ Position Selection ===============
        frame_position = Frame(frame_left)
        frame_position.pack(pady=5)

        # Sets the position variable to invoke a callback
        self.position.trace('w', self.callback_manual_pos)

        # Radiobutton options for the override
        POSITIONS = [
            ("Superior Vena Cava", 1),
            ("High Right Atrium", 2),
            ("Mid Right Atrium", 3),
            ("Low Right Atrium", 4),
            ("Right Ventricle", 5),
            ("Right Ventricular Wall", 6),
            ("Asystole", 0),
        ]

        Label(frame_position, text="Show Manual Position", font=self.default_style).pack()

        # Creates each radiobutton item
        for button_text, position_value in POSITIONS:
            Radiobutton(frame_position, text=button_text, value=position_value, variable=self.position, font=self.default_style).pack()

        # Creates a toggle button to start/stop the manual override
        self.btn_pos_override = Button(frame_position, text="Start Override", command=self.toggle_pos_override, fg="green", font=self.default_style)
        self.btn_pos_override.pack()

        # ============ Command Sends =============
        frame_command = Frame(frame_left)
        frame_command.pack(pady=5)

        Label(frame_command, text="Commands", font=self.default_style).pack()

        # Button to make the microcontroller recalibrate its sensor values
        btn_recalibrate = Button(frame_command, text="Calibrate Sensors", command=lambda: self.send_command('cal'), fg="green", font=self.default_style)
        btn_recalibrate.pack(side=tk.LEFT)

        # Button to make the student GUI restart the signal at asystole in case it gets stuck
        btn_reset_signal = Button(frame_command, text="Reset Signal (Asystole)", command=lambda: self.send_command('ressig'), fg="green", font=self.default_style)
        btn_reset_signal.pack(side=tk.LEFT)

        # ============ Connection Space ===============
        frame_connection = Frame(frame_mid)
        frame_connection.pack(pady=5)

        # Displays the host machine's IP address for use with the other GUI
        ip_label = "Device IP: {}".format(self.client.get_ip())
        Label(frame_connection, text=ip_label, font=self.default_style).pack()

        Label(frame_connection, text="Hostname", font=self.default_style).pack(side=tk.LEFT)

        # Area to enter the hostname to connect to
        entry_hostname = Entry(frame_connection, textvariable=self.host, font=self.default_style)
        entry_hostname.pack(side=tk.LEFT)

        # Button to start the TCP/IP connection
        btn_connect = Button(frame_connection, text="Connect", command=self.connect, fg="green", font=self.default_style)
        btn_connect.pack(side=tk.LEFT)

        # ============ Customisation Space ===============
        frame_signal = Frame(frame_mid)
        frame_signal.pack(pady=5)

        # Intrinsic heart rate entry area
        Label(frame_signal, text="Heart Rate", font=self.default_style).grid(row=0, column=0)
        
        scale_hr = Scale(frame_signal, from_=0, to=100, length=150, variable=self.hr, orient=tk.HORIZONTAL)
        scale_hr.grid(row=0, column=1)

        entry_hr = Entry(frame_signal, textvariable=self.hr, font=self.default_style, width=4)
        entry_hr.grid(row=0, column=2)

        # Pacing threshold entry area
        Label(frame_signal, text="Pacing Threshold", font=self.default_style).grid(row=1, column=0)

        scale_threshold = Scale(frame_signal, from_=0, to=50, length=150, variable=self.threshold, orient=tk.HORIZONTAL)
        scale_threshold.grid(row=1, column=1)

        entry_threshold = Entry(frame_signal, textvariable=self.threshold, font=self.default_style, width=4)
        entry_threshold.grid(row=1, column=2)

        # Paced heart rate entry area
        Label(frame_signal, text="Paced Heart Rate", font=self.default_style).grid(row=2, column=0)

        scale_hr_paced = Scale(frame_signal, from_=0, to=100, length=150, variable=self.hr_paced, orient=tk.HORIZONTAL)
        scale_hr_paced.grid(row=2, column=1)

        entry_hr_paced = Entry(frame_signal, textvariable=self.hr_paced, font=self.default_style, width=4)
        entry_hr_paced.grid(row=2, column=2)

        # Buttons for this area
        frame_signal_buttons = Frame(frame_signal)
        frame_signal_buttons.grid(row=3, columnspan=3)

        # Sends the updated settings (heart rate, pacing threshold, paced heart rate) to the student GUI
        btn_send_customisations = Button(frame_signal_buttons, text="Update ECG", command=self.send_customisations, fg="green", font=self.default_style, pady=5)
        btn_send_customisations.pack(side=tk.LEFT, fill=tk.X)

        # Starts a simulated pacer signal
        self.btn_pacing = Button(frame_signal_buttons, text="Start Pacing", command=self.toggle_pacing, fg="green", font=self.default_style, pady=5)
        self.btn_pacing.pack(side=tk.RIGHT, fill=tk.X)

        # ========== Pathway Selection ==============
        frame_pathway = Frame(frame_mid)
        frame_pathway.pack(pady=5)

        # Sets both pathway variables to invoke a callback
        self.pathway_1.trace('w', self.callback_pathway_1)
        self.pathway_2.trace('w', self.callback_pathway_2)

        # Alternate pathway options
        PATHWAYS_1 = [
            ("Low Right Atrium", 0),
            ("Inferior Vena Cava", 10)
        ]

        PATHWAYS_2 = [
            ("Right Ventricular Wall", 0),
            ("Pulmonary Artery", 10)
        ]

        Label(frame_pathway, text="Pathway Selection 1", font=self.header_2_style).pack(pady=5)

        # Create each radiobutton for the first pathway option
        for button_text, pathway_value in PATHWAYS_1:
            Radiobutton(frame_pathway, text=button_text, value=pathway_value, variable=self.pathway_1, font=self.default_style).pack()

        Label(frame_pathway, text="Pathway Selection 2", font=self.header_2_style).pack(pady=5)

        # Create each radiobutton for the second pathway option
        for button_text, pathway_value in PATHWAYS_2:
            Radiobutton(frame_pathway, text=button_text, value=pathway_value, variable=self.pathway_2, font=self.default_style).pack()

        # ======== Display Preview =========
        # Instantiated signals class to generate signals
        self.ecg_signals = Signals()

        self.new_x = [0.0]
        self.new_y = [0.0]

        self.last_x = 0
        self.last_x_lim = 0

        self.position_to_show = 0

        self.variation = 0

        self.flat_span = False
        self.end_flat = 0
        self.flat_span_y = 0
        self.plot_point = 0

        # Creates plotting canvas
        self.fig = plt.Figure(figsize=(10, 4.5), dpi=100,facecolor='k',edgecolor='k')
        
        canvas = FigureCanvasTkAgg(self.fig, master=frame_right)
        canvas.get_tk_widget().pack()

        # Sets plot customisations
        self.ax = self.fig.add_subplot(111)
        self.ax.set_xlim(self.last_x_lim, 4)
        self.ax.set_ylim(-3.0, 3.0)
        self.ax.set_yticklabels([])
        self.ax.set_xticklabels([])
        self.ax.xaxis.set_tick_params(width=1, top=True)
        self.ax.set_facecolor('black')

        self.line, = self.ax.plot(0, 0)
        self.ax.get_lines()[0].set_color("xkcd:lime")

        # Starts an animated plot for the ECG signal
        self.ani = animation.FuncAnimation(self.fig, self.animate, interval=24, blit=True)

    def animate(self, i):
        """Animation function that is called periodically
 
        Args:
            i (int): the current frame value (not used)

        Returns:
            line (matplotlib.line): The line to plot with the next value
        """

        # If currently overriding the student GUI, show the display preview
        if self.is_pos_overriding.get():
            # Set the position index value based on which source is responsible for the signal
            position_index = self.position.get()

            # Set initial heart rate to use
            hr_to_use = self.hr.get()

            # Adjust position and heart rate based on alternative pathways and pacer setting
            if position_index == 4:
                position_index = position_index + self.pathway_1.get()
            elif position_index == 6:
                position_index = position_index + self.pathway_2.get()

                # Show the paced signal if pacer override is active
                if not position_index == 16 and self.is_pacing.get():
                    position_index = 26
                    hr_to_use = self.hr_paced.get()
            else:
                # If no overrides or special settings, just keep the position the same
                position_index = position_index

            # Get the ECG signal values for the corresponding settings
            [x, y] = self.ecg_signals.get_signal(self.ecg_signals.signal_index[position_index], hr_to_use)

            # If not currently traveling between beats
            if not self.flat_span:
                # Set a variable to the potential next value
                x_val = self.last_x + x[self.plot_point]

                # If the potential new value is not going backwards
                if x_val > self.new_x[-1]:
                    # Add the new x and y values to the axis lists
                    self.new_x.append(x_val)
                    self.new_y.append(y[self.plot_point])

                    # Update the line
                    self.line.set_data(self.new_x, self.new_y)  # update the data
                
                # If at the end of the beat
                if self.plot_point== 29:
                    # Update where the last x value to build from is
                    self.last_x = self.new_x[-1]

                    # Start plotting for a flat area
                    self.end_flat = (x[-1] - x[-2]) + self.new_x[-1]
                    self.flat_span_y = y[-1]
                    self.flat_span = True
                    
                # Go back to the start of the heart beat if at the end of the beat
                if self.plot_point == 29:
                    self.plot_point = 0
                # Go to the next beat value otherwise
                else:
                    self.plot_point = self.plot_point + 1
            # If current traveling between beats
            else:
                # Add the new x and y values to the axis lists
                self.new_x.append(self.new_x[-1] + 0.05)
                self.new_y.append(self.flat_span_y)

                # Update the line
                self.line.set_data(self.new_x, self.new_y)  # update the data

                # If reached the end of the flat line area between beats
                if self.new_x[-1] >= self.end_flat:
                    # Stop plotting flat
                    self.flat_span = False
                    self.last_x = self.new_x[-1]
            
            # If at the end of the plotting window
            if self.new_x[-1] >= self.last_x_lim + 5:
                # Shift the plotting window (this is used instead of a reset to allow for future ECG output options)
                self.last_x_lim += 5
                self.ax.set_xlim(self.last_x_lim, self.last_x_lim + 5)

        # Returns the new line to the plot
        return self.line,

    def connect(self):
        """Connects the instructor GUI to the student GUI"""

        # Update the hostname
        self.client.set_hostname(self.host.get())

        # Actually connect
        self.client.start()

    def send_command(self, message):
        """Sends a message from the instructor GUI to the student GUI

        Args:
            message (str): Message to send through the socket connection
        """
        self.client.send_data(message)

    def send_customisations(self):
        """Sends updated customisation data from instructor GUI to the student GUI"""

        # Command code
        self.client.send_data("update")
        # Data
        self.client.send_data("{},{},{}".format(self.hr.get(), self.threshold.get(), self.hr_paced.get()))

    def toggle_pos_override(self):
        """Sends position override data from the instructor to the student GUI"""

        # Toggle if we are overriding
        self.is_pos_overriding.set(not self.is_pos_overriding.get())

        # If we are now overriding
        if self.is_pos_overriding.get():
            # Start command code
            self.client.send_data("start-pos")
            # Data
            self.client.send_data("%d" % self.position.get())
            # Switch the toggle button
            self.btn_pos_override.config(fg="red", text="Stop Override")

            self.ani.event_source.start()
        # If we are now not overriding
        else:
            # Stop command code
            self.client.send_data("stop-pos")
            # Switch the toggle button
            self.btn_pos_override.config(fg="green", text="Start Override")
            # Stop the preview plotting
            self.ani.event_source.stop()

    def toggle_pacing(self):
        """Toggles whether to be pacing"""

        # Toggles if we are pacing
        self.is_pacing.set(not self.is_pacing.get())

        # If we are now pacing
        if self.is_pacing.get():
            # Start pacing command code
            self.client.send_data("start-pace")
            # Send customisation network update
            self.send_customisations()
            # Switch the toggle button
            self.btn_pacing.config(fg="red", text="Stop Pacing")
        else:
            # Stop pacing command code
            self.client.send_data("stop-pace")
            # Switch the toggle button
            self.btn_pacing.config(fg="green", text="Start Pacing")
            
    def callback_pathway_1(self, *args):
        """Callback function for when the pathway is switched.

        This will automatically send an updated pathway.
 
        Args:
            *args: Variable length argument list
        """

        # Command code
        self.client.send_data("chpa1")
        # Data
        self.client.send_data("%d" % self.pathway_1.get())

    def callback_pathway_2(self, *args):
        """Callback function for when the pathway is switched.

        This will automatically send an updated pathway.
 
        Args:
            *args: Variable length argument list
        """
        
        # Command code
        self.client.send_data("chpa2")
        # Data
        self.client.send_data("%d" % self.pathway_2.get())
    
    def callback_manual_pos(self, *args):
        """Callback function for when the pathway is switched.

        This will automatically send an updated pathway.
 
        Args:
            *args: Variable length argument list
        """
        
        # If we are actively overriding
        if self.is_pos_overriding.get():
            # Command code
            self.client.send_data("manual-pos")
            # Data
            self.client.send_data("%d" % self.position.get())
    
    def stop_gui(self):
        """Stops the instructor GUI client"""
        self.client.stop()
Example #21
0
class MainFrame(Frame):
    MIN_ARCHIVE_SIZE = 100 * 1024
    MAX_ARCHIVE_SIZE = 2.5 * 1024 * 1024

    def __init__(self, master, **kwargs):
        super(MainFrame, self).__init__(master, **kwargs)
        self.master = master

        # Configurazione griglia
        self.grid_columnconfigure(0, weight=1, pad=10)
        self.grid_columnconfigure(1, weight=5, pad=10)
        self.grid_columnconfigure(2, weight=1, pad=10)
        # for i in range(0, 10):
        #     self.grid_rowconfigure(i, pad=0, weight=1)

        # Titolo
        # Label(self, text="Pigroman", font=("Segoe UI", 16), image=icons.get_icon("icon.png"),
        # compound="left").grid(row=0, column=0, padx=(0, 30), sticky="w")

        self.data_path = StringVar()
        self.data_path.trace(
            "w", lambda *_: self.subfolders_list_frame.reevaluate_statuses())
        self.output_path = StringVar()
        self.output_name = StringVar()
        self.max_block_size = IntVar()
        self.max_block_size.trace(
            "w", lambda *_: self.max_archive_size_label.configure(
                text=conversions.number_to_readable_size(self.max_block_size.
                                                         get())))
        self.compress = BooleanVar()
        self.create_esl = BooleanVar()
        self.file_types = defaultdict(BooleanVar)

        Label(self, text="Data path").grid(row=1, column=0, sticky="w")
        BrowseFrame(self, self.data_path).grid(row=1,
                                               column=1,
                                               sticky="we",
                                               columnspan=4)

        Label(self, text="Output path").grid(row=2, column=0, sticky="w")
        BrowseFrame(self, self.output_path).grid(row=2,
                                                 column=1,
                                                 sticky="we",
                                                 columnspan=4)

        Label(self, text="Output name").grid(row=3, column=0, sticky="w")
        Entry(self, textvariable=self.output_name).grid(row=3,
                                                        column=1,
                                                        sticky="we",
                                                        columnspan=2)

        Label(self, text="Max archive size").grid(row=4, column=0, sticky="w")
        Scale(self,
              from_=self.MIN_ARCHIVE_SIZE,
              to=self.MAX_ARCHIVE_SIZE,
              variable=self.max_block_size).grid(row=4, column=1, sticky="we")
        self.max_archive_size_label = Label(self)
        self.max_archive_size_label.grid(row=4, column=2, sticky="e")

        self.folders_group = LabelFrame(self, text="Subfolders")
        self.folders_group.grid(row=5, column=0, columnspan=5, sticky="nswe")
        self.subfolders_list_frame = SubfoldersListFrame(
            self.folders_group, self.data_path)
        self.subfolders_list_frame.pack(fill="both")

        self.options_group = LabelFrame(self, text="Options")
        self.options_group.grid(row=6, column=0, sticky="nswe", columnspan=3)
        Checkbutton(self.options_group,
                    text="Compress",
                    variable=self.compress).pack(side="left")
        Checkbutton(self.options_group,
                    text="Create ESLs",
                    variable=self.create_esl).pack(side="left")

        self.file_types_group = LabelFrame(self,
                                           text="File types (coming soon)")
        self.file_types_group.grid(row=7,
                                   column=0,
                                   sticky="nswe",
                                   columnspan=4)
        r = 0
        c = 0
        for i, x in enumerate(("Meshes", "Textures", "Menus", "Sounds",
                               "Voices", "Shaders", "Trees", "Fonts", "Misc")):
            Checkbutton(self.file_types_group,
                        text=x,
                        variable=self.file_types[x.lower()],
                        state="disabled").grid(row=r, column=c, sticky="we")
            c += 1
            if c == 3:
                c = 0
                r += 1

        self.build_button = Button(self,
                                   text="Create packages!",
                                   image=icons.get_icon("save.png"),
                                   compound="left",
                                   command=self.build)
        self.build_button.grid(row=8, column=0, columnspan=4, sticky="we")

        self.max_block_size.set(1.5 * 1024 * 1024)

    def build(self):
        try:
            self.master.check_archive()
            self.check_settings()
            self.build_button.config(state="disabled")
        except ValueError as e:
            messagebox.showerror("Error", str(e))
        finally:
            self.build_button.config(state="enabled")

    def check_settings(self):
        if not self.data_path.get().lower().strip().endswith("data"):
            raise ValueError("The data path must be a folder named 'data'.")
        if not os.path.isdir(self.data_path.get()):
            raise ValueError("The data path does not exist.")
        if not os.path.isdir(self.output_path.get()):
            raise ValueError("The output path does not exist.")
        if not self.MIN_ARCHIVE_SIZE < self.max_block_size.get(
        ) < self.MAX_ARCHIVE_SIZE:
            raise ValueError(
                "The archive size must be between 100MB and 2.5GB")
        if not self.output_name.get().strip():
            raise ValueError("Invalid output name")
        if not [
                x for x in self.subfolders_list_frame.subfolders
                if bool(x.var.get().strip())
        ]:
            raise ValueError("No subfolders specified")
        for subfolder in self.subfolders_list_frame.subfolders:
            path = subfolder.var.get()
            if not self.subfolders_list_frame.is_subfolder(path):
                raise ValueError(
                    f"{path} is not a data subfolder or does not exist!")
Example #22
0
class Hangman:
    """Hangman class,
    made in tkinter GUInterface"""
    def __init__(self, master=None):
        """Constructor"""

        # Screens init
        self.master = master
        self.screen()
        self.build_frame()
        self.build_grid()

        # All displayed labels
        self.banner_text = StringVar()
        self.banner_text.set('')
        self.banner_text.trace('w', self.build_title_label)

        self.counter = StringVar()
        self.counter.trace('w', self.build_countdown)

        self.clock_text = StringVar()
        self.clock_text.set('Clock')
        self.clock_text.trace('w', self.build_clock_label)

        self.word_text = StringVar()
        self.word_text.set('Choose your category')
        self.word_text.trace('w', self.build_word_label)

        self.secret_word = StringVar()
        self.prompt = StringVar()
        self.prompt.set('random')

        self.timer_text = StringVar()
        self.timer_text.set(strftime("%H:%M"))
        self.timer_text.trace('w', self.build_timer)

        self.time_left = IntVar()
        self.time_left.set(st.DEFAULT_GAP)
        self.time_left.trace('w', self.alert)

        self.score_text = IntVar()
        self.score_text.set('0\n0')
        self.score_text.trace('w', self.build_score_label)

        # Predefined variables
        self.sorted = ''
        self.char = ''
        self.level_status = 1
        self.total = 0
        self.abc = []
        self.category = []
        self.level = []
        self.misses = []
        self.guesses = []
        self.s_w_list = []
        self.first_last = []
        self.a = {}
        self.running = False
        self.rem = self.master.winfo_screenwidth()
        self.w = self.master.winfo_screenwidth() / 5
        self.h = self.master.winfo_screenheight() * 2 / 7

        # Methods call in __init__()
        self.build_title_label()
        self.build_word_label()
        self.build_countdown()
        self.build_welcome_label()
        self.build_score_label()
        self.build_timer()
        self.build_clock_label()

        self.buttons()
        self.category_buttons()
        self.abc_buttons()

        # Update time
        self.update()

    def screen(self):
        """Main window
        Minimal size 1024 / 600
        The window goes full screen if the width it's lower than 1440"""
        self.master.minsize(1024, 600)
        if self.master.winfo_screenwidth() <= 1440:
            # and self.master.winfo_screenheight() <= 720:
            self.master.overrideredirect(True)
            self.master.geometry('{0}x{1}+0+0'.format(
                self.master.winfo_screenwidth(),
                self.master.winfo_screenheight()))
        # elif 1024 < self.master.winfo_screenwidth() < 1920 \
        #         and 720 < self.master.winfo_screenheight() < 1080:
        #     a = int(float(70) * float(self.master.winfo_screenwidth()) / 100)
        #     b = int(float(70) * float(self.master.winfo_screenheight()) / 100)
        #     self.master.overrideredirect(True)
        #     self.master.geometry('{}x{}+{}+{}'.format(
        #         a, b, (self.master.winfo_screenwidth() // 2 - a // 2),
        #         (self.master.winfo_screenheight() // 2 - b // 2)))
        else:
            self.master.overrideredirect(True)
            x = int(float(65) * float(self.master.winfo_screenwidth()) / 100)
            y = int(float(75) * float(self.master.winfo_screenheight()) / 100)
            self.master.maxsize(x, y)
            self.master.geometry('{}x{}+{}+{}'.format(
                x, y, (self.master.winfo_screenwidth() // 2 - x // 2),
                (self.master.winfo_screenheight() // 2 - y // 2)))
        self.master.update_idletasks()

    def mini_screen(self):
        """Secondary window for result information"""
        # noinspection PyAttributeOutsideInit
        self.master_2 = Tk()
        # noinspection PyAttributeOutsideInit
        frame = Frame(self.master_2,
                      bg=st.color['fg'],
                      highlightbackground=st.color['fg_enter'],
                      highlightcolor=st.color['fg_enter'],
                      highlightthickness=3)
        if self.rem < 1440:
            x, y = 400, 500
        else:
            x, y = 700, 700
        self.master_2.geometry('{}x{}+{}+{}'.format(
            x, y, int(self.master.winfo_screenwidth() // 2 - x / 2),
            int(self.master.winfo_screenheight() // 2 - y / 2)))
        self.master_2.overrideredirect(True)
        self.master_2.update_idletasks()
        frame.pack(fill=BOTH, expand=True)
        niv = None
        if self.level_status == 1:
            niv = st.L_EN[2]
        if self.level_status == 2:
            niv = st.L_EN[1]
        if self.level_status == 3:
            niv = st.L_EN[0]
        text = st.P_EN['won'].format(niv.capitalize(), self.secret_word.get(),
                                     self.total, '')
        fg = 'green'
        bg = st.color['fg']
        if len(self.misses) == 7 or not self.time_left.get():
            text = st.P_EN['lose']
            fg = 'red'
        # noinspection PyAttributeOutsideInit
        label = Label(frame,
                      text=text,
                      bg=bg,
                      fg=fg,
                      justify=LEFT,
                      font=st.font(self.rem, 5))
        ok = Button(frame,
                    bg=st.color['fg'],
                    bd=0,
                    fg=st.color['fg_enter'],
                    font=st.font(self.rem, 3),
                    text='Ok',
                    command=lambda: self.master_2.destroy(),
                    activeforeground='black',
                    activebackground=st.color['fg'],
                    width=2)
        label.pack(fill=BOTH, expand=True)
        ok.place(relx=0.5, rely=0.96, anchor='s')

    def score_screen(self):
        """Score screen"""
        # noinspection PyAttributeOutsideInit
        self.master_3 = Tk()
        # noinspection PyAttributeOutsideInit
        self.frame = Frame(self.master_3,
                           bg=st.color['fg'],
                           highlightbackground=st.color['fg_enter'],
                           highlightcolor=st.color['fg_enter'],
                           highlightthickness=3)
        if self.rem < 1440:
            x, y = 450, 500
        else:
            x, y = 600, 700
        self.master_3.geometry('{}x{}+{}+{}'.format(
            x, y, int(self.master.winfo_screenwidth() // 2 - x / 2),
            int(self.master.winfo_screenheight() // 2 - y / 2)))
        self.master_3.overrideredirect(True)
        self.master_3.update_idletasks()
        self.frame.pack(fill=BOTH, expand=True)
        text = 'Level \t  Points\t\tTime\n' + 37 * '_' + '\n'
        for item in rc.search():
            text += '{}\t   {}     {}\n'.format(
                item.level, item.points,
                item.timestamp.strftime(' %B %d, %Y %I:%M %p'))
        label = Label(self.frame,
                      text=text,
                      bg=st.color['bg'],
                      fg=st.color['fg'],
                      justify=LEFT,
                      font=st.font(self.rem, 0))
        ok = Button(self.frame,
                    bg=st.color['bg'],
                    bd=0,
                    fg=st.color['fg_enter'],
                    font=st.font(self.rem, 3),
                    text='Ok',
                    command=lambda: self.master_3.destroy(),
                    activeforeground='black',
                    activebackground=st.color['fg'],
                    width=2)
        label.pack(fill=BOTH, expand=True)
        ok.place(relx=0.5, rely=0.96, anchor='s')

    def create_frame(self, frame, code):
        """Creates frames for the main window"""
        wa, wb, ha, hb = None, None, None, None
        if code is 'm':
            wa, wb, ha, hb = 1, 1, 1, 1
        elif code is 'r2':
            wa, wb, ha, hb = 1, 1, 3, 5
        elif code in ['r0', 'r1', 'r3']:
            wa, wb, ha, hb = 1, 1, 1, 5
        return Frame(frame,
                     bg=st.color['bg'],
                     width=self.master.winfo_width() * wa // wb,
                     height=self.master.winfo_height() * ha // hb)

    def create_button(self, frame, text):
        """General buttons creator"""
        font = None
        command = self.set_command(text)
        state = None
        bg, fg, abg, afg, dfg = st.set_buttons_color(text)
        button = Button(frame,
                        text=text.capitalize(),
                        bg=bg,
                        fg=fg,
                        font=font,
                        bd=0,
                        command=command,
                        justify='center',
                        overrelief=SUNKEN,
                        anchor=CENTER,
                        state=state,
                        disabledforeground=dfg,
                        activeforeground=afg,
                        activebackground=abg,
                        image=None)
        return button

    def build_frame(self):
        """All the frames for the main window"""
        # noinspection PyAttributeOutsideInit
        self.welcome = self.create_frame(self.master, 'm')
        self.welcome.config(highlightbackground=st.color['fg'],
                            highlightcolor=st.color['fg'],
                            highlightthickness=2)
        self.welcome.pack(fill=BOTH, expand=True)

        # noinspection PyAttributeOutsideInit
        self.mainframe = self.create_frame(self.master, 'm')
        self.mainframe.config(highlightbackground='black',
                              highlightcolor='black',
                              highlightthickness=2)
        self.mainframe.pack(fill=BOTH, expand=True)
        self.mainframe.pack_forget()
        # Row 0
        # noinspection PyAttributeOutsideInit
        self.row_0_grid = self.create_frame(self.mainframe, 'r0')
        self.row_0_grid.grid(row=0, column=0, sticky='news', pady=10)
        # Row 0 Column 1
        # noinspection PyAttributeOutsideInit
        self.row_0_column_1 = Frame(self.row_0_grid, bg=st.color['bg'])
        self.row_0_column_1.config(highlightbackground=st.color['fg'],
                                   highlightcolor=st.color['bg_start'],
                                   highlightthickness=2)
        self.row_0_column_1.grid(row=0,
                                 column=1,
                                 rowspan=2,
                                 sticky='news',
                                 pady=7)
        # Row 0 Column 2
        # noinspection PyAttributeOutsideInit
        self.row_0_column_2 = Frame(self.row_0_grid, bg=st.color['bg'])
        self.row_0_column_2.config(highlightbackground=st.color['fg'],
                                   highlightcolor=st.color['bg_start'],
                                   highlightthickness=2)
        self.row_0_column_2.grid(row=0,
                                 column=2,
                                 rowspan=2,
                                 sticky='news',
                                 padx=20,
                                 pady=6)
        # Row 0 Column 3
        # noinspection PyAttributeOutsideInit
        self.row_0_column_3 = Frame(self.row_0_grid, bg=st.color['bg'])
        self.row_0_column_3.grid(row=0,
                                 column=3,
                                 rowspan=2,
                                 columnspan=2,
                                 sticky='news',
                                 padx=3,
                                 pady=4)
        # Row 1
        # noinspection PyAttributeOutsideInit
        self.row_1_grid = self.create_frame(self.mainframe, 'r1')
        self.row_1_grid.grid(row=1, column=0, sticky='news', padx=13)
        # Row 2
        # noinspection PyAttributeOutsideInit
        self.row_2_grid = self.create_frame(self.mainframe, 'r2')
        self.row_2_grid.grid(row=2, column=0, sticky='news')
        # Row 3
        # noinspection PyAttributeOutsideInit
        self.row_3_grid = self.create_frame(self.mainframe, 'r3')
        self.row_3_grid.grid(row=3, column=0, sticky='news', padx=5, pady=5)

    def build_grid(self):
        """Grid layout for all the frames"""
        u, u2 = None, None
        # Main Frame
        self.mainframe.columnconfigure(0, weight=1, uniform=u)
        for m in range(0, 4):
            self.mainframe.rowconfigure(m, weight=1, uniform=u2)
        # Row 0
        for y in range(15):
            self.row_0_grid.columnconfigure(y, weight=1, uniform=u)
        self.row_0_grid.rowconfigure(0, weight=1, uniform=u2)
        self.row_0_grid.rowconfigure(1, weight=1, uniform=u2)
        # Row 0 Column 1
        self.row_0_column_1.columnconfigure(0, weight=1, uniform=u)
        for c in range(6):
            self.row_0_column_1.rowconfigure(c, weight=1, uniform=u2)
        # Row 0 Column 2
        self.row_0_column_2.columnconfigure(0, weight=1, uniform=u)
        for c2 in range(3):
            self.row_0_column_2.rowconfigure(c2, weight=1, uniform=u2)
        # Row 0 Column 3
        self.row_0_column_3.columnconfigure(0, weight=1, uniform=u)
        for c3 in range(3):
            self.row_0_column_3.rowconfigure(c3, weight=1, uniform=u2)
        # Row 1
        for x in range(13):
            self.row_1_grid.columnconfigure(x, weight=1, uniform=u)
        self.row_1_grid.rowconfigure(0, weight=1, uniform=u2)
        self.row_1_grid.rowconfigure(1, weight=1, uniform=u2)
        # Row 2
        self.row_2_grid.columnconfigure(0, weight=1, uniform=u)
        self.row_2_grid.columnconfigure(1, weight=1, uniform=u)
        self.row_2_grid.rowconfigure(0, weight=1, uniform=u2)
        # Row 3
        for n in range(0, 4):
            self.row_3_grid.columnconfigure(n, weight=1, uniform=u)
        self.row_3_grid.rowconfigure(0, weight=1, uniform=u2)
        self.row_3_grid.rowconfigure(1, weight=1, uniform=u2)

    # noinspection PyUnusedLocal
    def build_score_label(self, *args):
        """Word categories label"""
        bg, fg = st.set_buttons_color('points', 1)
        points = Label(self.row_0_column_1,
                       text='Points:',
                       bg=bg,
                       fg=fg,
                       font=st.font(self.rem, -4, style='normal'),
                       bd=0,
                       width=2)
        points.grid(row=0, column=0, sticky='news', padx=0, pady=0)
        b, f = st.set_buttons_color('score', 1)
        score = Label(self.row_0_column_1,
                      textvariable=self.score_text,
                      bg=b,
                      fg=f,
                      font=st.font(self.rem, 0),
                      justify=CENTER,
                      bd=0,
                      width=2)
        score.grid(row=1, column=0, sticky='news', padx=0, pady=0)

    # noinspection PyUnusedLocal
    def build_welcome_label(self, *args):
        """Word categories label"""
        bg, fg = st.set_buttons_color('title', 1)
        title = Label(self.welcome,
                      text='Guess Letters',
                      bg=bg,
                      fg=fg,
                      font=st.font(self.rem, 24),
                      justify=CENTER)
        title.pack(side=TOP, padx=20, pady=50)

    # noinspection PyUnusedLocal
    def build_title_label(self, *args):
        """Word categories label"""
        bg, fg = st.set_buttons_color('title', 1)
        title = Label(self.row_0_grid,
                      text=self.banner_text.get(),
                      bg=bg,
                      fg=fg,
                      font=st.font(self.rem, 10),
                      width=6)
        title.grid(row=0,
                   column=5,
                   rowspan=2,
                   columnspan=10,
                   sticky='ew',
                   padx=5,
                   pady=2)

    # noinspection PyUnusedLocal
    def build_countdown(self, *args):
        """Countdown label"""
        em = self.master.winfo_width() // 50
        # noinspection PyAttributeOutsideInit
        self.count = Label(
            self.row_2_grid,
            textvariable=self.counter,
            bg=st.color['bg'],
            fg=st.color['7'],
            font=('Times new Roman', int(4.5 * em), 'bold'),
        )
        self.count.grid(row=0, column=0, sticky='nws', padx=2 * em, pady=5)

    # noinspection PyUnusedLocal
    def build_clock_label(self, *args):
        """Main clock label"""
        bg, fg = st.set_buttons_color('points', 1)
        # noinspection PyAttributeOutsideInit
        self.clock = Label(self.row_0_column_2,
                           textvariable=self.clock_text,
                           bg=bg,
                           fg=fg,
                           font=st.font(self.rem, 0),
                           bd=0,
                           width=2)
        self.clock.grid(row=0, column=0, sticky='news', padx=0, pady=0)

    # noinspection PyUnusedLocal
    def build_timer(self, *args):
        """Timer label"""
        b, f = st.set_buttons_color('timer', 1)
        # noinspection PyAttributeOutsideInit
        self.timer = Label(self.row_0_column_2,
                           text=self.timer_text.get(),
                           bg=b,
                           fg=f,
                           font=st.font(self.rem, 14))
        self.timer.grid(row=1, column=0, rowspan=2, sticky='nsew')

    # noinspection PyUnusedLocal
    def build_word_label(self, *args):
        """Secret word label"""
        bg, fg = st.set_buttons_color('word', 1)
        # noinspection PyAttributeOutsideInit
        self.secret_word_label = Label(self.row_2_grid,
                                       textvariable=self.word_text,
                                       bg=bg,
                                       fg=fg,
                                       justify=RIGHT,
                                       font=st.font(self.rem, 16),
                                       anchor='e')
        self.secret_word_label.grid(row=0,
                                    column=1,
                                    sticky='nse',
                                    padx=20,
                                    pady=5)

    # noinspection PyUnusedLocal
    def alert(self, *args):
        """Timer alert for the hard level"""
        if not self.time_left.get():
            self.mini_screen()
            self.word_text.set('Choose your category')
            self.counter.set('')
            self.start_stop_action('stop')
            self.banner_text.set('Random')
            self.quit.config(state=NORMAL)
            self.back.config(state=NORMAL)
            self.timer_text.set(strftime("%H:%M"))

    def draw_word(self):
        """Drawing loop for the secret word label"""
        output = []
        index = 1

        # Draws the displayed word after each letter choice
        for x in self.secret_word.get():
            if self.level_status == 1:
                if x in self.first_last \
                        or x in self.guesses \
                        or x in [' ', '-']:
                    output.append(x)
                elif x == self.char:
                    output.append(self.char)
                else:
                    output.append('_')
                index += 1
            elif self.level_status >= 2:
                if x in self.guesses or x in [' ', '-']:
                    output.append(x)
                elif x == self.char:
                    output.append(self.char)
                else:
                    output.append('_')

        # Finally sets the label for display
        self.word_text.set(' '.join(output))

    def scoring(self):
        """Scoring method for each level"""
        new = self.char
        total = 'Total'
        xp = 7 - len(self.misses)
        n = 0
        t = 0

        # Scoring points are defined by the input letter
        # if vowels score are lower in each difficulty level
        # if console, score are higher in each difficulty level
        if self.char in self.secret_word.get() and self.char in 'AEIOU':
            if self.level_status == 1:
                n = 4 * xp
            if self.level_status == 2:
                n = 5 * xp
            if self.level_status == 3:
                n = 7 * xp
        elif self.char in self.secret_word.get() and self.char not in 'AEIOU':
            if self.level_status == 1:
                n = 5 * xp
            if self.level_status == 2:
                n = 6 * xp
            if self.level_status == 3:
                n = 8 * xp
        self.total += n

        # Finally sets up the scoring window
        self.score_text.set(f'{new}' +
                            (len(total) + 4 + len(str(t)) - 1) * ' ' + f'{n}\n'
                            f'{total}' + 6 * ' ' + f'{self.total}')

    def game_status(self):
        """Checks if it's a win or a loss
        Also changes the countdown label foreground color
        depending to the misses"""
        if len(self.misses) == 7:
            self.mini_screen()
            self.word_text.set('Choose your category')
            self.counter.set('')
            self.start_stop_action('stop')
            self.banner_text.set('Random')
            self.quit.config(state=NORMAL)
            self.back.config(state=NORMAL)
            self.timer_text.set(strftime("%H:%M"))

        if sorted(self.guesses) == sorted(self.sorted):
            # Add a score entry to database if entries are less than 13
            if len(rc.search()) < 13:
                rc.add_entry(self.level_status, self.total)

            # Checks if last score in database is less than new score
            # if is than delete the last entry and the new higher score
            elif len(rc.search()) == 13 \
                    and rc.search()[12].points < self.total:
                rc.delete()
                rc.add_entry(self.level_status, self.total)

            self.mini_screen()
            self.word_text.set('Choose your category')
            self.counter.set('')
            self.start_stop_action('stop')
            self.banner_text.set('Random')
            self.quit.config(state=NORMAL)
            self.back.config(state=NORMAL)
            self.s_w_list.append(self.secret_word.get())
            self.timer_text.set(strftime("%H:%M"))

    def abc_buttons(self):
        """Creates the alphabet buttons"""
        # noinspection PyAttributeOutsideInit
        r, c = 0, 0
        for item in st.ALPHABET:
            b = Button(
                self.row_1_grid,
                text='%s' % item,
                bg=st.color['bg'],
                fg=st.color['fg_abc'],
                font=st.font(self.rem, 1),
                command=lambda i=item: self.set_abc(i),
                justify='center',
                overrelief=SUNKEN,
                anchor=CENTER,
                bd=0,
                state=DISABLED,
                disabledforeground=st.color['dfg_abc'],
                activeforeground=st.color['afg_abc'],
                activebackground=st.color['abg_abc'],
            )
            if c == 13:
                r += 1
                c = 0
            b.grid(row=r, column=c, sticky='news', padx=7, pady=6)
            self.abc.append(b)
            c += 1

    def category_buttons(self):
        """Word category buttons"""
        r, c, bg, s = 0, 0, st.color['bg_cat_a'], NORMAL
        for item in st.EN:
            if item == 'random':
                bg = st.color['fg']
                s = DISABLED
            b = Button(self.row_3_grid,
                       text='%s' % item.capitalize(),
                       bg=bg,
                       fg=st.color['fg_cat'],
                       font=st.font(self.rem, 8),
                       command=lambda i=item: self.set_category(i),
                       overrelief=SUNKEN,
                       anchor=CENTER,
                       bd=0,
                       state=s,
                       disabledforeground=st.color['bg'],
                       activeforeground=st.color['fg_cat'],
                       activebackground=st.color['bg_cat_a'],
                       width=1)
            if c == 4:
                r += 1
                c = 0
            b.grid(row=r, column=c, sticky='news', padx=15, pady=10)
            self.category.append(b)
            c += 1

    def buttons(self):
        """Info, check buttons"""
        close = st.image('quit')
        info = st.image('info')
        rule = st.image('rule')
        # noinspection PyAttributeOutsideInit
        self.start = self.create_button(self.row_0_grid, 'start')
        self.start.config(width=2, font=st.font(self.rem, -2))
        self.start.grid(row=0, column=0, sticky='news', padx=20, pady=7)
        # noinspection PyAttributeOutsideInit
        self.stop = self.create_button(self.row_0_grid, 'stop')
        self.stop.config(state=DISABLED, width=2, font=st.font(self.rem, -2))
        self.stop.grid(row=1, column=0, sticky='news', padx=20, pady=7)
        # noinspection PyAttributeOutsideInit
        self.quit = self.create_button(self.master, 'exit')
        self.quit.config(font=st.font(self.rem, 1), image=close)
        self.quit.image = close
        self.quit.place(relx=0.99, rely=0.01, anchor='ne')
        # noinspection PyAttributeOutsideInit
        self.score = self.create_button(self.row_0_column_1, 'high score')
        self.score.config(font=st.font(self.rem, -2, style='normal'),
                          overrelief=None,
                          relief=SOLID,
                          bd=0,
                          command=lambda: self.score_screen())
        self.score.grid(row=5, column=0, sticky='news')
        # noinspection PyAttributeOutsideInit
        self.info = self.create_button(self.welcome, 'info')
        self.info.config(
            font=st.font(self.rem, 0),
            command=lambda: st.set_rules('Game Info', st.prompt['Game Info']),
            image=info)
        self.info.image = info
        self.info.place(relx=0.01, rely=0.01, anchor='nw')
        # noinspection PyAttributeOutsideInit
        self.rule = self.create_button(self.welcome, 'rules')
        self.rule.config(font=st.font(self.rem, 0),
                         command=lambda: st.set_rules('Game Rules', st.prompt[
                             'Game Rules']),
                         image=rule)
        self.rule.image = rule
        self.rule.place(relx=0.01, rely=0.1, anchor='nw')
        # noinspection PyAttributeOutsideInit
        self.go = self.create_button(self.welcome, 'ENTER')
        self.go.config(font=st.font(self.rem, 16, family='Verdana'),
                       text='ENTER')
        self.go.pack(side=BOTTOM, expand=False, padx=5, pady=60)
        back = st.image('back')
        # noinspection PyAttributeOutsideInit
        self.back = self.create_button(self.row_0_grid, 'Back')
        self.back.config(font=st.font(self.rem, 1), image=back)
        self.back.image = back
        self.back.place(relx=0.99, rely=0.42, x=0, y=0, anchor='se')
        r, s, bg = 0, NORMAL, st.color['bg_level']
        for item in st.L_EN:
            b = self.create_button(self.row_0_column_3, item)
            if item == 'easy':
                s = DISABLED
                bg = st.color['fg']
            b.config(state=s,
                     font=st.font(self.rem, -4, style='bold'),
                     command=lambda i=item: self.set_level(i),
                     bg=bg)
            b.grid(row=r, column=0, sticky='news', pady=3)
            self.level.append(b)
            r += 1

    def destroy_all(self):
        """Close all windows"""
        st.set_rules('Bye', st.prompt['Bye'])
        sleep(0.6)
        # noinspection PyBroadException
        try:
            self.master.destroy()
            # self.master_2.destroy()
        except NotImplementedError:
            pass

    def set_abc(self, item):
        """Sets the alphabet buttons to the desired state
        depending to guesses and misses.
        calls the draw_word and game_status methods
        """
        self.char = item
        self.scoring()
        bg, dfg, s = st.color['bg'], st.color['dfg_abc_b'], DISABLED
        if item in self.secret_word.get():
            bg, dfg, s = st.color['bg'], st.color['dfg_abc_g'], \
                         DISABLED
            self.guesses.append(item)
        else:
            self.misses.append(item)
        index = 0
        for x in st.ALPHABET:
            self.a.update({x: index})
            index += 1
        self.abc[self.a[item]].config(state=s, bg=bg, disabledforeground=dfg)
        c = 7 - len(self.misses)
        if c == 0:
            c = 7
        self.counter.set(f'{c}')
        self.count.config(fg=st.color[f'{c}'])
        self.draw_word()
        self.game_status()

    def set_welcome(self):
        self.welcome.pack_forget()
        sleep(0.6)

        self.mainframe.pack(fill=BOTH, expand=True)

    def set_back(self):
        self.mainframe.pack_forget()
        sleep(0.4)
        self.welcome.pack(fill=BOTH, expand=True)
        # noinspection PyBroadException
        # try:
        #     self.master_2.destroy()
        # except NotImplementedError:
        #     pass
        self.level[0].config(state=NORMAL)
        self.level[1].config(state=NORMAL)
        self.level[2].config(state=DISABLED)
        self.first_last.clear()

    def set_secret_word(self):
        """Sets the secret word by the user choice"""
        self.guesses.clear()
        self.misses.clear()
        self.char = ''
        search = self.prompt.get()
        lang = st.C_EN

        if search.lower() == 'random':
            from random import choice
            search_query = choice(lang)
        else:
            search_query = search.lower()

        # Opens available words from csv file
        try:
            word = st.open_csv(search_query, self.level_status).upper()
            if word in self.s_w_list:
                word = st.open_csv(search_query, self.level_status).upper()
            self.secret_word.set(word)
            for x in word:
                if x == word[0] or x == word[len(word) - 1]:
                    if x.upper() in self.first_last:
                        continue
                    self.first_last.append(x.upper())
            if self.level_status == 1:
                self.sorted = st.sort(word)
            else:
                self.sorted = st.sort(word, True)
            self.draw_word()
            self.banner_text.set(search_query.capitalize())
        except NotImplementedError:
            pass

    def set_command(self, text):
        """Sets command for buttons"""
        command = None
        if text == 'start':
            command = self.set_start_button
        elif text == 'stop':
            command = self.set_stop_button
        elif text == 'exit':
            command = self.destroy_all
        elif text == 'ENTER':
            command = self.set_welcome
        # elif text == 'english':
        #     command = self.set_language
        elif text == 'Back':
            command = self.set_back
        return command

    # def set_language(self):
    #     """Method for change the categories between english and french"""
    #     if self.choose.cget('text').lower() == 'english':
    #         self.choose.config(text='Francais')
    #         self.lang = 'ENG'
    #     elif self.choose.cget('text').lower() == 'francais':
    #         self.choose.config(text='English')
    #         self.lang = 'FRA'

    def start_stop_action(self, text):
        """Start and stop buttons common actions"""
        bc, ba, bb, dfg, s, s_2, s_3 = None, None, None, None, None, None, None
        sbg, stbg = None, None
        if text == 'start':
            if self.level_status == 3:
                self.clock_text.set('Timer')
            for x in range(3):
                if self.level[x].cget('state') == 'disabled':
                    continue
                else:
                    self.level[x].grid_forget()
            bc = st.color['fg']
            ba = st.color['fg']
            bb = st.color['bg_abc_a']
            dfg = st.color['bg']
            s = DISABLED
            s_2 = NORMAL
            s_3 = NORMAL
            sbg = st.color['fg']
            stbg = st.color['bg_stop']
        if text == 'stop':
            self.first_last.clear()
            self.sorted = ''
            self.banner_text.set('')
            self.clock_text.set('Clock')
            self.running = False
            # self.choose.place(relx=0.67, rely=0.23, x=0, y=0, anchor='se')
            r = 0
            for x in range(3):
                self.level[x].grid(row=r, column=0, sticky='news', pady=3)
                r += 1
            bc = st.color['bg_cat_a']
            ba = st.color['fg']
            bb = st.color['bg']
            dfg = st.color['fg']
            s = NORMAL
            s_2 = DISABLED
            s_3 = DISABLED
            sbg = st.color['bg_start']
            stbg = st.color['fg']
        self.start.config(state=s, bg=sbg)
        self.stop.config(state=s_2, bg=stbg)
        self.total = 0
        self.score_text.set('0\n0')
        for a in range(8):
            if self.category[a].cget('text').lower() == self.prompt.get():
                self.category[a].config(state=DISABLED, bg=ba)
            else:
                self.category[a].config(state=s, bg=bc)
        for i in range(0, 26):
            if self.level_status == 1:
                if self.abc[i].cget('text') in self.first_last:
                    self.abc[i].config(
                        bg=st.color['bg'],
                        state=DISABLED,
                        disabledforeground=st.color['dfg_abc_g'])
                else:
                    self.abc[i].config(bg=bb,
                                       disabledforeground=dfg,
                                       state=s_3)
            else:
                self.abc[i].config(bg=bb, disabledforeground=dfg, state=s_3)

    def set_start_button(self):
        """Sets start button action"""
        print("This is working")
        self.time_left.set(st.DEFAULT_GAP)
        self.running = True
        self.counter.set('7')
        self.set_secret_word()
        self.quit.config(state=DISABLED)
        self.back.config(state=DISABLED)
        self.start_stop_action('start')
        # self.choose.place_forget()
        # noinspection PyBroadException
        # try:
        #     if self.master_2:
        #         self.master_2.destroy()
        # except NotImplementedError:
        #     pass

    def set_stop_button(self):
        """Sets stop button action"""
        self.word_text.set('Choose your category')
        self.counter.set('')
        self.quit.config(state=NORMAL)
        self.back.config(state=NORMAL)
        self.start_stop_action('stop')
        self.timer_text.set(strftime("%H:%M"))

    def set_category(self, text):
        """Category buttons function"""
        self.prompt.set(text)
        for a in range(8):
            if self.category[a].cget('text').lower() == text:
                self.category[a].config(state=DISABLED, bg=st.color['fg'])
            else:
                self.category[a].config(state=NORMAL, bg=st.color['bg_cat_a'])

    def set_level(self, text):
        """Method for difficulty level"""
        if text == 'hard':
            self.level_status = 3
        if text == 'medium':
            self.level_status = 2
        if text == 'easy':
            self.level_status = 1
        for x in range(3):
            if self.level[x].cget('text').lower() == text:
                self.level[x].config(state=DISABLED, bg=st.color['fg'])
            else:
                self.level[x].config(state=NORMAL, bg=st.color['bg_level'])

    def update(self):
        """Master window refresh rate, 1 second"""
        time_left = self.time_left.get()
        if self.running and time_left and self.level_status == 3:
            minutes, seconds = st.minutes_seconds(time_left)
            self.timer_text.set('{:0>2}:{:0>2}'.format(minutes, seconds))
            self.time_left.set(time_left - 1)
        self.master.after(1000, self.update)
Example #23
0
class KarelWindow(Frame):
    def geometry(self, height):
        #        print "gemo " + str(self._oldHeight) + ' ' + str(height)
        self._oldHeight = self._height
        self._height = height
        self.__bottom = height - self._inset
        self.__left = self._inset
        self.__top = self._inset
        self.__right = height
#        self.__scaleFactor = ((self.__bottom - self.__top)*1.0/self.__streets)

    def __init__(self,
                 streets,
                 avenues,
                 size=800,
                 callback=None):  # avenues is ignored in this version
        #        self.__configControl = threading.Condition()
        self.__root = root = Tk(
            className=" Karel's World ")  # , geometry='800x600+60+10'
        global _windowBottom
        _windowBottom = size
        geometryString = '820x' + str(_windowBottom + 65) + "+55+25"
        root.geometry(newGeometry=geometryString
                      )  #'820x865+55+25') # placement of window on desktop
        #        print str(root.tk_menuBar())
        Frame.__init__(self, master=root, cnf={})

        bar = Menu()

        def endProgram(menu):
            exit()

        fil = Menu()
        fil.add_command(label='Quit   ^Q',
                        command=lambda x='Quit': endProgram(x))
        bar.add_cascade(label='File', menu=fil)
        root.config(menu=bar)
        self.bind_all('<Command-q>', exit)  # Mac standard
        self.bind_all('<Control-q>', exit)  # Windows
        self.__streets = streets
        self.__avenues = streets  # sic Avenues ignored
        self.__gBeepers = {}  #locations of the beeper imagess
        self.__contents = []  # , walls, beepers that need to move on a rescale
        self.__robots = []
        self.__beeperControl = threading.Condition(
        )  # helps multi threaded programs avoid anomalies
        self.__walls = [
        ]  # all the basic visual elements (boundary, streets, street labels, etc.

        top = self.winfo_toplevel()
        top.rowconfigure(2, weight=1)
        top.columnconfigure(0, weight=1)
        self.rowconfigure(2, weight=1)
        self.columnconfigure(0, weight=1)
        root.rowconfigure(2, weight=1)
        root.columnconfigure(0, weight=1)

        speedLabel = Label(text="Speed")
        speedLabel.grid(row=0, column=0, sticky=N + W + E + S)

        if callback != None:  # this makes the speed slider work.
            from tkinter import IntVar
            self.iv = IntVar()
            self.iv.trace('r', callback)

            self.scale = Scale(orient=HORIZONTAL, variable=self.iv)
            self.scale.set(20)
            self.scale.grid(row=1, column=0, sticky=N + S)
#        self.__callback = callback
#        global _windowRight
        global _inset
        #        root.minsize(_windowBottom, _windowRight)
        self._height = self._oldHeight = _windowBottom
        self.__bottom = _windowBottom - _inset  #770
        self.__left = _inset  #30
        self.__top = _inset  #30
        self.__right = self._height  #_windowRight - _inset #770
        self._inset = _inset

        #        print str(speedLabel.config())
        #        print str(self.scale.config())
        self._canvas = Canvas(root,
                              height=_windowBottom,
                              width=_windowBottom,
                              bg='white')
        self._canvas.grid(row=2, column=0, sticky=N + E + W + S)
        self.geometry(self._height)
        self.setSize(streets)

#        self._canvas.bind_all('<Expose>', self.expose)
#        self._canvas.bind('<Configure>', self.configure)

#    def expose(self, event):
##        print 'expose ' + str(event.width)+ ' ' + str(event.height)
#        pass

#    def configure(self, event):
##        print str(self._canvas.config())
##        print "config " + str(event.height)
##        self.__configControl.acquire()
#        self.geometry(event.height)
#        delta = (self._oldHeight - self._height)*1.0/self._oldHeight
#        scale = self._height*1.0/self._oldHeight
#        self._canvas.scale('all', 0, 0, scale, scale)
#        self._canvas.move('all', delta, delta)
##        print "config " + str(event.widget)+ ' ' +str(event.x)+ ' ' + str(event.y)+' ' + str(event.width)+ ' ' + str(event.height)
##        self.__configControl.notify()
##        self.__configControl.release()
#        pass

    def clear(self):
        for item in self.__contents + self.__robots:
            item.deleteAll()

    def setSize(self, streets):  #streets can change
        self.__streets = streets

        for x in self.__walls:  # boundary walls and street lines
            self._canvas.delete(x)
        self.makeStreetsAndAvenues()
        self.makeBoundaryWalls()
        self.labelStreetsAvenues()
        for item in self.__contents + self.__robots:  #rebuild the contents of the world
            item.moveScale()

    def scaleFactor(self):
        self.geometry(self._height)
        #        print "in scaler " + str(self.__bottom) + " " + str(self.__top) + ' ' + str(self.__streets)
        return ((self.__bottom - self.__top) * 1.0 / self.__streets
                )  #self.__scaleFactor

    class Beeper:
        bNumber = 0

        def __init__(self, street, avenue, number, window):
            self._street = street
            self._avenue = avenue
            self._number = number
            self._scaler = window._scaleToPixels
            self.scaleFactor = window.scaleFactor
            self._canvas = window._canvas
            self.tag = "b" + str(KarelWindow.Beeper.bNumber)
            KarelWindow.Beeper.bNumber += 1

        def place(self):
            sizeFactor = .5  #Change this to change beeper size. The others scale from it.
            placeFactor = .5 * sizeFactor
            val = str(self._number)
            if self._number < 0:
                val = "oo"
            (x, y) = self._scaler(self._street + placeFactor,
                                  self._avenue - placeFactor)
            #            print 'beeper ' + str(x) + ' ' + str(y)
            #            print 'factor ' + str(self.scaleFactor())
            #
            # circular beepers
            #            self._canvas.create_oval(x, y, x + self.__scaleFactor*sizeFactor, y + self.__scaleFactor*sizeFactor, fill= 'black', tags = self.tag)
            # triangular beepers
            where = []
            where.append(self._scaler(self._street + sizeFactor, self._avenue))
            where.append(
                self._scaler(self._street - placeFactor,
                             self._avenue - placeFactor))
            where.append(
                self._scaler(self._street - placeFactor,
                             self._avenue + placeFactor))
            self._canvas.create_polygon(where,
                                        fill="black",
                                        smooth=False,
                                        tags=self.tag)
            self._canvas.create_text(x + self.scaleFactor() * placeFactor,
                                     y + self.scaleFactor() * placeFactor,
                                     text=val,
                                     font=Font(size=int(-self.scaleFactor() *
                                                        placeFactor)),
                                     fill='white',
                                     tags=self.tag)

        def deleteAll(self):
            self._canvas.delete(self.tag)

        def moveScale(self):
            self._canvas.delete(self.tag)
            self.place()

    class Wall:
        def __init__(self, street, avenue, isVertical, window):
            self._street = street
            self._avenue = avenue
            self._isVertical = isVertical
            self.scaleFactor = window.scaleFactor
            self._scaler = window._scaleToPixels
            self._canvas = window._canvas
            if self._isVertical:
                (x, y) = self._scaler(street - .5, avenue + .5)
                self._code = self._canvas.create_line(x,
                                                      y,
                                                      x,
                                                      y - self.scaleFactor(),
                                                      width=2)
            else:
                (x, y) = self._scaler(street + .5, avenue - .5)
                self._code = self._canvas.create_line(x,
                                                      y,
                                                      x + self.scaleFactor(),
                                                      y,
                                                      width=2)
                # _code identifies the wall segment image in the tk layer

        def moveScale(self):
            self._canvas.delete(
                self._code
            )  #erase the current figure in prep to draw a new one
            if self._isVertical:
                (x, y) = self._scaler(self._street - .5, self._avenue + .5)
                self._code = self._canvas.create_line(x,
                                                      y,
                                                      x,
                                                      y - self.scaleFactor(),
                                                      width=2)
            else:
                (x, y) = self._scaler(self._street + .5, self._avenue - .5)
                self._code = self._canvas.create_line(x,
                                                      y,
                                                      x + self.scaleFactor(),
                                                      y,
                                                      width=2)

        def deleteAll(self):
            self._canvas.delete(self._code)

    def placeBeeper(self, street, avenue, number):
        #        self.__beeperControl.acquire() # sync was moved to tkworldadapter
        beeper = self.Beeper(street, avenue, number, self)
        beeper.place()
        self.__gBeepers[(street, avenue)] = beeper
        self.__contents.append(beeper)
#        self.__beeperControl.notify()
#        self.__beeperControl.release()
#        return beeper

    def deleteBeeper(self, beeperlocation):
        #        self.__beeperControl.acquire()
        beeper = self.__gBeepers.get(beeperlocation, None)
        if beeper != None:
            beeper.deleteAll()
            self.__gBeepers.pop(beeperlocation)
            i = 0
            for b in self.__contents:
                if b == beeper:
                    break
                i += 1
            self.__contents.pop(i)


#        self.__beeperControl.notify()
#        self.__beeperControl.release()

    def placeWallNorthOf(self, street, avenue):
        self.__contents.append(self.Wall(street, avenue, False, self))

    def removeWallNorthOf(self, street, avenue):
        i = 0
        for wall in self.__contents:
            if wall.__class__ is self.Wall and wall._street == street and wall._avenue == avenue and not wall._isVertical:
                wall.deleteAll()
                self.__contents.pop(i)
                #                print 'h gone'
                break
            i += 1

    def placeWallEastOf(self, street, avenue):
        self.__contents.append(self.Wall(street, avenue, True, self))

    def removeWallEastOf(self, street, avenue):
        i = 0
        for wall in self.__contents:
            if wall.__class__ is self.Wall and wall._street == street and wall._avenue == avenue and wall._isVertical:
                wall.deleteAll()
                self.__contents.pop(i)
                #                print 'v gone'
                break
            i += 1

    def makeBoundaryWalls(self):
        (x,
         y) = self._scaleToPixels(.5,
                                  .5)  # hardcode ok. Half way between streets
        self.__walls.append(self._canvas.create_line(
            x, 0, x, y, width=2))  # should width depend on number of streets?
        global _inset
        self.__walls.append(
            self._canvas.create_line(x, y, self.__right + _inset, y, width=2))

    def makeStreetsAndAvenues(self):
        for i in range(0, self.__streets):
            (x, y) = self._scaleToPixels(i + 1, .5)
            (tx, ty) = self._scaleToPixels(i + 1, self.__streets + .5)
            self.__walls.append(
                self._canvas.create_line(x, y, tx, ty, fill="red"))
            (x, y) = self._scaleToPixels(.5, i + 1)
            (tx, ty) = self._scaleToPixels(self.__streets + .5, i + 1)
            self.__walls.append(
                self._canvas.create_line(x, y, tx, ty, fill="red"))

    def labelStreetsAvenues(self):
        for i in range(self.__streets):
            (x, y) = self._scaleToPixels(i + 1, .25)
            self.__walls.append(
                self._canvas.create_text(x, y, fill='black', text=str(i + 1)))
            (x, y) = self._scaleToPixels(.25, i + 1)
            self.__walls.append(
                self._canvas.create_text(x, y, fill='black', text=str(i + 1)))

    def addRobot(self, street, avenue, direction, fill, outline):
        #        fill and outline are colors, default to blue, black
        robot = RobotImage(street, avenue, direction, self, fill, outline)
        self.__robots.append(robot)
        return robot  # the world matches these with the actual robot objects in the model.

    def moveRobot(self, robot, amount=-1):
        #If no amount is specified then it moves one block, Otherwise amount pixels, not blocks
        if amount < 0:
            amount = self.scaleFactor()
        robot.move(amount)

    def _scaleToPixels(self, street,
                       avenue):  # origin is at corner (0,0) outside the world
        scale = self.scaleFactor()
        return (self.__left + avenue * scale, self.__bottom - street * scale)

    def _scaleFromPixels(self, x, y):
        scale = self.scaleFactor()
        return (int(round(
            (self.__bottom - y) / scale)), int(round(
                (x - self.__left) / scale)))

    def _downScaleFromPixels(self, x, y):
        scale = self.scaleFactor()
        return (int((self.__bottom - y) / scale), int(
            (x - self.__left) / scale))

    def run(self, task, *pargs):  # this is the actual graphic main.
        mainThread = threading.Thread(target=task, args=pargs)
        mainThread.start()
        self.mainloop()

    def _test(self):
        pass
Example #24
0
class GraphyInspector:

    def __init__(self, parent):

        self.parent = parent

        self.width = self.parent.right_frame_width
        self.padding = self.parent.right_frame_padding

        self.frame = Frame(master=self.parent.right_frame)
        self.frame.pack(side='top', fill='y', )

        # "Inspector" title bar
        self.title_frame = Frame(master=self.frame)
        self.title_frame.pack(side='top')
        self.title_label = Label(master=self.title_frame, text="Inspector", width=self.width, bg='lightgray')
        self.title_label.pack()

        # identifier for type of object selected
        self.type_frame = Frame(master=self.frame, relief='sunken')
        self.type_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding)
        self.type_label1 = Label(master=self.type_frame, width=int(self.width/2)-self.padding, text='Object:')
        self.type_label1.pack(side='left', padx=self.padding, pady=self.padding)
        self.type_label2 = Label(master=self.type_frame, width=int(self.width / 2) - self.padding, text='', bg='white')
        self.type_label2.pack(side='right', padx=self.padding, pady=self.padding)

        # label of selected object (i.e. name user gives them, no canvas IDs here)
        self.label_frame = Frame(master=self.frame, relief='sunken')
        self.label_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding)
        self.label_label = Label(master=self.label_frame, width=int(self.width/2)-self.padding, text="Label:")
        self.label_label.pack(side='left', padx=self.padding, pady=self.padding)
        self.label_var = StringVar()
        self.label_var.set('')
        self.label_entry = Entry(self.label_frame, width=int(self.width/2)-self.padding, textvariable=self.label_var)
        self.label_entry.pack(side='right', padx=self.padding, pady=self.padding)
        self.label_entry.bind('<Button-1>', self.select_label_text)
        self.label_entry.bind('<Return>', self.drop_widget_focus)

        # status identifier (for vertices and layers)
        self.status_frame = Frame(master=self.frame, relief='sunken')
        self.status_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding)
        self.status_label1 = Label(master=self.status_frame, width=int(self.width/2)-self.padding, text='Status:')
        self.status_label1.pack(side='left', padx=self.padding, pady=self.padding)
        self.status_label2 = Label(master=self.status_frame, width=int(self.width/2)-self.padding, text='', bg='white')
        self.status_label2.pack(side='right', padx=self.padding, pady=self.padding)
        self.activation_var = StringVar()
        self.activation_var.set('')
        self.activation_menu = OptionMenu(self.status_frame, self.activation_var, "Identity", "Sigmoid", "ReLU", "Logarithmic", "Exponential")
        self.activation_menu.pack(side='right', padx=self.padding, pady=self.padding)

        # weight identifier (for edges only)
        self.weight_frame = Frame(master=self.frame, relief='sunken')
        self.weight_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding)
        self.weight_label = Label(master=self.weight_frame, width=int(self.width/2)-self.padding, text="Weight:")
        self.weight_label.pack(side='left', padx=self.padding, pady=self.padding)
        self.weight_var = DoubleVar()
        self.weight_entry = Entry(self.weight_frame, width=int(self.width / 2) - self.padding, textvariable=self.weight_var)
        self.weight_entry.pack(side='right', padx=self.padding, pady=self.padding)
        self.weight_entry.bind('<Button-1>', self.select_weight_text)
        self.weight_entry.bind('<Return>', self.drop_widget_focus)

        # node count identifier (for layers only)
        self.node_frame = Frame(master=self.frame, relief='sunken')
        self.node_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding)
        self.node_label = Label(master=self.node_frame, width=int(self.width/2)-self.padding, text="Node Count:")
        self.node_label.pack(side='left', padx=self.padding, pady=self.padding)
        self.node_var = IntVar()
        self.node_entry = Entry(self.node_frame, width=int(self.width / 2) - self.padding, textvariable=self.node_var)
        self.node_entry.pack(side='right', padx=self.padding, pady=self.padding)
        self.node_entry.bind('<Button-1>', self.select_node_text)
        self.node_entry.bind('<Return>', self.drop_widget_focus)

        # leakiness
        self.leakiness_frame = Frame(master=self.frame, relief='sunken')
        self.leakiness_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding)
        self.leakiness_label = Label(master=self.leakiness_frame, width=int(self.width/2)-self.padding, text="Leakiness")
        self.leakiness_label.pack(side='left', padx=self.padding, pady=self.padding)
        self.leakiness_var = DoubleVar()
        self.leakiness_entry = Entry(self.leakiness_frame, width=int(self.width / 2) - self.padding, textvariable=self.leakiness_var)
        self.leakiness_entry.pack(side='right', padx=self.padding, pady=self.padding)
        self.leakiness_entry.bind('<Button-1>', self.select_leakiness_text)
        self.leakiness_entry.bind('<Return>', self.drop_widget_focus)

        # bias
        self.bias_frame = Frame(master=self.frame, relief='sunken')
        self.bias_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding)
        self.bias_label = Label(master=self.bias_frame, width=int(self.width/2)-self.padding, text="Bias:")
        self.bias_label.pack(side='left', padx=self.padding, pady=self.padding)
        self.bias_var = DoubleVar()
        self.bias_entry = Entry(self.bias_frame, width=int(self.width / 2) - self.padding, textvariable=self.bias_var)
        self.bias_entry.pack(side='right', padx=self.padding, pady=self.padding)
        self.bias_entry.bind('<Button-1>', self.select_bias_text)
        self.bias_entry.bind('<Return>', self.drop_widget_focus)

        # output bound
        self.bound_frame = Frame(master=self.frame, relief='sunken')
        self.bound_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding)
        self.bound_label = Label(master=self.bound_frame, width=int(self.width/2)-self.padding, text="Output Bound:")
        self.bound_label.pack(side='left', padx=self.padding, pady=self.padding)
        self.bound_var = DoubleVar()
        self.bound_entry = Entry(self.bound_frame, width=int(self.width / 2) - self.padding, textvariable=self.bound_var)
        self.bound_entry.pack(side='right', padx=self.padding, pady=self.padding)
        self.bound_entry.bind('<Button-1>', self.select_bound_text)
        self.bound_entry.bind('<Return>', self.drop_widget_focus)

        # noise
        self.noise_frame = Frame(master=self.frame, relief='sunken')
        self.noise_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding)
        self.noise_label = Label(master=self.noise_frame, width=int(self.width/2)-self.padding, text="Weight Noise:")
        self.noise_label.pack(side='left', padx=self.padding, pady=self.padding)
        self.noise_var = DoubleVar()
        self.noise_entry = Entry(self.noise_frame, width=int(self.width / 2) - self.padding, textvariable=self.noise_var)
        self.noise_entry.pack(side='right', padx=self.padding, pady=self.padding)
        self.noise_entry.bind('<Button-1>', self.select_noise_text)
        self.noise_entry.bind('<Return>', self.drop_widget_focus)

        # input / output
        self.input_output_frame = Frame(master=self.frame, relief='sunken')
        self.input_output_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding)
        self.input_var = BooleanVar()
        self.input_var.set(False)
        self.output_var = BooleanVar()
        self.output_var.set(False)
        self.input_toggle = Checkbutton(master=self.input_output_frame, text="Is Input", variable=self.input_var)
        self.output_toggle = Checkbutton(master=self.input_output_frame, text="Is Output", variable=self.output_var)
        self.input_toggle.pack(side='left', padx=self.padding, pady=self.padding)
        self.output_toggle.pack(side='left', padx=self.padding, pady=self.padding)

        self.selected = None
        self.selected_type = None
        self.set_unselected()

        self.label_var.trace('w', self.set_selected_label)
        self.weight_var.trace('w', self.set_selected_weight)
        self.activation_var.trace('w', self.set_selected_activation)
        self.leakiness_var.trace('w', self.set_selected_leakiness)
        self.node_var.trace('w', self.set_selected_node_count)
        self.bias_var.trace('w', self.set_selected_bias)
        self.bound_var.trace('w', self.set_selected_bound)
        self.noise_var.trace('w', self.set_selected_noise)
        self.input_var.trace('w', self.set_input)
        self.output_var.trace('w', self.set_output)

        # mode
        self.mode = parent.mode
        self.set_mode(parent.mode)

    # object is a vertex or edge, type is 'vertex' or 'edge'...
    def set_selected(self, selected_object, selected_object_type):

        self.selected = selected_object
        self.selected_type = selected_object_type

        if self.mode == "Graph":
            if selected_object_type == 'vertex':
                self.type_label2.config(text="Vertex")
                self.status_label2.config(text=selected_object.status)

                self.type_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding)
                self.label_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding)
                self.status_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding)

                self.label_var.set(selected_object.label)

            elif selected_object_type == 'edge':
                self.type_label2.config(text="Edge")

                self.type_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding)
                self.label_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding)
                self.weight_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding)

                self.label_var.set(selected_object.label)
                self.weight_var.set(selected_object.weight)

            else:
                print('dafuq is going on')

        elif self.mode == "Net":
            if selected_object_type == 'vertex':
                self.type_label2.config(text="Layer")
                self.status_label2.config(text=selected_object.status)

                self.type_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding)
                self.label_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding)
                self.status_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding)
                self.node_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding)
                self.leakiness_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding)
                self.bias_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding)
                self.bound_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding)
                self.input_output_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding)

                self.label_var.set(selected_object.label)
                self.node_var.set(selected_object.node_count)
                self.activation_var.set(selected_object.status)
                self.leakiness_var.set(selected_object.leakiness)
                self.bias_var.set(selected_object.bias)
                self.bound_var.set(selected_object.bound)
                self.input_var.set(selected_object.is_input_layer)
                self.output_var.set(selected_object.is_output_layer)

            elif selected_object_type == 'edge':
                self.type_label2.config(text="Weights")

                self.type_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding)
                self.label_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding)
                self.weight_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding)
                self.noise_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding)

                self.label_var.set(selected_object.label)
                self.weight_var.set(selected_object.weight)
                self.noise_var.set(selected_object.noise)

        else:
            print('This will never happen.')

    # nothing is selected
    def set_unselected(self):
        self.type_frame.pack_forget()
        self.label_frame.pack_forget()
        self.status_frame.pack_forget()
        self.weight_frame.pack_forget()
        self.node_frame.pack_forget()
        self.bias_frame.pack_forget()
        self.bound_frame.pack_forget()
        self.noise_frame.pack_forget()
        self.input_output_frame.pack_forget()
        self.leakiness_frame.pack_forget()
        self.selected = None
        self.selected_type = None

    # set label of selected object
    def set_selected_label(self, *args):
        self.selected.set_label(self.label_var.get())

    # set weight of selected object
    def set_selected_weight(self, *args):
        self.selected.set_weight(self.weight_var.get())

    def set_selected_activation(self, *args):
        self.selected.set_status(self.activation_var.get())

    def set_selected_node_count(self, *args):
        self.selected.set_node_count(self.node_var.get())

    def set_selected_bias(self, *args):
        self.selected.set_bias(self.bias_var.get())

    def set_selected_bound(self, *args):
        self.selected.set_bound(self.bound_var.get())

    def set_selected_noise(self, *args):
        self.selected.set_noise(self.noise_var.get())

    def set_input(self, *args):
        self.selected.set_input_layer(self.input_var.get())

    def set_output(self, *args):
        self.selected.set_output_layer(self.output_var.get())

    def set_selected_leakiness(self, *args):
        self.selected.set_leakiness(self.leakiness_var.get())

    def update(self):
        if self.selected:
            selected = self.selected
            type = self.selected_type
            self.set_unselected()
            self.set_selected(selected, type)

    def select_label_text(self, event):
        if event:
            # delay so the default click doesn't undo selection, then recall and fall through to "else"
            self.parent.tk.after(50, self.select_label_text, False)
        else:
            self.label_entry.select_range(0, 'end')
            self.label_entry.icursor(0)

    def select_weight_text(self, event):
        if event:
            # delay so the default click doesn't undo selection, then recall and fall through to "else"
            self.parent.tk.after(50, self.select_weight_text, False)
        else:
            self.weight_entry.select_range(0, 'end')
            self.weight_entry.icursor(0)

    def select_node_text(self, event):
        if event:
            self.parent.tk.after(50, self.select_node_text, False)
        else:
            self.node_entry.select_range(0, 'end')
            self.node_entry.icursor(0)

    def select_bias_text(self, event):
        if event:
            self.parent.tk.after(50, self.select_bias_text, False)
        else:
            self.bias_entry.select_range(0, 'end')
            self.bias_entry.icursor(0)

    def select_noise_text(self, event):
        if event:
            self.parent.tk.after(50, self.select_noise_text, False)
        else:
            self.noise_entry.select_range(0, 'end')
            self.noise_entry.icursor(0)

    def select_bound_text(self, event):
        if event:
            self.parent.tk.after(50, self.select_bound_text, False)
        else:
            self.bound_entry.select_range(0, 'end')
            self.bound_entry.icursor(0)

    def select_leakiness_text(self, event):
        if event:
            self.parent.tk.after(50, self.select_leakiness_text, False)
        else:
            self.leakiness_entry.select_range(0, 'end')
            self.leakiness_entry.icursor(0)

    def drop_widget_focus(self, event):
        self.frame.focus()

    def set_mode(self, mode):
        if mode == "Graph":
            self.mode = mode
            self.weight_label.config(text="Weight:")
            self.status_label1.config(text="Status:")
            self.activation_menu.pack_forget()
            self.input_output_frame.pack_forget()
            self.status_label2.pack(side='right', padx=self.padding, pady=self.padding)
        elif mode == "Net":
            self.mode = mode
            self.weight_label.config(text="Start Weight:")
            self.status_label1.config(text="Activation:")
            self.status_label2.pack_forget()
            self.activation_menu.pack(side='right', padx=self.padding, pady=self.padding)
        else:
            print("This will never happen.")
Example #25
0
                        activeforeground=BUTTON_ACTIVE_FOREGROUND,
                        bg=BACKGROUND_COLOUR,
                        fg=FOREGROUNG_COLOUR,
                        font=TEXT_OPTIONS)
btn_restore.place(x=610, y=10)

btn_report = tk.Button(window,
                       width=BUTTON_WIDTH,
                       text='Отчет',
                       command=lambda: report(df),
                       activebackground=FOREGROUNG_COLOUR,
                       activeforeground=BUTTON_ACTIVE_FOREGROUND,
                       bg=BACKGROUND_COLOUR,
                       fg=FOREGROUNG_COLOUR,
                       font=TEXT_OPTIONS)
btn_report.place(x=760, y=10)

scrollbar_style = ttk.Style()
scrollbar_style.configure("My.Horizontal.TScrollbar", troughcolor="red")

tree = ttk.Treeview(window, show='headings')

selected_database = IntVar(window)
selected_database.set(OPTIONS[1])
options_menu = ttk.OptionMenu(window, selected_database, *OPTIONS)
options_menu.place(x=910, y=15)
selected_database.trace('w', update_table)
update_table()

window.mainloop()
Example #26
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()
Example #27
0
class PatternTab(Tab):
    def __init__(self, master):
        self._parameters = {}

        super().__init__(master)
        self.name = None
        # Setting dropdown menu for selecting pattern type
        patterns = [
            'layeredflowers', 'radialangular', 'sinespiral', 'spirals',
            'iterativerotation'
        ]
        self._patternselection = StringVar(self)
        self._patternselection.trace('w', self._setpattern)
        self._patternselection.set(patterns[0])
        self._patternmenu = OptionMenu(self, self._patternselection, *patterns)
        dropdownlabel = Label(self, text="Select a Pattern")
        dropdownlabel.grid(row=0, column=400, pady=(20, 0))
        self._patternmenu.grid(row=1, column=400)
        self._n_angles = None

    def _setpattern(self, *args):
        self.clear()  # clear tab of parameters from any previous pattern

        # get selected pattern type and run the setup method for that type
        patterntype = self._patternselection.get()
        if patterntype == 'layeredflowers':
            self._set_layered_flowers()
        elif patterntype == 'radialangular':
            self._set_radial_angular()
        elif patterntype == 'sinespiral':
            self.set_sin_spiral()
        elif patterntype == 'spirals':
            self.set_spirals()
        elif patterntype == 'iterativerotation':
            self.set_iterative_rotation()

    @property
    def angleparam(self):
        val1 = self._parameters['rotate'].get()
        val2 = self._parameters['rotationfactor'].get()
        return val1 * val2

    def _set_layered_flowers(self):
        # create, set, and grid each parameter:
        layers = Parameter(self, label="layers", from_=10, to=200, row=3)
        layers.set(100)
        angle1 = Parameter(self,
                           label="rotation angle",
                           from_=-18.0,
                           to=18.0,
                           resolution=0.1,
                           bigincrement=0.1,
                           row=5)
        angle2 = Parameter(self,
                           label="Rotation Multiplier",
                           from_=0,
                           to=10,
                           resolution=0.1,
                           row=10)
        angle2.set(1)
        npetals = Parameter(self,
                            label="petals",
                            from_=1.0,
                            to=80,
                            resolution=1,
                            tickinterval=9,
                            row=15)
        npetals.set(2)
        innerdepth = Parameter(self,
                               label="Petal Depth",
                               from_=0,
                               to=6,
                               resolution=0.1,
                               bigincrement=0.1,
                               row=20)
        innerdepth.set(1)
        size = Parameter(self,
                         label="size",
                         from_=1,
                         to=10,
                         row=25,
                         resolution=0.1)
        pensize = Parameter(self, label="pen size", from_=1, to=40, row=30)

        # add all parameters to the patterntab's master list.
        self._parameters = {
            'layers': layers,
            "npetals": npetals,
            "innerdepth": innerdepth,
            "rotate": angle1,
            "rotationfactor": angle2,
            "sizefactor": size,
            "pensize": pensize
        }

    def _set_radial_angular(self):
        # creating, adding, & setting parameters:
        size = Parameter(self, label="Size", from_=10, to=1000, row=3)
        size.set(500)
        pensize = Parameter(self, label="Pen Size", from_=1, to=40, row=10)

        self._n_angles = IntVar(self)  # variable for the angle number menu

        self._spacedarea.grid(
            row=6, column=0, columnspan=800
        )  # adding the frame from the superclass that allows for even spacing

        self._parameters = {
            "size": size,
            'pensize': pensize
        }  # for the parameters that feed into the pattern function
        self._progparams = {
            'n_angles': self._n_angles
        }  # for the parameters that help create function parameters, but dont feed in directly

        options = [1, 2, 3, 4]  # number of possible angles
        self._n_angles.trace(
            'w', self._make_angle_boxes
        )  # Making sure that _make_angle_boxes runs every time this control is moved
        self._n_angles.set(
            options[0])  # setting the default number of angles to 1

        # making, labeling, and adding dropdown menu
        self.n_angles_menu = OptionMenu(self, self._n_angles, *options)
        dropdownlabel = Label(self, text="Number of Angles:")
        dropdownlabel.grid(row=4, column=400, pady=(10, 0))
        self.n_angles_menu.grid(row=5, column=400)

        self._progparams['n_angles'] = self._n_angles
        self._progparams['n_angles_menu'] = self.n_angles_menu
        self._progparams['n_angle_label'] = dropdownlabel

    def _make_angle_boxes(self, *args):
        menu = self._progparams['n_angles']
        n = menu.get()  # this is the value chosen for number of angles
        prevparams = [
        ]  # making an empty array for previous options, so you don't lose your settings on re-render
        if 'angleparams' in self._progparams.keys(
        ):  # if there are user entered angle settings
            for box in self._progparams[
                    'angleparams']:  # for angle box in previous settings
                entry = []  # create entry tosave those settings to
                for i, widget in enumerate(
                        box):  # for each widget in previous settings
                    if i == 0 or i == 2:  # these are the indicies for the angle and curve amount variables
                        entry.append(
                            widget.get())  # add the values to the entry
                    widget.grid_forget()  # remove the old box from the frame
                prevparams.append(
                    entry)  # add the entry to the list of previous parameters

        # doing some cleanup from the last run of this method
        self._progparams['angleparams'] = []
        if 'turncycle' in self._parameters.keys():
            self._parameters['turncycle'].grid_forget()
        if 'jank' in self._parameters.keys():
            self._parameters['jank'].grid_forget()
        self._progparams['anglevariables'] = []

        for i in range(n):  # n is the number of angles we are setting
            anglevar = StringVar()
            anglevar.trace('w', self.set_angles)
            anglebox = Entry(self._spacedarea, width=5, textvariable=anglevar)
            label1 = Label(self._spacedarea, text=f"angle {str(i + 1)}")

            curvevar = StringVar()
            curvevar.trace('w', self.set_angles)
            curvebox = Entry(self._spacedarea, width=5, textvariable=curvevar)
            label2 = Label(self._spacedarea, text=f"curve {str(i + 1)}")
            if len(prevparams) > i:
                anglevar.set(prevparams[i][0])
                curvevar.set(prevparams[i][1])
            else:
                if i == 0:
                    anglevar.set(125)
                    curvevar.set(5)
                else:
                    anglevar.set(0)
                    curvevar.set(0)
            if i == 1:
                turncycle = Scale(self._spacedarea,
                                  orient='horizontal',
                                  from_=0,
                                  to=5,
                                  label='turn cycle')
                turncycle.grid(row=9, column=100, rowspan=3)
                jank = Scale(self._spacedarea,
                             orient='horizontal',
                             from_=0,
                             to=600,
                             label="jank")
                jank.grid(row=12, column=100, rowspan=3)
                self._parameters['turncycle'] = turncycle
                self._parameters['jank'] = jank
            col = 20 * (
                i + 1
            )  # just so that I have flexibility in positioning things later if I make changes
            label1.grid(row=9, column=col, pady=10)
            anglebox.grid(row=10, column=col, padx=20)
            label2.grid(row=12, column=col, padx=20)
            curvebox.grid(row=14, column=col)
            self._progparams['angleparams'].append(
                [anglebox, label1, curvebox, label2])
            self._progparams['anglevariables'].append([anglevar, curvevar])
            self.set_angles()

    def set_angles(self, *args):
        angleparams = self._progparams['anglevariables']
        # angles = [[i[0].get(), i[2].get()] for i in angleparams]
        angles = [[i[0].get(), i[1].get()] for i in angleparams]

        for i in range(len(angles)):
            angle = angles[i]
            for j in range(len(angle)):
                val = angle[j]
                try:
                    angles[i][j] = float(val)
                except ValueError:
                    angles[i][j] = len(val)
                    # print("angle values should be numerical. Using length of "
                    #       "input as angle")
        self._parameters['angles'] = [i for i in angles if i[0] != 0]

    def set_sin_spiral(self):
        pady = 3  # the y spacing needed for this particular pattern type

        n_strands = Parameter(self,
                              label="Number of Waves",
                              from_=1,
                              to=300,
                              row=10,
                              pady=pady)
        length = Parameter(self,
                           label="Length",
                           from_=0,
                           to=50,
                           row=12,
                           pady=pady)
        x_shift = Parameter(self,
                            label="Shift X",
                            from_=0,
                            to=50,
                            row=14,
                            pady=pady,
                            resolution=0.1,
                            bigincrement=0.1)
        y_shift = Parameter(self,
                            label="Shift Y",
                            from_=0,
                            to=50,
                            row=18,
                            pady=pady,
                            resolution=0.1,
                            bigincrement=0.1)
        rotation = Parameter(self,
                             label="Rotation",
                             from_=-18.0,
                             to=18.0,
                             row=22,
                             pady=pady)
        rotaterate = Parameter(self,
                               label="Rotation Multiplier",
                               from_=0,
                               to=10,
                               row=26,
                               resolution=0.1,
                               bigincrement=0.1,
                               pady=pady)
        wavelen = Parameter(self,
                            label="Wavelength",
                            from_=0,
                            to=500,
                            row=30,
                            pady=pady)
        wl_shift = Parameter(self,
                             label="Wavelength Shift",
                             from_=0,
                             to=10,
                             row=34,
                             pady=pady,
                             resolution=0.1,
                             bigincrement=0.1)
        amp = Parameter(self,
                        label="Ampitude",
                        from_=0,
                        to=500,
                        row=38,
                        pady=pady)
        amp_shift = Parameter(self,
                              label="Amplitude Shift",
                              from_=0,
                              to=20,
                              row=42,
                              pady=pady,
                              resolution=0.1,
                              bigincrement=0.1)
        pensize = Parameter(self,
                            label="Pen Size",
                            from_=1,
                            to=40,
                            row=46,
                            pady=pady)
        cosine = BooleanVar()
        sinebtn = Radiobutton(self,
                              text='Sine',
                              width=5,
                              indicatoron=False,
                              value=False,
                              variable=cosine)
        cosinebtn = Radiobutton(self,
                                text='Cosine',
                                width=5,
                                indicatoron=False,
                                value=True,
                                variable=cosine)

        sinebtn.grid(row=50, column=50, columnspan=100, pady=20)
        cosinebtn.grid(row=50, column=180, columnspan=100)

        n_strands.set(100)
        length.set(30)
        x_shift.set(1)
        rotaterate.set(1)
        wavelen.set(50)
        amp.set(100)

        self._progparams['cosinebuttons'] = [sinebtn, cosinebtn]

        self._parameters = {
            'strands': n_strands,
            'xshift': x_shift,
            'yshift': y_shift,
            "rotate": rotation,
            'rotaterate': rotaterate,
            'wavelength': wavelen,
            'amplitude': amp,
            'wlshift': wl_shift,
            'ampshift': amp_shift,
            'length': length,
            'pensize': pensize,
            'cosine': cosine
        }

    def set_spirals(self):
        reps = Parameter(self,
                         label="Number of Spirals",
                         from_=1,
                         to=600,
                         row=10)
        rotation = Parameter(self,
                             label="Rotation",
                             from_=-180,
                             to=180,
                             row=20,
                             resolution=0.1,
                             bigincrement=0.1)
        curve = Parameter(self, label="Scale", from_=1, to=50, row=22)
        diameter = Parameter(self,
                             label="Curve Length",
                             from_=1,
                             to=30,
                             row=25)
        scale = Parameter(self,
                          label="Spiral Tightness (Size)",
                          from_=5,
                          to=50,
                          row=27)
        poly = Parameter(self, label="Poly", from_=2, to=400, row=30)
        centerdist = Parameter(self,
                               label="Distance from Center",
                               from_=0,
                               to=50,
                               row=32)
        pensize = Parameter(self, label="Pen Size", from_=1, to=60, row=36)

        reps.set(60)
        rotation.set(5)
        curve.set(10)
        diameter.set(10)
        scale.set(20)
        poly.set(400)
        centerdist.set(0)

        self._parameters = {
            'reps': reps,
            'rotation': rotation,
            'curve': curve,
            'diameter': diameter,
            'scale': scale,
            'poly': poly,
            'centerdist': centerdist,
            'pensize': pensize
        }

    def set_iterative_rotation(self):
        pady = 0
        shapeoptions = ['Wave', 'Rectangle', 'Circle']
        shape = StringVar()
        shapemenulabel = Label(self, text="Shape")
        shapemenu = OptionMenu(self, shape, *shapeoptions)
        shapemenulabel.grid(column=10, row=23, columnspan=200)
        shapemenu.grid(column=10, row=25, columnspan=200)
        branches = Parameter(self,
                             label="Number of Branches",
                             from_=2,
                             to=60,
                             pady=pady,
                             row=27)
        repetitions = Parameter(self,
                                label='Repetitions',
                                from_=4,
                                to=80,
                                pady=pady,
                                row=30)
        shiftx = Parameter(self,
                           label='Shift X',
                           from_=-10,
                           to=10,
                           resolution=0.1,
                           pady=pady,
                           row=35)
        shifty = Parameter(self,
                           label='Shift Y',
                           from_=-10,
                           to=10,
                           resolution=0.1,
                           pady=pady,
                           row=40)
        rotation = Parameter(self,
                             label='Rotation',
                             from_=-30,
                             to=30,
                             resolution=0.1,
                             pady=pady,
                             row=42)
        stretch = Parameter(self,
                            label='Stretch (Wave Only)',
                            from_=0,
                            to=80,
                            pady=pady,
                            row=45)
        length = Parameter(self,
                           label='Length/Width',
                           from_=1,
                           to=150,
                           pady=pady,
                           row=50)
        depth = Parameter(self,
                          label='Amplitude/Height',
                          from_=0,
                          to=150,
                          pady=pady,
                          row=55)
        stretchshift = Parameter(self,
                                 label="Stretch Shift (Wave Only)",
                                 from_=-3,
                                 to=3,
                                 pady=pady,
                                 row=60,
                                 resolution=0.1)
        lenshift = Parameter(self,
                             label="Length/Width Shift",
                             from_=-3,
                             to=3,
                             pady=pady,
                             row=65,
                             resolution=0.1)
        depthshift = Parameter(self,
                               label="Amplitude/Height Shift",
                               from_=-3,
                               to=3,
                               pady=pady,
                               row=70,
                               resolution=0.1)
        distshift = Parameter(self,
                              label="Distance Shift",
                              from_=0,
                              to=0.5,
                              pady=pady,
                              row=75,
                              resolution=0.01)
        cosine = BooleanVar()
        sinebtn = Radiobutton(self,
                              text='Sine',
                              width=5,
                              indicatoron=False,
                              value=False,
                              variable=cosine)
        cosinebtn = Radiobutton(self,
                                text='Cosine',
                                width=5,
                                indicatoron=False,
                                value=True,
                                variable=cosine)
        sinebtn.grid(row=80, column=50, columnspan=100, pady=20)
        cosinebtn.grid(row=80, column=180, columnspan=100)

        shape.set('Rectangle')
        repetitions.set(30)
        rotation.set(2)
        stretch.set(20)
        length.set(30)
        depth.set(30)
        branches.set(8)

        self._progparams['shapemenu'] = shapemenu
        self._progparams['shapemenulabel'] = shapemenulabel
        self._progparams['cosinebuttons'] = [cosinebtn, sinebtn]

        self._parameters = {
            "function": shape,
            "reps": repetitions,
            "xshift": shiftx,
            "yshift": shifty,
            'stretch': stretch,
            "length": length,
            "depth": depth,
            "stretchshift": stretchshift,
            "lenshift": lenshift,
            "depthshift": depthshift,
            "cosine": cosine,
            "distshift": distshift,
            "individualrotation": rotation,
            "branches": branches
        }

    def clear(self):
        """
        This method is used to remove all Widgets from the frame when
        switching pattern types
        """
        for p in self._parameters.values():
            if isinstance(p, Widget):
                p.grid_forget()
        self._spacedarea.grid_forget()
        for item in self._progparams.values():
            if isinstance(item, Widget):
                item.grid_forget()
            elif isinstance(item, list):
                for i in item:
                    if isinstance(i, Widget):
                        i.grid_forget()
                    else:
                        for j in i:
                            if isinstance(j, (Widget, Parameter)):
                                j.grid_forget()

    def save(self, mode, name):
        if mode == 'patterns':
            self.name = name
        params = {}
        # converting all widget parameters to their values for save:
        for k, v in self._parameters.items():
            if isinstance(v, (Widget, BooleanVar, IntVar, StringVar)):
                params[k] = v.get()
            else:
                params[k] = v
        # Create new object with all necessary values for save:
        output = {
            'patterntype': self._patternselection.get(),
            'parameters': params
        }
        return self.name, output

    def load(self, name, data):
        """
        Sets parameters to the values in the data retrieved
        Args:
            data: a Dictionary with patterntype, parameters, and progparams if
            the retrieved pattern is of radialangular type

        Returns:
            None
        """
        # Set patternselection to retrieved value, which is bound to the _setpattern method:
        self._patternselection.set(data['patterntype'])
        params = data['parameters']
        # Set the angle boxes if it's a radialangular pattern type
        if data['patterntype'] == 'radialangular':
            angles = data['parameters']['angles']  # get list of angles
            self._n_angles.set(
                len(params['angles']
                    ))  # set number of angle boxes to number of angles in list
            angleparams = self._progparams[
                'anglevariables']  # retrieves the variables for the angle boxes
            for i in range(len(angleparams)):  # for each index in angleparams
                boxes = angleparams[
                    i]  # get the 2 boxes (angle, curve) for each angle
                anglebox, curvebox = boxes[0], boxes[
                    1]  # separate them from eachother
                anglebox.set(
                    angles[i][0])  # set the angle to the retrieved value
                curvebox.set(
                    angles[i][1])  # set the curve to the retrieeved value
        for k in params:  # for each key in the parameter dictionary:
            if k in self._parameters:  # if that parameter already exists in the master parameter dictionary:
                if not isinstance(self._parameters[k], (list, bool)):
                    self._parameters[k].set(
                        params[k]
                    )  # set that parameter to the retrieved value for that param
            else:  # if that parameter isn't already in the list:
                self._parameters[k] = params[k]  # add it
        self.name = name

    def run(self, colorscheme):
        """
        This method is called from the the master Application.

        Args:
            colorscheme: a SpiroGen ColorScheme object

        Returns:
            None. Draws pattern
        """
        parameters = {}
        # convert parameter widgets/variables to their values
        for param in self._parameters.items():
            label, value = param[0], param[1]
            if not isinstance(value, (int, float, str, list, tuple)):
                parameters[label] = value.get()
            else:
                parameters[label] = value

        # This is where the patterns are actually drawn based on selection:
        if self._patternselection.get() == "layeredflowers":
            parameters['rotate'] = self.angleparam
            parameters.pop('rotationfactor')
            LVL2.layered_flowers(**parameters, colors=colorscheme)
        elif self._patternselection.get() == "radialangular":
            self.set_angles()
            RadialAngularPattern(**parameters, colors=colorscheme).drawpath()
        elif self._patternselection.get() == 'sinespiral':
            pensize = parameters.pop('pensize')
            DrawPath(
                LVL2.sin_spiral(**parameters),
                colors=colorscheme,
                pensize=pensize,
            )
        elif self._patternselection.get() == 'spirals':
            LVL2.spiral_spiral(**parameters, colors=colorscheme)
        elif self._patternselection.get() == 'iterativerotation':
            LVL2.random_iterative_rotation(**parameters,
                                           colors=colorscheme,
                                           rotationcenter=(0, 0))
        #     LVL2.iterative_rotation(**parameters, colors=colorscheme)

        spiro.wait(
        )  # This keeps the pattern window from closing as soon as it's done drawing
Example #28
0
    def __init__(self, master, par=False):
        """
        GUI for selecting default parameters - will write parameters to file \
        of users choosing.

        :type master: Tk
        :param master: Tkinter window
        :type par: EQcorrscanParameters
        :param par: Default parameters to start-up with.
        """
        from tkinter import Label, Button, Entry, DoubleVar, StringVar, IntVar
        from tkinter import BooleanVar, OptionMenu, Checkbutton
        import tkMessageBox
        from eqcorrscan.utils import parameters
        from obspy import UTCDateTime
        import warnings

        # Set the default par, only if they don't already exist.
        if not par:
            par = parameters.EQcorrscanParameters([''], 2, 10, 4, 100, 2,
                                                  '1900-01-01', '2300-01-01',
                                                  '', 'seishub', 4, False, '',
                                                  'jpg', False, 8, 'MAD', 6)
        # Callback functions for all variables (ugly)

        def update_template_names(*args):
            par.template_names = [
                name.strip() for name in template_names.get().split(',')
            ]
            template_names.set(', '.join(par.template_names))

        def update_lowcut(*args):
            par.lowcut = lowcut.get()
            lowcut.set(par.lowcut)

        def update_highcut(*args):
            par.highcut = highcut.get()
            if par.highcut >= 0.5 * par.samp_rate:
                msg = ('Highcut must be less than the Nyquist, setting to ' +
                       str((par.samp_rate / 2.0) - 1))
                tkMessageBox.showwarning(title="Nyquist error", message=msg)
                par.highcut = (par.samp_rate / 2.0) - 1
            highcut.set(par.highcut)

        def update_filt_order(*args):
            par.filt_order = filt_order.get()
            filt_order.set(par.filt_order)

        def update_samp_rate(*args):
            par.samp_rate = samp_rate.get()
            if par.highcut >= 0.5 * par.samp_rate:
                msg = ('Highcut must be less than the Nyquist, setting to ' +
                       str((par.samp_rate / 2.0) - 1))
                tkMessageBox.showwarning(title="Nyquist error", message=msg)
                par.highcut = (par.samp_rate / 2.0) - 1
                highcut.set(par.highcut)
            samp_rate.set(par.samp_rate)

        def update_debug(*args):
            par.debug = debug.get()
            debug.set(par.debug)

        def update_startdate(*args):
            par.startdate = UTCDateTime(startdate.get())
            startdate.set(str(par.startdate))

        def update_enddate(*args):
            par.enddate = UTCDateTime(enddate.get())
            enddate.set(str(par.enddate))

        def update_archive(*args):
            par.archive = archive.get()
            archive.set(par.archive)

        def update_arc_type(*args):
            par.arc_type = arc_type.get()
            arc_type.set(par.arc_type)

        def update_cores(*args):
            par.cores = cores.get()
            cores.set(par.cores)

        def update_plotvar(*args):
            par.plotvar = plotvar.get()
            plotvar.set(par.plotvar)

        def update_plot_format(*args):
            par.plot_format = plot_format.get()
            plot_format.set(par.plot_format)

        def update_tempdir(*args):
            par.tempdir = tempdir.get()
            tempdir.set(par.tempdir)

        def update_threshold(*args):
            par.threshold = threshold.get()
            threshold.set(par.threshold)

        def update_threshold_type(*args):
            par.threshold_type = threshold_type.get()
            threshold_type.set(par.threshold_type)

        def update_plotdir(*args):
            par.plotdir = plotdir.get()
            plotdir.set(par.plotdir)

        def update_trigger_interval(*args):
            par.trigger_interval = trigger_interval.get()
            trigger_interval.set(par.trigger_interval)

        # Set some grid parameters
        nrows = 25
        ncolumns = 3
        self.master = master
        master.title("EQcorrscan parameter setup")
        self.label = Label(master, text="Alpha GUI for default setup")
        self.label.grid(column=0, columnspan=ncolumns, row=0)

        # Set up parameter input
        self.t_names_label = Label(master, text="Template names", anchor='e')
        self.t_names_label.grid(column=0, row=1, sticky='e')
        template_names = StringVar()
        template_names.set(', '.join(par.template_names))
        self.t_names_box = Entry(master, bd=2, textvariable=template_names)
        self.t_names_box.grid(column=1, row=1)
        template_names.trace("w", update_template_names)
        self.t_names_lookup = Button(
            master,
            text="Lookup",
            command=lambda: self.get_template_names(par))
        self.t_names_lookup.grid(column=2, row=1)

        self.lowcut_label = Label(master, text="Lowcut (Hz)", anchor='e')
        self.lowcut_label.grid(column=0, row=2, sticky='e')
        lowcut = DoubleVar()
        lowcut.set(par.lowcut)
        self.lowcut_box = Entry(master, bd=2, textvariable=lowcut)
        self.lowcut_box.grid(column=1, row=2)
        lowcut.trace("w", update_lowcut)

        self.highcut_label = Label(master, text="Highcut (Hz)", anchor='e')
        self.highcut_label.grid(column=0, row=3, sticky='e')
        highcut = DoubleVar()
        highcut.set(par.highcut)
        self.highcut_box = Entry(master, bd=2, textvariable=highcut)
        self.highcut_box.grid(column=1, row=3)
        highcut.trace("w", update_highcut)

        self.filt_order_label = Label(master, text="Filter order")
        self.filt_order_label.grid(column=0, row=4, sticky='e')
        filt_order = DoubleVar()
        filt_order.set(par.filt_order)
        self.filt_order_box = Entry(master, bd=2, textvariable=filt_order)
        self.filt_order_box.grid(column=1, row=4)
        filt_order.trace("w", update_filt_order)

        self.samp_rate_label = Label(master, text="Sample rate (Hz)")
        self.samp_rate_label.grid(column=0, row=5, sticky='e')
        samp_rate = DoubleVar()
        samp_rate.set(par.samp_rate)
        self.samp_rate_box = Entry(master, bd=2, textvariable=samp_rate)
        self.samp_rate_box.grid(column=1, row=5)
        samp_rate.trace("w", update_samp_rate)

        self.debug_label = Label(master, text="Debug")
        self.debug_label.grid(column=0, row=6, sticky='e')
        debug = IntVar()
        debug.set(par.debug)
        self.debug_box = Entry(master, bd=2, textvariable=debug)
        self.debug_box.grid(column=1, row=6)
        debug.trace("w", update_debug)

        self.startdate_label = Label(master, text="Start date (yyyy-mm-dd)")
        self.startdate_label.grid(column=0, row=6, sticky='e')
        startdate = StringVar()
        startdate.set(par.startdate)
        self.startdate_box = Entry(master, bd=2, textvariable=startdate)
        self.startdate_box.grid(column=1, row=6)
        startdate.trace("w", update_startdate)

        self.enddate_label = Label(master, text="End date (yyyy-mm-dd)")
        self.enddate_label.grid(column=0, row=8, sticky='e')
        enddate = StringVar()
        enddate.set(par.enddate)
        self.enddate_box = Entry(master, bd=2, textvariable=enddate)
        self.enddate_box.grid(column=1, row=8)
        enddate.trace("w", update_enddate)

        self.archive_label = Label(master, text="Archive")
        self.archive_label.grid(column=0, row=9, sticky='e')
        archive = StringVar()
        archive.set(par.archive)
        self.archive_box = Entry(master, bd=2, textvariable=archive)
        self.archive_box.grid(column=1, row=9)
        archive.trace("w", update_archive)
        self.archive_lookup = Button(master,
                                     text="Lookup",
                                     command=lambda: self.get_archive(par))
        self.archive_lookup.grid(column=2, row=9)

        self.arc_type_label = Label(master, text="Archive type")
        self.arc_type_label.grid(column=0, row=10, sticky='e')
        arc_type = StringVar()
        arc_type.set(par.arc_type)
        self.arc_type_box = OptionMenu(master, arc_type, "seishub", "fdsn",
                                       "day_vols")
        self.arc_type_box.grid(column=1, row=10, sticky='w,e')
        arc_type.trace("w", update_arc_type)

        self.cores_label = Label(master, text="Number of cores")
        self.cores_label.grid(column=0, row=11, sticky='e')
        cores = IntVar()
        cores.set(par.cores)
        self.cores_box = Entry(master, bd=2, textvariable=cores)
        self.cores_box.grid(column=1, row=11)
        cores.trace("w", update_cores)

        self.plotvar_label = Label(master, text="Plotting on/off")
        self.plotvar_label.grid(column=0, row=12, sticky='e')
        plotvar = BooleanVar()
        plotvar.set(par.plotvar)
        self.plotvar_box = Checkbutton(master,
                                       text='Plot on',
                                       var=plotvar,
                                       onvalue=True,
                                       offvalue=False)
        self.plotvar_box.grid(column=1, row=12)
        plotvar.trace("w", update_plotvar)

        self.plotdir_label = Label(master, text="Plot directory")
        self.plotdir_label.grid(column=0, row=13, sticky='e')
        plotdir = StringVar()
        plotdir.set(par.plotdir)
        self.plotdir_box = Entry(master, bd=2, textvariable=plotdir)
        self.plotdir_box.grid(column=1, row=13)
        plotdir.trace("w", update_plotdir)
        self.plotdir_lookup = Button(master,
                                     text="Lookup",
                                     command=lambda: self.get_plotdir(par))
        self.plotdir_lookup.grid(column=2, row=13)

        self.plot_format_label = Label(master, text="Plot format")
        self.plot_format_label.grid(column=0, row=14, sticky='e')
        plot_format = StringVar()
        plot_format.set(par.plot_format)
        self.plot_format_box = OptionMenu(master, plot_format, "jpg", "eps",
                                          "pdf", "png")
        self.plot_format_box.grid(column=1, row=14, sticky='w,e')
        plot_format.trace("w", update_plot_format)

        self.tempdir_label = Label(master, text="Temporary directory")
        self.tempdir_label.grid(column=0, row=15, sticky='e')
        tempdir = StringVar()
        tempdir.set(par.tempdir)
        self.tempdir_box = Entry(master, bd=2, textvariable=tempdir)
        self.tempdir_box.grid(column=1, row=15)
        tempdir.trace("w", update_tempdir)
        self.tempdir_lookup = Button(master,
                                     text="Lookup",
                                     command=lambda: self.get_tempdir(par))
        self.tempdir_lookup.grid(column=2, row=15)

        self.threshold_label = Label(master, text="Threshold")
        self.threshold_label.grid(column=0, row=16, sticky='e')
        threshold = DoubleVar()
        threshold.set(par.threshold)
        self.threshold_box = Entry(master, bd=2, textvariable=threshold)
        self.threshold_box.grid(column=1, row=16)
        threshold.trace("w", update_threshold)

        self.threshold_type_label = Label(master, text="Threshold type")
        self.threshold_type_label.grid(column=0, row=17, sticky='e')
        threshold_type = StringVar()
        threshold_type.set(par.threshold_type)
        self.threshold_type_box = OptionMenu(master, threshold_type, "MAD",
                                             "absolute", "av_chan_corr")
        self.threshold_type_box.grid(column=1, row=17, sticky='w,e')
        threshold_type.trace("w", update_threshold_type)

        self.trigger_interval_label = Label(master,
                                            text="Minimum trigger " +
                                            "interval (s)")
        self.trigger_interval_label.grid(column=0, row=18, sticky='e')
        trigger_interval = DoubleVar()
        trigger_interval.set(par.trigger_interval)
        self.trigger_interval_box = Entry(master,
                                          bd=2,
                                          textvariable=trigger_interval)
        self.trigger_interval_box.grid(column=1, row=18)
        trigger_interval.trace("w", update_trigger_interval)

        # End of user editable section, now we have read/write buttons
        self.read_button = Button(master,
                                  text="Read parameters",
                                  command=lambda: self.read_par(master))
        self.read_button.grid(column=0, row=nrows - 2, sticky='w,e')

        self.write_button = Button(master,
                                   text="Write parameters",
                                   command=lambda: self.write_par(par))
        self.write_button.grid(column=1, row=nrows - 2, sticky='w,e')
Example #29
0
class GUI(object):
    def __init__(self):
        self.classifier = ""

    # checks whether the files exist in the given folder and are not empty
    def check_files_exist(self, path):
        return os.path.isfile(path + "/Structure.txt") and os.path.isfile(
            path + "/train.csv"
        ) and os.path.isfile(path + "/test.csv") and os.path.getsize(
            path + "/Structure.txt") > 0 and os.path.getsize(
                path + "/train.csv") > 0 and os.path.getsize(path +
                                                             "/test.csv") > 0

    # gets the path for the folder
    def get_path(self, root):
        try:
            path = filedialog.askdirectory(parent=root,
                                           title='Naive Bayes Classifier')
            if path is None:
                messagebox.showerror("Error", "Couldn't get the path!")
            else:
                if self.check_files_exist(path):
                    self.folder_path.set(path)
                else:
                    messagebox.showerror("Error", "Missing / Empty Files!")
        except:
            messagebox.showerror("Error", "Couldn't get the path!")

    # builds the model from the given structure and training files
    def build(self):
        try:
            data_structure = {}
            structure_file = open(self.folder_path.get() + "/Structure.txt",
                                  "r")
            structure_content = structure_file.readlines()
            structure_file.close()

            # get the structure of the model from the structure file
            for line in structure_content:
                initial_split = line.split(" ")

                classifiers = initial_split[2]

                if len(initial_split) > 3:
                    i = 3
                    while i < len(initial_split):
                        classifiers = classifiers + " " + initial_split[i]
                        i = i + 1

                if '{' in classifiers:
                    classifiers = classifiers.replace("\n", "")
                    classifiers = classifiers.replace("{", "")
                    classifiers = classifiers.replace("}", "")
                    classifiers = classifiers.split(",")
                else:
                    classifiers = ['NUMERIC']

                # add a new entry to the structure
                data_structure[line.split(" ")[1]] = {
                    'attributes': classifiers
                }

            # get the raw data from the train file
            train_data = pd.read_csv(
                filepath_or_buffer=self.folder_path.get() + "/train.csv")

            # create the classifier from the training file
            self.classifier = Classifier.Classifier(self.folder_path.get(),
                                                    data_structure,
                                                    int(self.bins.get()))
            self.classifier.build_model(train_data)

            # enable the "Classify" button to be pressed
            self.classify_button.config(state='normal')

            messagebox.showinfo(
                "Information", "Building classifier using train-set is done!")
        except IOError:
            messagebox.showerror("Error",
                                 "There was a problem reading the files!")

    # classify the given test file by using the NB model generated earlier
    def classify(self):
        # get the raw data from the train file
        test_data = pd.read_csv(filepath_or_buffer=self.folder_path.get() +
                                "/test.csv")

        # get the classifications for the test file
        self.classifier.classify_input(test_data)

        answered_ok = messagebox.showinfo("Information",
                                          "Classification completed!")
        if answered_ok == 'OK':
            self.root.destroy()

    # builds and shows the GUI
    def show_gui(self):

        # create the main window
        self.root = Tk()
        self.root.title('Naive Bayes Classifier')
        self.root.geometry('500x300')

        # create the frame
        frame = Frame(self.root)
        frame.pack(side='top', fill='both', expand=True)

        # initialize the 2 global input variables
        self.bins = IntVar(self.root, value=2)
        self.folder_path = StringVar(self.root)

        # validates the input for enabling the "Build" button to be pressed
        def validate_input(*args):
            dir_ok = False
            bin_ok = False
            # check if the directory and its contents are ok
            if self.folder_path.get() is not None and os.path.isdir(
                    self.folder_path.get()):
                if self.check_files_exist(self.folder_path.get()):
                    dir_ok = True

            # checks if the a given string is a positive integer
            def represents_int(s):
                try:
                    int(s)
                    return True
                except ValueError:
                    return False

            # check if the entered bins are ok
            if represents_int(bins_text_box.get()):
                if int(bins_text_box.get()) > 0:
                    bin_ok = True

            # enable the Build button if both are ok
            if dir_ok and bin_ok:
                build_button.config(state='normal')
            else:
                build_button.config(state='disabled')
            x = browse_text_box.get()
            y = bins_text_box.get()

        # initialize labels and buttons
        browse_label = Label(frame, text="Directory Path:")
        browse_label.grid(column=0, row=1, padx=4, pady=4)
        browse_text_box = Entry(frame, width=40, textvariable=self.folder_path)
        browse_text_box.grid(column=1, row=1, padx=4, pady=4)
        browse_button = Button(frame,
                               text="Browse",
                               command=lambda: self.get_path(self.root))
        browse_button.grid(column=2, row=1, padx=4, pady=4)
        self.folder_path.trace("w", validate_input)
        bins_label = Label(frame, text="Discretization Bins:")
        bins_label.grid(column=0, row=2, padx=4, pady=4)
        self.bins.trace("w", validate_input)
        bins_text_box = Entry(frame, width=40, textvariable=self.bins)
        bins_text_box.grid(column=1, row=2, padx=4, pady=4)
        build_button = Button(frame,
                              text="Build",
                              command=lambda: self.build())
        build_button.grid(column=1, row=3, padx=5, pady=8)
        build_button.config(state='disabled')
        self.classify_button = Button(frame,
                                      text="Classify",
                                      command=lambda: self.classify())
        self.classify_button.grid(column=1, row=4, padx=5, pady=8)
        self.classify_button.config(state='disabled')

        self.root.mainloop()
Example #30
0
class TkContourEditor(FigureCanvasTkAgg):
    def __init__(self, parent, **kwargs):
        FigureCanvasTkAgg.__init__(self, Figure(), master=parent, **kwargs)
        self.plt = ContourEditor(self.figure, renderer=self)
        self.cell = IntVar()
        self.step = IntVar()
        self.variable = StringVar()
        self.integral = DoubleVar()
        self.cell.trace('w', self._cell)
        self.step.trace('w', self._step)
        self.variable.trace('w', self._variable)
        self.figure.canvas.mpl_connect('button_press_event', self.onclick)

        def _pass(*args, **kwargs):
            pass

        self.cell._report_exception = _pass
        self.step._report_exception = _pass
        self.plt.delaunay = True
        self.plt.callback = self.integral.set
        self.label = None

    def _cell(self, *args):
        c = self.cell.get()
        try:
            self.plt.cell = c
        except ValueError:
            self.cell.set(-1)
            raise
        except:
            print(traceback.format_exc())
            raise

    def _step(self, *args):
        s = self.step.get()
        if s < 1:
            self.step.set(1)
        else:
            try:
                self.plt.step = s
            except:
                print(traceback.format_exc())
                raise

    def _variable(self, *args):
        try:
            self.plt.variable = self.variable.get()
            if self.label is not None:
                if self.plt._variables[self.plt.variable][1:]:
                    self.label['text'] = 'Curl:'
                else:
                    self.label['text'] = 'Mean:'
        except:
            print(traceback.format_exc())
            raise

    def onclick(self, event):
        x, y = event.xdata, event.ydata
        if x is None or y is None:
            return
        self.cell.set(self.plt.find_cell((x, y)))

    def toggle_delaunay(self, *args):
        self.plt.delaunay = not self.plt._delaunay

    @property
    def cells(self):
        return self.plt.cells

    @cells.setter
    def cells(self, cs):
        try:
            self.plt.cells = cs
        except:
            print(traceback.format_exc())
            raise

    @property
    def map(self):
        return self.plt.map

    @map.setter
    def map(self, m):
        try:
            self.plt.map = m
        except:
            print(traceback.format_exc())
            raise

    @property
    def variables(self):
        return self.plt.variables

    @property
    def debug(self):
        return self.plt.debug

    @debug.setter
    def debug(self, d):
        self.plt.debug = d
class NewMorphLineWindow(Toplevel):
    def __init__(self, master=None):
        super().__init__(master=master)
        self.set_basic()
        self.set_widgets()

    def set_basic(self):
        self.minsize(600, 400)
        self.maxsize(600, 400)
        self.title("Ekstrakcja linii")
        self.protocol("WM_DELETE_WINDOW", lambda: self.cancel())

        self.handleBorder = {
            "Bez zmian (isolated)": 0,
            "Odbicie lustrzane (reflect)": 1,
            "Powielenie skrajnego piksela (replicate)": 2
        }

    def set_widgets(self):
        self.horizontalSizeW = StringVar(self, value="3")
        self.horizontalSizeH = StringVar(self, value="1")
        self.verticalSizeW = StringVar(self, value="3")
        self.verticalSizeH = StringVar(self, value="1")
        self.borderType = StringVar(self, list(self.handleBorder.keys())[0])
        self.cbVarHorizontal = IntVar(value=1)
        self.cbVarVertical = IntVar(value=1)
        self.cbVarOuter = IntVar(value=1)
        self.cbVarNegate = IntVar(value=0)

        self.sizeHorizontalWSpin = Spinbox(self,
                                           justify='center',
                                           font=("Helvetica", 15),
                                           from_=1,
                                           to=9999,
                                           textvariable=self.horizontalSizeW,
                                           command=self.update_preview,
                                           state='readonly',
                                           increment=2)
        self.sizeHorizontalHSpin = Spinbox(self,
                                           justify='center',
                                           font=("Helvetica", 15),
                                           from_=1,
                                           to=9999,
                                           textvariable=self.horizontalSizeH,
                                           command=self.update_preview,
                                           state='readonly',
                                           increment=2)
        self.sizeVerticalWSpin = Spinbox(self,
                                         justify='center',
                                         font=("Helvetica", 15),
                                         from_=1,
                                         to=9999,
                                         textvariable=self.verticalSizeW,
                                         command=self.update_preview,
                                         state='readonly',
                                         increment=2)
        self.sizeVerticalHSpin = Spinbox(self,
                                         justify='center',
                                         font=("Helvetica", 15),
                                         from_=1,
                                         to=9999,
                                         textvariable=self.verticalSizeH,
                                         command=self.update_preview,
                                         state='readonly',
                                         increment=2)

        self.horizontalSizeW.trace("w", self.update_preview)
        self.horizontalSizeH.trace("w", self.update_preview)
        self.verticalSizeW.trace("w", self.update_preview)
        self.verticalSizeH.trace("w", self.update_preview)
        self.borderType.trace("w", self.update_preview)
        self.cbVarHorizontal.trace("w", self.update_preview)
        self.cbVarVertical.trace("w", self.update_preview)
        self.cbVarOuter.trace("w", self.update_preview)
        self.cbVarNegate.trace("w", self.update_preview)

        self.cbHorizontal = Checkbutton(self,
                                        width=0,
                                        variable=self.cbVarHorizontal)
        self.cbVertical = Checkbutton(self,
                                      width=0,
                                      variable=self.cbVarVertical)
        self.cbOuterOnly = Checkbutton(self, width=0, variable=self.cbVarOuter)
        self.cbNegateFirst = Checkbutton(self,
                                         width=0,
                                         variable=self.cbVarNegate)

        self.borderList = OptionMenu(self, self.borderType)

        for border in self.handleBorder:
            self.borderList['menu'].add_command(
                label=border, command=lambda v=border: self.borderType.set(v))

        self.saveButton = Button(self,
                                 image=saveIcon,
                                 command=self.update_image)
        self.cancelButton = Button(self, image=closeIcon, command=self.cancel)

        self.update_preview()
        self.place_widgets()

    def update_image(self):
        self.master.image.cv2Image = copy.deepcopy(self.master.image.copy)
        self.master.image.morph_line(int(self.horizontalSizeW.get()),
                                     int(self.horizontalSizeH.get()),
                                     int(self.verticalSizeW.get()),
                                     int(self.verticalSizeH.get()),
                                     self.cbVarHorizontal.get(),
                                     self.cbVarVertical.get(),
                                     self.handleBorder[self.borderType.get()],
                                     self.cbVarOuter.get(),
                                     self.cbVarNegate.get())
        self.master.image.copy = copy.deepcopy(self.master.image.cv2Image)
        self.master.manager.new_state(self.master.image.cv2Image)
        self.master.update_visible_image()
        self.master.update_child_windows()
        self.destroy()

    def update_preview(self, *args):
        self.sizeHorizontalWSpin.config(from_=int(self.horizontalSizeH.get()) +
                                        2)
        self.sizeHorizontalHSpin.config(to=int(self.horizontalSizeW.get()) - 2)
        self.sizeVerticalWSpin.config(from_=int(self.verticalSizeH.get()) + 2)
        self.sizeVerticalHSpin.config(to=int(self.verticalSizeW.get()) - 2)
        self.master.image.cv2Image = copy.deepcopy(self.master.image.copy)
        self.master.image.morph_line(int(self.horizontalSizeW.get()),
                                     int(self.horizontalSizeH.get()),
                                     int(self.verticalSizeW.get()),
                                     int(self.verticalSizeH.get()),
                                     self.cbVarHorizontal.get(),
                                     self.cbVarVertical.get(),
                                     self.handleBorder[self.borderType.get()],
                                     self.cbVarOuter.get(),
                                     self.cbVarNegate.get())
        self.master.update_visible_image()
        self.master.update_child_windows()

    def place_widgets(self):
        Label(self, text="Poziome linie", font=("Helvetica", 15)).place(x=85,
                                                                        y=15)
        Label(self, text="Pionowe linie", font=("Helvetica", 15)).place(x=395,
                                                                        y=15)

        self.sizeHorizontalWSpin.place(width=100, height=50, x=150, y=60)
        self.sizeHorizontalHSpin.place(width=100, height=50, x=150, y=120)
        self.sizeVerticalWSpin.place(width=100, height=50, x=450, y=60)
        self.sizeVerticalHSpin.place(width=100, height=50, x=450, y=120)

        Label(self, text="Min. długość", font=("Helvetica", 15)).place(x=30,
                                                                       y=70)
        Label(self, text="Min. grubość", font=("Helvetica", 15)).place(x=30,
                                                                       y=130)
        Label(self, text="Min. długość", font=("Helvetica", 15)).place(x=330,
                                                                       y=70)
        Label(self, text="Min. grubość", font=("Helvetica", 15)).place(x=330,
                                                                       y=130)

        Label(self, text="Szukać poziomych?",
              font=("Helvetica", 9)).place(x=70, y=175)
        Label(self, text="Szukać pionowych?",
              font=("Helvetica", 9)).place(x=380, y=175)

        self.cbHorizontal.place(x=180, y=175)
        self.cbVertical.place(x=500, y=175)

        Label(self, text="Szukać tylko zewnętrznych?",
              font=("Helvetica", 11)).place(x=190, y=225)
        Label(self, text="Wstępnie zanegować?",
              font=("Helvetica", 11)).place(x=190, y=255)
        self.cbOuterOnly.place(x=390, y=225)
        self.cbNegateFirst.place(x=390, y=255)
        self.borderList.place(width=200, height=50, x=200, y=300)

        self.saveButton.place(width=40, height=40, x=220, y=355)
        self.cancelButton.place(width=40, height=40, x=340, y=355)

    def cancel(self):
        self.master.image.cv2Image = copy.deepcopy(self.master.image.copy)
        self.master.update_visible_image()
        self.master.image.fill_histogram()
        self.master.update_child_windows()
        self.destroy()