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
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)
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)
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
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()
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)
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
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)
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
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)
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
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
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)
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()
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
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
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
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
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
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
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()
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))
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 = '{}®={}'.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¬ams=0&firnot=0&maps=none'.format(url) # For show Simbiref. else: url = '{}&planformat=LIDO&units=KGS&navlog=1&etops=1&stepclimbs=0&tlr=0¬ams=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])
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)
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())
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
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")
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()
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()
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()