def check_newest_version(release_repo): try: from packaging import version as pk_version latest = get_releases('oskros/MF_counter_releases')[0] latest_ver = latest['tag_name'] if pk_version.parse(version) < pk_version.parse(latest_ver): mbox(b1='OK', b2='', msg='Your version is not up to date. Get the newest release from', title='New version', hyperlink=release_repo) except Exception as e: print(e) pass
def delete_archived_session(self): chosen = self.archive_dropdown.get() if chosen == '': # If nothing is selected the function returns return if chosen == 'Profile history': tk.messagebox.showerror('Error', 'You cannot delete profile history from here. Please delete all sessions manually, or delete the profile instead') return resp = tk_utils.mbox(msg='Type "DELETE" to confirm you wish to delete the session "%s" from archive\n\nIt will be permanently deleted' % chosen, title='WARNING', disabled_btn_input='DELETE') if resp == 'DELETE': if chosen == 'Active session': # Here we simply reset the timer module self.main_frame.ResetSession() self.main_frame.SaveActiveState() self.selected_archive.set('Profile history') self.update_descriptive_statistics() else: # Load the profile .json, delete the selected session and save the modified dictionary back to the .json cache = self.main_frame.load_state_file() removed = cache.pop(chosen, None) file = 'Profiles/%s.json' % self.active_profile.get() other_utils.atomic_json_dump(file, cache) # Update archive dropdown and descriptive statistics self.available_archive.remove(chosen) self.archive_dropdown['values'] = self.available_archive self.selected_archive.set('Profile history') self.tot_laps -= len(removed.get('laps', [])) self.main_frame.timer_tab._set_laps(self.main_frame.timer_tab.is_running) self.update_descriptive_statistics() self.profile_dropdown['values'] = self.main_frame.sorted_profiles()
def check_newest_version(release_repo): try: from packaging import version as pk_version releases = get_releases('oskros/MF_run_counter') latest_ver = releases[0]['tag_name'] if pk_version.parse(version) < pk_version.parse(latest_ver): mbox(b1='OK', b2='', msg= 'Your version is not up to date. Get the newest release from', title='New version', hyperlink=release_repo) return str(sum(r['assets'][0]['download_count'] for r in releases)) except Exception as e: logging.debug(str(e)) return ''
def add_drop(self): drop = autocompletion.acbox(enable=True, title='Add drop') if not drop or drop['input'] == '': return if drop['item_name'] is not None: for i, item in enumerate(self.main_frame.grail_tab.grail): if item['Item'] == drop['item_name']: if item.get('Found', False) is False: if self.main_frame.auto_upload_herokuapp: resp = self.main_frame.grail_tab.upload_to_herokuapp( upd_dict={item['Item']: True}, show_confirm=False, pop_up_msg="Congrats, a new drop! Add it to grail?\n\nHerokuapp login info:", pop_up_title="Grail item") else: resp = tk_utils.mbox(msg="Congrats, a new drop! Add it to local grail?", title="Grail item") if resp is not None: self.main_frame.grail_tab.update_grail_from_index(i) drop['input'] = '(*) ' + drop['input'] break run_no = len(self.main_frame.timer_tab.laps) if self.main_frame.timer_tab.is_running: run_no += 1 self.drops.setdefault(str(run_no), []).append(drop) self.display_drop(drop=drop, run_no=run_no)
def delete_archived_session(self): chosen = self.archive_dropdown.get() if chosen == '': # If nothing is selected the function returns return if chosen == 'Profile history': tk.messagebox.showerror('Error', 'You cannot delete profile history from here. Please delete all sessions manually, or delete the profile instead') return resp = tk_utils.mbox(msg='Do you really want to delete this session from archive? It will be permanently deleted', title='WARNING') if resp: if chosen == 'Active session': # Here we simply reset the timer module self.main_frame.ResetSession() self.main_frame.SaveActiveState() self.selected_archive.set('Profile history') self.update_descriptive_statistics() else: # Load the profile .json, delete the selected session and save the modified dictionary back to the .json cache = self.main_frame.load_state_file() # cache.setdefault('extra_data', dict())['Last update'] = time.time() removed = cache.pop(chosen, None) file = 'Profiles/%s.json' % self.active_profile.get() with open(file, 'w') as fo: json.dump(cache, fo, indent=2) # Update archive dropdown and descriptive statistics self.available_archive.remove(chosen) self.archive_dropdown['values'] = self.available_archive self.selected_archive.set('Profile history') self.tot_laps -= len(removed.get('laps', [])) self.main_frame.timer_tab._set_laps(self.main_frame.timer_tab.is_running) self.update_descriptive_statistics() self.profile_dropdown['values'] = self.main_frame.sorted_profiles()
def delete_selected_run(self): if not self.m.curselection(): return sel = self.m.selection_get() if tk_utils.mbox(msg='Do you want to delete the run:\n%s' % sel, title='Warning'): all_runs = self.m.get(0, tk.END) sel_idx = all_runs.index(sel) self.laps.pop(sel_idx) self.m.delete(sel_idx, tk.END) for run in all_runs[sel_idx + 1:]: tmp = run[:run.find(':')] run_no = tmp[tmp.rfind(' ') + 1:] prev_no = str(int(run_no) - 1) if len(prev_no) < len(run_no): self.m.insert(tk.END, run.replace(run_no, ' ' + prev_no, 1)) else: self.m.insert(tk.END, run.replace(run_no, prev_no, 1)) self.m.yview_moveto(1) self._set_laps(add_lap=self.is_running) self._set_fastest() self._set_average()
def reset_grail(self): resp = tk_utils.mbox(msg='Are you sure you want to reset the locally stored grail file?', title='WARNING') if resp: self.grail = self.create_empty_grail() self.update_statistics() if self.grail_table_open: self.select_from_filters() for var in dir(self): if var.startswith('grail_item'): getattr(self, var).set(0)
def ArchiveReset(self, skip_confirm=False, notify_msg=None, stamp_from_epoch=None): """ If any laps or drops have been recorded, this function saves the current session to the profile archive, and resets all info in the active session. In case no runs/drops are recorded, the session timer is simply reset """ if not self.timer_tab.laps and not self.drops_tab.drops: self.ResetSession() return xc = self.root.winfo_rootx() - self.root.winfo_width() // 12 yc = self.root.winfo_rooty() + self.root.winfo_height() // 3 if notify_msg is not None: tk.messagebox.showinfo(title='Archive', message=notify_msg) if skip_confirm or tk_utils.mbox( 'Would you like to save and reset session?', b1='Yes', b2='No', coords=[xc, yc], master_root=self.root): # Stop any active run and load current session info from timer and drop module. self.timer_tab.stop() active = self.timer_tab.save_state() self.profile_tab.tot_laps += len(active['laps']) active.update(self.drops_tab.save_state()) active.update(self.advanced_stats_tracker.save_state()) # Update session dropdown for the profile if stamp_from_epoch is None: stamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) else: stamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(stamp_from_epoch)) self.profile_tab.available_archive.append(stamp) self.profile_tab.archive_dropdown[ 'values'] = self.profile_tab.available_archive # self.profile_tab.update_descriptive_statistics() # Update profile .json with the session state = self.load_state_file() state['active_state'] = dict() state[stamp] = active state.setdefault('extra_data', dict())['Last update'] = time.time() file = 'Profiles/%s.json' % self.active_profile with open(file, 'w') as fo: json.dump(state, fo, indent=2) # When session has been successfully saved, the session is reset self.ResetSession()
def _delete_profile(self): chosen = self.profile_dropdown.get() if chosen == '': # If nothing is selected the function returns return if len(self.profile_dropdown['values']) <= 1: tk.messagebox.showerror('Error', 'You need to have at least one profile, create a new profile before deleting this one.') return resp1 = tk_utils.mbox(msg='Are you sure you want to delete this profile?\nThis will permanently delete all records stored for the profile.', title='WARNING') if resp1 is True: resp2 = tk_utils.mbox(msg='Are you really really sure you want to delete the profile?\nFinal warning!', b1='Cancel', b2='OK', title='WARNING') if resp2 is False: # False here because we switch buttons around in second confirmation file = 'Profiles/%s.json' % chosen os.remove(file) self.main_frame.profiles.remove(chosen) # We change active profile to an existing profile self.main_frame.active_profile = self.main_frame.profiles[0] self.active_profile.set(self.main_frame.profiles[0]) self.profile_dropdown['values'] = self.main_frame.profiles self._change_active_profile()
def delete_selected_drops(self): if self.focus_get()._name == 'droplist': cur_row = self.m.get('insert linestart', 'insert lineend+1c').strip() resp = tk_utils.mbox(msg='Do you want to delete the row:\n%s' % cur_row, title='Warning') if resp is True: sep = cur_row.find(':') run_no = cur_row[:sep].replace('Run ', '') drop = cur_row[sep+2:] try: self.drops[run_no].remove(next(d for d in self.drops[run_no] if d['input'] == drop)) self.m.config(state=tk.NORMAL) self.m.delete('insert linestart', 'insert lineend+1c') self.m.config(state=tk.DISABLED) except StopIteration: pass self.main_frame.img_panel.focus_force()
def search_statistics(self): if not self.tabcontrol.select().endswith('frame'): return search_inp = tk_utils.mbox(msg="Search", entry=True) if search_inp is not None: cvar = tk.StringVar() start_pos = self.last_pos if self.last_src == search_inp else 1.0 pos = self.txt_list.search(search_inp, start_pos, stopindex=tk.END, count=cvar) if pos: line = int(pos.split('.')[0]) self.txt_list.see(pos) self.txt_list.tag_remove("search", 1.0, tk.END) self.txt_list.tag_add("search", f'{line}.0', f'{line+1}.0') self.last_src = search_inp self.last_pos = str(float(pos) + 0.1)
def _delete_profile(self): chosen = self.profile_dropdown.get() if chosen == '': # If nothing is selected the function returns return if len(self.profile_dropdown['values']) <= 1: tk.messagebox.showerror('Error', 'You need to have at least one profile, create a new profile before deleting this one.') return resp = tk_utils.mbox(msg='Type "DELETE" to confirm you wish to delete the profile "%s"\n\nThis will permanently delete all records stored for the profile.' % chosen, title='WARNING', disabled_btn_input='DELETE') if resp == 'DELETE': file = 'Profiles/%s.json' % chosen os.remove(file) self.main_frame.profiles.remove(chosen) self.profile_dropdown['values'] = self.main_frame.profiles # We change active profile to an existing profile self.active_profile.set(self.main_frame.profiles[0]) self._change_active_profile()
def add_drop(self): drop = autocompletion.acbox(enable=True, title='Add drop', unid_mode=self.main_frame.autocompletion_unids, add_to_last_run=self.main_frame.add_to_last_run) if not drop or drop['input'] == '': return if drop['item_name'] is not None: for i, item in enumerate(self.main_frame.grail_tab.grail): if self.main_frame.autocompletion_unids: base = ' '.join(drop['item_name'].split(' ')[:-1]) if base in NO_UNIQUE_MAP: drop['TC'] = NO_UNIQUE_MAP[base].get('TC', '') drop['Item Class'] = NO_UNIQUE_MAP[base].get('Item Class', '') break if base == item['Base Item']: drop['TC'] = item.get('TC', '') drop['Item Class'] = item.get('Item Class', '') break if item['Item'] == drop['item_name']: prefix = '' drop['Grailer'] = 'False' drop['Eth Grailer'] = '' if item.get('Found', False) is False: if self.main_frame.auto_upload_herokuapp: resp = self.main_frame.grail_tab.upload_to_herokuapp( upd_dict={item['Item']: True}, show_confirm=False, pop_up_msg="Congrats, a new drop! Add it to grail?\n\nHerokuapp login info:", pop_up_title="Grail item") else: resp = tk_utils.mbox(msg="Congrats, a new drop! Add it to local grail?", title="Grail item") if resp is not None: self.main_frame.grail_tab.update_grail_from_index(i) prefix += '(*)' drop['Grailer'] = 'True' if drop.get('eth', False) is True: drop['Eth Grailer'] = 'False' if drop.get('eth', False) is True and item.get('FoundEth', False) is False: if self.main_frame.auto_upload_herokuapp: resp = self.main_frame.grail_tab.upload_to_herokuapp( eth_dict={item['Item']: True}, show_confirm=False, pop_up_msg="Congrats, a new eth drop! Add it to eth grail?\n\nHerokuapp login info:", pop_up_title="Eth grail item") else: resp = tk_utils.mbox(msg="Congrats, a new eth drop! Add it to local eth grail?", title="Eth grail item") if resp is not None: self.main_frame.grail_tab.grail[i].update({'FoundEth': True}) prefix += '(*)' drop['Eth Grailer'] = 'True' drop['input'] = (prefix + ' ' + drop['input']).strip() drop['TC'] = item.get('TC', '') drop['QLVL'] = item.get('QLVL', '') drop['Item Class'] = item.get('Item Class', '') break last_run = drop.pop('last_run', False) run_no = len(self.main_frame.timer_tab.laps) if self.main_frame.timer_tab.is_running: run_no += 1 if last_run and run_no > 0: run_no -= 1 self.main_frame.add_to_last_run = last_run drop['Real time'] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) drop['Profile'] = self.main_frame.active_profile self.drops.setdefault(str(run_no), []).append(drop) self.display_drop(drop=drop, run_no=run_no)
try: import win32gui import logging import sys from utils import tk_utils from master_frame import MasterFrame if win32gui.FindWindow(None, 'MF run counter'): resp = tk_utils.mbox(msg='It seems like you already have an instance of MF Run Counter open.\n' 'Opening another instance will make the app unstable (If this is a false positive, just ignore it)\n\n' 'Do you wish to continue anyway?', title='WARNING') if not resp: sys.exit(0) MasterFrame() except Exception as e: logging.exception(e) raise e
def toggle_automode_btn(self, first=False, show_error=True): got_val = other_utils.safe_eval(self.automode_var.get()) if first is False and self.main_frame.automode == got_val: return self.main_frame.automode = got_val if got_val == 1: self.gamemode_frame.pack(expand=False, fill=tk.X) self.gamemode_lab.pack(side=tk.LEFT) self.gamemode_cb.pack(side=tk.RIGHT) self.charname_frame.pack(expand=False, fill=tk.X) self.charname_text_lab.pack(side=tk.LEFT) self.charname_val_lab.pack(side=tk.RIGHT) self.sp_path_lab.pack(pady=[0, 0]) self.sp_path_entry.pack(fill=tk.BOTH, padx=4) self.sp_path_frame.pack() self.sp_path_get.pack(side=tk.LEFT, padx=1) self.sp_path_apply.pack(side=tk.LEFT) self.mp_path_lab.pack(pady=[0, 0]) self.mp_path_entry.pack(fill=tk.BOTH, padx=4) self.mp_path_frame.pack() self.mp_path_get.pack(side=tk.LEFT, padx=1) self.mp_path_apply.pack(side=tk.LEFT) else: self.gamemode_frame.forget() self.gamemode_lab.forget() self.gamemode_cb.forget() self.charname_frame.forget() self.charname_text_lab.forget() self.charname_val_lab.forget() self.sp_path_lab.forget() self.sp_path_entry.forget() self.sp_path_frame.forget() self.sp_path_get.forget() self.sp_path_apply.forget() self.mp_path_lab.forget() self.mp_path_entry.forget() self.mp_path_frame.forget() self.mp_path_get.forget() self.mp_path_apply.forget() if got_val == 2: if first is False and not tk_utils.mbox( msg= 'Activating "Advanced automode" is highly discouraged when playing multiplayer, as it might result in a ban.\n\n' 'Explanation: Advanced automode utilizes "memory reading" of the D2 process\n' 'to discover information about the current game state, and this could be deemed cheating\n\n' 'If you still wish to continue, click "OK"'): self.automode_var.set('0') return self.toggle_automode_btn(first=first, show_error=show_error) else: self.main_frame.load_memory_reader(show_err=show_error) if self.main_frame.advanced_error_thrown: self.automode_var.set('0') return self.toggle_automode_btn(first=first, show_error=show_error) else: self.advanced_mode_stop.pack(expand=False, fill=tk.X, pady=[4, 0]) self.advanced_pause_on_esc_menu.pack(expand=False, fill=tk.X) self.advanced_automode_warning.pack(pady=6) else: if not first and self.main_frame.advanced_stats_caret.active: self.main_frame.advanced_stats_caret.invoke() self.advanced_automode_warning.forget() self.advanced_mode_stop.forget() self.advanced_pause_on_esc_menu.forget() self.main_frame.d2_reader = None if not first: self.main_frame.toggle_automode()