Esempio n. 1
0
def make_color_swatch(parent: tk.Frame, var: tk.StringVar,
                      size: int) -> ttk.Label:
    """Make a single swatch."""

    # Note: tkinter requires RGB as ints, not float!
    def open_win(e) -> None:
        """Display the color selection window."""
        widget_sfx()
        r, g, b = parse_color(var.get())
        new_color, tk_color = askcolor(
            color=(r, g, b),
            parent=parent.winfo_toplevel(),
            title=gettext('Choose a Color'),
        )
        if new_color is not None:
            r, g, b = map(int,
                          new_color)  # Returned as floats, which is wrong.
            var.set('{} {} {}'.format(int(r), int(g), int(b)))

    swatch = ttk.Label(parent)

    def update_image(var_name: str, var_index: str, operation: str):
        img.apply(swatch, img.Handle.color(parse_color(var.get()), size, size))

    update_image('', '', '')

    # Register a function to be called whenever this variable is changed.
    var.trace_add('write', update_image)

    tk_tools.bind_leftclick(swatch, open_win)

    return swatch
Esempio n. 2
0
def use_file_logic(file_path_var: StringVar, markdown_var: StringVar):
    file = None

    # file opening logic
    def on_file_selected(*_):
        nonlocal file
        path = file_path_var.get()

        if file:
            file.close()

        if path == '':
            markdown_var.set('')  # clear markdown_string
            return

        file = open_file(path)
        content = file.read()

        markdown_var.set(content)

    # file updating logic todo: maybe with throttle
    def on_markdown_change(*_):
        if file:
            mdn_text = markdown_var.get()
            file.seek(0)
            file.write(mdn_text)
            file.truncate()

    on_file_selected()
    file_path_var.trace_add('write', on_file_selected)
    on_markdown_change()
    markdown_var.trace_add('write', on_markdown_change)
Esempio n. 3
0
class CellEntry(Entry):
    def __init__(self, index_of: int, register_type: str, list_of: List[Cell],
                 monitor_callback: Callable, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.register_type = register_type
        self.index_of = index_of
        self.__variable = StringVar()
        self.__variable.trace_add("write", self.__validate)
        self.config(textvariable=self.__variable)
        self.list_of = list_of

    def __validate(self, *others):
        value: Optional[str] = self.__variable.get()
        if value is None or value == "":
            value = "00"
        value_two: str = value[:2]
        if all(char in hexdigits for char in value_two):
            hex: int = int(value_two, base=16)
        else:
            hex: int = 0
        self.__variable.set(format(hex, "02X"))
        self.__on_edit()

    def __on_edit(self):
        self.list_of[self.index_of].value = self.__variable.get()

    def set(self, value):
        self.__variable.set(value)
Esempio n. 4
0
def use_recent_files_logic(files_var: StringVar):
    file = open_file(file_from_project_root('.litcache'))
    files_var.set(file.read())

    def add_to_recent(path: str):
        files = decode_lit_cache(files_var.get())

        # return if path already exists
        for p in files:
            if p == path:
                return

        if len(files) == max_cache:
            files.pop()
        files.append(path)
        files_var.set(encode_lit_cache(files))

    def on_files_var_change(*_):
        if file:
            file.seek(0)
            file.write(files_var.get())
            file.truncate()

    files_var.trace_add('write', on_files_var_change)

    return add_to_recent
Esempio n. 5
0
 def __open_mark(self):
     '''Open files by mark: Major dialogue for mark choices and logic'''
     ### despite using select, invoke, focus_set, event_generate,
     ### tk wouldn't set the OR radiobutton as default.
     ### So the following sub-function is a workaround to force the issue.
     def default_selection(var):
         var.set("or")
     def lookup(toc,gl,word):
         l = len(word)
         for i, item in enumerate(toc):
             if item.find(word,0,l) > -1:
                 gl.see(i)
                 break
     dprint(3, "\nTkMemobook::open_mark:: ")
     toc = [ item for item in self.data.toc() ]
     toc.sort(key=lambda x: x.lower())
     getter = Toplevel(self.root)
     getter_list = ListboxHV(getter, selectmode="multiple")
     for item in toc:
         getter_list.insert(END, item)
     getter_list.pack(fill="both", expand="true")
     quick_var = StringVar()
     quick_var.trace_add("write",
                         lambda i,j,k: lookup(toc,getter_list,quick_var.get()))
     quick_frame = Frame(getter)
     quick_label = Label(quick_frame,
                         text="Quick lookup:")
     quick_enter = Entry(quick_frame,
                         textvariable=quick_var,
                         width=24)
     quick_label.pack(side="left")
     quick_enter.pack(side="left")
     button_frame = Frame(getter)
     
     logic_variable = StringVar(None, "or")
     radiobutt_OR = Radiobutton(button_frame,
                                text="OR",
                                variable=logic_variable,
                                command=lambda: default_selection(logic_variable),  # get tk to give default selection
                                value="or")
     radiobutt_AND = Radiobutton(button_frame,
                                 text="AND",
                                 variable=logic_variable,
                                 value="and")
     retbutt = Button(button_frame,
                      text="Apply",
                      command=lambda: self.__open_mark_confirm(getter,
                                                               [ toc[int(j)] for j in getter_list.curselection() ],
                                                               logic_variable.get()))
     cancbutt = Button(button_frame,
                       text="Cancel",
                       command=lambda: getter.destroy())
     radiobutt_OR.pack(side="left")
     radiobutt_AND.pack(side="left")
     cancbutt.pack(side="left")
     retbutt.pack(side="left")
     button_frame.pack(side="bottom")
     quick_frame.pack(side="bottom")
     radiobutt_OR.invoke()
Esempio n. 6
0
    def create_numeric_entry(self, key, initial_value, callback):
        value_container = StringVar()
        value_container.trace_add(
            "write", lambda x, y, z: callback(value_container.get()))

        self.entries[key] = value_container

        return NumericEntry(self,
                            initial_value,
                            width=5,
                            textvariable=value_container)
Esempio n. 7
0
def make_color_swatch(parent: tk.Frame,
                      var: tk.StringVar,
                      size=16) -> ttk.Label:
    """Make a single swatch."""

    # Note: tkinter requires RGB as ints, not float!

    def get_color():
        """Parse out the color."""
        color = var.get()
        if color.startswith('#'):
            try:
                r = int(color[1:3], base=16)
                g = int(color[3:5], base=16)
                b = int(color[5:], base=16)
            except ValueError:
                LOGGER.warning('Invalid RGB value: "{}"!', color)
                r = g = b = 128
        else:
            r, g, b = map(int, Vec.from_str(color, 128, 128, 128))
        return r, g, b

    def open_win(e):
        """Display the color selection window."""
        widget_sfx()
        r, g, b = get_color()
        new_color, tk_color = askcolor(
            color=(r, g, b),
            parent=parent.winfo_toplevel(),
            title=_('Choose a Color'),
        )
        if new_color is not None:
            r, g, b = map(int,
                          new_color)  # Returned as floats, which is wrong.
            var.set('{} {} {}'.format(int(r), int(g), int(b)))

    swatch = ttk.Label(
        parent,
        relief='raised',
    )

    def update_image(var_name: str, var_index: str, operation: str):
        r, g, b = get_color()
        swatch['image'] = img.color_square(round(Vec(r, g, b)), size)

    update_image('', '', '')

    # Register a function to be called whenever this variable is changed.
    var.trace_add('write', update_image)

    utils.bind_leftclick(swatch, open_win)

    return swatch
Esempio n. 8
0
def use_tab_page_ui_logic(selected_tab_var: StringVar, pages: list):
    # change ui according to selected_tab
    def on_tab_selected(*_):
        for page in pages:
            page.pack_forget()

        tab = selected_tab_var.get()
        selected_tab_index = tabs.index(tab)

        pages[selected_tab_index].pack(expand=True, fill='both')

    on_tab_selected()
    selected_tab_var.trace_add('write', on_tab_selected)
Esempio n. 9
0
def get_side_nav(master, selected_tab: StringVar, file_path_var):
    tab_frame = Frame(master, bg=color['surface'])

    buttons = dict()
    last_selected_tab = selected_tab.get()

    for index, key in list(enumerate(tabs)):
        button = TabButton(tab_frame, image=key)

        button.grid(row=index, column=0, sticky=W, ipadx=16, ipady=16)
        button.configure(command=lambda k=key: selected_tab.set(k),
                         relief=FLAT,
                         bg=color['surface'])

        buttons.setdefault(key, button)

    def on_tab_selected(*_):
        nonlocal last_selected_tab
        _key = selected_tab.get()

        # get buttons
        _last_button = buttons.get(last_selected_tab)
        _button = buttons.get(_key)

        # remove selected look from this button
        _last_button.anim.animate_to(0)
        _last_button.configure(relief=FLAT, bg=color['surface'])

        # update selected look for this button
        _button.configure(bg=color['surface-dark'])
        _button.anim.animate_to(1)

        last_selected_tab = _key

    on_tab_selected()
    selected_tab.trace_add('write', on_tab_selected)

    # disable tabs if no file selected
    def on_file_selected(*_):
        if file_path_var.get() == '':
            for _key in buttons.keys():
                if _key != 'file_tab':
                    buttons[_key].configure(state=DISABLED)
        else:
            for _key in buttons.keys():
                buttons[_key].configure(state=NORMAL)

    on_file_selected()
    file_path_var.trace_add('write', on_file_selected)

    return tab_frame
Esempio n. 10
0
def make_color_swatch(parent: tk.Frame, var: tk.StringVar, size=16) -> ttk.Label:
    """Make a single swatch."""
    # Note: tkinter requires RGB as ints, not float!

    def get_color():
        """Parse out the color."""
        color = var.get()
        if color.startswith('#'):
            try:
                r = int(color[1:3], base=16)
                g = int(color[3:5], base=16)
                b = int(color[5:], base=16)
            except ValueError:
                LOGGER.warning('Invalid RGB value: "{}"!', color)
                r = g = b = 128
        else:
            r, g, b = map(int, Vec.from_str(color, 128, 128, 128))
        return r, g, b

    def open_win(e):
        """Display the color selection window."""
        widget_sfx()
        r, g, b = get_color()
        new_color, tk_color = askcolor(
            color=(r, g, b),
            parent=parent.winfo_toplevel(),
            title=_('Choose a Color'),
        )
        if new_color is not None:
            r, g, b = map(int, new_color)  # Returned as floats, which is wrong.
            var.set('{} {} {}'.format(int(r), int(g), int(b)))

    swatch = ttk.Label(
        parent,
        relief='raised',
    )

    def update_image(var_name: str, var_index: str, operation: str):
        r, g, b = get_color()
        swatch['image'] = img.color_square(round(Vec(r, g, b)), size)

    update_image('', '', '')

    # Register a function to be called whenever this variable is changed.
    var.trace_add('write', update_image)

    utils.bind_leftclick(swatch, open_win)

    return swatch
Esempio n. 11
0
        def gen_feff_folder(var,indx,mode):
            ncomp = self.ncomp.get()
            self.feff_file_toggle = [True for i in range(self.ncomp.get())]
            k = 4
            try:
                self.arr_feff
                # self.input_list
            except AttributeError:
                pass
            else:
                for i in range(len(self.feff_input_list)):
                    self.feff_input_list[i].destroy()
                    self.feff_button_list[i].destroy()
                    self.feff_description_list[i].destroy()
            self.feff_file_list = []
            self.arr_feff= []
            arr_row = []
            # parameter sets
            self.feff_input_list = []
            self.feff_button_list = []
            self.feff_description_list = []
            # Need to remove previous one
            for i in range(ncomp):
                self.arr_feff.append('FEFF Folder (' + str(i+1) +")")
                arr_row.append(i+k)

                # Generate a list of variables
                temp_var = StringVar(self.root,'Please choose a directory')
                self.feff_file_list.append(temp_var)

                # Add trace back
                temp_var.trace_add('write',feff_trace)
                # Setup each entry
                temp_entry = tk.Entry(self.tab_Inputs,textvariable=temp_var,
                    font=self.entryFont)

                temp_entry.grid(column=1,row=k+i,sticky=(W,E),
                    padx=self.padx,pady=self.pady)
                self.feff_input_list.append(temp_entry)

                # setup each button
                temp_button = ttk.Button(self.tab_Inputs,text="Choose",
                        command=lambda x=i:select_feff_folder(self.feff_file_list[x]), style='my.TButton')
                temp_button.grid(column=3, row=k+i, sticky=W,padx=self.padx,pady=self.pady)
                self.feff_button_list.append(temp_button)

            self.feff_description_list = self.description_tabs(self.arr_feff,self.tab_Inputs,row=arr_row,return_description=True)
Esempio n. 12
0
 def _set_force_regex(
         self, var: StringVar, regex: str, message: str,
         row: int, col: int, padx: int
         ) -> None:
     def force_regex(*args) -> None:
         value = var.get()
         if not search(regex, value):
             if message not in self._error_label_dict:
                 self._error_label(message, row, col, padx)
         else:
             if message in self._error_label_dict:
                 widget = self._error_label_dict[message]
                 widget.destroy()
                 del self._error_label_dict[message]
         return None
     var.trace_add('write', force_regex)
     return None
Esempio n. 13
0
def get_md_preview_frame(master, html_var: StringVar):
    md_prev_frame = HTMLLabel(master,
                              width='1',
                              height='1',
                              background=color['shell-dark'],
                              padx=16,
                              pady=16)

    def on_html_change(*_):
        # todo: find if there is any simpler way to change font color
        html_text = '<div style=\"color:' + color[
            'high'] + ';\">' + html_var.get() + '</div>'
        md_prev_frame.set_html(html_text)

    on_html_change()
    html_var.trace_add('write', on_html_change)
    return md_prev_frame
Esempio n. 14
0
        def ncomp_trace_cb(var, indx, mode):
            """
                Trace the number of components, if changes, recomputed
            """
            ncomp = self.ncomp.get()
            k = 5 # Starting components

            # Check if arr_pathlist exists
            try:
                self.arr_pathlist
            except AttributeError:
                pass
            else:
                # Destory all in the current list
                for i in range(len(self.path_input_list)):
                    self.path_description_list[i].destroy()
                    self.path_input_list[i].destroy()
            # Create the list
            self.arr_pathlist = []
            self.path_input_list = []
            self.path_file_list = []
            arr_row = []
            for i in range(ncomp):
                self.arr_pathlist.append('Pathlist Folder ('+ str(i+1) + ")")
                arr_row.append(i+k)
                # Create the entry variable
                temp_path_var = StringVar(self.root,'Path_list')
                temp_path_var.trace_add('write',output_var_trace)
                # temp_path_var.trace_add('write',)
                self.path_file_list.append(temp_path_var)
                # Create the entry
                temp_entry = tk.Entry(self.tab_Paths,textvariable=self.path_file_list[i],
                    font=self.entryFont)
                # lock to the grid
                temp_entry.grid(column=1,columnspan=2,row=k+i,sticky=(W,E),
                    padx=self.padx,pady=self.pady)

                self.path_input_list.append(temp_entry)

            # create the descriptions for it.
            self.path_description_list = self.description_tabs(self.arr_pathlist,self.tab_Paths,row=arr_row,return_description=True)
Esempio n. 15
0
class FensterRound(Frame):
    def __init__(self, master, controller):
        super().__init__(master)
        self.controller = controller

        self.int_round = StringVar(value=self.controller.int_round)
        self.int_round.trace_add('write', self.callback_int)
        if self.controller.bool_roundActive:
            self.bool_roundActive = BooleanVar(value=1)
        else:
            self.bool_roundActive = BooleanVar(value=0)

        self.grid()
        self.chkb = Checkbutton(master, text="Runden", var=self.bool_roundActive, command=self.callback_chkb)
        self.chkb.grid(row=0, column=0, padx=3, pady=3, sticky="w")
        Label(master, text="Anzahl Nachkommastellen").grid(row=1, column=0, padx=3, pady=3, sticky="w")
        self.entry_round = Entry(master,textvariable=self.int_round, validate="all", validatecommand=self.callback_int, width=5)
        self.entry_round.grid(row=1, column=1, padx=3, pady=3, sticky="e")
        self.init_chkb()
        self.callback_chkb()

    def callback_chkb(self):
        if self.bool_roundActive.get() == 1:
            self.entry_round.config(state="normal")
            self.controller.bool_roundActive = True
        else:
            self.entry_round.config(state="disabled")
            self.controller.bool_roundActive = False

    def callback_int(self, *args):
        if self.int_round.get() != "":
            self.controller.int_round = int(self.int_round.get())

    def init_chkb(self):
        if self.controller.bool_roundActive:
            self.chkb.select()
        else:
            self.chkb.deselect()
Esempio n. 16
0
class SpinBox(Widget):
    def __init__(self, master, **kwargs):
        self._textv = StringVar()
        super().__init__(tk=ttk.Spinbox(master, textvariable=self._textv),
                         **kwargs)
        self._setter = self.connect_to_prop("value", self.on_changed_value)
        self._trace = self._textv.trace_add(
            "write", lambda *_: self._setter(self._textv.get()))

    def on_changed_value(self, value):
        if value:
            self._textv.set(value)

    def on_disposed(self):
        self._textv.trace_remove("write", self._trace)
        self._setter = None
Esempio n. 17
0
class CalendarWidget(Widget):
    def __init__(self, master, **kwargs):
        self._textv = StringVar()
        super().__init__(tk=Datepicker(master,
                                       datevar=self._textv,
                                       dateformat=kwargs.get("node").get_attr(
                                           "dateformat", "%Y-%m-%d")),
                         **kwargs)

        self._setter = self.connect_to_prop("value", self.on_changed_value)
        self._trace = self._textv.trace_add(
            "write", lambda *_: self._setter(self._textv.get()))

    def on_changed_value(self, value):
        self._textv.set(value)

    def on_disposed(self):
        self._textv.trace_remove("write", self._trace)
        self._setter = None
Esempio n. 18
0
class RadioButton(Widget):
    def __init__(self, master, **kwargs):
        self._v = StringVar()
        super().__init__(tk=ttk.Radiobutton(master, variable=self._v),
                         **kwargs)
        self._setter = self.connect_to_prop("value", self.on_changed_value)
        self.connect_to_prop("sel", self.on_changed_sel)
        self._trace = self._v.trace_add("write",
                                        lambda *_: self._setter(self._v.get()))

    def on_changed_value(self, value):
        self._v.set(value)

    def on_changed_sel(self, value):
        self._tk.config(value=value)

    def on_disposed(self):
        self._v.trace_remove("write", self._trace)
        self._trace = None
        self._setter = None
Esempio n. 19
0
class ComboBox(Widget):
    def __init__(self, master, **kwargs):
        self._textv = StringVar()
        super().__init__(tk=AutocompleteCombobox(master=master,
                                                 textvariable=self._textv),
                         **kwargs)

        self._setter = self.connect_to_prop("value", self.on_changed_value)
        self._trace = self._textv.trace_add(
            "write", lambda *_: self._setter(self._textv.get()))
        self.connect_to_prop("suggestion", self.on_changed_suggestion)

    def on_changed_suggestion(self, value):
        if self._textv.get() == None or self._textv.get() == "":
            if value and len(value) > 0:
                self._setter(value[0])
        self._tk.set_completion_list(value if value else [])

    def on_changed_value(self, value):
        self._textv.set(value)

    def on_disposed(self):
        self._textv.trace_remove("write", self._trace)
        self._setter = None
Esempio n. 20
0
 def _user_form(self, row: int, col: int, padx: int, pady: int, width: int) -> None:
     Label(self._root_frame, text='Requirements Document')\
         .grid(row=row, column=col, columnspan=2, sticky='w', padx=padx, pady=pady)
     self._entry_box(
         'First Name', '^[A-Za-z]*$',
         width, row + 1, col, padx, pady,
         format_regex='^[A-Za-z]+$',
         format_message='First name is invalid.',
         required=True)
     self._entry_box(
         'Last Name', '^[A-Za-z]*$',
         width, row + 1, col + 1, padx, pady,
         format_regex='^[A-Za-z]+$',
         format_message='Last name is invalid.',
         required=True)
     self._entry_box(
         'Current Company',
         "(?!.*?  )(?!.*?\.\.)(?!.*?'')(?!.*? \.)(?!.*? ')(?!.*?\.')(?!.*?'\.)^[0-9A-Za-z \.']*$",
         width, row + 3, col, padx, pady,
         format_regex="(?!.*?  )(?!.*?\.\.)(?!.*?'')(?!.*? \.)(?!.*? ')(?!.*?\.')(?!.*?'\.)^[0-9A-Za-z][0-9A-Za-z \.']+[0-9A-Za-z]$",
         format_message='Company is invalid.')
     self._entry_box(
         'Current Job Title',
         "(?!.*?  )(?!.*?\.\.)(?!.*?'')(?!.*? \.)(?!.*? ')(?!.*?\.')(?!.*?'\.)^[0-9A-Za-z \.']*$",
         width, row + 3, col + 1, padx, pady,
         format_regex="(?!.*?  )(?!.*?\.\.)(?!.*?'')(?!.*? \.)(?!.*? ')(?!.*?\.')(?!.*?'\.)^[0-9A-Za-z][0-9A-Za-z \.']+[0-9A-Za-z]$",
         format_message='Job title is invalid.')
     self._entry_box(
         'Email Address',
         '(?!.*?\.\.)(?!.*?@\.)(?!.*?\.@)^[0-9a-z]{0,1}[0-9a-z\.]*[0-9a-z]*@{0,1}[a-z]*\.{0,1}[a-z]*$',
         width, row + 5, col, padx, pady,
         format_regex='^[a-z0-9/.]+@[a-z]+\.[a-z]+$',
         format_message='Invalid email address.',
         required=True)
     self._entry_box(
         'Phone Number', '^[0-9]{0,10}$',
         width, row + 5, col + 1, padx, pady,
         format_regex='^[0-9]{10}$',
         format_message='Must be 10 digits.',
         required=True)
     self._entry_box('LinkedIn', '', width, row + 7, col, padx, pady)
     self._entry_box('Website', '', width, row + 7, col + 1, padx, pady)
     self._entry_box(
         'Street Address',
         '(?!.*?  )(?!.*?,,)(?!.*? ,)^[0-9]*[0-9A-Za-z ,]*$',
         width, row + 9, col, padx, pady,
         format_regex='(?!.*?  )(?!.*?,,)(?!.*? ,)^[0-9]+ [0-9A-Za-z ]+[A-Za-z]$',
         format_message='Invalid street address.')
     self._entry_box(
         'City',
         '(?!.*?  )^[A-Za-z]*[A-Za-z ]*$',
         width, row + 9, col + 1, padx, pady,
         format_regex='(?!.*?  )^[A-Za-z][A-Za-z ]+[A-Za-z]$',
         format_message='City is invalid.',
         required=True)
     self._entry_box(
         'State',
         '(?!.*?  )^[A-Za-z]*[A-Za-z ]*$',
         width, row + 11, col, padx, pady,
         format_regex='(?!.*?  )^[A-Za-z][A-Za-z ]+[A-Za-z]$',
         format_message='State is invalid.',
         required=True)
     self._entry_box(
         'Postal Code',
         '^[0-9]*$',
         width, row + 11, col + 1, padx, pady,
         format_regex='^[0-9]+$',
         format_message='Invalid postal code.',
         required=True)
     self._entry_box(
         'Country',
         '(?!.*?  )^[A-Za-z]*[A-Za-z ]*$',
         width, row + 13, col, padx, pady,
         format_regex='(?!.*?  )^[A-Za-z][A-Za-z ]+[A-Za-z]$',
         format_message='Invalid country.',
         required=True)
     self._entry_box(
         'Country Code',
         '^[0-9]*$',
         width, row + 13, col + 1, padx, pady,
         format_regex='^[0-9]+$',
         format_message='Invalid country code.')
     self._entry_box(
         'Highest Education',
         "(?!.*?  )(?!.*?'')^[A-Za-z]{0,1}[A-Za-z ']*$",
         width, row + 15, col, padx, pady,
         format_regex="(?!.*?  )(?!.*?'')^[A-Za-z]{0,1}[A-Za-z ']+[A-Za-z]$",
         format_message='Invalid education',
         required=True)
     self._entry_box(
         'Clearance',
         '(?!.*?  )^[A-Za-z]{0,1}[A-Za-z ]*$',
         width, row + 15, col + 1, padx, pady,
         format_regex='(?!.*?  )^[A-Za-z]{0,1}[A-Za-z ]+[A-Za-z]$',
         format_message='Invalid clearance.')
     self._add_and_remove_widget(
         'Skills/Experience', ['Skill', 'Experience'], self._skill_entry_list,
         width, row, col + 2, padx, pady, 3, required=True)
     self._add_and_remove_widget(
         'Languages', ['Language'], self._lang_entry_list,
         width, row, col + 4, padx, pady, 3)
     self._add_and_remove_widget(
         'Cert/Lic', ['Cert/License'], self._cert_entry_list,
         width, row, col + 5, padx, pady, 3)
     self._entry_box(
         'Desired Salary',
         '^[0-9]*$',
         width, row + 7, col + 2, padx, pady,
         format_regex='^[0-9]+$',
         format_message='Invalid salary.')
     self._entry_box(
         'Salary Type', '',
         width, row + 7, col + 3, padx, pady, disable=True)
     self._entry_box(
         'Currency', '',
         width, row + 7, col + 4, padx, pady)
     self._entry_box(
         'Employment Type', '',
         width, row + 9, col + 2, padx, pady)
     self._entry_box(
         'Hours per Week', '',
         width, row + 9, col + 3, padx, pady)
     self._entry_box(
         'Start Date', '',
         width, row + 9, col + 4, padx, pady)
     self._entry_box(
         'Interview Date & Time', '',
         width, row + 9, col + 5, padx, pady)
     self._user_input['18 Years or Older'] = IntVar()
     self._user_input['18 Years or Older'].set(1)
     Checkbutton(
         self._root_frame, text='18 Years or Older',
         variable=self._user_input['18 Years or Older'])\
         .grid(row=row + 11, column=col + 2, sticky='w', padx=padx, pady=pady)
     self._user_input['Req. Sponsorship'] = IntVar()
     Checkbutton(
         self._root_frame, text='Req. Sponsorship',
         variable=self._user_input['Req. Sponsorship']) \
         .grid(row=row + 11, column=col + 3, sticky='w', padx=padx, pady=pady)
     self._user_input['Eligible to Work'] = IntVar()
     self._user_input['Eligible to Work'].set(1)
     Checkbutton(
         self._root_frame, text='Eligible to Work',
         variable=self._user_input['Eligible to Work']) \
         .grid(row=row + 11, column=col + 4, sticky='w', padx=padx, pady=pady)
     self._user_input['Remote'] = BooleanVar()
     Checkbutton(
         self._root_frame, text='Remote',
         variable=self._user_input['Remote']) \
         .grid(row=row + 11, column=col + 5, sticky='w', padx=padx, pady=pady)
     self._entry_box(
         'Search Job(s) (Comma Separated)', '',
         width, row + 13, col + 2, padx, pady, colspan=4, sticky='we', required=True)
     self._user_input['Word(s) or Phrase(s) to Avoid (Comma Separated)'] = StringVar()
     self._entry_box(
         'Word(s) or Phrase(s) to Avoid (Comma Separated)', '',
         width, row + 15, col + 2, padx, pady, colspan=4, sticky='we')
     self._user_input['Companies to Avoid (Comma Separated)'] = StringVar()
     self._entry_box(
         'Companies to Avoid (Comma Separated)', '',
         width, row + 17, col + 2, padx, pady, colspan=4, sticky='we')
     self._entry_box(
         'Search State(s)/Region(s) (Comma Separated)', '',
         width, row + 21, col + 2, padx, pady, colspan=4, sticky='we')
     search_country_var = StringVar()
     search_country_var.set('Search Country       ▼')
     search_country = OptionMenu(
         self._root_frame,
         search_country_var,
         *list(map(lambda s: s.title(), IndeedCrawler()._map_country.keys())),
         command=lambda *args: self._required_input['Search Country'].set(search_country_var.get()))
     search_country_var.trace_add(
         'write', lambda *args: search_country.config(fg='black', activeforeground='black'))
     search_country.grid(row=row + 21, column=col + 1)
     search_country.config(
         background='yellow', activebackground='yellow', cursor='hand2', anchor='w',
         direction='below', activeforeground='grey', fg='grey', relief='sunken',
         borderwidth=1, width=19, pady=2, indicatoron=False)
     self._entry_box(
         'Indeed Login', '',
         width, row + 17, col, padx, pady, required=True)
     self._entry_box(
         'Indeed Password', '',
         width, row + 17, col + 1, padx, pady, secure=True, required=True)
     self._entry_box(
         'Number of Jobs', '',
         width, row + 21, col, padx, pady, required=True)
     image = ImageTk.PhotoImage(Image.open(COOL_GLASSES_EMOJI))
     self._start_button.image = image
     self._start_button.configure(image=image)
     self._start_button.grid(row=row + 24, column=col + 5, sticky='e', padx=padx, pady=pady)
     Button(self._root_frame, text='Reset Cache', command=self._reset_cache)\
         .grid(row=row + 24, column=col + 4, columnspan=2, padx=padx, pady=pady)
     return None
Esempio n. 21
0
class FenetreDeConfiguration(Toplevel):
    def __init__(self, master=None):

        Toplevel.__init__(self, master)
        self.title("Nouvelle partie")
        self.configure(bg="light grey")
        self.protocol("WM_DELETE_WINDOW", self.quitter)

        # Variables

        self.nom_blancs = StringVar()
        self.nom_blancs.set("Blancs")
        self.nom_noirs = StringVar()
        self.nom_noirs.set("Noirs")

        self.chrono_selectionne = BooleanVar()
        self.temps = StringVar()
        self.temps.trace_add("write", self.valider_temps)
        self.temps.set("")
        self.aide_selectionnee = BooleanVar()

        self.jouer_contre_ordinateur = BooleanVar()
        self.jouer_contre_ordinateur.set("False")

        # Widgets

        self.fonte = ("default", 25, "normal")

        self.nom_blancs_label = Label(self,
                                      text="Nom du joueur blanc:  ",
                                      font=self.fonte,
                                      bg="light grey")
        self.nom_noirs_label = Label(self,
                                     text="Nom du joueur noir:  ",
                                     font=self.fonte,
                                     bg="light grey")
        self.type_noirs_label = Label(self,
                                      text="Le joueur noir sera:  ",
                                      font=self.fonte,
                                      bg="light grey")
        self.check_chrono = Checkbutton(self,
                                        text="Partie chronométrée",
                                        variable=self.chrono_selectionne,
                                        font=self.fonte,
                                        bg="light grey",
                                        command=self.chrono)
        self.check_aide = Checkbutton(self,
                                      text="Montrer les mouvements permis",
                                      variable=self.aide_selectionnee,
                                      font=self.fonte,
                                      bg="light grey")
        self.nom_blancs_entry = Entry(self,
                                      textvariable=self.nom_blancs,
                                      font=self.fonte,
                                      bg="light grey")
        self.nom_noirs_entry = Entry(self,
                                     textvariable=self.nom_noirs,
                                     state=NORMAL,
                                     font=self.fonte,
                                     bg="light grey")
        self.ordi_radio = Radiobutton(self,
                                      text="L'ordinateur",
                                      variable=self.jouer_contre_ordinateur,
                                      value=True,
                                      font=self.fonte,
                                      bg="light grey",
                                      command=self.ordi_radio_selectionne)
        self.humain_radio = Radiobutton(self,
                                        text="Un humain",
                                        variable=self.jouer_contre_ordinateur,
                                        value=False,
                                        font=self.fonte,
                                        bg="light grey",
                                        command=self.humain_radio_selectionne)
        self.chrono_entry = Entry(self,
                                  textvariable=self.temps,
                                  state=DISABLED,
                                  font=self.fonte,
                                  bg="light grey",
                                  width=4)
        self.chrono_label = Label(self,
                                  text="minutes (1-60)",
                                  font=self.fonte,
                                  bg="light grey")
        self.jouer_button = Button(self,
                                   text="JOUER!",
                                   font=self.fonte,
                                   bg="light grey",
                                   command=self.bouton_jouer_presse)

        self.nom_blancs_label.grid(row=1, column=1, sticky=W, pady=10, padx=10)
        self.type_noirs_label.grid(row=2, column=1, sticky=W, pady=10, padx=10)
        self.nom_noirs_label.grid(row=3, column=1, sticky=W, pady=10, padx=10)
        self.check_chrono.grid(row=4, column=1, sticky=W, pady=10, padx=10)
        self.check_aide.grid(row=5, column=1, sticky=W, pady=10, padx=10)
        self.nom_blancs_entry.grid(row=1,
                                   column=2,
                                   columnspan=2,
                                   sticky=EW,
                                   pady=10,
                                   padx=10)
        self.ordi_radio.grid(row=2, column=2, sticky=W, pady=10, padx=10)
        self.humain_radio.grid(row=2, column=3, sticky=W, pady=10, padx=10)
        self.nom_noirs_entry.grid(row=3,
                                  column=2,
                                  columnspan=2,
                                  sticky=EW,
                                  pady=10,
                                  padx=10)
        self.chrono_entry.grid(row=4, column=2, sticky=E, pady=10, padx=10)
        self.chrono_label.grid(row=4, column=3, sticky=W, pady=10, padx=10)
        self.jouer_button.grid(row=6, column=1, columnspan=3, pady=10, padx=10)

        self.options = {}

    def quitter(self):
        self.master.bienvenue_apparait()
        self.destroy()

    def chrono(self):
        if self.chrono_selectionne.get():
            self.temps.set("20")
            self.chrono_entry.config(state=NORMAL,
                                     validate=ALL,
                                     validatecommand=self.valider_temps())
        else:
            self.temps.set("")
            self.chrono_entry.config(state=DISABLED)

    def ordi_radio_selectionne(self):

        self.nom_noirs_entry.config(state=DISABLED)
        self.nom_noirs.set("Sunfish")
        print(self.jouer_contre_ordinateur.get())

    def humain_radio_selectionne(self):

        self.nom_noirs_entry.config(state=NORMAL)
        self.nom_noirs.set("")
        print(self.jouer_contre_ordinateur.get())

    def bouton_jouer_presse(self):

        self.withdraw()

        if self.nom_blancs.get() == "":
            self.options["nom_blancs"] = "Blancs"
        else:
            self.options["nom_blancs"] = self.nom_blancs.get()

        if self.jouer_contre_ordinateur.get():
            self.options["ordinateur"] = True
            self.options["nom_noirs"] = "Sunfish"
        else:
            self.options["ordinateur"] = False
            if self.nom_noirs.get() == "":
                self.options["nom_noirs"] = "Noirs"
            else:
                self.options["nom_noirs"] = self.nom_noirs.get()

        self.options["aide"] = self.aide_selectionnee.get()

        self.options["chrono"] = self.chrono_selectionne.get()
        if self.options["chrono"]:
            self.options["temps"] = int(self.temps.get())
        else:
            self.options["temps"] = 0

        if self.options["ordinateur"]:
            self.master.controleur = ControleurDePartieContre(
                adversaire=ControleurDeSunfish(),
                couleur_joueur="blanc",
                master=self.master,
                dictionnaire=None,
                nom_blancs=self.options["nom_blancs"],
                nom_noirs=self.options["nom_noirs"],
                option_aide=self.options["aide"],
                option_chrono=self.options["temps"])
        else:
            self.master.controleur = ControleurDePartie(
                master=self.master,
                dictionnaire=None,
                nom_blancs=self.options["nom_blancs"],
                nom_noirs=self.options["nom_noirs"],
                option_aide=self.options["aide"],
                option_chrono=self.options["temps"])

    def valider_temps(self, *args):

        try:
            v = int(self.temps.get())
        except ValueError:
            self.temps.set("")
            return False
        else:
            if 0 < v < 60:
                return True
            self.temps.set("")
            return False
Esempio n. 22
0
class SetupWizard:
    def __init__(self):
        self.config_data = {"LastAction": "NONE", "ActionTime": "NONE"}
        self.master = Tk()
        self.master.minsize(400, 165)
        self.master.maxsize(400, 165)
        self.master.title("Addon Sync Setup")
        self.master.iconbitmap("sync_icon.ico")
        self.location_screen()
        self.master.mainloop()

    def write_config_data(self):
        with open("config.json", "w+") as f:
            f.write(json.dumps(self.config_data))

        f.close()

    def select_callback(self):
        self.path_to_wow = filedialog.askdirectory()
        self.directory_textbox.delete(0, END)
        self.directory_textbox.insert(END, self.path_to_wow)

    def location_next_callback(self):
        self.active_frame.destroy()
        self.create_settings_screen()

    def create_repo_callback(self):
        self.active_frame.destroy()
        self.create_repo_screen()

    def repo_back_callback(self):
        self.active_frame.destroy()
        self.settings_screen()

    def create_repo_back_callback(self):
        self.active_frame.destroy()
        self.repo_screen()

    def settings_back_callback(self):
        self.active_frame.destroy()
        self.location_screen()

    def repo_next_callbak(self):
        self.config_data["RepoURL"] = self.repo_textbox.get()
        self.write_config_data()
        self.master.destroy()

    def settings_next_callback(self):
        self.load_settings_into_config()
        self.active_frame.destroy()
        self.repo_screen()

    def load_settings_into_config(self):
        checkbox_data = []
        if self.addons_var.get() == 1:
            checkbox_data.append("Addons")
        if self.addon_settings_var.get() == 1:
            checkbox_data.append("AddonSettings")
        if self.general_settings_var.get() == 1:
            checkbox_data.append("GeneralSettings")
        if self.interface_settings_var.get() == 1:
            checkbox_data.append("InterfaceSettings")
        if self.addons_enabled_var.get() == 1:
            checkbox_data.append("AddonsEnabled")
        if self.keybinds_var.get() == 1:
            checkbox_data.append("Keybinds")
        self.config_data["ObjectsTracked"] = checkbox_data

    def directory_callback(self, var_name, var_index, operation):
        self.path_to_wow = self.directory_textbox.get()
        if self.path_to_wow:
            self.location_next_button.config(state=NORMAL)
        else:
            self.location_next_button.config(state=DISABLED)

    def repo_callback(self, var_name, var_index, operation):
        self.path_to_repo = self.repo_textbox.get()
        if self.path_to_repo:
            self.repo_next_button.config(state=NORMAL)
        else:
            self.repo_next_button.config(state=DISABLED)

    def location_screen(self):
        self.master.minsize(400, 165)
        self.master.maxsize(400, 165)
        self.active_frame = Frame(self.master)

        self.location_label = Label(
            self.active_frame,
            text=
            "Please select the location of your World of Warcraft installation.",
        )
        self.location_label.grid(row=0, columnspan=2, pady=10)

        self.directory_button = Button(self.active_frame,
                                       text="Select Folder",
                                       command=self.select_callback)
        self.directory_button.grid(row=1, column=0, sticky=E, padx=5)

        self.directory_string = StringVar()
        self.directory_string.trace_add("write", self.directory_callback)

        self.directory_textbox = Entry(self.active_frame,
                                       width=48,
                                       textvariable=self.directory_string)
        self.directory_textbox.grid(row=1, column=1, sticky=W, pady=30)

        self.location_next_button = Button(
            self.active_frame,
            text="Next",
            command=self.location_next_callback,
            state="disabled",
        )
        self.location_next_button.grid(row=2, column=0, columnspan=2, pady=10)

        self.active_frame.grid()

    def repo_screen(self):
        self.master.minsize(400, 165)
        self.master.maxsize(400, 165)
        self.active_frame = Frame(self.master)

        repo_label = Label(
            self.active_frame,
            text="Please type in the address of your github repo.")
        repo_label.grid(row=0, pady=30)

        self.repo_string = StringVar()
        self.repo_string.trace_add("write", self.repo_callback)

        self.repo_textbox = Entry(self.active_frame,
                                  width=62,
                                  textvariable=self.repo_string)
        self.repo_textbox.grid(row=1, padx=10, pady=0)

        self.repo_next_button = Button(
            self.active_frame,
            text="Next",
            command=self.repo_next_callbak,
            state="disabled",
        )
        self.repo_next_button.grid(row=2,
                                   column=0,
                                   pady=30,
                                   sticky=W,
                                   padx=100)

        self.back_button = Button(self.active_frame,
                                  text="Back",
                                  command=self.repo_back_callback)
        self.back_button.grid(row=2, column=0, sticky=W, pady=30, padx=150)

        no_repo_button = Button(
            self.active_frame,
            text="I don't have a repo",
            command=self.create_repo_callback,
        )
        no_repo_button.grid(row=2, pady=30, column=0, sticky=E, padx=85)

        self.active_frame.grid()

    def create_repo_screen(self):
        self.master.minsize(425, 190)
        self.master.maxsize(425, 190)
        self.active_frame = Frame(self.master)

        create_repo_label = Label(
            self.active_frame,
            text=
            "In order for this program to work you will need to set up a github repository.\nThis is free and should only take a few minutes.",
        )
        create_repo_label.grid(row=0, pady=10)

        repo_instructions_label = Label(
            self.active_frame,
            text=
            "1. Go to github.com and create an account, or if you already have one, login.\n2. Go to github.com/new and create a new repo with any name you want.\n3. Copy and paste the repo URL into the box below.",
        )
        repo_instructions_label.grid(row=1, pady=10)

        self.repo_string = StringVar()
        self.repo_string.trace_add("write", self.repo_callback)

        self.repo_textbox = Entry(self.active_frame,
                                  width=65,
                                  textvariable=self.repo_string)
        self.repo_textbox.grid(row=2, padx=10)

        self.repo_next_button = Button(
            self.active_frame,
            text="Next",
            command=self.repo_next_callbak,
            state="disabled",
        )
        self.repo_next_button.grid(row=3, sticky=W, pady=10, padx=140)

        self.back_button = Button(self.active_frame,
                                  text="Back",
                                  command=self.create_repo_back_callback)
        self.back_button.grid(row=3, sticky=E, pady=10, padx=140)

        self.active_frame.grid()

    def create_settings_screen(self):
        self.master.minsize(400, 165)
        self.master.maxsize(400, 165)
        self.active_frame = Frame(self.master)

        settings_info_label = Label(
            self.active_frame,
            text="Please select the data you would like to keep in sync.",
        )
        settings_info_label.grid(row=0,
                                 pady=10,
                                 padx=60,
                                 columnspan=2,
                                 sticky=W)

        self.addons_var = IntVar()
        self.addons_var.set(1)
        addons_checkbox = Checkbutton(self.active_frame,
                                      text="Addons",
                                      variable=self.addons_var)
        addons_checkbox.grid(row=1, column=0, sticky=W, padx=20)

        self.addon_settings_var = IntVar()
        addon_settings_checkbox = Checkbutton(self.active_frame,
                                              text="Addon Settings",
                                              variable=self.addon_settings_var)
        addon_settings_checkbox.grid(row=2, column=0, sticky=W, padx=20)

        self.general_settings_var = IntVar()
        general_settings_checkbox = Checkbutton(
            self.active_frame,
            text="Game Settings",
            variable=self.general_settings_var)
        general_settings_checkbox.grid(row=3, column=0, sticky=W, padx=20)

        self.chat_settings_var = IntVar()
        interface_settings_checkbox = Checkbutton(
            self.active_frame,
            text="Interface Settings",
            variable=self.chat_settings_var)
        interface_settings_checkbox.grid(row=1, column=1, sticky=W)

        self.addons_enabled_var = IntVar()
        addons_enabled_checkbox = Checkbutton(self.active_frame,
                                              text="Addons Enabled/Disabled",
                                              variable=self.addons_enabled_var)
        addons_enabled_checkbox.grid(row=2, column=1, sticky=W)

        self.keybinds_var = IntVar()
        keybinds_checkbox = Checkbutton(self.active_frame,
                                        text="Keybindings",
                                        variable=self.keybinds_var)
        keybinds_checkbox.grid(row=3, column=1, sticky=W)

        self.settings_next_button = Button(
            self.active_frame,
            text="Next",
            command=self.settings_next_callback,
        )
        self.settings_next_button.grid(row=4,
                                       columnspan=2,
                                       pady=15,
                                       padx=210,
                                       sticky=W)

        self.back_button = Button(
            self.active_frame,
            text="Back",
            command=self.settings_back_callback,
        )
        self.back_button.grid(row=4, columnspan=2, pady=15, padx=140, sticky=W)

        self.active_frame.grid()
Esempio n. 23
0
class BaseWidget(Toplevel):
    def __init__(self, name, master=None, **kw):
        """
        Create a  desktop widget that sticks on the desktop.
        """
        Toplevel.__init__(self, master)
        self.name = name
        if CONFIG.getboolean('General', 'splash_supported', fallback=True):
            self.attributes('-type', 'splash')
        else:
            self.attributes('-type', 'toolbar')

        self.style = Style(self)

        self._position = StringVar(self, CONFIG.get(self.name, 'position'))
        self._position.trace_add(
            'write',
            lambda *x: CONFIG.set(self.name, 'position', self._position.get()))

        self.ewmh = EWMH()
        self.title('scheduler.{}'.format(self.name.lower()))

        self.withdraw()

        # control main menu checkbutton
        self.variable = BooleanVar(self, False)

        # --- menu
        self.menu = Menu(self, relief='sunken', activeborderwidth=0)
        self._populate_menu()

        self.create_content(**kw)

        self.x = None
        self.y = None

        # --- geometry
        geometry = CONFIG.get(self.name, 'geometry')
        self.update_idletasks()
        if geometry:
            self.geometry(geometry)
        self.update_idletasks()

        if CONFIG.getboolean(self.name, 'visible', fallback=True):
            self.show()

        # --- bindings
        self.bind('<Configure>', self._on_configure)

    def create_content(self):
        # to be overriden by subclass
        pass

    def _populate_menu(self):
        self.menu_pos = Menu(self.menu, relief='sunken', activeborderwidth=0)
        self.menu_pos.add_radiobutton(label=_('Normal'),
                                      value='normal',
                                      variable=self._position,
                                      command=self.show)
        self.menu_pos.add_radiobutton(label=_('Above'),
                                      value='above',
                                      variable=self._position,
                                      command=self.show)
        self.menu_pos.add_radiobutton(label=_('Below'),
                                      value='below',
                                      variable=self._position,
                                      command=self.show)
        self.menu.add_cascade(label=_('Position'), menu=self.menu_pos)
        self.menu.add_command(label=_('Hide'), command=self.hide)

    def update_style(self):
        bg = CONFIG.get(self.name, 'background', fallback='grey10')
        fg = CONFIG.get(self.name, 'foreground', fallback='white')
        active_bg = active_color(*self.winfo_rgb(bg))
        self.attributes('-alpha', CONFIG.get(self.name, 'alpha',
                                             fallback=0.85))
        self.configure(bg=bg)
        self.menu.configure(bg=bg,
                            fg=fg,
                            selectcolor=fg,
                            activeforeground=fg,
                            activebackground=active_bg)
        self.menu_pos.configure(bg=bg,
                                fg=fg,
                                selectcolor=fg,
                                activeforeground=fg,
                                activebackground=active_bg)

    def update_position(self):
        if self._position.get() == 'normal':
            if CONFIG.getboolean('General', 'splash_supported', fallback=True):
                self.attributes('-type', 'splash')
            else:
                self.attributes('-type', 'toolbar')
        if self.variable.get():
            self.withdraw()
            self.deiconify()

    def _on_configure(self, event):
        CONFIG.set(self.name, 'geometry', self.geometry())
        save_config()

    def hide(self):
        CONFIG.set(self.name, 'visible', 'False')
        self.variable.set(False)
        save_config()
        self.withdraw()

    def show(self):
        ''' make widget sticky '''
        self.deiconify()
        self.update_idletasks()
        splash_supp = CONFIG.getboolean('General',
                                        'splash_supported',
                                        fallback=True)
        try:
            pos = self._position.get()
            for w in self.ewmh.getClientList():
                if w.get_wm_name() == self.title():
                    if pos == 'above':
                        self.attributes('-type', 'dock')
                        self.ewmh.setWmState(w, 1, '_NET_WM_STATE_ABOVE')
                        self.ewmh.setWmState(w, 0, '_NET_WM_STATE_BELOW')
                        self.ewmh.setWmState(w, 1, '_NET_WM_STATE_STICKY')
                        self.ewmh.setWmState(w, 1,
                                             '_NET_WM_STATE_SKIP_TASKBAR')
                        self.ewmh.setWmState(w, 1, '_NET_WM_STATE_SKIP_PAGER')
                    elif pos == 'below':
                        self.attributes('-type', 'desktop')
                        self.ewmh.setWmState(w, 0, '_NET_WM_STATE_ABOVE')
                        self.ewmh.setWmState(w, 1, '_NET_WM_STATE_BELOW')
                        self.ewmh.setWmState(w, 1, '_NET_WM_STATE_STICKY')
                        self.ewmh.setWmState(w, 1,
                                             '_NET_WM_STATE_SKIP_TASKBAR')
                        self.ewmh.setWmState(w, 1, '_NET_WM_STATE_SKIP_PAGER')
                    else:
                        if splash_supp:
                            self.attributes('-type', 'splash')
                        else:
                            self.attributes('-type', 'toolbar')
                        self.ewmh.setWmState(w, 0, '_NET_WM_STATE_BELOW')
                        self.ewmh.setWmState(w, 0, '_NET_WM_STATE_ABOVE')
                        self.ewmh.setWmState(w, 1, '_NET_WM_STATE_STICKY')
                        self.ewmh.setWmState(w, 1,
                                             '_NET_WM_STATE_SKIP_TASKBAR')
                        self.ewmh.setWmState(w, 1, '_NET_WM_STATE_SKIP_PAGER')
            self.ewmh.display.flush()
            if not splash_supp:
                self.withdraw()
                self.deiconify()
            CONFIG.set(self.name, 'visible', 'True')
            self.variable.set(True)
            save_config()
        except TypeError:
            pass

    def _start_move(self, event):
        self.x = event.x
        self.y = event.y

    def _stop_move(self, event):
        self.x = None
        self.y = None
        self.configure(cursor='arrow')

    def _move(self, event):
        if self.x is not None and self.y is not None:
            self.configure(cursor='fleur')
            deltax = event.x - self.x
            deltay = event.y - self.y
            x = self.winfo_x() + deltax
            y = self.winfo_y() + deltay
            self.geometry("+%s+%s" % (x, y))
Esempio n. 24
0
class FPLGUI:
    
    SPLASH_WIDTH = 350
    SPLASH_HEIGHT = 250
    
    def __init__(self):
        # Get database folder.
        self.srcDir = os.path.dirname(os.path.abspath(__file__))
        self.databaseDir = os.path.join(os.path.dirname(self.srcDir),'database')
        self.supportFilesDir = os.path.join(os.path.dirname(self.srcDir),'supportFiles')
        
        # Create Database folder.
        if not os.path.isdir(self.databaseDir):
            os.makedirs(self.databaseDir)
        
        # Show splash
        splashWindow = Tk()
        splashWindow.title('FPLGUI')
        self.screenWidth = splashWindow.winfo_screenwidth() # width of the screen
        self.screenHeight = splashWindow.winfo_screenheight() # height of the screen
        x = round((self.screenWidth/2) - (self.SPLASH_WIDTH/2))
        y = round((self.screenHeight/2) - (self.SPLASH_HEIGHT/2))
        splashWindow.geometry('{}x{}+{}+{}'.format(self.SPLASH_WIDTH,self.SPLASH_HEIGHT,x,y))
        splashWindow.resizable(0, 0)
        splashWindow.iconbitmap(os.path.join(self.supportFilesDir,'FPLGUI.ico'))
        Label(splashWindow,text="Loading Navdata, Please wait.",justify='left',font=("Helvetica", 14)).place(relx=0.1,rely=0.1,anchor='nw')
        with open(os.path.join(self.supportFilesDir,'startupMessage.txt')) as startupFile:
            Label(splashWindow, text=startupFile.read(),justify='left',font=("Helvetica", 8)).place(relx=0.1, rely=0.4, anchor='nw')
        splashWindow.update()
        
        # check options for X-Plane directory
        self.getOptions()
        
        # Let user select X-Plane dir and write options.
        if self.xPlaneDir is None:
            OptionsWindow(splashWindow,self.databaseDir,'FPLGUI: Set inital options')
            self.getOptions()
            
        while self.xPlaneDir is None:
            showwarning('XplaneDir not specified', 'XplaneDir is mandatory. Specify it first!')
            OptionsWindow(splashWindow,self.databaseDir,'FPLGUI: Set inital options')
            self.getOptions()
        
        # Get navdata folder.
        if os.path.exists(os.path.join(self.xPlaneDir,'Custom Data','earth_fix.dat')) and \
           os.path.exists(os.path.join(self.xPlaneDir,'Custom Data','earth_nav.dat')) and \
           os.path.exists(os.path.join(self.xPlaneDir,'Custom Data','earth_awy.dat')) and \
           os.path.exists(os.path.join(self.xPlaneDir,'Custom Data','apt.csv')):
            self.navdataDir = os.path.join(self.xPlaneDir,'Custom Data')
        else:
            self.navdataDir = os.path.join(self.xPlaneDir,'Resources','default data')
        
        #inititalize Fpl-object
        self.fplPath = os.path.join(self.xPlaneDir,'Resources\\plugins\\X-IvAp Resources\\Flightplans')
        self.fpl = Fpl(self.fplPath)
        
        # Load Fixes
#         self.fpl.getFixes(os.path.join(self.navdataDir,'earth_fix.dat'))
#         self.fpl.getNavaids(os.path.join(self.navdataDir,'earth_nav.dat'))
#         self.fpl.getAirports(os.path.join(self.navdataDir,'apt.csv'))
#         self.fpl.getAirways(os.path.join(self.navdataDir,'earth_awy.dat'))
        
        # Remove Splash.
        splashWindow.destroy()
        
        # Create main window
        self.master = Tk()
        self.master.title('FPLGUI')
        self.master.resizable(0, 0)
        
        ## menu ##
        menubar = Menu(self.master)
        
        filemenu = Menu(menubar,tearoff=0)
        filemenu.add_command(label="Clear",command=self.clear)
        filemenu.add_command(label="Send to XP",command=self.send)
        filemenu.add_separator()
        filemenu.add_command(label="Load",command=self.load)
        filemenu.add_command(label="Save",command=self.save)
        filemenu.add_separator()
        filemenu.add_command(label="Exit",command=self.master.quit)
        menubar.add_cascade(label="File", menu=filemenu)
        
        acmenu = Menu(menubar,tearoff=0)
        acmenu.add_command(label="Load Template",command=self.acLoad)
        acmenu.add_command(label="Save Template",command=self.acSave)
        menubar.add_cascade(label="Aircraft", menu=acmenu)
        
        utilmenu = Menu(menubar,tearoff=0)
        utilmenu.add_command(label="Import Route",command=self.importRoute)
        utilmenu.add_separator()
        utilmenu.add_command(label="Open Simbrief",command=self.simbrief)
        utilmenu.add_command(label="Simbrief process",command= lambda: self.simbrief(True))
        utilmenu.add_command(label="Flightaware",command=self.flightaware)
        utilmenu.add_separator()
        utilmenu.add_command(label="Show at Skyvector",command=self.showSkyvector)
        utilmenu.add_command(label="Export to X-Plane",command=self.export2xp)
        utilmenu.add_command(label="Export to FF A320",command=self.export2FFA320)
        utilmenu.add_separator()
        utilmenu.add_command(label="Show FPL text",command=self.showFplText)
        utilmenu.add_separator()
        utilmenu.add_command(label="Options",command=lambda: OptionsWindow(self.master,self.databaseDir))
        menubar.add_cascade(label="Extras",menu=utilmenu)
        
        self.master.config(menu=menubar)
        
        ## row 0-1 ##
        ## send button
        self.b_send = Button(self.master, text = "Send", command=self.send)
        self.b_send.grid(row=0, column=0, rowspan = 2)
        
        ## callsign
        self.l_callsign = Label(self.master, text="7 a/c ident")
        self.l_callsign.grid(row=0, column=1)
        
        self.callsign = StringVar(self.master)
        self.e_callsign = Entry(self.master, textvariable=self.callsign)
        self.e_callsign.grid(row=1, column=1)
        self.callsign.trace_add('write', self.e_callsignCB)
        
        ## rules
        self.l_rules = Label(self.master, text="8 flight rules")
        self.l_rules.grid(row=0, column=2)
        
        self.rules = StringVar(self.master)
        self.rules.set("V")
        self.o_rules = OptionMenu(self.master, self.rules, "V", "I", "Y", "Z")
        self.o_rules.grid(row=1, column=2)
        
        
        ## flighttype
        self.l_flighttype = Label(self.master, text="  type of flight")
        self.l_flighttype.grid(row=0, column=3)
        
        self.flighttype = StringVar(self.master)
        self.flighttype.set("S")
        self.o_flighttype = OptionMenu(self.master, self.flighttype, "S", "N", "G", "M", "X")
        self.o_flighttype.grid(row=1, column=3)
        
        
        ## row 2-3 ##
        ## number
        self.l_number = Label(self.master, text="9 number")
        self.l_number.grid(row=2, column=0)
        
        self.number = StringVar(self.master)
        self.e_number = Entry(self.master, textvariable=self.number)
        self.e_number.grid(row=3, column=0)
        self.number.trace_add('write', self.e_numberCB)
        
        ## type of aircraft
        self.l_actype = Label(self.master, text="type of aircraft")
        self.l_actype.grid(row=2, column=1)
        
        self.actype = StringVar(self.master)
        self.e_actype = Entry(self.master, textvariable=self.actype)
        self.e_actype.grid(row=3, column=1)
        self.actype.trace_add('write', self.e_actypeCB)
        
        ## wakecat
        self.l_wakecat = Label(self.master, text="wake turb cat")
        self.l_wakecat.grid(row=2, column=2)
        
        self.wakecat = StringVar(self.master)
        self.wakecat.set("L")
        self.o_wakecat = OptionMenu(self.master, self.wakecat, "L", "M", "H", "J")
        self.o_wakecat.grid(row=3, column=2)
        
        ## equipment
        self.l_equipment = Label(self.master, text="10 equipment")
        self.l_equipment.grid(row=2, column=3)
        
        self.equipment = StringVar(self.master)
        self.e_equipment = Entry(self.master, textvariable=self.equipment)
        self.e_equipment.grid(row=3, column=3)
        self.equipment.trace_add('write', self.e_equipmentCB)
        
        ## equipment
        self.l_transponder = Label(self.master, text="transponder")
        self.l_transponder.grid(row=2, column=4)
        
        self.transponder = StringVar(self.master)
        self.e_transponder = Entry(self.master, textvariable=self.transponder)
        self.e_transponder.grid(row=3, column=4)
        self.transponder.trace_add('write', self.e_transponderCB)
        
        
        ## row 4-5 ##
        ## depicao
        self.l_depicao = Label(self.master, text="13 departure aerodrome")
        self.l_depicao.grid(row=4, column=0)
        
        self.depicao = StringVar(self.master)
        self.e_depicao = Entry(self.master, textvariable=self.depicao)
        self.e_depicao.grid(row=5, column=0)
        self.depicao.trace_add('write', self.e_depicaoCB)
        
        ## deptime
        self.l_deptime = Label(self.master, text="departure time")
        self.l_deptime.grid(row=4, column=1)
        
        self.deptime = StringVar(self.master)
        self.e_deptime = Entry(self.master, textvariable=self.deptime)
        self.e_deptime.grid(row=5, column=1)
        self.deptime.trace_add('write', self.e_deptimeCB)
        
        ## row 6-7 ##
        ## speed
        self.l_speed = Label(self.master, text="15 cruising speed")
        self.l_speed.grid(row=6, column=0, columnspan=2)
        
        self.speedtype = StringVar(self.master)
        self.speedtype.set("N")
        self.o_speedtype = OptionMenu(self.master, self.speedtype, "N", "M")
        self.o_speedtype.grid(row=7, column=0)
        
        self.speed = StringVar(self.master)
        self.e_speed = Entry(self.master, textvariable=self.speed)
        self.e_speed.grid(row=7, column=1)
        self.speed.trace_add('write', self.e_speedCB)
        
        ## level
        self.l_level = Label(self.master, text="flight altutude/level")
        self.l_level.grid(row=6, column=2, columnspan=2)
        
        self.leveltype = StringVar(self.master)
        self.leveltype.set("F")
        self.o_level = OptionMenu(self.master, self.leveltype, "F", "A", "VFR")
        self.o_level.grid(row=7, column=2)
        
        self.level = StringVar(self.master)
        self.e_level = Entry(self.master, textvariable=self.level)
        self.e_level.grid(row=7, column=3)
        self.level.trace_add('write', self.e_levelCB)
        
        
        ## row 8-9 ##
        ##route
        self.l_route = Label(self.master, text="    route")
        self.l_route.grid(row=8, column=0, sticky=W)
        
        self.route = StringVar(self.master)
        self.e_route = Entry(self.master, width=105, textvariable=self.route)
        self.e_route.grid(row=9, column=0, columnspan=5)
        self.route.trace_add('write', self.e_routeCB)
        
        ## row 10-11 ##
        ## destinationAP
        self.l_desticao = Label(self.master, text="13 destination aerodrome")
        self.l_desticao.grid(row=10, column=0)
        
        self.desticao = StringVar(self.master)
        self.e_desticao = Entry(self.master, textvariable=self.desticao)
        self.e_desticao.grid(row=11, column=0)
        self.desticao.trace_add('write', self.e_desticaoCB)
        
        ## duration
        self.l_eet = Label(self.master, text="EET")
        self.l_eet.grid(row=10, column=1)
        
        self.eet = StringVar(self.master)
        self.e_eet = Entry(self.master, textvariable=self.eet)
        self.e_eet.grid(row=11, column=1)
        self.eet.trace_add('write', self.e_eetCB)
        
        ## alternates
        self.l_alticao = Label(self.master, text="alternate")
        self.l_alticao.grid(row=10, column=2)
        
        self.alticao = StringVar(self.master)
        self.e_alticao = Entry(self.master, textvariable=self.alticao)
        self.e_alticao.grid(row=11, column=2)
        self.alticao.trace_add('write', self.e_alticaoCB)
        
        self.l_alt2icao = Label(self.master, text="2nd alternate")
        self.l_alt2icao.grid(row=10, column=3)
        
        self.alt2icao = StringVar(self.master)
        self.e_alt2icao = Entry(self.master, textvariable=self.alt2icao)
        self.e_alt2icao.grid(row=11, column=3)
        self.alt2icao.trace_add('write', self.e_alt2icaoCB)
        
        
        ## row 12-13 ##
        ##other
        self.l_other = Label(self.master, text="other")
        self.l_other.grid(row=12, column=0, sticky=W)
        
        self.other = StringVar(self.master)
        self.e_other = Entry(self.master, width=105, textvariable=self.other)
        self.e_other.grid(row=13, column=0, columnspan=5)
        self.other.trace_add('write', self.e_otherCB)
        
        
        ## row 14-15 ##
        ##endurance
        self.l_endurance = Label(self.master, text="19 endurance")
        self.l_endurance.grid(row=14, column=0)
        
        self.endurance = StringVar(self.master)
        self.e_endurance = Entry(self.master, textvariable=self.endurance)
        self.e_endurance.grid(row=15, column=0)
        self.endurance.trace_add('write', self.e_enduranceCB)
        
        ##persons
        self.l_pob = Label(self.master, text="persons on board")
        self.l_pob.grid(row=14, column=1)
        
        self.pob = StringVar(self.master)
        self.e_pob = Entry(self.master, textvariable=self.pob)
        self.e_pob.grid(row=15, column=1)
        self.pob.trace_add('write', self.e_pobCB)
        
        ##pic
        self.l_pic = Label(self.master, text="pilot in command")
        self.l_pic.grid(row=14, column=2)
        
        self.pic = StringVar(self.master)
        self.e_pic = Entry(self.master, width=40, textvariable=self.pic)
        self.e_pic.grid(row=15, column=2, columnspan=2)
        self.pic.trace_add('write', self.e_picCB)
        
        ## row 16 ##
        ##empty
        empty = Label(self.master, text="")
        empty.grid(row=16, column=0)
        
        self.updateContent()
        
        # Set master window options
        self.master.update()
        masterWidth = self.master.winfo_width()
        masterHeight = self.master.winfo_height()
        x = round((self.screenWidth/2) - (masterWidth/2))
        y = round((self.screenHeight/2) - (masterHeight/2))
        self.master.geometry('{}x{}+{}+{}'.format(masterWidth,masterHeight,x,y))
        self.master.title('FPLGUI')
        self.master.resizable(0, 0)
        self.master.iconbitmap(os.path.join(self.supportFilesDir,'FPLGUI.ico'))
        
        # Start master mainloop.
        self.master.mainloop()
        
    def updateContent(self):
        ## row 0-1 ##
        ## callsign
        self.e_callsign.delete(0, END)
        self.e_callsign.insert(0,self.fpl.callsign)
        
        ## rules    
        if self.fpl.rules:
            self.rules.set(self.fpl.rules)
        else:
            self.rules.set("V")
        
        ## flightType
        if self.fpl.flighttype:
            self.flighttype.set(self.fpl.flighttype)
        else:
            self.flighttype.set("S")
        
        
        ## row 2-3 ##
        ## number
        self.e_number.delete(0, END)
        self.e_number.insert(0,self.fpl.number)
        
        ## type of aircraft
        self.e_actype.delete(0, END)
        self.e_actype.insert(0,self.fpl.actype)
        
        ## wakecat
        if self.fpl.wakecat:
            self.wakecat.set(self.fpl.wakecat)
        else:
            self.wakecat.set("L")
        
        ## equipment
        self.e_equipment.delete(0, END)
        self.e_equipment.insert(0,self.fpl.equipment)
        
        ## equipment
        self.e_transponder.delete(0, END)
        self.e_transponder.insert(0,self.fpl.transponder)
        
        
        ## row 4-5 ##
        ## depicao
        self.e_depicao.delete(0, END)
        self.e_depicao.insert(0,self.fpl.depicao)
        
        ## deptime
        self.e_deptime.delete(0, END)
        self.e_deptime.insert(0,self.fpl.deptime)
        
        ## row 6-7 ##
        ## speed
        if self.fpl.speedtype:
            self.speedtype.set(self.fpl.speedtype)
        else:
            self.speedtype.set("N")
        
        self.e_speed.delete(0, END)
        self.e_speed.insert(0,self.fpl.speed)
        
        ## level
        if self.fpl.leveltype:
            self.leveltype.set(self.fpl.leveltype)
        else:
            self.leveltype.set("N")
        
        self.e_level.delete(0, END)
        self.e_level.insert(0,self.fpl.level)
        
        ## row 8-9 ##
        ##route
        self.e_route.delete(0, END)
        self.e_route.insert(0,self.fpl.route)
        
        ## row 10-11 ##
        ## destinationAP        
        self.e_desticao.delete(0, END)
        self.e_desticao.insert(0,self.fpl.desticao)
        
        ## eet        
        self.e_eet.delete(0, END)
        self.e_eet.insert(0,self.fpl.eet)
        
        ## alternates
        self.e_alticao.delete(0, END)
        self.e_alticao.insert(0,self.fpl.alticao)
        
        self.e_alt2icao.delete(0, END)
        self.e_alt2icao.insert(0,self.fpl.alt2icao)
        
        
        ## row 12-13 ##
        ##other
        self.e_other.delete(0, END)
        self.e_other.insert(0,self.fpl.other)
        
        
        ## row 14-15 ##
        ##endurance
        self.e_endurance.delete(0, END)
        self.e_endurance.insert(0,self.fpl.endurance)
        
        ##persons
        self.e_pob.delete(0, END)
        self.e_pob.insert(0,self.fpl.pob)
        
        ##pic
        self.e_pic.delete(0, END)
        self.e_pic.insert(0,self.fpl.pic)
    
    
    def updateFpl(self):
        self.fpl.callsign = self.e_callsign.get()
        self.fpl.pic = self.e_pic.get()
        self.fpl.speedtype = self.speedtype.get()
        self.fpl.pob = self.e_pob.get()
        self.fpl.endurance = self.e_endurance.get()
        self.fpl.other = self.e_other.get()
        self.fpl.alt2icao = self.e_alt2icao.get()
        self.fpl.alticao = self.e_alticao.get()
        self.fpl.eet = self.e_eet.get()
        self.fpl.desticao = self.e_desticao.get()
        self.fpl.route = self.e_route.get()
        self.fpl.level = self.e_level.get()
        self.fpl.leveltype = self.leveltype.get()
        self.fpl.speed = self.e_speed.get()
        self.fpl.deptime = self.e_deptime.get()
        self.fpl.depicao = self.e_depicao.get()
        self.fpl.transponder = self.e_transponder.get()
        self.fpl.equipment = self.e_equipment.get()
        self.fpl.wakecat = self.wakecat.get()
        self.fpl.actype = self.e_actype.get()
        self.fpl.number = self.e_number.get()
        self.fpl.flighttype = self.flighttype.get()
        self.fpl.rules = self.rules.get()
        
    
    def load(self):
        filepath = askopenfilename(filetypes=[("X-Plane Flightplan","*.fpl"),("All","*")],initialdir=self.fpl.path)
        self.fpl.load(filepath)
        self.updateContent()
    
    def save(self):
        self.updateFpl()
        filepath = asksaveasfilename(filetypes=[("X-Plane Flightplan","*.fpl"),("All","*")],initialdir=self.fpl.path)
        if filepath[-4:] != ".fpl":
            filepath += ".fpl"
        
        self.fpl.save(filepath)
        print("saved!")
        
    
    
    def send(self):
        self.updateFpl()
        self.fpl.save(self.fpl.path + "\\Default.fpl")
        
        if (len(self.fpl.route) + len(self.fpl.other)) > 255:
            showwarning("Too long entries",'"Route" and "Other" entries are too long ({}/255 characters combined)!\nThis will lead to disconnection in flight.\nTry to shorten these fields.'.format(len(self.fpl.route) + len(self.fpl.other)))
        print("generated!")
    
    
    def clear(self):
        self.e_callsign.delete(0, END)
        self.e_number.delete(0, END)
        self.e_actype.delete(0, END)
        self.e_equipment.delete(0, END)
        self.e_transponder.delete(0, END)
        self.e_depicao.delete(0, END)
        self.e_deptime.delete(0, END)
        self.e_speed.delete(0, END)
        self.e_level.delete(0, END)
        self.e_route.delete(0, END)
        self.e_desticao.delete(0, END)
        self.e_eet.delete(0, END)
        self.e_alticao.delete(0, END)
        self.e_alt2icao.delete(0, END)
        self.e_other.delete(0, END)
        self.e_endurance.delete(0, END)
        self.e_pob.delete(0, END)
        self.e_pic.delete(0, END)
    
    def getOptions(self):
        # Init options with None
        self.xPlaneDir = None
        
        # Get options
        if os.path.isfile(os.path.join(self.databaseDir,'FPLGUI.cfg')):
            self.config = ConfigParser.RawConfigParser()
            self.config.read(os.path.join(self.databaseDir,'FPLGUI.cfg'))
            # xPlaneDir
            try:
                self.xPlaneDir = self.config.get('FPLGUI','XPLANEDIR')
            except ConfigParser.NoSectionError:
                return
            except ConfigParser.NoOptionError:
                pass
            if self.xPlaneDir is not None and not re.match(r'[A-Za-z]:\\',self.xPlaneDir):
                self.xPlaneDir = None
        
    def updateRouteDbButtonCB(self):
#         dbUpdated = False
        downloadUrl = "https://www.ivao.de/scripts/php/cms/pfpx"
#         curDate = int(time.time())
#         lastUpdate = 0 #TODO: replace, when options implemented
#         timeDiff = curDate - lastUpdate
        timeDiff = 0

        if timeDiff > 864000:
            ivaoDatabase = urlopen(downloadUrl)
            database = ivaoDatabase.read()
#             lastUpdate = int(time.time())
#             dbUpdated = True
            
            DataFile = open(self.srcDir + r"\routeDatabase.txt",'w')
            DataFile.write(database)
            DataFile.close()
    
    def optionsButtonOkCB(self):
        self.top.destroy()
    
    def optionsButtonCancelCB(self):
        self.top.destroy()
    
    def simbrief(self,*args):
        self.updateFpl()
        
        url = 'https://www.simbrief.com/system/dispatch.php?'
        options = ''
        
        # Airports.
        if self.fpl.depicao and self.fpl.desticao:
            url = '{}&orig={}&dest={}'.format(url,self.fpl.depicao,self.fpl.desticao)
        else:
            showwarning('Airport missing','Departure and destination airport is mandatory for Simbrief.\nSpecify them first!')
        
        # Times
        if self.fpl.deptime:
            deph = self.fpl.deptime[0:1]
            depm = self.fpl.deptime[2:3]
            options = '{}&deph={}&depm={}'.format(options,deph,depm)
        else:
            showwarning('Time missing','Departure time is mandatory for Simbrief.\nSpecify it first!')
            
            return
        
        # Aircraft.
        if self.fpl.actype:
            url = '{}&type={}'.format(url,self.fpl.actype)
        else:
            showwarning('Aircraft type missing','Aircraft type is mandatory for Simbrief.\nSpecify it first!')
        
        # Route.
        if self.fpl.route:
            url = '{}&route={}'.format(url,self.fpl.route)
        else:
            showinfo('Route missing','The route is missing and will be created by Simbrief.\nNote: These routes are often not CFMU valid!')
        
        # Flightlevel.
        if self.fpl.level:
            url = '{}&fl={}00'.format(url,self.fpl.level)
        
        # Date.
        reFind = re.search('(?<=DOF/)\d{6}',self.fpl.other)
        if reFind:
            date = reFind.group()
            date = datetime.datetime(int(date[0:2])+2000,int(date[2:4]),int(date[4:6]))
        else:
            date = datetime.datetime.today()
        url = '{}&date={}'.format(url,date.strftime('%d%b%y'))
        
        # Airline, fltnum, registration, selcal.
        airline = ''
        fltnum = ''
        registration = ''
        selcal = ''
        
        # Airline, fltnum, registration from Callsign field.
        reFind = re.search('[A-Z]{5}',self.fpl.callsign)
        if reFind:
            registration = reFind.group()
        else:
            reFindAirline = re.search('[A-Z]{3}(?=\w+)',self.fpl.callsign)
            reFindFltnum = re.search('(?<=[A-Z]{3})\w+',self.fpl.callsign)
            if reFindAirline and reFindFltnum:
                airline = reFindAirline.group()
                fltnum = reFindFltnum.group()
            else:
                print('invalid Callsign!')
        
        # Registration (REG/) from other field.
        if not registration:
            reFind = re.search('(?<=REG/)[A-Z]{5}',self.fpl.other)
            if reFind:
                registration = reFind.group()
        
        # Airline (OPR/) from other field.
        if not airline:
            reFind = re.search('(?<=OPR/)[A-Z]{3}',self.fpl.other)
            if reFind:
                airline = reFind.group()
        
        # Selcal (SEL) from other field.
        reFind = re.search('(?<=SEL/)[A-S]{4}',self.fpl.other)
        if reFind:
            selcal = reFind.group()
        
        # Add the found values
        if airline:
            url = '{}&airline={}'.format(url,airline)
        if fltnum:
            url = '{}&fltnum={}'.format(url,fltnum)
        if registration:
            url = '{}&reg={}'.format(url,registration)
        if selcal:
            url = '{}&selcal={}'.format(url,selcal)
        
        # ----FPL----
        # Alternates.
        
        # ----ADD----
        # Extra Fuel.
        # Cont fuel
        # Reserve Fuel.
        # Taxi out.
        # Taxi in.
        # Cargo.
        # Pax.
        # Dep rwy.
        # Arr rwy.
        # CI
        # ETOPS.
        
        
        # Specify options.
        # For Simbrief process.
        if len(args) and args[0]:
            url = '{}&planformat=LIDO&units=KGS&navlog=0&etops=0&stepclimbs=0&tlr=0&notams=0&firnot=0&maps=none'.format(url)
        
        # For show Simbiref.
        else:
            url = '{}&planformat=LIDO&units=KGS&navlog=1&etops=1&stepclimbs=0&tlr=0&notams=1&firnot=1&maps=detail'.format(url)
#         print(url)
#         pass

        # Open simbrief.
        webbrowser.open(url,new=2)
    
    
    def simbriefOpen(self):
        pass
        
    def simbriefProcess(self):
        pass
            
    ## Display flights for departure and destination airport on flightaware.
    def flightaware(self):
        self.updateFpl()
        
        url = 'https://de.flightaware.com/live/findflight?origin={}&destination={}'.format(self.fpl.depicao,self.fpl.desticao)
        webbrowser.open(url,new=2)
    
    
    def importRoute(self):
        self.updateFpl()
        
        with open(self.databaseDir + r"\routeDatabase.txt") as dataFile:
            database = dataFile.read()
            
        self.fpl.desticao = self.fpl.desticao.upper()
        self.fpl.depicao = self.fpl.depicao.upper()
        
        patRte = re.compile(self.fpl.depicao + self.fpl.desticao + r"\d{2};.+\n")
        routes = patRte.findall(database)
        
        ## parse routes
        self.routing = []
        self.comment = []
        self.fl = []

        maxLenCom = 0

        for k in range(len(routes)):
            parts = re.split(";",routes[k])
            self.routing.append(parts[1])
            
            curComment = parts[2]
            #curComment = curComment[1:-2]
            curComment = re.split(",",curComment)
            
            curFL = curComment[0]
            curCommentList = curComment[1:]
            
            curComment = ""
            
            for l in curCommentList:
                curComment += l
            
            self.comment.append(curComment[1:-2])
            
            if len(curComment[1:-2]) > maxLenCom:
                maxLenCom = len(curComment[1:-2])
            
            self.fl.append(curFL)
        
        ## show window
        self.importRouteTop = Toplevel(self.master)
        
        if len(self.routing) > 0:
            Label(self.importRouteTop, text="Choose a Route").pack()
            
            self.importRouteListboxTl = Listbox(self.importRouteTop,width=180)
            self.importRouteListboxTl.pack()
            for k in range(len(self.routing)):
                self.importRouteListboxTl.insert(END, "{:11} {:50} {}".format(self.fl[k],self.comment[k],self.routing[k]))
            self.importRouteListboxTl.selection_set(0)
            
            self.tlOkButton = Button(self.importRouteTop,text="OK",command=self.routeListCB,width=80)
            self.tlOkButton.pack()
            
            self.master.wait_window(self.importRouteTop)
        else:
            Label(self.importRouteTop, text="No Routes found!").pack()
            self.tlOkButton = Button(self.importRouteTop,text="OK",command=self.importRouteTop.destroy,width=10)
            self.tlOkButton.pack()

    def export2FFA320(self):
        """
        Write the route to Flight Factor A320 Company Routes Database.
        Import to aircraft via the MCDU
        """
        self.updateFpl()
        #self.fpl.actype,self.fpl.depicao,self.fpl.desticao,deph,depm,reg,selcal,self.fpl.route,self.fpl.level
        fpl = self.fpl
        # check if ac type is A320
        if fpl.actype != 'A320':
            warn('A/C type is not A320!')
        
        # remove SID/STAR from route
        route = re.sub('[A-Z]{5}\d[A-Z]','',fpl.route).strip()
        
        # write route string
        routeString = 'RTE {}{} {} {} {} CI30 FL{}'.format(fpl.depicao,
                                                           fpl.desticao,
                                                           fpl.depicao,
                                                           route,
                                                           fpl.desticao,
                                                           fpl.level
                                                           )
        
        # find and open route database
        # check for duplicate and overwrite or append route
        coRoutePath = os.path.join(self.xPlaneDir,r'Aircraft\FlightFactorA320\data\corte.in')
        with open(coRoutePath,'r') as coRouteFile:
            lines = coRouteFile.readlines()
            written = False
            for k in range(len(lines)):
                if 'RTE {}{}'.format(fpl.depicao,fpl.desticao) in lines[k]:
                    lines[k] = '{}\n'.format(routeString)
                    written = True
                    break
            
            if not written:
                if lines[-1][-1] == '\n':
                    lines.append(routeString)
                else:
                    lines.append('\n{}'.format(routeString))
        
        # write new file
        with open(coRoutePath,'w') as coRouteFile:
            for k in lines:
                coRouteFile.write(k)
        
        # print success message
        print('exported (FF A320)!')
        
    def export2xp(self):
        self.updateFpl()
        
        # Get file path for export.
        fileCount = 0
        fmsFilePath = os.path.abspath(__file__)
        while os.path.isfile(fmsFilePath):
            fileCount += 1
            fmsFilePath = os.path.join(self.xPlaneDir,'Output','FMS plans','{}{}{:02}.fms'.format(self.fpl.depicao,self.fpl.desticao,fileCount))

        # Get coordinates of dep.
        curCoordinates = self.fpl.airports[self.fpl.depicao]
        
        # Get start altitude.
        curAltitude = int(self.fpl.level) * 100
        newAltitude = curAltitude
        
        # Remove SID/STAR from route and split in parts
        route = re.sub('[A-Z]{5}\d[A-Z]','',self.fpl.route).strip()
        route = route.split()
        
#         with open(fmsFilePath,'w') as fmsFile:
        # Write header and departure.
        fmsStr = ''
        
        # Process route.
        nWaypoints = 1
        curWaypoint = None
        curWaypointName = None
        lastWaypointName = None
        curAirway = None
        for rpId,rp in enumerate(route):
            if not(rpId % 2):
                # Waypoint
                
                # Split altitude from wp
                if '/' in rp:
                    wpSplit = rp.split('/')
                    curWaypointName = wpSplit[0]
                    altMatch = re.search('F\d+', wpSplit[1])
                    if altMatch is not None:
                        newAltitude = int(wpSplit[1][altMatch.start()+1:altMatch.end()]) * 100
                else:
                    curWaypointName = rp
                
                if curAirway is None:
                    # After DCT
                    curAltitude = newAltitude
                    
                    curWaypoint = self.fpl.waypoints[curWaypointName]
                    minDistance = 3.2 # slightly greater than pi
                    for wp in curWaypoint:
                        distance = avFormula.gcDistance(curCoordinates[0],curCoordinates[1],wp[0],wp[1])
                        if distance < minDistance:
                            minDistance = distance
                            nearWp = wp
                    fmsStr = '{}{} {} DRCT {} {} {}\n'.format(fmsStr,nearWp[2],curWaypointName,curAltitude,nearWp[0],nearWp[1])
                    nWaypoints += 1
                    
                else:
                    # After Airway
                    curAirwayParts = self.fpl.airways[curAirway].parts
                    curAirwayName = curAirway
                    curAirway = None
                    # Get part with both waypoints.
                    for pa in curAirwayParts:
                        if lastWaypointName in [k for m in pa for k in m] and curWaypointName in [n for o in pa for n in o]:
                            curAirway = pa
                            break
                    if curAirway is None:
                        print('One or both waypoints are no part of airway {}!'.format(curAirwayName))
                        raise(Exception,'Airway Error!')
                    
                    curWaypointId = None
                    lastWaypointId = None
                    for wpId,wp in enumerate(curAirway):
                        if curWaypointName in wp:
                            curWaypointId = wpId
                        elif lastWaypointName in wp:
                            lastWaypointId = wpId
                        if curWaypointId is not None and lastWaypointId is not None:
                            step = int(copysign(1,curWaypointId - lastWaypointId))
                            break
                    for wp in range(lastWaypointId+step,curWaypointId+step,step):
                        if curAirway[wp][0] == curWaypointName:
                            curAltitude = newAltitude
                        
                        fmsStr = '{}{} {} {} {} {} {}\n'.format(fmsStr,curAirway[wp][3],curAirway[wp][0],curAirwayName,curAltitude,curAirway[wp][1],curAirway[wp][2])
                        nWaypoints += 1
                        
                    curAirway = None
            elif rp != 'DCT':
                # Airway
                curAirway = rp
                lastWaypointName = curWaypointName
                
        curCoordinates = self.fpl.airports[self.fpl.desticao]
        fmsStr = '{}1 {} ADES 0.000000 {} {}'.format(fmsStr,self.fpl.desticao,curCoordinates[0],curCoordinates[1])
        nWaypoints += 1
        
        curCoordinates = self.fpl.airports[self.fpl.depicao]
        fmsStr = 'I\n1100 Version\nCYCLE {}\nADEP {}\nADES {}\nNUMENR {}\n1 {} ADEP 0.000000 {} {}\n{}'.format(self.fpl.cycleNumber,
                                                                                                               self.fpl.depicao,
                                                                                                               self.fpl.desticao,
                                                                                                               nWaypoints,
                                                                                                               self.fpl.depicao,
                                                                                                               curCoordinates[0],curCoordinates[1],
                                                                                                               fmsStr)
        
#         print(fmsStr)
        
        with open(fmsFilePath,'w') as fmsFile:
            fmsFile.write(fmsStr)
            
        print('fms file exported to XP!')
        
    def acLoad(self):
        
        self.updateFpl()
        
        self.getAcTemplates()
        
        # get the right template.
        if self.fpl.actype in self.acTemplates:
            template = self.acTemplates[self.fpl.actype]
            
            # Assign values to FPL.
            self.fpl.equipment = template[0]
            self.fpl.transponder = template[1]
            
            matchObj = re.search(r'PBN/\w+', self.fpl.other,flags=re.A)  # @UndefinedVariable
            if matchObj is not None:
                self.fpl.other = self.fpl.other.replace(self.fpl.other[matchObj.start():matchObj.end()], '')
            self.fpl.other = re.sub('  +',' ',self.fpl.other)
            self.fpl.other = self.fpl.other.strip()
            self.fpl.other = 'PBN/{} {}'.format(template[2],self.fpl.other)
            self.fpl.other = self.fpl.other.strip()
            
            self.fpl.wakecat = template[3]
            self.fpl.speed = template[4]
            self.fpl.pob = template[5]
            
            # Update Fields.
            self.updateContent()
            
        else:
            messagebox.showinfo("FPL", "No templates found for\naircraft {}!".format(self.fpl.actype))
                
    def acSave(self):
        # Preparations.
        self.updateFpl()
        self.getAcTemplates()
        
        # Check if template already exists and ask what to do.
        if self.fpl.actype in self.acTemplates:
            if messagebox.askyesno("Overwrite?","A template for the aircraft {} already exists.\nOverwrite?".format(self.fpl.actype)):
                self.acTemplates.pop(self.fpl.actype)
            else:
                return
        
        # Update Aircraft templates.
        self.acTemplates[self.fpl.actype] = [] 
        self.acTemplates[self.fpl.actype].append(self.fpl.equipment)
        self.acTemplates[self.fpl.actype].append(self.fpl.transponder)
        
        matchObj = re.search(r'PBN/\w+', self.fpl.other,flags=re.A)  # @UndefinedVariable
        if matchObj is not None:
            self.acTemplates[self.fpl.actype].append(self.fpl.other[matchObj.start():matchObj.end()].replace('PBN/',''))
        else:
            self.acTemplates[self.fpl.actype].append('')
        
        self.acTemplates[self.fpl.actype].append(self.fpl.wakecat)
        self.acTemplates[self.fpl.actype].append(self.fpl.speed)
        self.acTemplates[self.fpl.actype].append(self.fpl.pob)
        
        # Write the new list
        with open(os.path.join(self.databaseDir,'aircraft.csv'),'w') as acFile:
            acFile.write('ac;equip;transponder;PBN;wakeCat;speed;x;POB\n')
            for te in self.acTemplates:
                curTemplate = self.acTemplates[te]
                acFile.write('{};{};{};{};{};{};{}\n'.format(te,curTemplate[0],curTemplate[1],curTemplate[2],curTemplate[3],curTemplate[4],curTemplate[5]))
    
    def getAcTemplates(self):
        self.acTemplates = {}
        if not os.path.exists(os.path.join(self.databaseDir,'aircraft.csv')):
            open(os.path.join(self.databaseDir,'aircraft.csv'),'w').close()
        with open(os.path.join(self.databaseDir,'aircraft.csv')) as acFile:
            for lineNr,line in enumerate(acFile):
                if lineNr:
                    lineSplit = line.rstrip('\n').split(';')
                    self.acTemplates[lineSplit[0]] = [lineSplit[1],lineSplit[2],lineSplit[3],lineSplit[4],lineSplit[5],lineSplit[6]]
    
    
    
    def showSkyvector(self):
        # Calculate middle point.
        depCoordinates = self.fpl.airports[self.fpl.depicao]
        destCoordinates = self.fpl.airports[self.fpl.desticao]
        intermediatePoint = avFormula.gcIntermediatePoint(depCoordinates[0], depCoordinates[1], destCoordinates[0], destCoordinates[1])
        
        skyvectorUrl = 'http://skyvector.com/?ll={:9.6f},{:9.6f}&chart=304&zoom=6&fpl=%20{}%20{}%20{}'.format(intermediatePoint[0],
                                                                                                     intermediatePoint[1],
                                                                                                     self.fpl.depicao,
                                                                                                     self.fpl.route.replace(' ','%20'),
                                                                                                     self.fpl.desticao)
        webbrowser.open(skyvectorUrl,new=2)
        
        
    def showFplText(self):
        # Get Field contents.
        self.updateFpl()
        
        # Init string.
        fplString = '(FPL\n'
        
        # Complete string.
        fplString = '{}-{}-{}{}\n'.format(fplString,
                                          self.fpl.callsign,
                                          self.fpl.rules,
                                          self.fpl.flighttype)
        fplString = '{}-{}{}/{}-{}/{}\n'.format(fplString,
                                                self.fpl.number,
                                                self.fpl.actype,
                                                self.fpl.wakecat,
                                                self.fpl.equipment,
                                                self.fpl.transponder)
        fplString = '{}-{}{}\n'.format(fplString,
                                       self.fpl.depicao,
                                       self.fpl.deptime)
        fplString = '{}-N{:04}F{:03} {}\n'.format(fplString,
                                                  int(self.fpl.speed),
                                                  int(self.fpl.level),
                                                  self.fpl.route)
        fplString = '{}-{}{} {} {}\n'.format(fplString,
                                             self.fpl.desticao,
                                             self.fpl.eet,
                                             self.fpl.alticao,
                                             self.fpl.alt2icao)
        fplString = '{}-{})'.format(fplString,self.fpl.other)
        
        # Print string.
        print(fplString)
        
        # Copy to clipboard.
        r = Tk()
        r.withdraw()
        r.clipboard_clear()
        r.clipboard_append(str(fplString))
        r.update()
        r.destroy()
        
        # Show in window.
        showinfo("Flightplan text", '{}\n\n(Copied to clipboard.)'.format(fplString))

        
    # Callbacks
    def routeListCB(self):
        selectedRoute = self.importRouteListboxTl.curselection()
        selectedRoute = selectedRoute[0]
        self.fpl.route = self.routing[selectedRoute]
        self.fpl.route = self.fpl.route[5:-5]
        self.importRouteTop.destroy()
        self.updateContent()
    
    def e_callsignCB(self,*args):  #@UnusedVariable
        string = self.callsign.get()
        if len(string):
            enteredChar = string[-1]
            if not enteredChar.isalnum():
                self.callsign.set(string[0:-1])
            else:
                self.callsign.set(self.callsign.get().upper())
        
    def e_numberCB(self,*args):  #@UnusedVariable
        string = self.number.get()
        if len(string):
            enteredChar = string[-1]
            if not enteredChar.isdigit():
                self.number.set(string[0:-1])
        
    def e_actypeCB(self,*args):  #@UnusedVariable
        string = self.actype.get()
        if len(string):
            enteredChar = string[-1]
            if not enteredChar.isalnum() or len(string) > 4:
                self.actype.set(string[0:-1])
            else:
                self.actype.set(self.actype.get().upper())
        
    def e_equipmentCB(self,*args):  #@UnusedVariable
        string = self.equipment.get()
        if len(string):
            enteredChar = string[-1]
            if not enteredChar.isalnum():
                self.equipment.set(string[0:-1])
            else:
                self.equipment.set(self.equipment.get().upper())
        
    def e_transponderCB(self,*args):  #@UnusedVariable
        string = self.transponder.get()
        if len(string):
            enteredChar = string[-1]
            if not enteredChar.isalnum():
                self.transponder.set(string[0:-1])
            else:
                self.transponder.set(self.transponder.get().upper())
        
    def e_depicaoCB(self,*args):  #@UnusedVariable
        string = self.depicao.get()
        if len(string):
            enteredChar = string[-1]
            if not enteredChar.isalnum() or len(string) > 4:
                self.depicao.set(string[0:-1])
            else:
                self.depicao.set(self.depicao.get().upper())
        
    def e_deptimeCB(self,*args):  #@UnusedVariable
        string = self.deptime.get()
        if len(string):
            enteredChar = string[-1]
            if not enteredChar.isdigit() or len(string) > 4:
                self.deptime.set(string[0:-1])
        
    def e_speedCB(self,*args):  #@UnusedVariable
        string = self.speed.get()
        if len(string):
            enteredChar = string[-1]
            if not enteredChar.isdigit() or len(string) > 4:
                self.speed.set(string[0:-1])
        
    def e_levelCB(self,*args):  #@UnusedVariable
        string = self.level.get()
        if len(string):
            enteredChar = string[-1]
            if not enteredChar.isdigit() or len(string) > 3:
                self.level.set(string[0:-1])
        
    def e_routeCB(self,*args):  #@UnusedVariable
        string = self.route.get()
        if len(string):
            enteredChar = string[-1]
            if not enteredChar.isalnum() and enteredChar != '/' and enteredChar != ' ':
                self.route.set(string[0:-1])
            else:
                self.route.set(self.route.get().upper())
        
    def e_desticaoCB(self,*args):  #@UnusedVariable
        string = self.desticao.get()
        if len(string):
            enteredChar = string[-1]
            if not enteredChar.isalnum() or len(string) > 4:
                self.desticao.set(string[0:-1])
            else:
                self.desticao.set(self.desticao.get().upper())
        
    def e_eetCB(self,*args):  #@UnusedVariable
        string = self.eet.get()
        if len(string):
            enteredChar = string[-1]
            if not enteredChar.isdigit() or len(string) > 4:
                self.eet.set(string[0:-1])
        
    def e_alticaoCB(self,*args):  #@UnusedVariable
        string = self.alticao.get()
        if len(string):
            enteredChar = string[-1]
            if not enteredChar.isalnum() or len(string) > 4:
                self.alticao.set(string[0:-1])
            else:
                self.alticao.set(self.alticao.get().upper())
        
    def e_alt2icaoCB(self,*args):  #@UnusedVariable
        string = self.alt2icao.get()
        if len(string):
            enteredChar = string[-1]
            if not enteredChar.isalnum() or len(string) > 4:
                self.alt2icao.set(string[0:-1])
            else:
                self.alt2icao.set(self.alt2icao.get().upper())
        
    def e_otherCB(self,*args):  #@UnusedVariable
        string = self.other.get()
        if len(string):
            enteredChar = string[-1]
            if not enteredChar.isalnum() and enteredChar != '/' and enteredChar != ' ':
                self.other.set(string[0:-1])
            else:
                self.other.set(self.other.get().upper())
        
    def e_enduranceCB(self,*args):  #@UnusedVariable
        string = self.endurance.get()
        if len(string):
            enteredChar = string[-1]
            if not enteredChar.isdigit() or len(string) > 4:
                self.endurance.set(string[0:-1])
        
    def e_pobCB(self,*args):  #@UnusedVariable
        string = self.pob.get()
        if len(string):
            enteredChar = string[-1]
            if not enteredChar.isdigit():
                self.pob.set(string[0:-1])
        
    def e_picCB(self,*args):  #@UnusedVariable
        string = self.pic.get()
        if len(string):
            enteredChar = string[-1]
            if not enteredChar.isalpha() and enteredChar != ' ' and enteredChar != "'" and enteredChar != '-':
                self.pic.set(string[0:-1])
Esempio n. 25
0
class Floodgauge(Progressbar):
    """A ``Floodgauge`` widget shows the status of a long-running operation with an optional text indicator.

    Similar to the ``ttk.Progressbar``, this widget can operate in two modes: **determinate** mode shows the amount
    completed relative to the total amount of work to be done, and **indeterminate** mode provides an animated display
    to let the user know that something is happening.

    Variable are generated automatically for this widget and can be linked to other widgets by referencing them via
    the ``textvariable`` and ``variable`` attributes.

    The ``text`` and ``value`` properties allow you to easily get and set the value of these variables without the need
    to call the ``get`` and ``set`` methods of the related tkinter variables. For example: ``Floodgauge.value`` or
    ``Floodgauge.value = 55`` will get or set the amount used on the widget.
    """

    def __init__(self,
                 master=None,
                 cursor=None,
                 font=None,
                 length=None,
                 maximum=100,
                 mode='determinate',
                 orient='horizontal',
                 style='TFloodgauge',
                 takefocus=False,
                 text=None,
                 value=0,
                 **kw):
        """
        Args:
            master (Widget): Parent widget
            cursor (str): The cursor that will appear when the mouse is over the progress bar.
            font (Font or str): The font to use for the progress bar label.
            length (int): Specifies the length of the long axis of the progress bar (width if horizontal, height if
                vertical); defaults to 300.
            maximum (float): A floating point number specifying the maximum ``value``. Defaults to 100.
            mode (str): One of **determinate** or **indeterminate**. Use `indeterminate` if you cannot accurately
                measure the relative progress of the underlying process. In this mode, a rectangle bounces back and
                forth between the ends of the widget once you use the ``.start()`` method.  Otherwise, use `determinate`
                if the relative progress can be calculated in advance. This is the default mode.
            orient (str): Specifies the orientation of the widget; either `horizontal` or `vertical`.
            style (str): The style used to render the widget; `TFloodgauge` by default.
            takefocus (bool): This widget is not included in focus traversal by default. To add the widget to focus
                traversal, use ``takefocus=True``.
            text (str): A string of text to be displayed in the progress bar. This is assigned to the ``textvariable``
                ``StringVar`` which is automatically generated on instantiation. This value can be get and set using the
                ``Floodgauge.text`` property without having to directly call the ``textvariable``.
            value: The current value of the progressbar. In `determinate` mode, this represents the amount of work
                completed. In `indeterminate` mode, it is interpreted modulo ``maximum``; that is, the progress bar
                completes one "cycle" when the ``value`` increases by ``maximum``.
            **kw: Other configuration options from the option database.
        """
        # create a custom style in order to adjust the text inside the progress bar layout
        if any(['Horizontal' in style, 'Vertical' in style]):
            self._widgetstyle = f'{uuid4()}.{style}'
        elif orient == 'vertical':
            self._widgetstyle = f'{uuid4()}.Vertical.TFloodgauge'
        else:
            self._widgetstyle = f'{uuid4()}.Horizontal.TFloodgauge'

        # progress bar value variable
        self.variable = IntVar(value=value)

        super().__init__(parent=parent, class_='Floodgauge', cursor=cursor, length=length, maximum=maximum, mode=mode,
                         orient=orient, style=self._widgetstyle, takefocus=takefocus, variable=self.variable, **kw)

        # set the label font
        if font:
            self.tk.call("ttk::style", "configure", self._widgetstyle, '-%s' % 'font', font, None)

        # progress bar text variable
        self.textvariable = StringVar(value=text or '')
        self.textvariable.trace_add('write', self._textvariable_write)
        self._textvariable_write()

    @property
    def text(self):
        return self.textvariable.get()

    @text.setter
    def text(self, value):
        self.textvariable.set(value)

    @property
    def value(self):
        return self.variable.get()

    @value.setter
    def value(self, value):
        self.variable.set(value)

    def _textvariable_write(self, *args):
        """Callback to update the label text when there is a `write` action on the textvariable

        Args:
            *args: if triggered by a trace, will be `variable`, `index`, `mode`.
        """
        self.tk.call("ttk::style", "configure", self._widgetstyle, '-%s' % 'text', self.textvariable.get(), None)
Esempio n. 26
0
class _GeneralSettings(ttk.LabelFrame):  # pylint: disable=too-many-ancestors,too-many-instance-attributes
    '''Miscellaneous settings'''
    def __init__(self, container: tkContainer, config: WahooConfig):
        super().__init__(container, text="General Settings", padding=5)
        self._config = config
        self.columnconfigure(0, weight=1)
        self.columnconfigure(1, weight=1)
        self.columnconfigure(2, weight=1)
        self.rowconfigure(0, weight=1)
        self.rowconfigure(1, weight=1)
        self.rowconfigure(2, weight=1)
        self.rowconfigure(3, weight=1)
        self.rowconfigure(4, weight=1)
        self.rowconfigure(5, weight=1)
        self.rowconfigure(6, weight=1)
        self._color_swatch("1st place:", "place_1",
                           "Color of 1st place marker text").grid(column=0,
                                                                  row=0,
                                                                  sticky="es")
        self._lanes().grid(column=1, row=0, sticky="es")
        self._color_swatch("Text color:", "color_fg",
                           "Scoreboard foreground text color").grid(
                               column=2, row=0, sticky="es")
        self._color_swatch("2nd place:", "place_2",
                           "Color of 2nd place marker text").grid(column=0,
                                                                  row=1,
                                                                  sticky="es")
        self._inhibit().grid(column=1, row=1, sticky="es")
        self._color_swatch("Background:", "color_bg",
                           "Scoreboard background color").grid(column=2,
                                                               row=1,
                                                               sticky="es")
        self._color_swatch("3rd place:", "place_3",
                           "Color of 3rd place marker text").grid(column=0,
                                                                  row=2,
                                                                  sticky="es")
        self._color_swatch(
            "Title color:", "color_ehd",
            "Scoreboard event, heat, and description text color").grid(
                column=2, row=2, sticky="es")
        self._bg_img().grid(column=0, row=3, columnspan=3, sticky="news")
        self._bg_brightness().grid(column=0, row=4, columnspan=2, sticky="ws")
        self._bg_scale().grid(column=2, row=4, sticky="news")
        self._font_chooser("Normal font:", "normal_font",
                           "Font for the scoreboard text").grid(column=0,
                                                                row=5,
                                                                columnspan=2,
                                                                sticky="news")
        self._font_scale().grid(column=2, row=5, sticky="es")
        self._font_chooser(
            "Time font:", "time_font",
            "Font for result times (recommend fixed width font)").grid(
                column=0, row=6, columnspan=2, sticky="news")

    def _color_swatch(self,
                      label_text: str,
                      config_item: str,
                      tip_text: str = "") -> ttk.Widget:
        frame = ttk.Frame(self, padding=1)
        frame.rowconfigure(0, weight=1)
        frame.columnconfigure(0, weight=1)
        ttk.Label(frame, text=label_text).grid(column=0, row=0, sticky="news")
        ColorButton(frame, self._config, config_item).grid(column=1,
                                                           row=0,
                                                           sticky="news")
        if tip_text != "":
            ToolTip(frame, tip_text)
        return frame

    def _bg_img(self) -> ttk.Widget:
        frame = ttk.Frame(self, padding=1)
        frame.rowconfigure(0, weight=1)
        frame.columnconfigure(1, weight=1)
        ttk.Label(frame, text="Background image:").grid(column=0,
                                                        row=0,
                                                        sticky="news")
        bgi_text = os.path.basename(self._config.get_str("image_bg"))
        if bgi_text == "":
            bgi_text = "-None-"
        self._set_btn = ttk.Button(frame,
                                   text=bgi_text[0:20],
                                   command=self._browse_bg_image)
        self._set_btn.grid(column=1, row=0, sticky="news")
        ToolTip(self._set_btn, "Set the scoreboard background image")
        clear_btn = ttk.Button(frame,
                               text="Clear",
                               command=self._clear_bg_image)
        clear_btn.grid(column=2, row=0, sticky="news")
        ToolTip(clear_btn, "Remove the scoreboard background image")
        return frame

    def _clear_bg_image(self):
        self._set_btn.configure(text="-None-")
        self._config.set_str("image_bg", "")

    def _browse_bg_image(self):
        image = filedialog.askopenfilename(
            filetypes=[("image", "*.gif *.jpg *.jpeg *.png")])
        if len(image) == 0:
            return
        image = os.path.normpath(image)
        self._config.set_str("image_bg", image)
        self._set_btn.configure(text=os.path.basename(image)[0:20])

    def _bg_brightness(self) -> ttk.Widget:
        frame = ttk.Frame(self, padding=1)
        frame.rowconfigure(0, weight=1)
        frame.columnconfigure(0, weight=1)
        ttk.Label(frame, text="Image brightness:").grid(column=0,
                                                        row=0,
                                                        sticky="nes")
        self._bg_spin_var = StringVar(
            frame, value=str(self._config.get_float("image_bright")))
        self._bg_spin_var.trace_add("write", self._handle_bg_spin)
        ttk.Spinbox(frame,
                    from_=0,
                    to=1,
                    increment=0.05,
                    width=5,
                    textvariable=self._bg_spin_var).grid(column=1,
                                                         row=0,
                                                         sticky="news")
        ToolTip(frame, "Brightness of background image [0.0, 1.0]")
        return frame

    def _handle_bg_spin(self, *_arg):
        try:
            value = float(self._bg_spin_var.get())
            if 0 <= value <= 1:
                self._config.set_float("image_bright", value)
        except ValueError:
            pass

    def _bg_scale(self) -> ttk.Widget:
        frame = ttk.Frame(self, padding=1)
        frame.rowconfigure(0, weight=1)
        frame.columnconfigure(0, weight=1)
        ttk.Label(frame, text="Scale:").grid(column=0, row=0, sticky="nes")
        self._bg_scale_var = StringVar(
            frame, value=str(self._config.get_str("image_scale")))
        self._bg_scale_var.trace_add("write", self._handle_bg_scale)
        ttk.Combobox(frame,
                     state="readonly",
                     textvariable=self._bg_scale_var,
                     values=["none", "cover", "fit", "stretch"],
                     width=7).grid(column=1, row=0, sticky="news")
        ToolTip(
            frame, "How to scale the background image\nnone: as-is\n"
            "cover: cover screen\nfit: fit within screen\nstretch: stretch all dimensions"
        )
        return frame

    def _handle_bg_scale(self, *_arg):
        self._config.set_str("image_scale", self._bg_scale_var.get())

    def _lanes(self) -> ttk.Widget:
        frame = ttk.Frame(self, padding=1)
        frame.rowconfigure(0, weight=1)
        frame.columnconfigure(0, weight=1)
        ttk.Label(frame, text="Lane count:").grid(column=0,
                                                  row=0,
                                                  sticky="news")
        self._lane_spin_var = StringVar(frame,
                                        value=str(
                                            self._config.get_int("num_lanes")))
        self._lane_spin_var.trace_add("write", self._handle_lane_spin)
        ttk.Spinbox(frame,
                    from_=6,
                    to=10,
                    increment=1,
                    width=3,
                    textvariable=self._lane_spin_var).grid(column=1,
                                                           row=0,
                                                           sticky="news")
        ToolTip(frame, "Number of lanes to display on the scoreboard")
        return frame

    def _handle_lane_spin(self, *_arg):
        try:
            value = int(self._lane_spin_var.get())
            if 6 <= value <= 10:
                self._config.set_int("num_lanes", value)
        except ValueError:
            pass

    def _font_chooser(self, text, config_option, tooltip) -> ttk.Widget:
        frame = ttk.Frame(self, padding=1)
        frame.rowconfigure(0, weight=1)
        frame.columnconfigure(0, weight=0)
        ttk.Label(frame, text=text).grid(column=0, row=0, sticky="nws")

        def callback(fontname):
            self._config.set_str(config_option, fontname)

        dropdown = ttkwidgets.font.FontFamilyDropdown(frame, callback)
        dropdown.set(self._config.get_str(config_option))
        dropdown.grid(column=1, row=0, sticky="news")
        ToolTip(frame, tooltip)
        return frame

    def _font_scale(self) -> ttk.Widget:
        frame = ttk.Frame(self, padding=1)
        frame.rowconfigure(0, weight=1)
        frame.columnconfigure(0, weight=1)
        ttk.Label(frame, text="Font scale:").grid(column=0,
                                                  row=0,
                                                  sticky="nes")
        self._font_spin_var = StringVar(
            frame, value=str(self._config.get_float("font_scale")))
        self._font_spin_var.trace_add("write", self._handle_font_spin)
        ttk.Spinbox(frame,
                    from_=0,
                    to=1,
                    increment=0.01,
                    width=5,
                    textvariable=self._font_spin_var).grid(column=1,
                                                           row=0,
                                                           sticky="news")
        ToolTip(frame, "Scale of font relative to line height [0.0, 1.0]")
        return frame

    def _handle_font_spin(self, *_arg):
        try:
            value = float(self._font_spin_var.get())
            if 0 <= value <= 1:
                self._config.set_float("font_scale", value)
        except ValueError:
            pass

    def _inhibit(self) -> ttk.Widget:
        frame = ttk.Frame(self, padding=1)
        frame.rowconfigure(0, weight=1)
        frame.columnconfigure(0, weight=1)
        ttk.Label(frame, text="Suppress >0.3s:").grid(column=0,
                                                      row=0,
                                                      sticky="nes")
        self._inhibit_var = BooleanVar(
            frame, value=self._config.get_bool("inhibit_inconsistent"))
        ttk.Checkbutton(frame,
                        variable=self._inhibit_var,
                        command=self._handle_inhibit).grid(column=1,
                                                           row=0,
                                                           sticky="news")
        ToolTip(
            frame,
            "Select to suppress final time if times differ by more than 0.3s")
        return frame

    def _handle_inhibit(self, *_arg):
        self._config.set_bool("inhibit_inconsistent", self._inhibit_var.get())
Esempio n. 27
0
def widget_minute_seconds(parent: tk.Frame, var: tk.StringVar, conf: Property) -> tk.Widget:
    """A widget for specifying times - minutes and seconds.

    The value is saved as seconds.
    Max specifies the largest amount.
    """
    max_value = conf.int('max', 60)
    min_value = conf.int('min', 0)
    if min_value > max_value:
        raise ValueError('Bad min and max values!')

    values = timer_values(min_value, max_value)

    # Stores the 'pretty' value in the actual textbox.
    disp_var = tk.StringVar()

    existing_value = var.get()

    def update_disp(var_name: str, var_index: str, operation: str) -> None:
        """Whenever the string changes, update the displayed text."""
        seconds = conv_int(var.get(), -1)
        if min_value <= seconds <= max_value:
            disp_var.set('{}:{:02}'.format(seconds // 60, seconds % 60))
        else:
            LOGGER.warning('Bad timer value "{}" for "{}"!', var.get(), conf['id'])
            # Recurse, with a known safe value.
            var.set(values[0])

    # Whenever written to, call this.
    var.trace_add('write', update_disp)

    def set_var():
        """Set the variable to the current value."""
        try:
            minutes, seconds = disp_var.get().split(':')
            var.set(str(int(minutes) * 60 + int(seconds)))
        except (ValueError, TypeError):
            pass

    def validate(reason: str, operation_type: str, cur_value: str, new_char: str, new_value: str):
        """Validate the values for the text.

        This is called when the textbox is modified, to allow cancelling bad
        inputs.

        Reason is the reason this was fired: 'key', 'focusin', 'focusout', 'forced'.
        operation_type is '1' for insert, '0' for delete', '-1' for programmatic changes.
        cur_val is the value before the change occurs.
        new_char is the added/removed text.
        new_value is the value after the change, if accepted.
        """
        if operation_type == '0' or reason == 'forced':
            # Deleting or done by the program, allow that always.
            return True

        if operation_type == '1':  # Inserted text.
            # Disallow non number and colons
            if new_char not in '0123456789:':
                return False
            # Only one colon.
            if ':' in cur_value and new_char == ':':
                return False

            # Don't allow more values if it has more than 2 numbers after
            # the colon - if there is one, and it's not in the last 3 characters.
            if ':' in new_value and ':' not in new_value[-3:]:
                return False

        if reason == 'focusout':
            # When leaving focus, apply range limits and set the var.
            try:
                str_min, str_sec = new_value.split(':')
                seconds = int(str_min) * 60 + int(str_sec)
            except (ValueError, TypeError):
                seconds = min_value
            else:
                if seconds < min_value:
                    seconds = min_value
                if seconds > max_value:
                    seconds = max_value
            var.set(str(seconds))  # This then re-writes the textbox.
        return True

    validate_cmd = parent.register(validate)

    spinbox = tk.Spinbox(
        parent,
        exportselection=False,
        textvariable=disp_var,
        command=set_var,
        wrap=True,
        values=values,
        width=5,

        validate='all',
        # These define which of the possible values will be passed along.
        # http://tcl.tk/man/tcl8.6/TkCmd/spinbox.htm#M26
        validatecommand=(validate_cmd, '%V', '%d', '%s', '%S', '%P'),
    )
    # We need to set this after, it gets reset to the first one.
    var.set(existing_value)
    return spinbox
Esempio n. 28
0
 def font_dialogue(self):
     '''Font selection: dialogue for font choices'''
     dprint(3, "\nTkMemobook::font_dialogue:: ")
     def set_string_variable(var, val):
         var.set(val)
     def set_font(getter, fam, sz, wt):
         self.ctrl["font"]["family"] = fam
         self.ctrl["font"]["size"] = sz
         self.ctrl["font"]["weight"] = wt
         self.tabs.set_page_font(fam, sz, wt)
         getter.destroy()
     getter = Toplevel(self.root)
     getter.title("Font selection")
     family_str = StringVar(getter)
     family_str.set(self.ctrl["font"]["family"])
     size_str = StringVar(getter)
     size_str.set(self.ctrl["font"]["size"])
     weight_str = StringVar(getter)
     weight_str.set(self.ctrl["font"]["weight"])
     possible_fams = list(font.families(root=self.root))
     possible_fams.sort(key=lambda x: x[0])
     display_frame = Frame(getter)
     instr_frame = Frame(display_frame)
     instr_instr = Label(instr_frame,
                         text="Please select a font:")
     instr_examp = Label(instr_frame,
                         text=family_str.get(),
                         font=(family_str.get(),12,"normal"))
     family_str.trace_add("write",
                          lambda x,y,z: instr_examp.config(text=family_str.get(),
                                                           font=(family_str.get(),
                                                                 12,
                                                                 weight_str.get())))
     weight_str.trace_add("write",
                          lambda x,y,z: instr_examp.config(text=family_str.get(),
                                                           font=(family_str.get(),
                                                                 12,
                                                                 weight_str.get())))
     label_frame = Frame(display_frame)
     label_family = Label(label_frame,text="Family:")
     label_size = Label(label_frame,text="Size:")
     label_weight = Label(label_frame,text="Weight:")
     choice_frame = Frame(display_frame)
     choice_family = Combobox(choice_frame,
                              textvariable=family_str,
                              values=possible_fams)
     choice_size = Combobox(choice_frame,
                            textvariable=size_str,
                            values=(6,8,10,12,14,16,18,20,24,28,32,48,64,72),
                            postcommand=lambda: set_string_variable(size_str,
                                                                    self.ctrl["font"]["size"]))
     choice_weight = Combobox(choice_frame,
                              textvariable=weight_str,
                              values=("normal","bold","italic"))
     finish_frame = Frame(getter)
     finish_cancel = Button(finish_frame,
                            text="Cancel",
                            command=lambda: getter.destroy())
     finish_apply = Button(finish_frame,
                           text="Apply",
                           command=lambda: set_font(getter,
                                                    family_str.get(),
                                                    size_str.get(),
                                                    weight_str.get()))
     instr_instr.pack(side="top", anchor="w")
     instr_examp.pack(side="bottom", anchor="w")
     instr_frame.pack()
     label_family.pack(anchor="w")
     label_size.pack(anchor="w")
     label_weight.pack(anchor="w")
     label_frame.pack(side="left")
     choice_family.pack(anchor="w", fill="x", expand="true")
     choice_size.pack(anchor="w", fill="x", expand="true")
     choice_weight.pack(anchor="w", fill="x", expand="true")
     choice_frame.pack(side="left", expand="true", fill="x")
     display_frame.pack(side="top", expand="true", fill="both")
     finish_cancel.pack(side="left")
     finish_apply.pack(side="right")
     finish_frame.pack(side="bottom")
Esempio n. 29
0
class CreateReminderGUI:
    def __init__(self, master):
        self.master = master
        master.title("Reminder")
        self.label = Label(master, text="PyReminder")
        self.label.pack()
        self.start_button = Button(master, text="Start", command=self.start)
        self.start_button.pack()
        # Target = reminder date/time
        self.target_var = StringVar()
        self.target_var.trace_add("write", self.parse)
        self.target_e = Entry(master, textvariable=self.target_var)
        self.target_e.pack()
        self.target_var.set("10 m")
        # Message = message to show
        self.msg_var = StringVar()
        self.msg_e = Entry(master, textvariable=self.msg_var)
        self.msg_e.pack()
        self.msg_var.set("Message")

    def validateInput(self, value):
        if value is not None:
            self.target_e.config(background="green")
        else:
            self.target_e.config(background="red")
        self.target = value

    def parse(*args, **kwargs):
        self = args[0]
        try:
            text = self.target_var.get().strip()
            t = text[0]
            if t == 'd' or t == 't':
                text = text[1:].strip()
                if t == 't':
                    text = str(dt.now())[0:10] + ' ' + text
                try:
                    dt.strptime(text, '%Y-%m-%d %H:%M:%S')
                except:
                    text += ":00"
                    try:
                        dt.strptime(text, '%Y-%m-%d %H:%M:%S')
                    except:
                        text += ":00"
                target = dt.strptime(text, '%Y-%m-%d %H:%M:%S')
                if target < dt.now():
                    target += timedelta(days=1)
                self.validateInput(target)
            else:
                text = text.split(' ')[0:2]
                num = float(text[0])
                kind = text[1]
                target = dt.now() + _time_types[kind](num)
                self.validateInput(target)
        except Exception as e:
            print(e)
            self.validateInput(None)

    def start(self):
        logging.debug("#start")
        if self.target is not None:
            output = str(self.target)
            if output[-7] != '.':
                output += ".000000"
            logging.debug("new target: " + output)
            with open(rand_file(), 'w+') as f:
                f.write(output)
                f.write("\n")
                f.write(self.msg_var.get())
        else:
            print("No reminder set.")
        logging.debug("closing gui")
        self.master.quit()
Esempio n. 30
0
class App(Frame):
    def log(self, value):
        self.text.configure(state="normal")
        self.text.insert(END, value + "\n")
        self.text.see(END)
        self.text.configure(state="disabled")

    def _set_download_buttons(self, state):
        if state:
            self.btn_download.configure(state="normal")
            self.btn_download_txt.configure(state="normal")
        else:
            self.btn_download.configure(state="disabled")
            self.btn_download_txt.configure(state="disabled")

    def download(self):
        username_text = self.entry_filename.get()
        if not username_text:
            messagebox.showinfo(title="Warning", message="Please input usernames")
            return
        self._set_download_buttons(False)
        usernames = username_text.split(",")
        self.core.root_path = self.root_path.get()
        self.core.download_by_usernames(usernames, self.combobox_type.current())
        self._set_download_buttons(True)

    def download_txt(self):
        self.btn_download.configure(state="disabled")
        self.btn_download_txt.configure(state="disabled")
        filename = os.path.normpath(
            filedialog.askopenfilename(
                filetypes=(("text files", "*.txt"), ("all files", "*.*"))
            )
        )
        if filename != ".":
            with open(filename, "r", encoding="utf-8") as f:
                usernames = []
                # 预处理,去掉注释与空白符
                for username in f.readlines():
                    username = username.strip()
                    if not username:
                        continue
                    sharp_at = username.find("#")
                    if sharp_at == 0:
                        continue
                    if sharp_at != -1:
                        username = username[:sharp_at]
                    usernames.append(username.strip())
            self.core.root_path = self.root_path.get()
            self.core.download_by_usernames(usernames, self.combobox_type.current())
        self.btn_download.configure(state="normal")
        self.btn_download_txt.configure(state="normal")

    def load_root_path(self):
        return config.read_config("config.ini", "Paths", "root_path")

    def save_root_path(self, root_path):
        config.write_config("config.ini", "Paths", "root_path", root_path)

    def browse_directory(self):
        root_path = os.path.normpath(filedialog.askdirectory())
        if root_path:
            self.root_path.set(root_path)
            self.save_root_path(root_path)

    def createWidgets(self):
        frame_tool = Frame(self.window)
        frame_path = Frame(self.window)
        frame_log = Frame(self.window)
        self.lbl_username = Label(frame_tool, text="Usernames(split by ','):")
        self.entry_filename = Entry(frame_tool)
        self.btn_download = Button(
            frame_tool,
            text="Download",
            command=lambda: self.invoke(self.download),
        )
        self.btn_download_txt = Button(
            frame_tool,
            text="Download txt",
            command=lambda: self.invoke(self.download_txt),
        )
        self.lbl_type = Label(frame_path, text="Type:")
        self.combobox_type = ttk.Combobox(frame_path, state="readonly")
        self.combobox_type["values"] = ("all", "image", "video")
        self.combobox_type.current(0)
        self.lbl_path = Label(frame_path, text="Path:")
        self.entry_path = Entry(frame_path, textvariable=self.root_path)
        self.btn_path_dialog = Button(
            frame_path, text="Browse", command=self.browse_directory
        )
        self.scrollbar = Scrollbar(frame_log)
        self.text = Text(frame_log)
        self.text.configure(state="disabled")
        self.lbl_status = Label(
            self.window,
            text="Feel free to use! Support: Sean Feng([email protected])",
        )

        frame_tool.pack(side=TOP, fill=X)
        self.lbl_username.pack(side=LEFT)
        self.entry_filename.pack(side=LEFT, fill=X, expand=True)
        self.btn_download.pack(side=LEFT)
        self.btn_download_txt.pack(side=LEFT)
        frame_path.pack(side=TOP, fill=X)
        self.lbl_type.pack(side=LEFT)
        self.combobox_type.pack(side=LEFT)
        self.lbl_path.pack(side=LEFT)
        self.entry_path.pack(side=LEFT, fill=X, expand=True)
        self.btn_path_dialog.pack(side=LEFT)
        self.text.pack(side=LEFT, fill=BOTH, expand=True)
        self.scrollbar.pack(side=LEFT, fill=Y)
        frame_log.pack(side=TOP, fill=BOTH, expand=True)
        self.scrollbar.config(command=self.text.yview)
        self.text.config(yscrollcommand=self.scrollbar.set)
        self.text.focus()
        self.lbl_status.pack(side=LEFT, fill=X, expand=True)

    def invoke(self, func):
        def done_callback(worker):
            worker_exception = worker.exception()
            if worker_exception:
                self.log(str(worker_exception))
                self._set_download_buttons(True)
                raise(worker_exception)
        return self.executor_ui.submit(func).add_done_callback(done_callback)

    def __init__(self, version):
        self.core = Core(self.log)
        master = Tk()
        Frame.__init__(self, master)
        master.title("ArtStation Downloader " + version)  # 定义窗体标题
        self.root_path = StringVar()
        self.root_path.trace_add(
            "write", lambda name, index, mode: self.save_root_path(self.root_path.get())
        )
        root_path_config = self.load_root_path()
        self.root_path.set(
            root_path_config or os.path.join(os.path.expanduser("~"), "ArtStation")
        )
        self.executor_ui = futures.ThreadPoolExecutor(1)
        self.window = master
        self.pack()
        self.createWidgets()
Esempio n. 31
0
def widget_minute_seconds(parent: tk.Frame, var: tk.StringVar,
                          conf: Property) -> tk.Widget:
    """A widget for specifying times - minutes and seconds.

    The value is saved as seconds.
    Max specifies the largest amount.
    """
    max_value = conf.int('max', 60)
    min_value = conf.int('min', 0)
    if min_value > max_value:
        raise ValueError('Bad min and max values!')

    values = timer_values(min_value, max_value)

    # Stores the 'pretty' value in the actual textbox.
    disp_var = tk.StringVar()

    existing_value = var.get()

    def update_disp(var_name: str, var_index: str, operation: str) -> None:
        """Whenever the string changes, update the displayed text."""
        seconds = conv_int(var.get(), -1)
        if min_value <= seconds <= max_value:
            disp_var.set('{}:{:02}'.format(seconds // 60, seconds % 60))
        else:
            LOGGER.warning('Bad timer value "{}" for "{}"!', var.get(),
                           conf['id'])
            # Recurse, with a known safe value.
            var.set(values[0])

    # Whenever written to, call this.
    var.trace_add('write', update_disp)

    def set_var():
        """Set the variable to the current value."""
        try:
            minutes, seconds = disp_var.get().split(':')
            var.set(str(int(minutes) * 60 + int(seconds)))
        except (ValueError, TypeError):
            pass

    def validate(reason: str, operation_type: str, cur_value: str,
                 new_char: str, new_value: str):
        """Validate the values for the text.

        This is called when the textbox is modified, to allow cancelling bad
        inputs.

        Reason is the reason this was fired: 'key', 'focusin', 'focusout', 'forced'.
        operation_type is '1' for insert, '0' for delete', '-1' for programmatic changes.
        cur_val is the value before the change occurs.
        new_char is the added/removed text.
        new_value is the value after the change, if accepted.
        """
        if operation_type == '0' or reason == 'forced':
            # Deleting or done by the program, allow that always.
            return True

        if operation_type == '1':  # Inserted text.
            # Disallow non number and colons
            if new_char not in '0123456789:':
                return False
            # Only one colon.
            if ':' in cur_value and new_char == ':':
                return False

            # Don't allow more values if it has more than 2 numbers after
            # the colon - if there is one, and it's not in the last 3 characters.
            if ':' in new_value and ':' not in new_value[-3:]:
                return False

        if reason == 'focusout':
            # When leaving focus, apply range limits and set the var.
            try:
                str_min, str_sec = new_value.split(':')
                seconds = int(str_min) * 60 + int(str_sec)
            except (ValueError, TypeError):
                seconds = min_value
            else:
                if seconds < min_value:
                    seconds = min_value
                if seconds > max_value:
                    seconds = max_value
            var.set(str(seconds))  # This then re-writes the textbox.
        return True

    validate_cmd = parent.register(validate)

    spinbox = tk.Spinbox(
        parent,
        exportselection=False,
        textvariable=disp_var,
        command=set_var,
        wrap=True,
        values=values,
        width=5,
        validate='all',
        # These define which of the possible values will be passed along.
        # http://tcl.tk/man/tcl8.6/TkCmd/spinbox.htm#M26
        validatecommand=(validate_cmd, '%V', '%d', '%s', '%S', '%P'),
    )
    # We need to set this after, it gets reset to the first one.
    var.set(existing_value)
    return spinbox
Esempio n. 32
0
class MacForger(Frame):

    def __init__(self):
        super().__init__()

        self.interfaces = []                            # List of all network interfaces
        self.adapters = {}                              # Dict with all network interfaces info { name : (MAC, GUID) }
        self.mac = StringVar()                          # Written MAC
        self.actual_mac = StringVar()                   # Actual MAC of selected interface
        self.interface_selected = StringVar()           # Actual interface selected
        self.reload_interface = BooleanVar(value=True)  # Reload after MAC change
        self.fix_first_byte = BooleanVar(value=True)    # Some adapters accept MAC spoof only if the first byte's equal than 22

        self.update_interfaces()        # Update adapters info
        self.init_ui()                  # Initialize user interface
        self.set_default_interface()    # We set Wifi adapter as default one
        self.reload_mac()               # Reload MAC shown

    def init_ui(self):
        self.master.title("MAC forger")
        self.master.resizable(False, False)

        Style().configure("TButton", padding=(0, 5, 0, 5), font='serif 10', foreground='Blue')
        Style().configure("TLabel", font='serif 10')
        Style().configure("TCheckbutton", foreground='Blue')
        Style().configure("mac.TLabel", font='serif 12', foreground='Red')

        title = Label(self, text="MAC forger")
        title.grid(row=0)

        interfaces_label = Label(self.master, text="Interface:")
        interfaces_label.grid(row=1, column=0, sticky=E, pady=10, padx=20)
        self.interface_selected.set(self.interfaces[0])
        interfaces = OptionMenu(self.master, self.interface_selected, *self.interfaces)
        interfaces.grid(row=1, column=1, sticky=(E, W), pady=10, padx=20)
        self.interface_selected.trace_add('write', self.reload_mac)
        reload_button = Button(self.master, text="Reload", command=self.update_interface)
        reload_button.grid(row=1, column=2, pady=10, padx=10)
        reload_button = Button(self.master, text="Reload All", command=self.update_interfaces)
        reload_button.grid(row=1, column=3, pady=10, padx=10)

        actual_mac_label = Label(self.master, text="Actual MAC Address:")
        actual_mac_label.grid(row=2, column=0, sticky=E, pady=10, padx=20, rowspan=2)
        actual_mac = Label(self.master, style='mac.TLabel', justify=CENTER, textvariable=self.actual_mac)
        actual_mac.grid(row=2, column=1, pady=10, padx=20, rowspan=2)
        self.actual_mac.set("FF : FF : FF : FF : FF : FF")
        reload_interface = Checkbutton(self.master, text=" Reload interface\n after changes", variable=self.reload_interface)
        reload_interface.grid(row=2, column=2, padx=5, rowspan=2, sticky=(N, W))
        fix_button = Checkbutton(self.master, text="  Fix 1° byte", variable=self.fix_first_byte)
        fix_button.grid(row=3, column=2, padx=5, rowspan=2, sticky=(N, W))
        reset_button = Button(self.master, text="Restore\n Default", command=self.reset_mac)
        reset_button.grid(row=2, column=3, pady=10, padx=5, rowspan=2)

        mac_label = Label(self.master, text="MAC Address: ")
        mac_label.grid(row=4, column=0, sticky=E, pady=10, padx=20)
        mac_entry = Entry(width=25, justify=CENTER, font='serif 12', textvariable=self.mac)
        mac_entry.grid(row=4, column=1, pady=10, padx=20)
        self.mac.set("FF:FF:FF:FF:FF:FF")
        self.mac.trace_add('write', self.mac_entry_rules)
        random_button = Button(self.master, text="Generate\nRandom", command=self.random_mac)
        random_button.grid(row=4, column=2, pady=10, padx=5)
        change_button = Button(self.master, text="Confirm\n  MAC", command=self.change_mac)
        change_button.grid(row=4, column=3, pady=10, padx=5)

    def set_default_interface(self):
        # Set Wifi adapter as default one if exists
        try:
            self.adapters["Wi-Fi"]
            self.interface_selected.set("Wi-Fi")
        except KeyError:
            self.interface_selected.set(self.interfaces[0])

    def mac_entry_rules(self, *args):
        # This function provides a real time validation for the MAC address
        pattern = re.compile('(?:[0-9a-fA-F:]:?)')
        if len(self.mac.get()) > 17:
            self.mac.set(self.mac.get()[0:17])
        for i in self.mac.get():
            if not bool(pattern.match(i)):
                self.mac.set(self.mac.get().replace(i, ''))
        self.mac.set(self.mac.get().upper())

    def validate_mac(self):
        # This function validates the MAC given before trying to change the old one
        tmp = self.mac.get()
        # If starts with : we add two zero
        if tmp[0] == ":":
            tmp = "00"+tmp
        # Check the correct length
        if len(tmp) == 12:
            # Add the two points where are missing
            tmp = tmp[:2] + ':' + tmp[2:4] + ':' + tmp[4:6] + ':' + tmp[6:8] + ':' + tmp[8:10] + ':' + tmp[10:12]
        if len(tmp) < 17:
            tmp = tmp + "0" * (17 - len(tmp))
        # Add : where are needed
        hot_idx = [2, 5, 8, 11, 14]
        for i in hot_idx:
            if tmp[i] != ":":
                tmp = tmp[:i] + ":" + tmp[i+1:]
        self.mac.set(tmp)
        # Check again the length
        if len(self.mac.get()) > 17:
            self.mac.set(self.mac.get()[0:17])
        # Check the pattern
        pattern = re.compile('(?:[0-9a-fA-F]:?){12}')
        return bool(pattern.match(self.mac.get()))

    def reload_mac(self, *args):
        # Reload the MAC label with the actual MAC of the selected interface
        try:
            self.actual_mac.set(self.adapters[self.interface_selected.get()][0].replace(":", " : ").upper())
        except AttributeError:
            # This occurs when the interface is virtual and doesn't have a physical address
            self.actual_mac.set("00 : 00 : 00 : 00 : 00 : 00")

    def reload(self, guid):
        # If required reload the network interface
        if self.reload_interface.get():
            if changeMac.restart_network_interface(guid=guid):
                messagebox.showinfo("Info", "Interface reloaded")
            else:
                messagebox.showinfo("Info", "Cannot reload interface.\nProbably the interface is off.")

    def change_mac(self):
        # Set the MAC of the selected interfaces with the given value
        guid = self.adapters[self.interface_selected.get()][1]
        if self.validate_mac():
            print("Trying to change the MAC of {} to {}".format(guid, self.mac.get()))
            if messagebox.askokcancel("Spoof", "Trying to change the MAC of {} to {}".format(guid, self.mac.get()), default="cancel", icon="warning"):
                changeMac.set_mac_value(self.mac.get(), guid=guid)
                self.reload(guid)
                self.update_interface()
                self.reload_mac()
            else:
                print("MAC changes cancelled")
        else:
            print("Not valid MAC inserted")
            messagebox.showwarning("Warning", "Not valid MAC inserted")

    def reset_mac(self):
        # Restore the original MAC
        guid = self.get_guid()
        print("Trying to restore the MAC of {}".format(guid))
        if messagebox.askokcancel("Restore", "Trying to restore the MAC of {}".format(guid), default="cancel", icon="warning"):
            changeMac.remove_mac_value(guid=guid)
            self.reload(guid)
            self.update_interface()
            self.reload_mac()
        else:
            print("MAC restore cancelled")

    def random_mac(self):
        # Define a Random MAC
        mac = ''
        # Manufacturer's bytes
        r = randint(0, 13)
        if r == 0:
            mac += "CC:46:D6"
        elif r == 1:
            mac += "3C:5A:B4"
        elif r == 2:
            mac += "3C:D9:2B"
        elif r == 3:
            mac += "00:9A:CD"
        elif r == 4:
            mac += "D0:D0:03"
        elif r == 5:
            mac += "B0:65:F1"
        elif r == 6:
            mac += "AC:B1:EE"
        elif r == 7:
            mac += "AC:81:F3"
        elif r == 8:
            mac += "E8:CC:32"
        elif r == 9:
            mac += "00:30:48"
        elif r == 10:
            mac += "40:D3:AE"
        elif r == 11:
            mac += "10:72:23"
        elif r == 12:
            mac += "00:21:2F"
        elif r == 13:
            mac += "00:14:5A"
        if self.fix_first_byte.get():
            # Some adapters allow to spoof MAC but with the first byte must be equal then 22
            mac = "22" + ':' + mac[3:]
        mac += ":"
        # Random bytes
        for i in range(6):
            r = randint(0, 15)
            if r >= 10:
                if r == 10:
                    r = "A"
                elif r == 11:
                    r = "B"
                elif r == 12:
                    r = "C"
                elif r == 13:
                    r = "D"
                elif r == 14:
                    r = "E"
                elif r == 15:
                    r = "F"
            else:
                r = str(r)
            mac += r
            if i != 5 and (i % 2) != 0:
                mac += ":"
        self.mac.set(mac)

    def get_guid(self, interface=None):
        # GUID is properly name of network interfaces in Windows
        guid = None
        adapters = get_adapters()
        if interface is None:
            interface = self.interface_selected.get()
        for a in adapters:
            if a.ips[0].nice_name == interface:
                guid = a.name
        return str(guid)

    def update_interface(self):
        # Update the selected interface info ( MAC, GUID)
        self.adapters[self.interface_selected.get()] = \
            (get_mac_address(self.interface_selected.get()), self.get_guid())
        # Reload MAC label
        self.reload_mac()

    def update_interfaces(self):
        # Get the available interfaces and the relative info
        self.interfaces = []
        self.adapters = {}
        interfaces = list(net_if_addrs().keys())
        for i in interfaces:
            # Get MAC
            mac = get_mac_address(i)
            if mac is not None:
                # Get GUID
                guid = self.get_guid(interface=i)
                self.interfaces.append(i)
                self.adapters[i] = (mac, guid)
        # Set the default
        self.set_default_interface()
        # Reload MAC label
        self.reload_mac()