def _add_option(self, name, description, value=False): var = BooleanVar() self._vars[name] = var var.set(value) var.trace('w', self._options_update) cb = Checkbutton(self, text=description, variable=var) cb.pack(side=LEFT)
class RootFrame(Frame): def __init__(self, master): Frame.__init__(self, master) self._value = os.getcwd() self._var = StringVar(self) self._var.set(self._value) self._var.trace('w', self._validate) label = Label(self, text="Root:") label.pack(side=LEFT) self._entry = Entry(self, textvariable=self._var) self._entry.pack(side=LEFT, fill=X, expand=True) self._recursive_var = BooleanVar() recursive_cb = Checkbutton(self, text='Recursive', variable=self._recursive_var) recursive_cb.pack(side=LEFT) self._recursive_var.trace('w', self._validate) open_button = Button(self, text="Open", command=self._select_root) open_button.pack(side=LEFT) refresh_button = Button(self, text="Refresh", command=self._refresh) refresh_button.pack(side=LEFT) repad(self, 'padx', 0, 5) def _refresh(self): self.event_generate('<<Refresh>>', when='tail') def _validate(self, *_): res = self._var.get().strip() if os.path.isdir(res): self._entry.config(fg='black') self._value = res else: self._entry.config(fg='red') self._value = None self.event_generate('<<RootUpdate>>', when='tail') def _select_root(self): value = askdirectory() if value: self._var.set(value) self._validate() @property def root(self): return self._value @property def recursive(self): return self._recursive_var.get()
def validateTableInfosetMenuEntender(cntlr, validateMenu): # Extend menu with an item for the save infoset plugin cntlr.modelManager.generateTableInfoset = cntlr.config.setdefault("generateTableInfoset",False) from tkinter import BooleanVar generateTableInfoset = BooleanVar(value=cntlr.modelManager.generateTableInfoset) def setTableInfosetOption(*args): cntlr.config["generateTableInfoset"] = cntlr.modelManager.generateTableInfoset = generateTableInfoset.get() generateTableInfoset.trace("w", setTableInfosetOption) validateMenu.add_checkbutton(label=_("Generate table infosets (instead of diffing them)"), underline=0, variable=generateTableInfoset, onvalue=True, offvalue=False)
class Preferences(Frame): def __init__(self, client): # Basic setup super(Preferences, self).__init__() self.client = client # Setup the variables used self.echo_input = BooleanVar() self.echo_input.set(self.client.config['UI'].getboolean('echo_input')) self.echo_input.trace("w", self.echo_handler) self.logging = BooleanVar() self.logging.set( self.client.config['logging'].getboolean('log_session')) self.logging.trace('w', self.logging_handler) self.log_dir = self.client.config['logging']['log_directory'] # Build the actual window and widgets prefs = Toplevel(self) prefs.wm_title("Preferences") echo_input_label = Label(prefs, text="Echo Input:") logging_label = Label(prefs, text='Log to file:') echo_checkbox = Checkbutton(prefs, variable=self.echo_input) logging_checkbox = Checkbutton(prefs, variable=self.logging) logging_button_text = 'Choose file...' if self.log_dir == "" else self.log_dir logging_button = Button(prefs, text=logging_button_text, command=self.logging_pick_location) # Pack 'em in. echo_input_label.grid(row=0, column=0) echo_checkbox.grid(row=0, column=1) logging_label.grid(row=1, column=0) logging_checkbox.grid(row=1, column=1) logging_button.grid(row=1, column=2) def logging_pick_location(self): location = askdirectory(initialdir="%UserProfile%\Documents\\") self.client.config['logging']['log_directory'] = location self.write_config() def echo_handler(self, arg1, arg2, mode): pprint(self.echo_input.get()) self.client.config['UI']['echo_input'] = 'yes' if self.echo_input.get( ) else 'no' self.write_config() def logging_handler(self, arg1, arg2, mode): self.client.config['logging'][ 'log_session'] = 'yes' if self.logging.get else 'no' self.write_config() def write_config(self, file='config.ini'): self.client.config.write(open(file, 'w'))
class Preferences(Frame): def __init__(self, client): # Basic setup super(Preferences, self).__init__() self.client = client # Setup the variables used self.echo_input = BooleanVar() self.echo_input.set(self.client.config['UI'].getboolean('echo_input')) self.echo_input.trace("w", self.echo_handler) self.logging = BooleanVar() self.logging.set(self.client.config['logging'].getboolean('log_session')) self.logging.trace('w', self.logging_handler) self.log_dir = self.client.config['logging']['log_directory'] # Build the actual window and widgets prefs = Toplevel(self) prefs.wm_title("Preferences") echo_input_label = Label(prefs, text="Echo Input:") logging_label = Label(prefs, text='Log to file:') echo_checkbox = Checkbutton(prefs, variable=self.echo_input) logging_checkbox = Checkbutton(prefs, variable=self.logging) logging_button_text = 'Choose file...' if self.log_dir == "" else self.log_dir logging_button = Button(prefs, text=logging_button_text, command=self.logging_pick_location) # Pack 'em in. echo_input_label.grid(row=0, column=0) echo_checkbox.grid(row=0, column=1) logging_label.grid(row=1, column=0) logging_checkbox.grid(row=1, column=1) logging_button.grid(row=1, column=2) def logging_pick_location(self): location = askdirectory(initialdir="%UserProfile%\Documents\\") self.client.config['logging']['log_directory'] = location self.write_config() def echo_handler(self, arg1, arg2, mode): pprint(self.echo_input.get()) self.client.config['UI']['echo_input'] = 'yes' if self.echo_input.get() else 'no' self.write_config() def logging_handler(self, arg1, arg2, mode): self.client.config['logging']['log_session'] = 'yes' if self.logging.get else 'no' self.write_config() def write_config(self, file='config.ini'): self.client.config.write(open(file, 'w'))
def weatherprediction(): frame1.destroy() frame2.destroy() global frame3 frame3 = Frame(root) frame3.pack(side='top',fill='x') Label(frame3,text="WEATHER PREDICTION", relief='flat', padx=10,pady=10).grid(row=0,column=0, columnspan=3) Label(frame3,text="STEPS", relief='ridge',width=15,bg='white').grid(row=1,column=0) Label(frame3,text="PROCESS", relief='ridge',width=25,bg='white').grid(row=1,column=1) Label(frame3,text="VALUES", relief='ridge',width=15,bg='white').grid(row=1,column=2) steps = ['Step1','Step2','Step3','Step4','Step5','Step6','Step7'] r = 2 for c in steps: Label(frame3,text=c, relief='ridge',width=15).grid(row=r,column=0) r = r + 1 tr_start1 = tk.Button(frame3, text ="Input Training Set Start Date", command = getTrainSD) tr_end1 = tk.Button(frame3, text ="Input Training Set End Date", command = getTrainED) test_start1 = tk.Button(frame3, text ="Input Test Set Start Date", command = getTestSD) test_end1 = tk.Button(frame3, text ="Input Test Set End Date", command = getTestED) dep_var_idx1 = tk.Button(frame3, text ="Choose What to Predict", command = getPredictor) indep_var_idx1 = tk.Button(frame3, text ="Choose Plantes", command = getIPredictor) def getBool(): # get rid of the event argument global feature_scaling feature_scaling=boolvar.get() Label(frame3,text=feature_scaling, relief='flat',width=15).grid(row=8,column=2) print(feature_scaling) boolvar = BooleanVar() boolvar.set(False) boolvar.trace('w', lambda *_: print("The value was changed")) cb = tk.Checkbutton(frame3, text = "Is Feature Scaling applicable?", variable = boolvar, command = getBool) LR = tk.Button(frame3, text = "Run Prediction", command = MLR ) tr_start1.grid(row=2,column=1) tr_end1.grid(row=3,column=1) test_start1.grid(row=4,column=1) test_end1.grid(row=5,column=1) dep_var_idx1.grid(row=6,column=1) indep_var_idx1.grid(row=7,column=1) cb.grid(row=8,column=1) LR.grid(row=9,column=0,columnspan=3)
def addValidateMenuTools(cntlr, validateMenu, name, map_name): # Extend menu with an item for the save infoset plugin attr_name = 'validate{}'.format(name.strip()) attr_value = cntlr.config.setdefault(attr_name, False) setattr(cntlr.modelManager, attr_name, attr_value) #cntlr.modelManager.validateDQC = cntlr.config.setdefault("validateDQC",False) from tkinter import BooleanVar validate_var = BooleanVar(value=getattr(cntlr.modelManager, attr_name)) def setValidateXuleOption(*args): setattr(cntlr.modelManager, attr_name, validate_var.get()) cntlr.config[attr_name] = getattr(cntlr.modelManager, attr_name) validate_var.trace("w", setValidateXuleOption) validateMenu.add_checkbutton(label=_("{} Rules".format(name)), underline=0, variable=validate_var, onvalue=True, offvalue=False) xuleRegisterValidators(name, map_name, validate_var)
class _CheckButton(WidgetBase): def __init__(self, master, label=None, command=None, default=False, **options): super().__init__(master) from tkinter import BooleanVar from tkinter.ttk import Checkbutton self.var = BooleanVar(self.master, value=default) self.value = "" self.var.trace("w", self.callback) self.command = [command] if not label is None: options.update(text=label) self.widget = Checkbutton(self.master, variable=self.var, **options) def callback(self, *args): self.value = self.var.get() if callable(self.command[0]): self.command[0]()
class Boolean(Editor): def __init__(self, master, style_def=None): super().__init__(master, style_def) self.config(**self.style.dark, **self.style.dark_highlight_active) self._var = BooleanVar() self._var.trace('w', self.check_change) self._check = Checkbutton(self, text='') self._check['variable'] = self._var self._check.pack(fill="x") def check_change(self, *_): self._check.config(text=str(self._var.get())) if self._on_change is not None: self._on_change(self._var.get()) def set(self, value): self._var.set(bool(value)) self._check.config(text=str(self._var.get())) def get(self): return bool(self._var.get())
class LoginViewController(GenericController): def __init__(self, app: Tk, sock: socket = None, model: User = None, view: LoginView = None): super().__init__(app, sock, model, view) self.model = model if model is not None else User() self.socket = socket(AF_INET, SOCK_STREAM) self.sha256 = sha256() self.user_logged_state = BooleanVar(app) self.user_logged_state.trace('w', self.user_state_changed) def connect(self): self.socket = socket(AF_INET, SOCK_STREAM) self.socket.connect(server_host.host) def send_request(self, request: dict): serialized_request = json.dumps(request) self.socket.sendall(serialized_request.encode()) def receive_response(self): data = bytes() while True: received = self.socket.recv(4096) if not received: break elif len(received) < 4096: data += received break else: data += received response = json.loads(data.decode()) return response def hash_password(self, raw_password: str): self.sha256.update(raw_password.encode()) return self.sha256.hexdigest() def username_var_changed(self, *args): username = self.view.username.get() password = self.view.password.get() # Unlock the password entry only if there's something in the username entry if username != '': self.view.pass_entry['state'] = 'normal' # Unlock the submit button only if both the username and password typed are at least 8 chars long self.view.submit_button['state'] = 'normal' if len( username) >= 8 and len(password) >= 8 else 'disabled' else: self.view.pass_entry['state'] = 'disabled' self.view.submit_button['state'] = 'disabled' def password_var_changed(self, *args): username = self.view.username.get() password = self.view.password.get() # Unlock the submit button only if both the username and password typed are at least 8 chars long if password != '': self.view.submit_button['state'] = 'normal' if len( username) >= 8 and len(password) >= 8 else 'disabled' else: self.view.submit_button['state'] = 'disabled' def submit(self, *args): username = self.view.username.get() self.model.username = username hashed_password = self.hash_password(self.view.password.get()) self.model.password = hashed_password self.connect() request = { 'request': self.view.action.get(), 'user': { 'username': self.model.username, 'password': self.model.password } } # print(request) self.send_request(request) response = self.receive_response() if response['info'] == 'Logged': self.user_logged_state.set(True) else: messagebox.showwarning('Server says', response['info']) def start_main_view(self): controller = MainViewController(self.app, self.socket, self.model) view = MainView(self.app, controller=controller) controller.view = view def user_state_changed(self, *args): if self.user_logged_state.get() is True: for widget in self.app.slaves(): widget.destroy() self.app.geometry('500x400') self.start_main_view()
class EarthquakeGUI: def _quit(self): # Quit program quit() # win will exist when this function is called Tk.destroy(self) exit() def _refreshData(self): logging.debug("") t1 = datetime.now() JSONdata = getWebData(urlData) t2 = datetime.now() tdweb = t2 - t1 if JSONdata: hList = loadHeaderInfo(JSONdata) logging.info(f"Web Retrieval - {hList['count']:,} " f"records in {tdweb.total_seconds(): .3}s") eList = loadList(JSONdata) eList = self.sortData(eList) self.updateHeaderFields(hList) self.updateFields(eList, self.summarySelected.current()) else: messagebox.showerror( "USGS File error", "Error retrieving " "data from USGS web site. Check console for error.") logging.error("Error retrieving file") def _comboCallbackFunc(self, event, data): logging.debug("") # When combo box changes, updated data with new selection self.updateFields(data, self.summarySelected.current()) def _webCallbackFunc(self, data): logging.debug("") webbrowser.open_new(data) def getNewData(self, timeString): logging.debug("") global urlData x = urlData.find("summary/") y = urlData.find(".geojson") logging.debug(urlData[x + 8:y]) urlData = str(urlData.replace(urlData[x + 8:y], timeString, 1)) logging.debug(urlData) self._refreshData() # When combo box changes, updated data with new selection # self.updateFields(data, self.summarySelected.current()) # return urlData def updateComboBoxData(self, data): logging.debug("") dropdownlist = [] self.summarySelected.delete(0) if len(data) > 0: for n, _ in enumerate(data): mag = f"{data[n][1]:.1f}" mmi = f"{data[n][8]:.3f}" dropdownlist.append(mag + " - " + mmi + " - " + str(data[n][2])) self.summarySelected["values"] = dropdownlist self.summarySelected.current(0) self.summarySelected.bind( "<<ComboboxSelected>>", lambda event, arg=data: self._comboCallbackFunc(event, arg), ) else: self.summarySelected["values"] = dropdownlist self.summarySelected.set("") def sortData(self, data): logging.debug(f"{self.sortOption.get()}") try: if self.sortOption.get() == '2': s_eList = sorted(data, key=lambda x: (x[8], x[1], x[7]), reverse=True) else: s_eList = sorted(data, key=lambda x: (x[1], x[7]), reverse=True) eList = s_eList[:] except: logging.error("Error sorting list - most likely bad data") self.updateComboBoxData(eList) return eList def __init__(self, data, header): self.win = Tk() self.win.title("USGS Current Earthquake Data") self.checked = BooleanVar() self.checked.trace("w", self.mark_checked) self.sortOption = StringVar() self.sortOption.set("1") self.sortOption.trace("w", self.mark_sortOption) # ----- Menu Bar - Create the Menu Bar ------------------------- menuBar = Menu() self.win.config(menu=menuBar) fileMenu = Menu(menuBar, tearoff=False) dataMenu = Menu(menuBar, tearoff=False) optionsMenu = Menu(menuBar, tearoff=False) helpMenu = Menu(menuBar, tearoff=False) menuBar.add_cascade(menu=fileMenu, label="File") menuBar.add_cascade(menu=dataMenu, label="Data") menuBar.add_cascade(menu=optionsMenu, label="Options") menuBar.add_cascade(menu=helpMenu, label="Help") # ----- Menu Bar - Create the File Menu ------------------------ fileMenu.add_separator() fileMenu.add_command(label="Exit", command=self._quit) # ----- Menu Bar - Create the Options Menu --------------------- sortSubMenu = Menu(optionsMenu, tearoff=False) optionsMenu.add_cascade(menu=sortSubMenu, label="Sort") sortSubMenu.add_radiobutton(label="Sort by Magnitude", value="1", variable=self.sortOption) sortSubMenu.add_radiobutton( label="Sort by Predictive damage or Shake(MMI)", value="2", variable=self.sortOption) # ----- Menu Bar - Create the Data Menu------------------------- dataMenu.add_command(label="Refresh current Data source", command=self._refreshData) dataMenu.add_separator() dataSubMenu = Menu(dataMenu, tearoff=False) dataMenu.add_cascade(menu=dataSubMenu, label="New Data Source") # ----- Menu Bar - Create the Data submenu --------------------- d1 = [ ["Significant", "significant"], ["Magnitude 4.5+", "4.5"], ["Magnitude 2.5+", "2.5"], ["Magnitude 1.0+", "1.0"], ["All Earthquakes+", "all"], ] d2 = [ ["hour", "hour"], ["day", "day"], ["7 days", "week"], ["30 days", "month"], ] for i in range(len(d2)): for j in range(len(d1)): s1 = str(d1[j][0] + ", past " + d2[i][0]) s2 = str(d1[j][1] + "_" + d2[i][1]) dataSubMenu.add_command( label=s1, command=lambda widget=s2: self.getNewData(widget)) if j == (len(d1) - 1) and i != (len(d2) - 1): dataSubMenu.add_separator() # ----- Set up frames and subframes to store widgets ----------- self.mainFrame = ttk.LabelFrame() self.headings_frame = ttk.LabelFrame(self.mainFrame) self.headings_frame.grid(row=0) self.selection_frame = ttk.LabelFrame(self.headings_frame, text="selection frame") self.selection_frame.configure(text=header["title"]) self.selection_frame.grid(column=0, columnspan=2, row=0, sticky="NW") self.file_frame = ttk.LabelFrame(self.headings_frame, text="File Info") self.file_frame.grid(column=2, row=0, rowspan=3, sticky="NW") self.details_frame = ttk.LabelFrame(self.mainFrame) self.details_frame.grid(row=1) self.summary_frame = ttk.LabelFrame(self.details_frame, text="Event Details") self.summary_frame.grid(row=0, columnspan=2) self.location_frame = ttk.LabelFrame(self.details_frame, text="Event Location") self.location_frame.grid(row=1, column=0, sticky="NW") self.time_frame = ttk.LabelFrame(self.details_frame, text="Time of Event") self.time_frame.grid(row=1, column=1, sticky="NW") ttk.Label(self.selection_frame).grid(column=0, row=0, sticky="W") # ----- Set up combo box and data to populate it --------------- self.summarySelected = ttk.Combobox(self.selection_frame, width=85, state="readonly") self.summarySelected.grid(column=0, row=1) self.updateComboBoxData(data) # ----- Add File widget - File delta --------------------------- self.fileDelta = StringVar() fileDeltaEntry = ttk.Label(self.file_frame, width=25, textvariable=self.fileDelta, state="readonly") fileDeltaEntry.grid(column=1, row=0, columnspan=2, sticky="W") # ----- Add File widget - File Time ---------------------------- ttk.Label(self.file_frame, text="File Time:").grid(column=0, row=1, sticky="E") self.fileTime = StringVar() fileTimeEntry = ttk.Label(self.file_frame, width=25, textvariable=self.fileTime, state="readonly") fileTimeEntry.grid(column=1, row=1, sticky="W") # ----- Add File widget - Event count -------------------------- ttk.Label(self.file_frame, text="Count:").grid(column=0, row=2, sticky="E") self.fileCount = StringVar() fileCountEntry = ttk.Label(self.file_frame, width=25, textvariable=self.fileCount, state="readonly") fileCountEntry.grid(column=1, row=2, sticky="W") # ----- Add Summary widget - Magnitude ------------------------- ttk.Label(self.summary_frame, text="Magnitude:").grid(column=0, row=0, sticky="E") self.mag = StringVar() magEntry = ttk.Label(self.summary_frame, width=7, textvariable=self.mag, state="readonly") magEntry.grid(column=1, row=0, sticky="W") # ----- Add Summary widget - Alert ----------------------------- ttk.Label(self.summary_frame, text="Alert:").grid(column=2, row=0, sticky="E") self.alert = StringVar() alertEntry = ttk.Label(self.summary_frame, width=7, textvariable=self.alert, state="readonly") alertEntry.grid(column=3, row=0, sticky="W") # ----- Add Summary widget - Shake ----------------------------- ttk.Label(self.summary_frame, text="Shake (MMI):").grid(column=4, row=0, sticky="E") self.shake = StringVar() shakeEntry = ttk.Label(self.summary_frame, width=7, textvariable=self.shake, state="readonly") shakeEntry.grid(column=5, row=0, sticky="W") # ----- Add Summary widget - Report Felt ----------------------- ttk.Label(self.summary_frame, text="Reported felt:").grid(column=6, row=0, sticky="E") self.felt = StringVar() feltEntry = ttk.Label(self.summary_frame, width=7, textvariable=self.felt, state="readonly") feltEntry.grid(column=7, row=0, sticky="W") # ----- Add Summary widget - Url/More Info --------------------- ttk.Label(self.summary_frame, text="More info:").grid(column=0, row=1, sticky="E") self.urlName = StringVar() self.urlEntry = ttk.Button(self.summary_frame) self.urlEntry.grid(column=1, row=1, columnspan=8, sticky="W") # ----- Add Location widget - Place ---------------------------- ttk.Label(self.location_frame, text="Place:").grid(column=0, row=4, sticky="E") self.place = StringVar() locEntry = ttk.Label(self.location_frame, width=45, textvariable=self.place, state="readonly") locEntry.grid(column=1, row=4, sticky="W") # ----- Add Location widget - Latitude ------------------------- ttk.Label(self.location_frame, text="Latitude:").grid(column=0, row=10, sticky="E") self.lat = StringVar() latEntry = ttk.Label(self.location_frame, width=25, textvariable=self.lat, state="readonly") latEntry.grid(column=1, row=10, sticky="W") # ----- Add Location widget - Longitude ------------------------ ttk.Label(self.location_frame, text="Longitude:").grid(column=0, row=11, sticky="E") self.lon = StringVar() longEntry = ttk.Label(self.location_frame, width=25, textvariable=self.lon, state="readonly") longEntry.grid(column=1, row=11, sticky="W") # ----- Add Location widget - Depth ---------------------------- ttk.Label(self.location_frame, text="Depth:").grid(column=0, row=12, sticky="E") self.depth = StringVar() depthEntry = ttk.Label(self.location_frame, width=25, textvariable=self.depth, state="readonly") depthEntry.grid(column=1, row=12, sticky="W") # ----- Add Time widget - Event delta -------------------------- self.deltaEntry = StringVar() deltaEntry = ttk.Label(self.time_frame, width=25, textvariable=self.deltaEntry, state="readonly") deltaEntry.grid(column=1, row=0, sticky="W") # ----- Add Time widget - Event Time --------------------------- ttk.Label(self.time_frame, text="Time:").grid(column=0, row=1, sticky="E") self.time = StringVar() timeEntry = ttk.Label(self.time_frame, width=25, textvariable=self.time, state="readonly") timeEntry.grid(column=1, row=1, sticky="W") # ----- Add Time widget - Event Local Time --------------------- ttk.Label(self.time_frame, text="Your local time:").grid(column=0, row=2, sticky="E") self.tz = StringVar() tzEntry = ttk.Label(self.time_frame, width=25, textvariable=self.tz, state="readonly") tzEntry.grid(column=1, row=2, sticky="W") # ----- Add padding around fields self.mainFrame.grid_configure(padx=8, pady=4) for child in self.mainFrame.winfo_children(): child.grid_configure(padx=8, pady=4) for grandChild in child.winfo_children(): grandChild.grid_configure(padx=8, pady=4) for widget in grandChild.winfo_children(): widget.grid_configure(padx=8, pady=4) # ----- Call funtion to update fields -------------------------- data = self.sortData(data) self.updateHeaderFields(header) self.updateFields(data, self.summarySelected.current()) def mark_checked(self, *args): logging.debug("") print(self.checked.get()) def mark_sortOption(self, *args): logging.debug("") print(self.sortOption.get()) JSONdata = getDataFile() if not JSONdata: self._refreshData() else: hList = loadHeaderInfo(JSONdata) eList = loadList(JSONdata) eList = self.sortData(eList) self.updateHeaderFields(hList) self.updateFields(eList, self.summarySelected.current()) def updateHeaderFields(self, header): # Update header fields for the file logging.debug("") global urlData urlData = header["url"] self.selection_frame.configure(text=header["title"]) self.fileCount.set(header["count"]) utc_time = datetime.utcfromtimestamp(header["timeStamp"] / 1000).replace(tzinfo=pytz.utc) self.fileTime.set(utc_time.strftime("%Y-%m-%d %H:%M:%S %Z")) self.fileDelta.set(deltaTime(self, utc_time)) def updateFields(self, data, rec): logging.debug("") if len(data) > 0: # Update fields in the display from the data record self.mag.set(f"{data[rec][1]:.1f}") self.place.set(data[rec][2]) utc_time = datetime.utcfromtimestamp(data[rec][3] / 1000).replace(tzinfo=pytz.utc) self.time.set(utc_time.strftime("%Y-%m-%d %H:%M:%S %Z")) current_tz = utc_time.astimezone(get_localzone()) self.tz.set(current_tz.strftime("%Y-%m-%d %H:%M:%S %Z")) self.urlName.set(data[rec][5]) self.felt.set(data[rec][6]) self.alert.set(data[rec][7]) self.shake.set(f"{data[rec][8]:.3f}") tmpLat = data[rec][10] if tmpLat == 0: self.lat.set("{} \xb0".format(tmpLat)) elif tmpLat > 0: self.lat.set("{} \xb0 N".format(tmpLat)) else: tmpLat *= -1 self.lat.set("{} \xb0 S".format(tmpLat)) tmpLong = data[rec][9] if tmpLong == 0: self.lon.set("{} \xb0".format(tmpLong)) elif tmpLong > 0: self.lon.set("{} \xb0 E".format(tmpLong)) else: tmpLong *= -1 self.lon.set("{} \xb0 W".format(tmpLong)) self.depth.set("{} km".format(data[rec][11])) # self.lon.set(data[rec][5]) self.deltaEntry.set(deltaTime(self, utc_time)) else: self.mag.set(None) self.place.set(None) self.time.set(None) self.tz.set(None) self.urlName.set(None) self.felt.set(None) self.alert.set(None) self.shake.set(None) self.lat.set(None) self.lon.set(None) self.depth.set(None) self.deltaEntry.set(None) self.urlEntry.config( text=self.urlName.get(), command=lambda arg=self.urlName.get(): self._webCallbackFunc(arg))
class GraphyInspector: def __init__(self, parent): self.parent = parent self.width = self.parent.right_frame_width self.padding = self.parent.right_frame_padding self.frame = Frame(master=self.parent.right_frame) self.frame.pack(side='top', fill='y', ) # "Inspector" title bar self.title_frame = Frame(master=self.frame) self.title_frame.pack(side='top') self.title_label = Label(master=self.title_frame, text="Inspector", width=self.width, bg='lightgray') self.title_label.pack() # identifier for type of object selected self.type_frame = Frame(master=self.frame, relief='sunken') self.type_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.type_label1 = Label(master=self.type_frame, width=int(self.width/2)-self.padding, text='Object:') self.type_label1.pack(side='left', padx=self.padding, pady=self.padding) self.type_label2 = Label(master=self.type_frame, width=int(self.width / 2) - self.padding, text='', bg='white') self.type_label2.pack(side='right', padx=self.padding, pady=self.padding) # label of selected object (i.e. name user gives them, no canvas IDs here) self.label_frame = Frame(master=self.frame, relief='sunken') self.label_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.label_label = Label(master=self.label_frame, width=int(self.width/2)-self.padding, text="Label:") self.label_label.pack(side='left', padx=self.padding, pady=self.padding) self.label_var = StringVar() self.label_var.set('') self.label_entry = Entry(self.label_frame, width=int(self.width/2)-self.padding, textvariable=self.label_var) self.label_entry.pack(side='right', padx=self.padding, pady=self.padding) self.label_entry.bind('<Button-1>', self.select_label_text) self.label_entry.bind('<Return>', self.drop_widget_focus) # status identifier (for vertices and layers) self.status_frame = Frame(master=self.frame, relief='sunken') self.status_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.status_label1 = Label(master=self.status_frame, width=int(self.width/2)-self.padding, text='Status:') self.status_label1.pack(side='left', padx=self.padding, pady=self.padding) self.status_label2 = Label(master=self.status_frame, width=int(self.width/2)-self.padding, text='', bg='white') self.status_label2.pack(side='right', padx=self.padding, pady=self.padding) self.activation_var = StringVar() self.activation_var.set('') self.activation_menu = OptionMenu(self.status_frame, self.activation_var, "Identity", "Sigmoid", "ReLU", "Logarithmic", "Exponential") self.activation_menu.pack(side='right', padx=self.padding, pady=self.padding) # weight identifier (for edges only) self.weight_frame = Frame(master=self.frame, relief='sunken') self.weight_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.weight_label = Label(master=self.weight_frame, width=int(self.width/2)-self.padding, text="Weight:") self.weight_label.pack(side='left', padx=self.padding, pady=self.padding) self.weight_var = DoubleVar() self.weight_entry = Entry(self.weight_frame, width=int(self.width / 2) - self.padding, textvariable=self.weight_var) self.weight_entry.pack(side='right', padx=self.padding, pady=self.padding) self.weight_entry.bind('<Button-1>', self.select_weight_text) self.weight_entry.bind('<Return>', self.drop_widget_focus) # node count identifier (for layers only) self.node_frame = Frame(master=self.frame, relief='sunken') self.node_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.node_label = Label(master=self.node_frame, width=int(self.width/2)-self.padding, text="Node Count:") self.node_label.pack(side='left', padx=self.padding, pady=self.padding) self.node_var = IntVar() self.node_entry = Entry(self.node_frame, width=int(self.width / 2) - self.padding, textvariable=self.node_var) self.node_entry.pack(side='right', padx=self.padding, pady=self.padding) self.node_entry.bind('<Button-1>', self.select_node_text) self.node_entry.bind('<Return>', self.drop_widget_focus) # leakiness self.leakiness_frame = Frame(master=self.frame, relief='sunken') self.leakiness_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.leakiness_label = Label(master=self.leakiness_frame, width=int(self.width/2)-self.padding, text="Leakiness") self.leakiness_label.pack(side='left', padx=self.padding, pady=self.padding) self.leakiness_var = DoubleVar() self.leakiness_entry = Entry(self.leakiness_frame, width=int(self.width / 2) - self.padding, textvariable=self.leakiness_var) self.leakiness_entry.pack(side='right', padx=self.padding, pady=self.padding) self.leakiness_entry.bind('<Button-1>', self.select_leakiness_text) self.leakiness_entry.bind('<Return>', self.drop_widget_focus) # bias self.bias_frame = Frame(master=self.frame, relief='sunken') self.bias_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.bias_label = Label(master=self.bias_frame, width=int(self.width/2)-self.padding, text="Bias:") self.bias_label.pack(side='left', padx=self.padding, pady=self.padding) self.bias_var = DoubleVar() self.bias_entry = Entry(self.bias_frame, width=int(self.width / 2) - self.padding, textvariable=self.bias_var) self.bias_entry.pack(side='right', padx=self.padding, pady=self.padding) self.bias_entry.bind('<Button-1>', self.select_bias_text) self.bias_entry.bind('<Return>', self.drop_widget_focus) # output bound self.bound_frame = Frame(master=self.frame, relief='sunken') self.bound_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.bound_label = Label(master=self.bound_frame, width=int(self.width/2)-self.padding, text="Output Bound:") self.bound_label.pack(side='left', padx=self.padding, pady=self.padding) self.bound_var = DoubleVar() self.bound_entry = Entry(self.bound_frame, width=int(self.width / 2) - self.padding, textvariable=self.bound_var) self.bound_entry.pack(side='right', padx=self.padding, pady=self.padding) self.bound_entry.bind('<Button-1>', self.select_bound_text) self.bound_entry.bind('<Return>', self.drop_widget_focus) # noise self.noise_frame = Frame(master=self.frame, relief='sunken') self.noise_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.noise_label = Label(master=self.noise_frame, width=int(self.width/2)-self.padding, text="Weight Noise:") self.noise_label.pack(side='left', padx=self.padding, pady=self.padding) self.noise_var = DoubleVar() self.noise_entry = Entry(self.noise_frame, width=int(self.width / 2) - self.padding, textvariable=self.noise_var) self.noise_entry.pack(side='right', padx=self.padding, pady=self.padding) self.noise_entry.bind('<Button-1>', self.select_noise_text) self.noise_entry.bind('<Return>', self.drop_widget_focus) # input / output self.input_output_frame = Frame(master=self.frame, relief='sunken') self.input_output_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.input_var = BooleanVar() self.input_var.set(False) self.output_var = BooleanVar() self.output_var.set(False) self.input_toggle = Checkbutton(master=self.input_output_frame, text="Is Input", variable=self.input_var) self.output_toggle = Checkbutton(master=self.input_output_frame, text="Is Output", variable=self.output_var) self.input_toggle.pack(side='left', padx=self.padding, pady=self.padding) self.output_toggle.pack(side='left', padx=self.padding, pady=self.padding) self.selected = None self.selected_type = None self.set_unselected() self.label_var.trace('w', self.set_selected_label) self.weight_var.trace('w', self.set_selected_weight) self.activation_var.trace('w', self.set_selected_activation) self.leakiness_var.trace('w', self.set_selected_leakiness) self.node_var.trace('w', self.set_selected_node_count) self.bias_var.trace('w', self.set_selected_bias) self.bound_var.trace('w', self.set_selected_bound) self.noise_var.trace('w', self.set_selected_noise) self.input_var.trace('w', self.set_input) self.output_var.trace('w', self.set_output) # mode self.mode = parent.mode self.set_mode(parent.mode) # object is a vertex or edge, type is 'vertex' or 'edge'... def set_selected(self, selected_object, selected_object_type): self.selected = selected_object self.selected_type = selected_object_type if self.mode == "Graph": if selected_object_type == 'vertex': self.type_label2.config(text="Vertex") self.status_label2.config(text=selected_object.status) self.type_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.label_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.status_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.label_var.set(selected_object.label) elif selected_object_type == 'edge': self.type_label2.config(text="Edge") self.type_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.label_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.weight_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.label_var.set(selected_object.label) self.weight_var.set(selected_object.weight) else: print('dafuq is going on') elif self.mode == "Net": if selected_object_type == 'vertex': self.type_label2.config(text="Layer") self.status_label2.config(text=selected_object.status) self.type_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.label_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.status_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.node_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.leakiness_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.bias_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.bound_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.input_output_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.label_var.set(selected_object.label) self.node_var.set(selected_object.node_count) self.activation_var.set(selected_object.status) self.leakiness_var.set(selected_object.leakiness) self.bias_var.set(selected_object.bias) self.bound_var.set(selected_object.bound) self.input_var.set(selected_object.is_input_layer) self.output_var.set(selected_object.is_output_layer) elif selected_object_type == 'edge': self.type_label2.config(text="Weights") self.type_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.label_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.weight_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.noise_frame.pack(side='top', fill='x', pady=self.padding, padx=self.padding) self.label_var.set(selected_object.label) self.weight_var.set(selected_object.weight) self.noise_var.set(selected_object.noise) else: print('This will never happen.') # nothing is selected def set_unselected(self): self.type_frame.pack_forget() self.label_frame.pack_forget() self.status_frame.pack_forget() self.weight_frame.pack_forget() self.node_frame.pack_forget() self.bias_frame.pack_forget() self.bound_frame.pack_forget() self.noise_frame.pack_forget() self.input_output_frame.pack_forget() self.leakiness_frame.pack_forget() self.selected = None self.selected_type = None # set label of selected object def set_selected_label(self, *args): self.selected.set_label(self.label_var.get()) # set weight of selected object def set_selected_weight(self, *args): self.selected.set_weight(self.weight_var.get()) def set_selected_activation(self, *args): self.selected.set_status(self.activation_var.get()) def set_selected_node_count(self, *args): self.selected.set_node_count(self.node_var.get()) def set_selected_bias(self, *args): self.selected.set_bias(self.bias_var.get()) def set_selected_bound(self, *args): self.selected.set_bound(self.bound_var.get()) def set_selected_noise(self, *args): self.selected.set_noise(self.noise_var.get()) def set_input(self, *args): self.selected.set_input_layer(self.input_var.get()) def set_output(self, *args): self.selected.set_output_layer(self.output_var.get()) def set_selected_leakiness(self, *args): self.selected.set_leakiness(self.leakiness_var.get()) def update(self): if self.selected: selected = self.selected type = self.selected_type self.set_unselected() self.set_selected(selected, type) def select_label_text(self, event): if event: # delay so the default click doesn't undo selection, then recall and fall through to "else" self.parent.tk.after(50, self.select_label_text, False) else: self.label_entry.select_range(0, 'end') self.label_entry.icursor(0) def select_weight_text(self, event): if event: # delay so the default click doesn't undo selection, then recall and fall through to "else" self.parent.tk.after(50, self.select_weight_text, False) else: self.weight_entry.select_range(0, 'end') self.weight_entry.icursor(0) def select_node_text(self, event): if event: self.parent.tk.after(50, self.select_node_text, False) else: self.node_entry.select_range(0, 'end') self.node_entry.icursor(0) def select_bias_text(self, event): if event: self.parent.tk.after(50, self.select_bias_text, False) else: self.bias_entry.select_range(0, 'end') self.bias_entry.icursor(0) def select_noise_text(self, event): if event: self.parent.tk.after(50, self.select_noise_text, False) else: self.noise_entry.select_range(0, 'end') self.noise_entry.icursor(0) def select_bound_text(self, event): if event: self.parent.tk.after(50, self.select_bound_text, False) else: self.bound_entry.select_range(0, 'end') self.bound_entry.icursor(0) def select_leakiness_text(self, event): if event: self.parent.tk.after(50, self.select_leakiness_text, False) else: self.leakiness_entry.select_range(0, 'end') self.leakiness_entry.icursor(0) def drop_widget_focus(self, event): self.frame.focus() def set_mode(self, mode): if mode == "Graph": self.mode = mode self.weight_label.config(text="Weight:") self.status_label1.config(text="Status:") self.activation_menu.pack_forget() self.input_output_frame.pack_forget() self.status_label2.pack(side='right', padx=self.padding, pady=self.padding) elif mode == "Net": self.mode = mode self.weight_label.config(text="Start Weight:") self.status_label1.config(text="Activation:") self.status_label2.pack_forget() self.activation_menu.pack(side='right', padx=self.padding, pady=self.padding) else: print("This will never happen.")
class GeoDemonstrator(Tk, GeoMixinTrans, GeoMixinHelp): # Konstruktor aplikacije def __init__(self): # Log poruka o pokretanju aplikacije print('Dobro došli u aplikaciju GeoDemonstrator!') # Pozivanje konstruktora roditeljske klase super().__init__() # Postavljanje naziva aplikacije self.title('GeoDemonstrator') # Inicijalizacija liste tačaka self.tačke = [] self.ttačke = [] # Inicijalizacija liste identifikatora # na platnu trenutno iscrtanih tačaka self.id_tač = [] # Inicijalizacija figure self.figura = None # Inicijalizacija transformacija iz platna # u iscrtani koordinatni sistem i obrnuto self.puk = Skal(1 / 7, -1 / 7) * Trans(-204, -132) self.kup = Trans(204, 132) * Skal(7, -7) # Inicijalizacija elemenata GKI self.init_gki() # Inicijalizacija elemenata GKI def init_gki(self): # Postavljanje veličine i pozicije prozora self.geometry('450x450+75+75') # Onemogućavanje promene veličine prozora, # pošto je Tk prilično plastičan, pa promene # ugrožavaju zamišljeni izgled aplikacije self.resizable(False, False) # Inicijalizacija glavnog menija self.init_meni() # Inicijalizacija platna self.init_platno() # Kontrola unosa tačaka self.init_unos() # Inicijalizacija glavnog menija def init_meni(self): # Pravljenje glavnog menija meni = Menu(self) # Postavljanje sporednog padajućeg menija self.umeni = Menu(meni) self.umeni.add_command(label='Zaključi unos', command=self.promena_unosa) self.umeni.add_command(label='Ispravi figuru', command=self.ispravi) self.umeni.add_command(label='Očisti platno', command=partial(self.novo_platno, True)) # Postavljanje glavnog menija i vezivanje # komandi za odgovarajuće funkcionalnosti meni.add_cascade(label='Opcije', menu=self.umeni) meni.add_command(label='Pomoć (H)', command=self.pomoć) meni.add_command(label='Info (G)', command=self.info) self.config(menu=meni) # Vezivanje tipki za akcije analogne # onima iz prethodno postavljenog menija; # od F1 se odustalo jer se ne ponaša kako # treba na operativnom sistemu Windows self.bind('<H>', self.pomoć) self.bind('<h>', self.pomoć) self.bind('<G>', self.info) self.bind('<g>', self.info) self.bind('<Escape>', self.kraj) # Vezivanje protokola zatvaranja prozora # za istu akciju kao za Kraj i Escape self.protocol('WM_DELETE_WINDOW', self.kraj) # Inicijalizacija platna def init_platno(self): # Pravljenje okvira za platno okvir_p = LabelFrame(self, text='Zakoračite u svet geometrijskih' ' transformacija', padx=10, pady=10) okvir_p.place(x=10, y=10, height=300, width=430) # Postavljanje platna unutar okvira self.platno = Canvas(okvir_p, height=261, width=405) self.platno.place(x=0, y=0) # Postavljanje koordinatnog sistema na platno; # slika nije lokalna promenljiva, pošto bi je u # tom slučaju 'pojeo' sakupljač otpadaka self.slika = self.učitaj_sliku('koord.gif') self.platno.create_image(203, 131, image=self.slika) # Vezivanje čuvanja tačke za klik na platno self.unos = True self.platno.bind('<Button-1>', self.dodaj_tačku) # Vezivanje promene unosa za desni klik, # a ispravljanja figure za srednji, prema # sugestiji asistenta, čime se dobija na # lakoći korišćenja, bez potrebe za menijem self.platno.bind('<Button-2>', self.ispravi) self.platno.bind('<Button-3>', self.promena_unosa) # Okvir za magični svet transformacija def init_unos(self): # Pravljenje okvira za elemente self.okvir_d = LabelFrame(self, text='Unosite tačke klikovima' ' po platnu', padx=10, pady=10) self.okvir_d.place(x=10, y=315, height=128, width=430) # Inicijalizacija polja sa transformacijama self.init_trans() # Oznake parametara koje korisnik unosi x_koord_labela = Label(self, text='x:') y_koord_labela = Label(self, text='y:') ugao_labela = Label(self, text='\u03b8:') # Postavljanje oznaka na prozor x_koord_labela.place(x=185, y=348) y_koord_labela.place(x=185, y=375) ugao_labela.place(x=185, y=403) # Polja za unos vrednosti transformacija self.x_koord = Entry(self, width=4, state='disabled') self.y_koord = Entry(self, width=4, state='disabled') self.ugao = Entry(self, width=4, state='disabled') # Postavljanje polja na prozor self.x_koord.place(x=200, y=348) self.y_koord.place(x=200, y=375) self.ugao.place(x=200, y=403) # Postavljanje ostalih elemenata self.init_centar() self.init_inverz() # Transformacijski okvir def init_trans(self): # Mapa za preslikavanje niske u # odgavarajuću klasu transformacije self.funkcije = { 'translacija': Trans, 'skaliranje': Skal, 'smicanje': Smic, 'rotacija': Rot, 'refleksija': Refl } # Pravljenje okvira za odabir transformacije okvir_t = LabelFrame(self, text='Izaberite transformaciju', padx=23, pady=7) okvir_t.place(x=18, y=337, height=95, width=158) # U zavisnosti od vrednosti var koju pročitamo iz # padajućeg menija, poziva se prava transformacija self.tr = StringVar(self) self.tr.set('') # Funkcija za praćenje promenljive; izveštava o odabiru # transformacije i kontroliše pristup poljima za unos # parametara u zavisnosti od odabira; nepakovana lista # argumenata *args je neophodna kako bi se prosledili # (i zanemarili) dodatni podaci o promeni odabira, slično # kao što npr. kolbek funkcije u GLUT-u obavezno primaju # koordinate događaja, iako one često nisu nužan podatak self.tr.trace( 'w', lambda *args: print('Odabrana transformacija:' ' {}.'.format(self.tr.get())) or self.kontrola( )) # Padajuća lista geometrijskih transformacija; # umesto dosad korišćene fje place za postavljanje # objekta na tačno određeno mesto na prozoru, ovde # se koristi pack, koji objekat optimalno spakuje # na raspoloživom prostoru; iz tog razloga je i # roditeljski element upravo transformacioni okvir, # u koji se pakuje, a ne self, kako je dosad bilo OptionMenu(okvir_t, self.tr, 'translacija', 'skaliranje', 'smicanje', 'rotacija', 'refleksija').pack(fill='x') # Dugme za pokretanje transformacije self.dugme_t = Button(okvir_t, text='Transformiši', command=self.transformiši, state='disabled') self.dugme_t.pack(fill='x') # Odabir centra transformacije def init_centar(self): # Promenljiva za praćenje self.centar = StringVar(self) self.centar.set(None) self.centar.trace( 'w', lambda *args: print('Odabran {}' ' za centar transformacije.'.format( self.centar.get())) if self.centar.get() != 'None' else None) # Oznaka za odabir centra odabir_centra = Label(self, text='Centar transformacije:') odabir_centra.place(x=265, y=330) # Dugme za transformaciju sa centrom # u koordinatnom početku self.radio1 = Radiobutton(self, text='centar platna', padx=3, variable=self.centar, value='centar platna', state='disabled', command=partial(self.kontrola, True)) self.radio1.place(x=242, y=350) # Dugme za transformaciju sa centrom # u centru mase (baricentru) figure self.radio2 = Radiobutton(self, text='centar mase', padx=3, variable=self.centar, value='centar mase', state='disabled', command=partial(self.kontrola, True)) self.radio2.place(x=242, y=370) # Dugme za transformaciju sa centrom # u korisnički unetoj tački self.radio3 = Radiobutton(self, text='uneta tačka', padx=3, variable=self.centar, value='korisnički unos', state='disabled', command=partial(self.kontrola, True)) self.radio3.place(x=242, y=390) # Oznake za unos centra transformacija t1_labela = Label(self, text='t1:') t2_labela = Label(self, text='t2:') # Postavljanje oznaka na prozor t1_labela.place(x=360, y=358) t2_labela.place(x=360, y=385) # Polja za unos centra transformacija self.t1_koord = Entry(self, width=4, state='disabled') self.t2_koord = Entry(self, width=4, state='disabled') # Postavljanje polja na prozor self.t1_koord.place(x=380, y=358) self.t2_koord.place(x=380, y=385) # Funkcija za praćenje inverza def init_inverz(self): self.inv = BooleanVar(self) self.inv.trace( 'w', lambda *args: print('Odabrana inverzna' ' transformacija.') if self.inv.get() else print('Odabrana klasična transformacija.')) # Dugme za odabir inverza self.inverz = Checkbutton(self, text='Invertuj promenu', variable=self.inv, state='disabled') self.inverz.place(x=262, y=410)
class Statistics(ttk.Frame): data = dict() def __init__(self, master, axis): ttk.Frame.__init__(self, master, padding="5") self.axis = axis self.is_gaussian = BooleanVar(value=False) self.is_gaussian.trace("w", self.replot) self.fig = Figure(figsize=(5, 4), dpi=100) self.plot = self.fig.add_subplot(111) self._create_widgets() def replot(self, *args): self.plot.clear() if self.is_gaussian.get(): for name, errors in self.data.iter(): self.plot_probability(name, errors[1]) self.plot.set_xlabel("Error (microns)") self.plot.set_ylabel("Probability") else: for name, errors in self.data.iter(): self.plot.plot(errors[0], errors[1], label=name) self.plot.set_xlabel("Encoder Value") self.plot.set_ylabel("Error (microns)") self.plot.legend() self.fig.canvas.draw() def plot_probability(self, label, errors): hist, bins = np.histogram(errors, bins=50, density=True) centers = (bins[:-1] + bins[1:]) / 2 prob = interpolate.InterpolatedUnivariateSpline(centers, hist) color = (random(), random(), random()) xp = np.linspace(centers[0], centers[-1], 100) self.plot.plot(centers, hist, '.', color=color) self.plot.plot(xp, prob(xp), '-', color=color, label=label) def analyse_data(self, name, data): i = 0 temp_name = name while temp_name in self.data.keys(): i += 1 temp_name = name + "_{}".format(i) name = temp_name errors = self.get_errors(data) self.data[name] = np.array([data[0], errors]) def analyse_data_from_file(self, filename): data = np.loadtxt(filename, delimiter=",") filename = os.path.split(filename)[1].split(".")[0] self.analyse_data(filename, data) def more_details(self): window = Toplevel(self) u = " (" + chr(181) + "m)" columns = ("Min Error" + u, "Max Error" + u, "Mean Error" + u, "Standard Deviation" + u) data_grid = ttk.Treeview(window, columns=columns) i = 0 data_grid.heading('#{}'.format(i), text="File") for c in columns: i += 1 data_grid.heading('#{}'.format(i), text=c) for name, errors in self.data.iteritems(): err = errors[1] data_grid.insert('', 0, iid=name, text=name) i = 0 for v in [np.min(err), np.max(err), np.mean(err), np.std(err)]: data_grid.set(name, column=i, value="{:.3g}".format(v)) i += 1 data_grid.pack() def get_errors(self, data): b, c = np.polyfit(data[0], data[1], 1) b = self.axis.microstep.get() / (self.axis.motor_res.get() * self.axis.enc_res.get()) # Calculate the error from the theoretical value and convert to microns vfunc = np.vectorize(lambda x, y: ((b * x + c) - y) * self.axis. motor_res.get() / self.axis.microstep.get()) return vfunc(data[0], data[1]) def change_axis(self, axis): self.axis = axis def _load_data(self): files = filedialog.askopenfilename(initialdir=os.getcwd(), filetypes=[("Text", "*.txt")], multiple=1) for f in files: self.analyse_data_from_file(f) self.replot() def _clear(self): self.data = dict() self.plot.clear() self.fig.canvas.draw() def _create_widgets(self): canvas = FigureCanvasTkAgg(self.fig, master=self) canvas.show() canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1) toolbar = NavigationToolbar2TkAgg(canvas, self) toolbar.update() toolbar.pack(side=BOTTOM) ttk.Separator(toolbar).pack(side=LEFT, fill=Y, padx="10") ttk.Button(toolbar, text="Load", command=self._load_data).pack(side=LEFT, fill=Y) ttk.Button(toolbar, text="Clear", command=self._clear).pack(side=LEFT, fill=Y) ttk.Button(toolbar, text="More Details", command=self.more_details).pack(side=LEFT, fill=Y) ttk.Checkbutton(toolbar, text="Prob. Dist.", variable=self.is_gaussian).pack(side=LEFT, fill=Y) canvas._tkcanvas.pack(side=TOP)
def __init__(self, master, par=False): """ GUI for selecting default parameters - will write parameters to file \ of users choosing. :type master: Tk :param master: Tkinter window :type par: EQcorrscanParameters :param par: Default parameters to start-up with. """ from tkinter import Label, Button, Entry, DoubleVar, StringVar, IntVar from tkinter import BooleanVar, OptionMenu, Checkbutton import tkMessageBox from eqcorrscan.utils import parameters from obspy import UTCDateTime import warnings # Set the default par, only if they don't already exist. if not par: par = parameters.EQcorrscanParameters([''], 2, 10, 4, 100, 2, '1900-01-01', '2300-01-01', '', 'seishub', 4, False, '', 'jpg', False, 8, 'MAD', 6) # Callback functions for all variables (ugly) def update_template_names(*args): par.template_names = [name.strip() for name in template_names.get().split(',')] template_names.set(', '.join(par.template_names)) def update_lowcut(*args): par.lowcut = lowcut.get() lowcut.set(par.lowcut) def update_highcut(*args): par.highcut = highcut.get() if par.highcut >= 0.5 * par.samp_rate: msg = ('Highcut must be less than the Nyquist, setting to ' + str((par.samp_rate / 2.0) - 1)) tkMessageBox.showwarning(title="Nyquist error", message=msg) par.highcut = (par.samp_rate / 2.0) - 1 highcut.set(par.highcut) def update_filt_order(*args): par.filt_order = filt_order.get() filt_order.set(par.filt_order) def update_samp_rate(*args): par.samp_rate = samp_rate.get() if par.highcut >= 0.5 * par.samp_rate: msg = ('Highcut must be less than the Nyquist, setting to ' + str((par.samp_rate / 2.0) - 1)) tkMessageBox.showwarning(title="Nyquist error", message=msg) par.highcut = (par.samp_rate / 2.0) - 1 highcut.set(par.highcut) samp_rate.set(par.samp_rate) def update_debug(*args): par.debug = debug.get() debug.set(par.debug) def update_startdate(*args): par.startdate = UTCDateTime(startdate.get()) startdate.set(str(par.startdate)) def update_enddate(*args): par.enddate = UTCDateTime(enddate.get()) enddate.set(str(par.enddate)) def update_archive(*args): par.archive = archive.get() archive.set(par.archive) def update_arc_type(*args): par.arc_type = arc_type.get() arc_type.set(par.arc_type) def update_cores(*args): par.cores = cores.get() cores.set(par.cores) def update_plotvar(*args): par.plotvar = plotvar.get() plotvar.set(par.plotvar) def update_plot_format(*args): par.plot_format = plot_format.get() plot_format.set(par.plot_format) def update_tempdir(*args): par.tempdir = tempdir.get() tempdir.set(par.tempdir) def update_threshold(*args): par.threshold = threshold.get() threshold.set(par.threshold) def update_threshold_type(*args): par.threshold_type = threshold_type.get() threshold_type.set(par.threshold_type) def update_plotdir(*args): par.plotdir = plotdir.get() plotdir.set(par.plotdir) def update_trigger_interval(*args): par.trigger_interval = trigger_interval.get() trigger_interval.set(par.trigger_interval) # Set some grid parameters nrows = 25 ncolumns = 3 self.master = master master.title("EQcorrscan parameter setup") self.label = Label(master, text="Alpha GUI for default setup") self.label.grid(column=0, columnspan=ncolumns, row=0) # Set up parameter input self.t_names_label = Label(master, text="Template names", anchor='e') self.t_names_label.grid(column=0, row=1, sticky='e') template_names = StringVar() template_names.set(', '.join(par.template_names)) self.t_names_box = Entry(master, bd=2, textvariable=template_names) self.t_names_box.grid(column=1, row=1) template_names.trace("w", update_template_names) self.t_names_lookup = Button(master, text="Lookup", command=lambda: self.get_template_names(par)) self.t_names_lookup.grid(column=2, row=1) self.lowcut_label = Label(master, text="Lowcut (Hz)", anchor='e') self.lowcut_label.grid(column=0, row=2, sticky='e') lowcut = DoubleVar() lowcut.set(par.lowcut) self.lowcut_box = Entry(master, bd=2, textvariable=lowcut) self.lowcut_box.grid(column=1, row=2) lowcut.trace("w", update_lowcut) self.highcut_label = Label(master, text="Highcut (Hz)", anchor='e') self.highcut_label.grid(column=0, row=3, sticky='e') highcut = DoubleVar() highcut.set(par.highcut) self.highcut_box = Entry(master, bd=2, textvariable=highcut) self.highcut_box.grid(column=1, row=3) highcut.trace("w", update_highcut) self.filt_order_label = Label(master, text="Filter order") self.filt_order_label.grid(column=0, row=4, sticky='e') filt_order = DoubleVar() filt_order.set(par.filt_order) self.filt_order_box = Entry(master, bd=2, textvariable=filt_order) self.filt_order_box.grid(column=1, row=4) filt_order.trace("w", update_filt_order) self.samp_rate_label = Label(master, text="Sample rate (Hz)") self.samp_rate_label.grid(column=0, row=5, sticky='e') samp_rate = DoubleVar() samp_rate.set(par.samp_rate) self.samp_rate_box = Entry(master, bd=2, textvariable=samp_rate) self.samp_rate_box.grid(column=1, row=5) samp_rate.trace("w", update_samp_rate) self.debug_label = Label(master, text="Debug") self.debug_label.grid(column=0, row=6, sticky='e') debug = IntVar() debug.set(par.debug) self.debug_box = Entry(master, bd=2, textvariable=debug) self.debug_box.grid(column=1, row=6) debug.trace("w", update_debug) self.startdate_label = Label(master, text="Start date (yyyy-mm-dd)") self.startdate_label.grid(column=0, row=6, sticky='e') startdate = StringVar() startdate.set(par.startdate) self.startdate_box = Entry(master, bd=2, textvariable=startdate) self.startdate_box.grid(column=1, row=6) startdate.trace("w", update_startdate) self.enddate_label = Label(master, text="End date (yyyy-mm-dd)") self.enddate_label.grid(column=0, row=8, sticky='e') enddate = StringVar() enddate.set(par.enddate) self.enddate_box = Entry(master, bd=2, textvariable=enddate) self.enddate_box.grid(column=1, row=8) enddate.trace("w", update_enddate) self.archive_label = Label(master, text="Archive") self.archive_label.grid(column=0, row=9, sticky='e') archive = StringVar() archive.set(par.archive) self.archive_box = Entry(master, bd=2, textvariable=archive) self.archive_box.grid(column=1, row=9) archive.trace("w", update_archive) self.archive_lookup = Button(master, text="Lookup", command=lambda: self.get_archive(par)) self.archive_lookup.grid(column=2, row=9) self.arc_type_label = Label(master, text="Archive type") self.arc_type_label.grid(column=0, row=10, sticky='e') arc_type = StringVar() arc_type.set(par.arc_type) self.arc_type_box = OptionMenu(master, arc_type, "seishub", "fdsn", "day_vols") self.arc_type_box.grid(column=1, row=10, sticky='w,e') arc_type.trace("w", update_arc_type) self.cores_label = Label(master, text="Number of cores") self.cores_label.grid(column=0, row=11, sticky='e') cores = IntVar() cores.set(par.cores) self.cores_box = Entry(master, bd=2, textvariable=cores) self.cores_box.grid(column=1, row=11) cores.trace("w", update_cores) self.plotvar_label = Label(master, text="Plotting on/off") self.plotvar_label.grid(column=0, row=12, sticky='e') plotvar = BooleanVar() plotvar.set(par.plotvar) self.plotvar_box = Checkbutton(master, text='Plot on', var=plotvar, onvalue=True, offvalue=False) self.plotvar_box.grid(column=1, row=12) plotvar.trace("w", update_plotvar) self.plotdir_label = Label(master, text="Plot directory") self.plotdir_label.grid(column=0, row=13, sticky='e') plotdir = StringVar() plotdir.set(par.plotdir) self.plotdir_box = Entry(master, bd=2, textvariable=plotdir) self.plotdir_box.grid(column=1, row=13) plotdir.trace("w", update_plotdir) self.plotdir_lookup = Button(master, text="Lookup", command=lambda: self.get_plotdir(par)) self.plotdir_lookup.grid(column=2, row=13) self.plot_format_label = Label(master, text="Plot format") self.plot_format_label.grid(column=0, row=14, sticky='e') plot_format = StringVar() plot_format.set(par.plot_format) self.plot_format_box = OptionMenu(master, plot_format, "jpg", "eps", "pdf", "png") self.plot_format_box.grid(column=1, row=14, sticky='w,e') plot_format.trace("w", update_plot_format) self.tempdir_label = Label(master, text="Temporary directory") self.tempdir_label.grid(column=0, row=15, sticky='e') tempdir = StringVar() tempdir.set(par.tempdir) self.tempdir_box = Entry(master, bd=2, textvariable=tempdir) self.tempdir_box.grid(column=1, row=15) tempdir.trace("w", update_tempdir) self.tempdir_lookup = Button(master, text="Lookup", command=lambda: self.get_tempdir(par)) self.tempdir_lookup.grid(column=2, row=15) self.threshold_label = Label(master, text="Threshold") self.threshold_label.grid(column=0, row=16, sticky='e') threshold = DoubleVar() threshold.set(par.threshold) self.threshold_box = Entry(master, bd=2, textvariable=threshold) self.threshold_box.grid(column=1, row=16) threshold.trace("w", update_threshold) self.threshold_type_label = Label(master, text="Threshold type") self.threshold_type_label.grid(column=0, row=17, sticky='e') threshold_type = StringVar() threshold_type.set(par.threshold_type) self.threshold_type_box = OptionMenu(master, threshold_type, "MAD", "absolute", "av_chan_corr") self.threshold_type_box.grid(column=1, row=17, sticky='w,e') threshold_type.trace("w", update_threshold_type) self.trigger_interval_label = Label(master, text="Minimum trigger " + "interval (s)") self.trigger_interval_label.grid(column=0, row=18, sticky='e') trigger_interval = DoubleVar() trigger_interval.set(par.trigger_interval) self.trigger_interval_box = Entry(master, bd=2, textvariable=trigger_interval) self.trigger_interval_box.grid(column=1, row=18) trigger_interval.trace("w", update_trigger_interval) # End of user editable section, now we have read/write buttons self.read_button = Button(master, text="Read parameters", command=lambda: self.read_par(master)) self.read_button.grid(column=0, row=nrows-2, sticky='w,e') self.write_button = Button(master, text="Write parameters", command=lambda: self.write_par(par)) self.write_button.grid(column=1, row=nrows-2, sticky='w,e')
class App(Tk): def __init__(self): super().__init__() self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.connect(communication.SERVER_ADDRESS) self.sock.setblocking(False) self.title('OnlineDrive - Welcome') # Informa à aplicação qual método chamar ao fechar. self.protocol('WM_DELETE_WINDOW', self.on_close) self.current_frame = Frame() self.user_logged = BooleanVar() self.user_logged.set(False) self.user_logged.trace('w', self.user_state_changed) # Inicia o thread que recebe dados do servidor. Thread(target=self.get_server_response).start() def run(self): self.current_frame = LoginViewController.Controller(self).view self.mainloop() def on_close(self): # Abre um pop-up para confirmar se o usuário realmente deseja fechar a aplicação if messagebox.askquestion('Quit', 'Você deseja mesmo sair?') == 'yes': # Manda uma mensagem para o servidor informando-o para desconectar o cliente self.sock.sendall( json.dumps({ 'action': communication.ACTIONS[2] }).encode()) self.sock.close() # Fecha a aplicação self.destroy() def send_request(self, request: dict): serialized_request = json.dumps(request) Thread(target=self.sock.sendall, args=(serialized_request.encode(), )).start() def get_server_response(self): while True: data = self.sock.recv(communication.BUFFSIZE) response = json.loads(data.decode()) if 'file_name' in response: # Recebe o nome do arquivo. print('Received file name. Preparing to receive file data.') self.recv_file_data(response['file_name']) else: if response == communication.RESULTS[0]: print('response:', response['message']) def recv_file_data(self, file_name): # Recebe os dados do arquivo. with open(os.path.join('downloads', file_name), 'wb') as file: while True: print('Recebendo dados do arquivo.') data = self.sock.recv(communication.BUFFSIZE) file.write(data) def user_state_changed(self, *args): if self.user_logged.get() is True: # Remove o frame atual da tela. for widget in self.slaves(): widget.destroy() # Abre um pop-up que informa que o usuário está logado. messagebox.showinfo('UHUU', 'Você está logado')
class MyApp(Tk): """ This class serves as a central control. Where are all process are launched and variables are set and shared. """ __title__ = "Senior" def __init__(self, *args, **kwargs): Tk.__init__(self, *args, **kwargs) container = Frame(self) container.grid() self.serial = SerialComm() self.frames = {} self.q = LifoQueue() self.eye_tracker = EyeTracker(self.q) self.variables = {} self.fps = IntVar() self.fps.set(20) self.temp = IntVar() self.temp.set(20) self.temp.trace("w", self.send_command) self.variables[self.temp.__str__()] = 't', self.temp self.temp_current = StringVar() self.temp_current.set('Temperature') self.tts = StringVar() self.tts.set('Type and Play') self.temp_offset = IntVar() self.temp_offset.set(5) self.temp_offset.trace("w", self.send_command) self.variables[self.temp_offset.__str__()] = 'o', self.temp_offset self.samples = 9 self.window = IntVar() self.window.trace("w", self.send_command) self.variables[self.window.__str__()] = 'w', self.window self.mouse_control = BooleanVar() self.mouse_control.set(False) self.talk = BooleanVar() self.talk.set(True) self.alarm = BooleanVar() self.alarm.set(False) self.alarm.trace("w", self.send_command) self.variables[self.alarm.__str__()] = 'a', self.alarm self.light = BooleanVar() self.light.set(False) self.light.trace("w", self.send_command) self.variables[self.light.__str__()] = 'l', self.light self.heater = BooleanVar() self.heater.set(False) self.heater.trace("w", self.send_command) self.variables[self.heater.__str__()] = 'h', self.heater self.ac = BooleanVar() self.ac.set(False) self.ac.trace("w", self.send_command) self.variables[self.ac.__str__()] = 'f', self.ac self.move = BooleanVar() self.move.set(False) self.w, self.h = pyautogui.size() self.hor_div = DoubleVar() self.hor_div.set(5) self.hor_div.trace("w", self.send_command) self.variables[self.hor_div.__str__()] = 'hor', self.hor_div self.ver_div = DoubleVar() self.ver_div.set(5) self.ver_div.trace("w", self.send_command) self.variables[self.ver_div.__str__()] = 'ver', self.ver_div self.mouse_directions = [] self.mouse = MouseAndSpeech(self) self.t = Thread(target=self.mouse.process) self.t.start() self.frame = None self.draw = False frame = Preview(container, self) self.frames[Preview] = frame frame.grid(row=0, column=1, sticky="nsew", rowspan=100) self.current_frame = frame frame = Settings(container, self) self.frames[Settings] = frame frame.grid(row=0, column=0, sticky="nsew", pady=10, padx=10) frame.grid_remove() frame = Applications(container, self) self.frames[Applications] = frame frame.grid(row=0, column=0, sticky="nsew", pady=10, padx=10) frame.grid_remove() # Menu Bar menu = MyMenu(self) self.config(menu=menu) Tk.iconbitmap(self, default=resource_path('icon.ico')) Tk.wm_title(self, "Senior") w = (self.winfo_screenwidth() - self.eye_tracker.window_size) // 2 self.geometry('+{}+{}'.format(w, 0)) self.protocol("WM_DELETE_WINDOW", lambda: self.close()) def show_frame(self, cont): """ This method is used to switch betwen views. """ if self.current_frame is not None: self.current_frame.grid_remove() frame = self.frames[cont] frame.grid() self.current_frame = frame if cont is Applications: frame.button_alarm.focus() def send_command(self, widget, *args): """ This method send data to the Arduino whenever a button is clicked. """ w = self.variables[widget] try: indicator = w[0] value = str(int(w[1].get())) if indicator == 'f' and value == '1': self.heater.set(False) self.serial.send_serial('h0') elif indicator == 'h' and value == '1': self.ac.set(False) self.serial.send_serial('f0') elif indicator == 't': self.heater.set(False) self.ac.set(False) s = indicator + value self.serial.send_serial(s) if len(value) > 0: value = float(w[1].get()) if indicator in ['ver', 'hor']: x_offset = self.ver_div.get() y_offset = self.hor_div.get() if indicator == 'ver': y_offset = value * (self.h // 100) elif indicator == 'hor': x_offset = value * (self.w // 100) self.mouse_directions = [(-x_offset, -y_offset), (0, -y_offset), (x_offset, -y_offset), (-x_offset, 0), 0, (x_offset, 0), (-x_offset, y_offset), (0, y_offset), (x_offset, y_offset), 0] except: pass def close(self): """ Method used to to close the program orderly so no threads are left hanging. """ print(self.__title__) while self.mouse.isTalking: print('Is Talking') self.eye_tracker.video_capture.release() clear_queue(self.q) self.q.put(False) self.q.join() self.t.join() self.destroy()
class MainWindow: def __init__(self): global root self.provider = None self.session = None self.show_image = False self.original_image = None self.original_image_name = None self.main_image = None self.main_image_orig = None self.resized = False self.thumb_prefix = None self.proxies = None self.gallery_url = None self.hist_stack = [] self.fwd_stack = [] self.interrupt = False self.thumb_url = None self.image_url = None self.menu_bar = Menu(root) self.menu_bar.add_command(label="<< Back", command=self.back_in_history) self.menu_bar.add_command(label="Forward >>", command=self.forward_in_history) self.menu_bar.add_command(label="View gallery", command=self.view_gallery_url) self.menu_bar.add_command(label="Cancel", command=self.calcel) root.config(menu=self.menu_bar) frm_top = Frame(root) self.frm_main = ScrollFrame(root) frm_status = Frame(root) frm_center = Frame(self.frm_main.view_port) frm_left = Frame(self.frm_main.view_port) frm_right = Frame(self.frm_main.view_port) frm_caption = Frame(frm_center) frm_image = Frame(frm_center) self.btn_prev = LinkButton(self, frm_caption, text="Previous") self.btn_prev.link = "prev link" self.btn_prev.pack(side=LEFT) self.btn_save = Button(frm_caption, text="Save", command=self.save_image) self.btn_save.pack(side=LEFT) self.btn_next = LinkButton(self, frm_caption, text="Next") self.btn_next.link = "next link" self.btn_next.pack(side=LEFT) self.btn_paste = Button(frm_top, text="Paste", command=self.paste_from_clipboard) self.btn_paste.pack(side=LEFT) self.btn_update = Button(frm_top, text="Load image", command=self.load_image_from_input) self.btn_update.pack(side=LEFT) self.sv_url = StringVar() self.entry_url = Entry(frm_top, textvariable=self.sv_url, width=100) self.entry_url.bind("<FocusIn>", self.focus_callback) self.entry_url.bind('<Return>', self.enter_callback) self.entry_url.pack(side=LEFT) self.use_proxy = BooleanVar() self.use_proxy.set(False) self.use_proxy.trace('w', self.on_use_proxy_change) self.chk_use_proxy = Checkbutton(frm_top, text='Use proxy', variable=self.use_proxy) self.chk_use_proxy.pack(side=LEFT) self.sv_proxy = StringVar() self.entry_proxy = Entry(frm_top, textvariable=self.sv_proxy, width=30, state=DISABLED) self.entry_proxy.pack(side=LEFT) self.btn_force = Button(frm_top, text="Force load", command=self.force_load_image) self.btn_force.pack(side=LEFT) try: with open("proxy.txt") as f: self.sv_proxy.set(f.readline().strip()) except BaseException as error: print(error) traceback.print_exc() self.btn_image = Button(frm_image, command=self.resize_image) self.btn_image.bind("<Button-3>", self.load_original_image_in_thread) self.btn_image.pack() self.left_buttons = self.fill_panel(frm_left) self.right_buttons = self.fill_panel(frm_right) self.status = StringVar() self.status_label = Label(frm_status, bd=1, relief=SUNKEN, anchor=W, textvariable=self.status) self.status_label.pack(side=LEFT, fill=BOTH, expand=1) self.status.set('Status Bar') self.progress_bar = ttk.Progressbar(frm_status, orient=HORIZONTAL, length=200, mode='indeterminate') root.bind("<FocusIn>", self.focus_callback) root.bind("<BackSpace>", self.backspace_callback) root.bind("<space>", self.space_callback) root.protocol("WM_DELETE_WINDOW", self.on_close) frm_caption.pack() frm_image.pack() frm_left.pack(side=LEFT, fill=BOTH, expand=1) frm_center.pack(side=LEFT) frm_right.pack(side=RIGHT, fill=BOTH, expand=1) frm_top.pack() self.frm_main.pack(fill=BOTH, expand=1) frm_status.pack(fill=X) self.hist_logger = logging.getLogger('history') self.hist_logger.setLevel(logging.INFO) self.fh_hist = logging.FileHandler( os.path.join(LOGS, f'hist_{int(time.time())}.log')) self.fh_hist.setLevel(logging.INFO) self.hist_logger.addHandler(self.fh_hist) def force_load_image(self): self.load_page_in_thread(self.sv_url.get().strip(), True, True) def load_image_from_input(self): self.load_page_in_thread(self.sv_url.get().strip()) def load_page_in_thread(self, input_url, remember=True, ignore_cache=False): self.set_controls_state(DISABLED) self.interrupt = False future = executor.submit(self.load_image_retry, input_url, remember, ignore_cache) future.add_done_callback(lambda f: self.set_controls_state(NORMAL)) def load_image_retry(self, input_url, remember, ignore_cache): global root try: err_count = 0 while err_count < MAX_ERRORS: root.after_idle(self.set_undefined_state) if self.load_image(input_url, remember, ignore_cache): break if self.interrupt: break err_count += 1 except BaseException as error: print("Exception URL: " + input_url) print(error) traceback.print_exc() def load_image(self, input_url, remember, ignore_cache): global root if len(input_url) == 0: return False root.after_idle(self.sv_url.set, input_url) self.provider = self.get_provider() if self.provider is None: return False cache_path = os.path.join(CACHE, self.provider.get_domen()) if not os.path.exists(cache_path): os.mkdir(cache_path) proxy = self.sv_proxy.get().strip() if self.use_proxy.get() and len(proxy.strip()) > 0: self.proxies = { "http": "http://" + proxy, "https": "https://" + proxy } with open("proxy.txt", "w") as f: f.write(proxy) else: self.proxies = None http_session = requests.Session() http_session.headers.update(HEADERS) ident = self.get_id(input_url) if ident is None: print("ident is None") return False input_url = "https://" + self.provider.get_host() + "/" + ident root.after_idle(root.title, input_url) try: html = self.get_from_cache(ident) if ignore_cache or (html is None) or (len(html) == 0): html = self.get_final_page(ident, input_url, http_session) if (html is None) or (len(html) == 0): return False html = html.decode('utf-8') if not self.render_page(ident, html, http_session): return False if remember and (input_url is not None): if len(self.hist_stack) == 0 or (input_url != self.hist_stack[-1]): self.hist_stack.append(input_url) self.hist_logger.info(f'{input_url}\t{self.thumb_url}') if len(self.fwd_stack) > 0 and (input_url == self.fwd_stack[-1]): self.fwd_stack.pop() else: self.fwd_stack.clear() except BaseException as error: print("Exception URL: " + input_url) print(error) traceback.print_exc() return False finally: http_session.close() return True def get_final_page(self, ident, input_url, http_session): global root response = http_session.get(input_url, proxies=self.proxies, timeout=TIMEOUT) if response.status_code == 404: print("input_url response.status_code == 404") return None html = response.content.decode('utf-8') if DEBUG: with open('1.html', 'w') as f: f.write(html) # sometimes this functions fails (i don't want to tamper with this) redirect_url = self.provider.get_redirect_url(html) if redirect_url is not None: if len(redirect_url) == 0: print("(redirect_url is None) or (len(redirect_url) == 0)") return None http_session.headers.update({'Referer': input_url}) response = http_session.get(redirect_url, proxies=self.proxies, timeout=TIMEOUT) if response.status_code == 404: print("redirect_url response.status_code == 404") return None html = response.content.decode('utf-8') if DEBUG: with open('2.html', 'w') as f: f.write(html) pos = html.find('File Not Found') if pos >= 0: print("File Not Found: " + input_url) return None param = self.provider.get_post_param(html) if len(param) == 0: print("len(param) == 0") return None post_fields = {'op': 'view', 'id': ident, 'pre': 1, param: 1} response = http_session.post(redirect_url, data=post_fields, proxies=self.proxies, timeout=TIMEOUT) if response.status_code == 404: print("POST: redirect_url response.status_code == 404") return None html = response.content if DEBUG: with open('3.html', 'wb') as f: f.write(html) self.put_to_cache(ident, html) return html def render_page(self, ident, html, http_session): self.thumb_url = get_thumb(html) if (self.thumb_url is None) or (len(self.thumb_url) == 0): print("len(thumb_url) == 0") return False slash_pos = self.thumb_url.rfind('/') self.thumb_prefix = self.thumb_url[:slash_pos + 1] thumb_filename = self.thumb_url[slash_pos + 1:] dot_pos = thumb_filename.rfind('.') thumb_filename = thumb_filename[:dot_pos] self.gallery_url = search('href="([^"]*)">More from gallery</a>', html) self.reconfigure_prev_button(http_session, html) self.reconfigure_next_button(http_session, html) executor.submit(self.reconfigure_left_buttons, html) executor.submit(self.reconfigure_right_buttons, html) self.image_url = self.provider.get_image_url(html) fname = get_filename(self.image_url) dot_pos = fname.rfind('.') self.original_image_name = fname[:dot_pos] + '_' + ident self.original_image = self.get_from_cache(self.original_image_name) bg_color = 'green' self.resized = True if (self.original_image is None) or (len(self.original_image) == 0): self.original_image = self.get_from_cache(thumb_filename) self.resized = False if (self.original_image is None) or (len(self.original_image) == 0): response = http_session.get(self.thumb_url, proxies=self.proxies, timeout=TIMEOUT) if response.status_code == 404: print("image_url response.status_code == 404") return False self.original_image = response.content # if DEBUG: # with open(self.original_image_name, 'wb') as f: # f.write(self.original_image) self.put_to_cache(thumb_filename, self.original_image) bg_color = 'red' self.resized = False img = Image.open(io.BytesIO(self.original_image)) w, h = img.size k = MAIN_IMG_WIDTH / w img_resized = img.resize((MAIN_IMG_WIDTH, int(h * k))) root.after_idle(root.title, f"{root.title()} ({w}x{h})") self.main_image_orig = ImageTk.PhotoImage(img) self.main_image = ImageTk.PhotoImage(img_resized) photo_image = self.main_image if self.resized else self.main_image_orig root.after_idle(self.btn_image.config, { 'image': photo_image, 'background': bg_color }) if os.path.exists(os.path.join(OUTPUT, self.original_image_name)): root.after_idle(self.btn_save.config, {'background': 'green'}) return True def focus_callback(self, event): self.entry_url.selection_range(0, END) def enter_callback(self, event): self.load_image_from_input() def backspace_callback(self, event): self.back_in_history() def space_callback(self, event): self.forward_in_history() def on_close(self): global root root.update_idletasks() root.destroy() self.fh_hist.close() self.hist_logger.removeHandler(self.fh_hist) def calcel(self): self.interrupt = True def set_undefined_state(self): global root self.main_image = None self.main_image_orig = None self.original_image = None self.original_image_name = None self.btn_image.config(image='', background="SystemButtonFace") self.btn_save.config(background="SystemButtonFace") root.title(None) self.btn_prev.reset() self.btn_next.reset() for btn in self.left_buttons: btn.reset() for btn in self.right_buttons: btn.reset() self.frm_main.scroll_top_left() def paste_from_clipboard(self): self.sv_url.set(clipboard.paste()) self.entry_url.selection_range(0, END) def save_image(self): if self.original_image is None: return filename = self.original_image_name i = 1 while os.path.exists(os.path.join(OUTPUT, filename)): filename = f'{self.original_image_name}_{i:04}' i += 1 with open(os.path.join(OUTPUT, filename), 'wb') as f: f.write(self.original_image) self.btn_save.config(background="green") def on_enter(self, event): self.status.set(event.widget.link) def on_leave(self, enter): self.status.set("") def fill_panel(self, panel): buttons = [] for i in range(4): Grid.columnconfigure(panel, i, weight=1) for j in range(2): Grid.rowconfigure(panel, j, weight=1) btn = LinkButton(self, panel, text=f"({i}, {j})") btn.link = None btn.grid(row=i, column=j, sticky=NSEW, padx=PAD, pady=PAD) buttons.append(btn) return buttons def resize_image(self): self.btn_image.config( image=(self.main_image_orig if self.resized else self.main_image)) self.resized = not self.resized self.frm_main.scroll_top_left() def get_id(self, url): found = re.search( r"https?://" + self.provider.get_domen() + r"\.[a-z]+/(.+?)(?:/|$)", url) if (found is None) or (found.group(0) is None): return None return found.group(1) def reconfigure_left_buttons(self, html): tab = get_more_from_author(html) self.reconfigure_buttons(self.left_buttons, tab) def reconfigure_right_buttons(self, html): tab = get_more_from_gallery(html) self.reconfigure_buttons(self.right_buttons, tab) def reconfigure_prev_button(self, http_session, html): url = get_prev_url(html) if len(url) == 0: return ident = self.get_id(url) img_url = self.thumb_prefix + ident + '_t.jpg' self.reconfigure_button(http_session, self.btn_prev, url, img_url) def reconfigure_next_button(self, http_session, html): url = get_next_url(html) if len(url) == 0: return ident = self.get_id(url) img_url = self.thumb_prefix + ident + '_t.jpg' self.reconfigure_button(http_session, self.btn_next, url, img_url) def reconfigure_button(self, http_session, btn, url, img_url): global root filename = get_filename(img_url) dot_pos = filename.rfind('.') filename = filename[:dot_pos] bg_color = "green" image = self.get_from_cache(filename) if (image is None) or (len(image) == 0): image = download_image(http_session, img_url) self.put_to_cache(filename, image) bg_color = "red" if (image is None) or (len(image) == 0): return img = Image.open(io.BytesIO(image)) w, h = img.size k = IMG_WIDTH / w img_resized = img.resize((IMG_WIDTH, int(h * k))) photo_image = ImageTk.PhotoImage(img_resized) if photo_image is None: return root.after_idle(btn.set_values, url, partial(self.load_page_in_thread, url), photo_image, bg_color) def reconfigure_buttons(self, buttons, html): http_session = requests.Session() http_session.headers.update(HEADERS) try: for btn in buttons: btn.reset() i = 0 for m in re.finditer('<td>.*?href="(.*?)".*?src="(.*?)".*?</td>', html, re.MULTILINE | re.DOTALL): self.reconfigure_button(http_session, buttons[i], m.group(1), m.group(2)) i += 1 except BaseException as error: print(error) traceback.print_exc() finally: http_session.close() def on_use_proxy_change(self, *args): if self.use_proxy.get(): self.entry_proxy.config(state=NORMAL) self.entry_proxy.focus_set() self.entry_proxy.selection_range(0, END) else: self.entry_proxy.config(state=DISABLED) def get_provider(self): input_url = self.sv_url.get() pos = input_url.find(ImgRock.DOMEN) if pos >= 0: return ImgRock() pos = input_url.find(ImgView.DOMEN) if pos >= 0: return ImgView() pos = input_url.find(ImgTown.DOMEN) if pos >= 0: return ImgTown() pos = input_url.find(ImgOutlet.DOMEN) if pos >= 0: return ImgOutlet() pos = input_url.find(ImgMaze.DOMEN) if pos >= 0: return ImgMaze() pos = input_url.find(ImgDew.DOMEN) if pos >= 0: return ImgDew() return None def view_gallery_url(self): if (self.gallery_url is None) or (len(self.gallery_url) == 0): return clipboard.copy(self.gallery_url) GalleryWindow(self, Toplevel(root)) def back_in_history(self): if len(self.hist_stack) < 2: return self.fwd_stack.append(self.hist_stack.pop()) self.load_page_in_thread(self.hist_stack[-1], False) def forward_in_history(self): if len(self.fwd_stack) == 0: return self.load_page_in_thread(self.fwd_stack[-1]) def get_from_cache(self, filename): full_path = os.path.join(CACHE, self.provider.get_domen(), filename) if not os.path.exists(full_path): return None mod_time = time.time() os.utime(full_path, (mod_time, mod_time)) with open(full_path, 'rb') as f: return f.read()[::-1] def put_to_cache(self, filename, data): if (data is None) or (len(data) == 0): return full_path = os.path.join(CACHE, self.provider.get_domen(), filename) with open(full_path, 'wb') as f: f.write(data[::-1]) def set_controls_state(self, status): self.btn_prev.config(state=status) self.btn_next.config(state=status) self.btn_update.config(state=status) self.btn_force.config(state=status) self.entry_url.config(state=status) self.btn_paste.config(state=status) self.chk_use_proxy.config(state=status) self.entry_proxy.config(state=status) self.menu_bar.entryconfig("<< Back", state=status) self.menu_bar.entryconfig("Forward >>", state=status) for btn in self.left_buttons: btn.config(state=status) for btn in self.right_buttons: btn.config(state=status) if status == DISABLED: self.progress_bar.pack(side=LEFT) self.progress_bar.start() else: self.progress_bar.pack_forget() self.progress_bar.stop() def load_original_image_in_thread(self, event): executor.submit(self.load_original_image) def load_original_image(self): response = requests.get(self.image_url, proxies=self.proxies, timeout=TIMEOUT) if response.status_code == 404: print("image_url response.status_code == 404") return self.original_image = response.content # if DEBUG: # with open(self.original_image_name, 'wb') as f: # f.write(self.original_image) self.put_to_cache(self.original_image_name, self.original_image) bg_color = 'red' self.resized = True img = Image.open(io.BytesIO(self.original_image)) w, h = img.size k = MAIN_IMG_WIDTH / w img_resized = img.resize((MAIN_IMG_WIDTH, int(h * k))) root.after_idle(root.title, f"{root.title()} ({w}x{h})") self.main_image_orig = ImageTk.PhotoImage(img) self.main_image = ImageTk.PhotoImage(img_resized) root.after_idle(self.btn_image.config, { 'image': self.main_image, 'background': bg_color })
class CurrencyEditor(Frame): def __init__(self, master, **kwargs): super().__init__(master, **kwargs) self.bvar_modified = BooleanVar() self.create_currency_ui() def create_currency_ui(self): self.rowconfigure(0, weight=1) self.columnconfigure(0, weight=1) self.frm_currency = Frame(self) self.frm_currency.rowconfigure(2, weight=1) self.frm_currency.columnconfigure(0, weight=1) self.frm_currency.grid(padx=10, pady=10, sticky='nsew') # Tree Frame self.frm_tree = Frame(self.frm_currency) self.frm_tree.grid(row=2, sticky='nsew') self.frm_tree.rowconfigure(0, weight=1) self.frm_tree.columnconfigure(0, weight=1) self.tree = EditableTreeview(self.frm_tree, on_cell_update=self.onCellUpdate) scrly = AutoScrollbar(self.frm_tree, command=self.tree.yview) scrlx = AutoScrollbar(self.frm_tree, command=self.tree.xview, orient=HORIZONTAL) self.tree.config(yscrollcommand=scrly.set, xscrollcommand=scrlx.set) self.tree.grid(row=0, column=0, sticky='nsew') scrly.grid(row=0, column=1, sticky='nsew') scrlx.grid(row=1, column=0, sticky='nsew') self.tree.insert('', 0, text='Exalted Orb', values=('90', '85')) frm = Frame(self.frm_currency, relief=SOLID, borderwidth=2) frm.columnconfigure(10, weight=1) frm.grid(row=1, column=0, pady=(0, 5), sticky='nsew') # self.entry_currency = \ # Combobox_Autocomplete(frm, list_of_items=['one of many currencies'], startswith_match=False) self.search_var = StringVar() self.entry_search = PlaceholderEntry(frm, 'Search..', style='Default.TEntry', textvariable=self.search_var) self.search_var.trace_variable( 'w', lambda a, b, c: self.tree.search(self.entry_search.get_value())) self.entry_search.bind( '<Return>', lambda event: self.tree.search(self.entry_search.get_value(), find_next=True)) # self.btn_currency_search = Button(frm, text='Search', command=lambda event: self.tree_currency.search(self.entry_currency_search.get_value(), find_next=True)) self.btn_apply = Button(frm, text='Apply', command=self.applyChanges) self.btn_reload = Button( frm, text='Reload', command=lambda: self.loadCurrency(force_reload=True)) self.entry_search.grid(row=2, column=0, pady=5, padx=5) # self.btn_currency_search.grid(row=2, column=1, pady=5) self.btn_apply.grid(row=2, column=2, pady=5) # frm.columnconfigure(3, weight=1) self.btn_reload.grid(row=2, column=3, sticky='e', pady=5) # Confidence Level lbl = Label(frm, text="Confidence level:") lbl.grid(row=2, column=10, padx=5, sticky='nse', pady=(3, 5)) self.lbl_confidence_lvl = lbl self.var_confidence_lvl = IntVar() self.entry_confidence_lvl = ConfidenceScale( frm, variable=self.var_confidence_lvl) self.entry_confidence_lvl.grid(row=2, column=11, padx=5, pady=5) self.var_confidence_lvl.trace( 'w', lambda a, b, c: self.on_entry_change(self.entry_confidence_lvl)) # Tree Config tree = self.tree tree['columns'] = currencyColumns[1:] tree.register_column( 'Override', ColEntry(TooltipEntry(tree), func_validate=_validate_price_override)) def init_tree_column(col): col_name = currencyColumns[0] if col == '#0' else col tree.heading(col, text=CurrencyColumn[col_name].value, anchor=W, command=lambda col=col: tree.sort_col(col)) tree.column(col, width=140, stretch=False) for col in ('#0', ) + tree['columns']: init_tree_column(col) tree.heading('#0', anchor=CENTER) tree.column('#0', width=250, stretch=False) tree.column(CurrencyColumn.Filler.name, stretch=True) tree.heading(CurrencyColumn.Rate.name, command=lambda col=CurrencyColumn.Rate.name: tree. sort_col(col, key=float, default=0)) tree.heading(CurrencyColumn.EffectiveRate.name, command=lambda col=CurrencyColumn.EffectiveRate.name: tree .sort_col(col, key=float, default=0)) tree.heading(CurrencyColumn.Override.name, command=lambda col=CurrencyColumn.Override.name: tree. sort_col(col, key=self._price_key)) self.bvar_modified.trace('w', lambda a, b, c: self._updateApplyState()) def _price_key(self, key): if key == '': return None # this means it will be ignored while sorting try: return cm.compilePrice(key, base_price=0) except Exception: return 0 def _updateApplyState(self): if self.bvar_modified.get(): self.btn_apply.config(state=NORMAL) else: self.btn_apply.config(state=DISABLED) def loadCurrency(self, force_reload=False): if not cm.initialized: return if not force_reload and self.bvar_modified.get(): return self.var_confidence_lvl.set(cm.confidence_level) tree = self.tree tree.clear() table = {} for curr in cm.shorts: effective_rate = cm.crates.get(curr, '0') table[curr] = (_to_display_rate(cm.rates.get(curr, '')), cm.overrides.get(curr, ''), _to_display_rate(effective_rate)) for curr in table: tree.insert('', END, '', text=curr, values=table[curr]) tree.sort_col(CurrencyColumn.EffectiveRate.name, key=float, default=0) self.bvar_modified.set(False) def applyChanges(self, event=None): if not self.bvar_modified.get() or not cm.initialized: return overrides = {} for iid in self.tree.get_children(): #TODO: hide #0 col and move names to a value column currency_name_col = '#0' # CurrencyColumn.Currency.name # id = self.tree.set(iid, currency_name_col) id = self.tree.item(iid, 'text') override = self.tree.set(iid, CurrencyColumn.Override.name) if override: overrides[id] = override # ids = set([self.tree.set(iid, currency_name_col) for iid in self.tree.get_children()]) ids = set( [self.tree.item(iid, 'text') for iid in self.tree.get_children()]) # preserve unhandled ids configuration for key in (set(cm.overrides) - ids): overrides[key] = cm.overrides[key] cm.confidence_level = self.entry_confidence_lvl.get() try: cm.compile(overrides=overrides) if fm.initialized: threading.Thread(target=fm.compileFilters).start() self.bvar_modified.set(False) except AppException as e: messagebox.showerror('Update error', e, parent=self.winfo_toplevel()) except Exception as e: logexception() messagebox.showerror( 'Update error', 'Failed to apply changes, unexpected error:\n{}'.format(e), parent=self.winfo_toplevel()) def onCellUpdate(self, iid, col, old, new): if not self.bvar_modified.get() and old != new: self.bvar_modified.set(True) def on_entry_change(self, entry): self.bvar_modified.set(True)
class PricesEditor(Frame): def __init__(self, master, **kwargs): super().__init__(master, **kwargs) self.create_prices_ui() self.initial_values = {} self.table_modified = False def create_prices_ui(self): self.rowconfigure(0, weight=1) self.columnconfigure(0, weight=1) self.frm_prices = Frame(self) self.frm_prices.rowconfigure(2, weight=1) self.frm_prices.columnconfigure(0, weight=1) self.frm_prices.grid(padx=10, pady=10, sticky='nsew') # Button Frame frm_btns = Frame(self.frm_prices, relief=SOLID, borderwidth=2) frm_btns.grid(row=0, column=0, pady=(0, 5), sticky='nsew') frm_btns.columnconfigure(10, weight=1) # self.entry_currency = \ # Combobox_Autocomplete(frm, list_of_items=['one of many currencies'], startswith_match=False) self.search_var = StringVar() self.entry_search = PlaceholderEntry(frm_btns, 'Search..', style='Default.TEntry', textvariable=self.search_var) self.search_var.trace_variable( 'w', lambda a, b, c: self.tree.search(self.entry_search.get_value())) self.entry_search.bind( '<Return>', lambda event: self.tree.search(self.entry_search.get_value(), find_next=True)) self.btn_apply = Button(frm_btns, text='Apply', command=self.applyChanges) self.btn_reload = Button( frm_btns, text='Reload', command=lambda: self.loadPrices(force_reload=True)) self.entry_search.grid(row=2, column=0, pady=5, padx=5) self.btn_apply.grid(row=2, column=2, pady=5) # frm.columnconfigure(3, weight=1) self.btn_reload.grid(row=2, column=3, sticky='e', pady=5) self.var_advanced = BooleanVar(False) self.var_advanced.trace_variable( 'w', lambda a, b, c: self._on_view_option_change()) self.cb_advanced = Checkbutton(frm_btns, text='Advanced', variable=self.var_advanced) self.cb_advanced.grid(row=2, column=10, sticky='e', padx=10) frm_border = Frame(self.frm_prices, relief=SOLID, borderwidth=2) frm_border.grid(row=2, column=0, sticky='nsew') frm_border.rowconfigure(2, weight=1) frm_border.columnconfigure(0, weight=1) # Tree Frame self.frm_tree = Frame(frm_border) self.frm_tree.grid(row=2, column=0, sticky='nsew', padx=5, pady=(0, 0)) self.frm_tree.rowconfigure(0, weight=1) self.frm_tree.columnconfigure(0, weight=1) self.tree = EditableTreeview(self.frm_tree, on_cell_update=self.onCellUpdate) scrly = AutoScrollbar(self.frm_tree, command=self.tree.yview) scrlx = AutoScrollbar(self.frm_tree, command=self.tree.xview, orient=HORIZONTAL) self.tree.config(yscrollcommand=scrly.set, xscrollcommand=scrlx.set) self.tree.grid(row=0, column=0, sticky='nsew') scrly.grid(row=0, column=1, sticky='nsew') scrlx.grid(row=1, column=0, sticky='nsew') # Button Frame frm = Frame(frm_border) #, relief=SOLID, borderwidth=1) # frm = Frame(self.frm_prices) frm.grid(row=0, column=0, sticky='nsew') # self.entry_currency = \ # Combobox_Autocomplete(frm, list_of_items=['one of many currencies'], startswith_match=False) lbl = Label(frm, text='Item value threshold:') lbl.grid(row=0, column=0, padx=5, pady=5, sticky='w') self.var_threshold = StringVar() self.entry_threshold = TooltipEntry(frm, textvariable=self.var_threshold) self.entry_threshold.bind( '<FocusOut>', lambda event: self._validate_threshold_entry()) self.entry_threshold.grid(row=0, column=1, padx=5, pady=5) self.var_threshold.trace( 'w', lambda a, b, c: self.on_entry_change(self.entry_threshold)) lbl = Label(frm, text='Budget:') lbl.grid(row=0, column=2, padx=5, pady=5) self.var_budget = StringVar() self.entry_budget = TooltipEntry(frm, textvariable=self.var_budget) self.entry_budget.bind('<FocusOut>', lambda event: self._validate_budget_entry()) self.entry_budget.grid(row=0, column=3, padx=5, pady=5) self.var_budget.trace( 'w', lambda a, b, c: self.on_entry_change(self.entry_budget)) lbl = Label(frm, text='Minimum price:') lbl.grid(row=0, column=4, padx=5, pady=5) self.var_min_price = StringVar() self.entry_min_price = TooltipEntry(frm, textvariable=self.var_min_price) self.entry_min_price.bind( '<FocusOut>', lambda event: self._validate_min_price_entry()) self.entry_min_price.grid(row=0, column=5, padx=5, pady=5) self.var_min_price.trace( 'w', lambda a, b, c: self.on_entry_change(self.entry_min_price)) lbl = Label(frm, text='Default filter override:') lbl.grid(row=0, column=6, padx=5, pady=5) self.lbl_fprice_override = lbl self.var_fprice_override = StringVar() self.entry_fprice_override = TooltipEntry( frm, textvariable=self.var_fprice_override) self.entry_fprice_override.bind( '<FocusOut>', lambda event: self._validate_fprice_override_entry()) self.entry_fprice_override.grid(row=0, column=7, padx=5, pady=5) self.var_fprice_override.trace( 'w', lambda a, b, c: self.on_entry_change(self.entry_fprice_override)) # Advanced lbl = Label(frm, text='Default item value override:') lbl.grid(row=1, column=0, padx=5, pady=(2, 5), sticky='w') self.lbl_price_override = lbl self.var_price_override = StringVar() self.entry_price_override = TooltipEntry( frm, textvariable=self.var_price_override) self.entry_price_override.bind( '<FocusOut>', lambda event: self._validate_price_override_entry()) self.entry_price_override.grid(row=1, column=1, padx=5, pady=(2, 5)) self.var_price_override.trace( 'w', lambda a, b, c: self.on_entry_change(self.entry_price_override)) # Confidence Level lbl = Label(frm, text="Confidence level:") lbl.grid(row=1, column=2, padx=5, pady=(2, 5), sticky='w') self.lbl_confidence_lvl = lbl self.var_confidence_lvl = IntVar() self.entry_confidence_lvl = ConfidenceScale( frm, variable=self.var_confidence_lvl) self.entry_confidence_lvl.grid(row=1, column=3, padx=5, pady=(2, 5)) self.var_confidence_lvl.trace( 'w', lambda a, b, c: self.on_entry_change(self.entry_confidence_lvl)) self.var_5l_filters = BooleanVar(False) self.cb_5l_filters = VarCheckbutton(frm, text='Enable 5L filters', variable=self.var_5l_filters) self.cb_5l_filters.var = self.var_5l_filters self.cb_5l_filters.grid(row=1, column=4, padx=5, pady=(2, 5), columnspan=1) self.var_5l_filters.trace_variable( 'w', lambda a, b, c: self.on_entry_change(self.cb_5l_filters)) # Tree Config tree = self.tree def init_tree_column(col): col_name = pricesColumns[0] if col == '#0' else col tree.heading(col, text=PricesColumn[col_name].value, anchor=W, command=lambda col=col: tree.sort_col(col)) tree.column(col, width=140, stretch=False) # self.tree['columns'] = ('ID', 'Item Price', 'Override', 'Filter Price', 'Filter Override', 'Effective Filter Price', 'Filter State Override', '') self.tree['columns'] = pricesColumns[1:] self.tree.register_column( PricesColumn.Override.name, ColEntry(TooltipEntry(self.tree), func_validate=_validate_price_override)) self.tree.register_column( PricesColumn.FilterOverride.name, ColEntry(TooltipEntry(self.tree), func_validate=_validate_price_override)) self.tree.register_column( PricesColumn.FilterStateOverride.name, ColEntry(Combobox(self.tree, values=filterStateOptions, state=READONLY), accept_events=('<<ComboboxSelected>>', '<Return>'))) for col in (('#0', ) + tree['columns']): init_tree_column(col) tree.heading('#0', anchor=CENTER) tree.column('#0', width=200, stretch=False) tree.column(PricesColumn.Filler.name, stretch=True) tree.heading(PricesColumn.ItemPrice.name, command=lambda col=PricesColumn.ItemPrice.name: tree. sort_col(col, key=self._price_key)) tree.heading(PricesColumn.Override.name, command=lambda col=PricesColumn.Override.name: tree. sort_col(col, key=self._price_key)) tree.heading(PricesColumn.FilterOverride.name, command=lambda col=PricesColumn.FilterOverride.name: tree. sort_col(col, key=self._price_key)) tree.heading(PricesColumn.FilterPrice.name, command=lambda col=PricesColumn.FilterPrice.name: tree. sort_col(col, key=self._rate_key, default=0)) tree.heading(PricesColumn.EffectiveFilterPrice.name, command=lambda col=PricesColumn.EffectiveFilterPrice.name: tree.sort_col(col, key=self._rate_key, default=0)) self.bvar_modified = BooleanVar() self.bvar_modified.trace('w', lambda a, b, c: self._updateApplyState()) self.bvar_modified.set(False) self.var_advanced.set(False) def _rate_key(self, key): if key == 'N/A': return 0 return float(key) def _price_key(self, key): if key == '': return None # this means it will be ignored while sorting try: return cm.compilePrice(key, base_price=0) except Exception: return 0 def on_entry_change(self, entry): val = entry.get() if self.initial_values[entry] != val: self.bvar_modified.set(True) # def on_price_entry_focusout(self, widget): # valid = _validate_price(widget, accept_empty=False) # if valid and not self.bvar_modified.get() and self.initial_values[widget] != widget.get(): # self.bvar_modified.set(True) # return valid # # def on_override_entry_focusout(self, widget): # valid = _validate_price_override(widget, accept_empty=False) # if valid and not self.bvar_modified.get() and self.initial_values[widget] != widget.get(): # self.bvar_modified.set(True) # return valid def _validate_threshold_entry(self): return _validate_price(self.entry_threshold, accept_empty=False) def _validate_budget_entry(self): return _validate_price(self.entry_budget, accept_empty=True) def _validate_min_price_entry(self): return _validate_price(self.entry_min_price, accept_empty=True) def _validate_price_override_entry(self): return _validate_price_override(self.entry_price_override, accept_empty=False) def _validate_fprice_override_entry(self): return _validate_price_override(self.entry_fprice_override, accept_empty=False) def _update_modified(self): modified = any(entry.get() != self.initial_values[entry] for entry in self.initial_values) or self.table_modified self.bvar_modified.set(modified) def _updateApplyState(self): if self.bvar_modified.get(): self.btn_apply.config(state=NORMAL) else: self.btn_apply.config(state=DISABLED) def _validateForm(self): if not self._validate_threshold_entry(): return False if not self._validate_budget_entry(): return False if not self._validate_min_price_entry(): return False if not self._validate_price_override_entry(): return False if not self._validate_fprice_override_entry(): return False return True def applyChanges(self, event=None): if not self.bvar_modified.get() or not fm.initialized: return if not self._validateForm(): return price_threshold = self.entry_threshold.get() default_price_override = self.entry_price_override.get() default_fprice_override = self.entry_fprice_override.get() budget = self.entry_budget.get() min_price = self.entry_min_price.get() confidence_lvl = self.entry_confidence_lvl.get( ) or fm.DEFAULT_CONFIDENCE_LEVEL enable_5l_filters = self.var_5l_filters.get() price_overrides = {} filter_price_overrides = {} filter_state_overrides = {} for iid in self.tree.get_children(): id = self.tree.set(iid, PricesColumn.ID.name) iprice = self.tree.set(iid, PricesColumn.Override.name) if iprice: price_overrides[id] = iprice fprice = self.tree.set(iid, PricesColumn.FilterOverride.name) if fprice: filter_price_overrides[id] = fprice fstate = self.tree.set(iid, PricesColumn.FilterStateOverride.name) try: filter_state_overrides[id] = FilterStateOption[fstate].value except KeyError: pass ids = set([ self.tree.set(iid, PricesColumn.ID.name) for iid in self.tree.get_children() ]) # preserve unhandled ids configuration for key in (set(fm.price_overrides) - ids): price_overrides[key] = fm.price_overrides[key] for key in (set(fm.filter_price_overrides) - ids): filter_price_overrides[key] = fm.filter_price_overrides[key] for key in (set(fm.filter_state_overrides) - ids): filter_state_overrides[key] = fm.filter_state_overrides[key] try: fm.updateConfig(default_price_override, default_fprice_override, price_threshold, budget, min_price, price_overrides, filter_price_overrides, filter_state_overrides, int(confidence_lvl), enable_5l_filters) except AppException as e: messagebox.showerror( 'Validation error', 'Failed to update configuration:\n{}'.format(e), parent=self.winfo_toplevel()) except Exception as e: logexception() messagebox.showerror( 'Update error', 'Failed to apply changes, unexpected error:\n{}'.format(e), parent=self.winfo_toplevel()) else: # SHOULD always work since config is valid, main console will report any failures # background thread because schema validating takes a bit of time threading.Thread(target=fm.compileFilters).start() self._initFormState() def loadPrices(self, force_reload=False): if not cm.initialized or not fm.initialized: return if not force_reload: self._update_modified() # in case of reverted changes if self.bvar_modified.get(): # dont interrupt user changes return tree = self.tree tree.clear() table = {} for fltr in fm.autoFilters: # effective_rate = cm.crates.get(curr, '') # if effective_rate != '': # effective_rate = round(effective_rate, 3) fid = fltr.id fstate_override = fm.filter_state_overrides.get(fid, '') try: fstate_override = FilterStateOption(fstate_override).name except ValueError: fstate_override = '' table[fid] = (fltr.title, fid, fm.item_prices[fid], fm.price_overrides.get(fid, ''), _to_display_rate( fm.compiled_item_prices.get(fid, 'N/A')), fm.filter_price_overrides.get(fid, ''), _to_display_rate( fm.compiled_filter_prices.get(fid, 'N/A')), fstate_override) for fid in table: tree.insert('', END, '', text=table[fid][0], values=table[fid][1:]) # tree.sort_by('#0', descending=True) tree.sort_col('#0', reverse=False) self._initFormState() # def onItemPriceUpdate(self, iid, col, old, new): # print('IPrice update: iid {}, col {}'.format(iid, col)) def onCellUpdate(self, iid, col, old, new): if old != new: self.table_modified = True self.bvar_modified.set(True) # self._update_modified() def _initFormState(self): self.table_modified = False self.initial_values[self.entry_threshold] = fm.price_threshold self.initial_values[self.entry_budget] = fm.budget self.initial_values[self.entry_min_price] = fm.default_min_price self.initial_values[ self.entry_price_override] = fm.default_price_override self.initial_values[ self.entry_fprice_override] = fm.default_fprice_override self.initial_values[self.entry_confidence_lvl] = fm.confidence_level self.initial_values[self.cb_5l_filters] = fm.enable_5l_filters self.var_threshold.set(fm.price_threshold) self.var_budget.set(fm.budget) self.var_min_price.set(fm.default_min_price) self.var_price_override.set(fm.default_price_override) self.var_fprice_override.set(fm.default_fprice_override) self.var_confidence_lvl.set(fm.confidence_level) self.var_5l_filters.set(fm.enable_5l_filters) self.bvar_modified.set(False) def _on_view_option_change(self): advanced_widgets = [ self.entry_price_override, self.lbl_price_override, self.lbl_confidence_lvl, self.entry_confidence_lvl, self.cb_5l_filters ] if not self.var_advanced.get(): for w in advanced_widgets: w.grid_remove() self.tree.config(displaycolumn=[ PricesColumn.FilterPrice.name, PricesColumn.FilterOverride. name, PricesColumn.EffectiveFilterPrice.name, PricesColumn.Filler.name ]) else: for w in advanced_widgets: w.grid() self.tree.config(displaycolumn='#all') self.tree.on_entry_close()
class MainWindow: def __init__(self): global root self.http_session = requests.Session() self.http_session.headers.update(HEADERS) self.menu_bar = Menu(root) self.menu_bar.add_command(label="Back", command=self.back_in_history) self.menu_bar.add_command(label="Toggle image", command=self.toggle_image) hist_menu = Menu(self.menu_bar, tearoff=0) hist_menu.add_command(label="All time", command=lambda: self.show_full_history(ALL_TIME)) hist_menu.add_command(label="Last day", command=lambda: self.show_full_history(DAY)) hist_menu.add_command(label="Two days", command=lambda: self.show_full_history(TWO_DAYS)) hist_menu.add_command(label="Week", command=lambda: self.show_full_history(WEEK)) hist_menu.add_command(label="Month", command=lambda: self.show_full_history(MONTH)) hist_menu.add_command( label="Three months", command=lambda: self.show_full_history(THREE_MONTHS)) self.menu_bar.add_cascade(label="History", menu=hist_menu) root.config(menu=self.menu_bar) self.session = None self.show_image = False self.hist_window = None self.proxies = None self.model_name = None self.update_title() self.level = 0 self.image_label = Label(root) self.level += 1 self.cb_model = ttk.Combobox(root, width=60) self.cb_model.bind("<FocusIn>", self.focus_callback) self.cb_model.bind("<Button-1>", self.drop_down_callback) self.cb_model.bind('<Return>', self.enter_callback) self.cb_model.focus_set() self.cb_model.grid(row=self.level, column=0, columnspan=4, sticky=W + E, padx=PAD, pady=PAD) self.btn_remove = Button(root, text="-", command=self.remove_from_favorites) self.btn_remove.grid(row=self.level, column=4, sticky=W + E, padx=PAD, pady=PAD) self.level += 1 self.btn_update = Button(root, text="Update info", command=lambda: self.update_model_info(True)) self.btn_update.grid(row=self.level, column=0, sticky=W + E, padx=PAD, pady=PAD) self.cb_resolutions = ttk.Combobox(root, state="readonly", values=[]) self.cb_resolutions.grid(row=self.level, column=1, columnspan=4, sticky=W + E, padx=PAD, pady=PAD) self.cb_resolutions['values'] = ['1080', '720', '480', '240'] self.level += 1 self.btn_show_recording = Button(root, text="Show recording model", command=self.show_recording_model, state=DISABLED) self.btn_show_recording.grid(row=self.level, column=0, sticky=W + E, padx=PAD, pady=PAD) self.use_proxy = BooleanVar() self.use_proxy.set(False) self.use_proxy.trace('w', self.on_use_proxy_change) self.chk_use_proxy = Checkbutton(root, text='Use proxy', variable=self.use_proxy) self.chk_use_proxy.grid(row=self.level, column=1, sticky=W, padx=PAD, pady=PAD) self.cb_proxy = ttk.Combobox(root, width=30, state=DISABLED) self.cb_proxy.grid(row=self.level, column=2, columnspan=3, sticky=W + E, padx=PAD, pady=PAD) self.level += 1 self.btn_start = Button(root, text="Start", command=self.on_btn_start) self.btn_start.grid(row=self.level, column=0, sticky=W + E, padx=PAD, pady=PAD) self.btn_stop = Button(root, text="Stop", command=self.on_btn_stop, state=DISABLED) self.btn_stop.grid(row=self.level, column=1, sticky=W + E, padx=PAD, pady=PAD) self.copy_button = Button(root, text="Copy", command=self.copy_model_name) self.copy_button.grid(row=self.level, column=2, sticky=W + E, padx=PAD, pady=PAD) self.paste_button = Button(root, text="Paste", command=self.paste_model_name) self.paste_button.grid(row=self.level, column=3, columnspan=2, sticky=W + E, padx=PAD, pady=PAD) self.level += 1 self.progress = ttk.Progressbar(root, orient=HORIZONTAL, length=120, mode='indeterminate') root.bind("<FocusIn>", self.focus_callback) root.protocol("WM_DELETE_WINDOW", self.on_close) self.play_list_url = None self.base_url = None self.model_image = None self.img_url = None self.hist_logger = logging.getLogger('history') self.hist_logger.setLevel(logging.INFO) self.fh_hist = logging.FileHandler( os.path.join(LOGS, f'hist_{int(time.time())}.log')) self.fh_hist.setLevel(logging.INFO) self.hist_logger.addHandler(self.fh_hist) self.proxy_logger = logging.getLogger('proxy') self.proxy_logger.setLevel(logging.INFO) self.fh_proxy = logging.FileHandler( os.path.join(LOGS, f'proxy_{int(time.time())}.log')) self.fh_proxy.setLevel(logging.INFO) self.proxy_logger.addHandler(self.fh_proxy) self.proxy_dict = {} self.load_proxy_dict() self.hist_stack = [] self.load_image() def on_btn_start(self): self.btn_start.config(state=DISABLED) self.stop() idx = self.cb_resolutions.current() success = self.update_model_info(True) if not success: self.set_default_state() return items_count = len(self.cb_resolutions['value']) if items_count == 0: return if items_count <= idx or idx < 0: idx = 0 self.cb_resolutions.current(idx) self.session = RecordSession(self, self.base_url, self.model_name, "chunks.m3u8") self.session.start() self.btn_stop.config(state=NORMAL) self.btn_show_recording.config(state=NORMAL) self.progress.grid(row=self.level, column=0, columnspan=5, sticky=W + E, padx=PAD, pady=PAD) self.progress.start() self.update_title() root.configure(background='green') def on_btn_stop(self): self.stop() self.set_default_state() def stop(self): if self.session is None: return self.session.stop() self.session = None def copy_model_name(self): clipboard.copy(self.cb_model.get()) def paste_model_name(self): self.cb_model.set(clipboard.paste()) self.cb_model.selection_range(0, END) def update_model_info(self, remember): if remember and (self.model_name is not None): if len(self.hist_stack) == 0 or (self.model_name != self.hist_stack[-1]): self.hist_stack.append(self.model_name) self.set_undefined_state() input_url = self.cb_model.get().strip() if len(input_url) == 0: self.set_undefined_state() return False proxy = self.cb_proxy.get().strip() if self.use_proxy.get() and len(proxy.strip()) != 0: self.proxies = { "http": "http://" + proxy, "https": "https://" + proxy } else: self.proxies = None ## https://live-edge66.bcvcdn.com/hls/stream_-icebabyice-/public/stream_-icebabyice-/chunks.m3u8 ## https://live-edge3.bcvcdn.com/hls/stream_TemariShi/public-aac/stream_TemariShi/chunks.m3u8 self.base_url = None if input_url.startswith('https://ded'): public_pos = input_url.rfind('public') self.base_url = input_url[:public_pos + 7] stream_pos = self.base_url.find('stream_') slash_pos = self.base_url.find('/', stream_pos) self.model_name = self.base_url[stream_pos + 7:slash_pos] elif input_url.startswith('https://live-edge'): slash_pos = input_url[:-1].rfind('/') self.base_url = input_url[:slash_pos + 1] stream_pos = self.base_url.find('stream_') slash_pos = self.base_url.find('/', stream_pos) self.model_name = self.base_url[stream_pos + 7:slash_pos] elif input_url.startswith('http'): slash_pos = input_url[:-1].rfind('/') self.model_name = input_url[slash_pos + 1:-1] if input_url.endswith( '/') else input_url[slash_pos + 1:] else: self.model_name = input_url if self.base_url is None: info = self.get_model_info() print(info) if 'localData' not in info: self.set_undefined_state() return False server_url = info['localData']['videoServerUrl'] self.model_name = info['performerData']['username'] self.cb_resolutions.set(info['performerData']['videoQuality']) self.base_url = f"https:{server_url}/hls/stream_{self.model_name}/public-aac/stream_{self.model_name}/" if self.use_proxy.get() and len(proxy) != 0: self.add_to_proxies(proxy) self.get_image_url() self.add_to_history(self.model_name) self.update_title() return True def add_to_history(self, name): if name not in self.cb_model['values']: self.cb_model['values'] = (name, *self.cb_model['values']) self.hist_logger.info(name) def remove_from_favorites(self): name = self.cb_model.get().strip() values = list(self.cb_model['values']) if name not in values: return values.remove(name) self.cb_model['values'] = tuple(values) if len(values) == 0: self.cb_model.set('') else: self.cb_model.set(values[0]) def add_to_proxies(self, proxy): if len(self.cb_proxy['values']) == 0: self.cb_proxy['values'] = proxy elif proxy not in self.cb_proxy['values']: self.cb_proxy['values'] = (proxy, *self.cb_proxy['values']) self.proxy_logger.info(proxy) count = self.proxy_dict.get(proxy, 0) self.proxy_dict[proxy] = count + 1 def focus_callback(self, event): self.cb_model.selection_range(0, END) if self.hist_window is not None: self.hist_window.lift() def drop_down_callback(self, event): self.cb_model.focus_set() self.cb_model.selection_range(0, END) self.cb_model.event_generate('<Down>') def enter_callback(self, event): self.update_model_info(True) def get_image_url(self): edge_pos = self.base_url.find('-edge') point_pos = self.base_url.find('.', edge_pos) # hyphen_pos = self.base_url.find('-', edge_pos + 1) # if point_pos > hyphen_pos: # point_pos = hyphen_pos vsid = self.base_url[edge_pos + 5:point_pos] self.img_url = f"https://mobile-edge{vsid}.bcvcdn.com/stream_{self.model_name}.jpg" def get_model_info(self): post_fields = { 'method': 'getRoomData', 'args[]': [self.model_name, "", ""] } headers = { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'X-Requested-With': 'XMLHttpRequest' } try: response = self.http_session.post( "https://rt.chat-s-devushkami.com/tools/amf.php?x-country=a1", data=post_fields, headers=headers, proxies=self.proxies, timeout=TIMEOUT) except RequestException as error: print("GetRoomData exception model: " + self.model_name) print(error) traceback.print_exc() return {} return response.json() def load_image(self): global executor global root if (self.img_url is not None) or self.show_image: executor.submit(self.fetch_image) root.update_idletasks() root.after(DELAY, self.load_image) def fetch_image(self): global root try: response = self.http_session.get(self.img_url, timeout=TIMEOUT) img = Image.open(io.BytesIO(response.content)) w, h = img.size k = 200 / w img_resized = img.resize((200, int(h * k))) root.after_idle(self.update_image, img_resized) except BaseException as error: root.after_idle(self.set_undefined_state) print("Exception URL: " + self.img_url) print(error) traceback.print_exc() def update_image(self, img): self.model_image = ImageTk.PhotoImage(img) self.image_label.config(image=self.model_image) def on_close(self): global root self.stop() root.update_idletasks() root.destroy() self.fh_hist.close() self.hist_logger.removeHandler(self.fh_hist) self.fh_proxy.close() self.proxy_logger.removeHandler(self.fh_proxy) self.http_session.close() def set_default_state(self): global root self.session = None self.btn_stop.config(state=DISABLED) self.btn_start.config(state=NORMAL) self.btn_show_recording.config(state=DISABLED) self.progress.stop() self.progress.grid_forget() self.update_title() root.configure(background='SystemButtonFace') def update_title(self): global root root.title(self.model_name or '<Undefined>') if self.session is None: return if not self.session.is_alive(): return if self.session.model_name != root.title(): return root.title(root.title() + " - Recording") def set_undefined_state(self): self.model_image = None self.image_label.config(image=None) self.model_name = None self.img_url = None self.update_title() def show_recording_model(self): if self.session is None: return self.cb_model.set(self.session.model_name) self.cb_model.selection_range(0, END) self.update_model_info(True) def on_use_proxy_change(self, *args): if self.use_proxy.get(): self.cb_proxy.config(state=NORMAL) self.cb_proxy.focus_set() self.cb_proxy.selection_range(0, END) else: self.cb_proxy.config(state=DISABLED) def toggle_image(self): if self.show_image: self.model_image = None self.image_label.config(image=None) self.img_url = None self.image_label.grid_forget() self.show_image = False else: self.show_image = True self.image_label.grid(row=0, column=0, columnspan=5, sticky=W + E, padx=PAD, pady=PAD) self.update_model_info(True) def show_full_history(self, period): if self.hist_window is not None: self.hist_window.on_close() self.hist_window = HistoryWindow(self, Toplevel(root), load_hist_dict(period)) def back_in_history(self): if len(self.hist_stack) == 0: return self.cb_model.set(self.hist_stack.pop()) self.update_model_info(False) def load_proxy_dict(self): for file in os.listdir(LOGS): if not file.startswith('proxy_'): continue full_path = os.path.join(LOGS, file) if os.path.getsize(full_path) == 0: continue with open(full_path) as f: for line in f.readlines(): name = line.strip() count = self.proxy_dict.get(name, 0) self.proxy_dict[name] = count + 1 hist = sorted(self.proxy_dict.items(), key=lambda x: x[1], reverse=True) self.cb_proxy.configure(values=[x[0] for x in hist[:10]])
class Assembly(Frame): def __init__(self, parent): Frame.__init__(self, parent, background="white") self.parent = parent self.parent.title("SMBP_logs") self.pack(fill='both', expand=True) panelFrame = Frame(self) textFrame = Frame(self) """ создаем фреймы верх-низ и далее в каждом фрейме создаем лево-право или верх=низ фреймы, к которым будут привязаны объекты, чтобы в случае расширения формы нормально отображалось и перемещалось все """ panelFrame.pack(side='top', fill='both') textFrame.pack(side='bottom', fill='both', expand=True) panelFrameUp = Frame(panelFrame) panelFrameDown = Frame(panelFrame) panelFrameUp.pack(side='top', fill='both', expand=True) panelFrameDown.pack(side='bottom', fill='both', expand=True) panelFrameUpTop = Frame(panelFrameUp) panelFrameUpBottom = Frame(panelFrameUp) panelFrameUpTop.pack(side='top', fill='both', expand=True) panelFrameUpBottom.pack(side='bottom', fill='both', expand=True) panelFrameTop = Frame(panelFrameDown) panelFrameBottom = Frame(panelFrameDown, relief='raised', borderwidth=1) panelFrameTop.pack(side='top', fill='both', expand=True) panelFrameBottom.pack(side='bottom', fill='both', expand=True) textFrameLeft = Frame(textFrame) textFrameRight = Frame(textFrame) textFrameLeft.pack(side='left', fill='both', expand=True) textFrameRight.pack(side='right', fill='both', expand=True) textFrameRightTop = Frame(textFrameRight) textFrameRightBottom = Frame(textFrameRight) textFrameRightTop.pack(side='top', fill='both', expand=True) textFrameRightBottom.pack(side='bottom', fill='both', expand=True) # создаем экземпляры класса текстбокса передаем нужный фрейм self.txtBox_left = TxtBox(textFrameLeft) self.txtBox_rtop = TxtBox(textFrameRightTop) self.txtBox_rbottom = TxtBox(textFrameRightBottom) label_param = Label(panelFrameBottom, text="Параметры поиска. Маска: ") label_param.pack(side='left', padx=5, pady=5) mask_entry = Entry(panelFrameBottom, width=15) mask_entry.pack(side='left', padx=5, pady=5) mask_entry.insert('end', date_mask) label_delta = Label(panelFrameBottom, text="Дельта в минутах: ") label_delta.pack(side='left', padx=5, pady=5) def def_delta(): if str(delta_btn['text']) == '0': delta_btn['text'] = '10' else: delta_btn['text'] = '0' delta_btn = Button(panelFrameBottom, text=delta, width=3, command=def_delta) delta_btn.pack(side='left', padx=5, pady=5) # чекбокс проверки поиска по ТД self.check_on_td_var = BooleanVar() check_on_td = Checkbutton(panelFrameBottom, text='Поиск по TD', variable=self.check_on_td_var) self.check_on_td_var.set(True) check_on_td.pack(side='left', padx=5, pady=5) btn_run = Button(panelFrameBottom, text='Поиск по файлам', width=15) #, command=pass) btn_run.pack(side='right', pady=1) btn_find_files = Button(panelFrameBottom, text='Поиск файлов', width=15) #, command=pass) btn_find_files.pack(side='right', padx=3, pady=1) # строка поиска label_find = Label(panelFrameTop, text="Строка поиска: ") label_find.pack(side='left', padx=5, pady=5) find_entry = Entry(panelFrameTop) find_entry.pack(fill='x', padx=5, expand=True) find_entry.insert('end', search_word) # если строка не изменялась при нажатии мышкой должна быть очищена def clean_find_entry(*args): if str(find_entry.get()) == search_word: find_entry.delete(0, 'end') find_entry.bind('<Button-1>', clean_find_entry) # textbox txt_mr = Text(panelFrameUpBottom, width=1, height=8) scrollbar_mr = Scrollbar(panelFrameUpBottom) scrollbar_mr['command'] = txt_mr.yview txt_mr['yscrollcommand'] = scrollbar_mr.set scrollbar_mr.pack(side='right', fill='y') txt_mr.pack(fill='both', expand=True) # для передачи аргументов при нажатии кнопки поиска файлов btn_find_files.bind( '<Button-1>', lambda _: find_files( txt_mr, self.txtBox_rbottom.txt_box, self.txtBox_rtop.txt_box, self.txtBox_left.txt_box, mask_entry, delta_btn)) btn_run.bind( '<Button-1>', lambda _: find_session(self.txtBox_rtop.txt_box, self.txtBox_rbottom.txt_box, self.txtBox_left.txt_box, find_entry, mask_entry, self .check_on_td_var, btn_run)) def select_mr(self, btn_mr): # все кнопки закрашиваем в цвет по умолчанию btn_mr1['bg'] = btn_mr2['bg'] = btn_mr3['bg'] = btn_mr4[ 'bg'] = btn_mr5['bg'] = 'SystemButtonFace' # подкрашиваем только ту кнопку, что нажата btn_mr['bg'] = 'gray75' global mr # меняем регион с которым работаем mr = btn_mr['text'] # при смене мр меняем список серверов print_mr_list() #(btn_mr['text']) def print_mr_list(*args): # очищаем текстбокс и выводим новый список серверов исходя из МР txt_mr.delete('1.0', 'end') # mr = args[0] for host in mr_dict[mr]['hosts']: for path in mr_dict[mr]['paths']: txt_mr.insert('end', host + ' | ' + path + '\n') for host in mr_dict[mr]['new_hosts']: for path in mr_dict[mr]['new_paths']: txt_mr.insert('end', host + ' | ' + path + '\n') # если чекбокс стоит на архивных серверах - добавляем их if self.check_arch_var.get(): for path in mr_dict[mr]['arch_paths']: txt_mr.insert( 'end', str(mr_dict[mr]['arch_host']) + ' | ' + path + '\n') label_mr = Label(panelFrameUpTop, text="Выбор МР: ") label_mr.pack(side='left', padx=5, pady=5) btn_mr1 = Button(panelFrameUpTop, text='МРЮг', width=10, bg='gray75') btn_mr1.pack(side='left', padx=1, pady=1) # при нажатии кнопки вызываем процедуру перекраски мр btn_mr1.bind('<Button-1>', lambda _: select_mr(self, btn_mr1)) btn_mr2 = Button(panelFrameUpTop, text='МРМосква', width=10) btn_mr2.pack(side='left', padx=1, pady=1) btn_mr2.bind('<Button-1>', lambda _: select_mr(self, btn_mr2)) btn_mr3 = Button(panelFrameUpTop, text='МРСибирь', width=10) btn_mr3.pack(side='left', padx=1, pady=1) btn_mr3.bind('<Button-1>', lambda _: select_mr(self, btn_mr3)) btn_mr4 = Button(panelFrameUpTop, text='МРСЗ', width=10) btn_mr4.pack(side='left', padx=1, pady=1) btn_mr4.bind('<Button-1>', lambda _: select_mr(self, btn_mr4)) btn_mr5 = Button(panelFrameUpTop, text='МРПоволжье', width=10) btn_mr5.pack(side='left', padx=1, pady=1) btn_mr5.bind('<Button-1>', lambda _: select_mr(self, btn_mr5)) self.check_arch_var = BooleanVar() check_arch = Checkbutton(panelFrameUpTop, text='Поиск по архивным серверам', variable=self.check_arch_var) self.check_arch_var.set(True) check_arch.pack(side='left', padx=5, pady=5) self.check_arch_var.trace('w', print_mr_list) # один разок надо вызвать эту процедуру при инициализации select_mr(self, btn_mr1)
class CopyToMoveTo: """ Minimalist file manager intended to be used independently or alongside Windows Explorer """ def __init__(self, root): """ Setup window geometry, init settings, define widgets + layout """ self.master = root self.master.title("CopyTo-MoveTo") self.master.iconbitmap(f"{dirname(__file__)}/icon.ico") if system() != "Windows": self.master.withdraw() messagebox.showwarning( "Incompatible platform", "CopyTo-MoveTo currently supports Windows platforms only.") raise SystemExit #Settings: self.settings_show_hidden = BooleanVar() self.settings_include_files = BooleanVar(value=True) self.settings_ask_overwrite = BooleanVar() self.settings_ask_overwrite.trace("w", self.settings_exclusives) self.settings_rename_dupes = BooleanVar(value=True) self.settings_rename_dupes.trace("w", self.settings_exclusives) self.settings_multiselect = BooleanVar(value=True) self.settings_select_dirs = BooleanVar(value=True) self.settings_select_dirs.trace("w", self.settings_mutuals) self.settings_select_files = BooleanVar(value=True) self.settings_select_files.trace("w", self.settings_mutuals) self.settings_geometry = None self.appdata_dir = getenv("APPDATA") + "/CopyTo-MoveTo" self.appdata_path = self.appdata_dir + "/settings.json" self.settings = self.init_settings() if self.settings: self.settings_geometry = self.settings["geometry"] self.settings_show_hidden.set(self.settings["show_hidden"]) self.settings_include_files.set(self.settings["include_files"]) self.settings_ask_overwrite.set(self.settings["ask_overwrite"]) self.settings_rename_dupes.set(self.settings["rename_dupes"]) self.settings_multiselect.set(self.settings["multiselect"]) self.settings_select_dirs.set(self.settings["select_dirs"]) self.settings_select_files.set(self.settings["select_files"]) self.dialog_showing = BooleanVar() self.help_showing = BooleanVar() self.about_showing = BooleanVar() self.master.protocol("WM_DELETE_WINDOW", self.master_close) self.master.bind("<Control-w>", self.master_close) #Geometry: self.master.minsize(width=450, height=200) if self.settings_geometry: self.master.geometry(self.settings_geometry) self.master.update() else: self.master.geometry("600x400") self.master.update_idletasks() (width_offset, height_offset) = Ufd.get_offset(self.master) self.master.geometry(f"+{width_offset}+{height_offset}") self.master.update_idletasks() # Menu: self.main_menu = Menu(self.master) self.master.config(menu=self.main_menu) self.file_menu = Menu(self.main_menu, tearoff=0) self.settings_menu = Menu(self.main_menu, tearoff=0) self.main_menu.add_cascade(label="File", menu=self.file_menu) self.main_menu.add_cascade(label="Settings", menu=self.settings_menu) self.file_menu.add_command(label="Open Source(s)", accelerator="Ctrl+O", command=lambda: self.show_ufd(source=True)) self.master.bind("<Control-o>", lambda event: self.show_ufd(source=True)) self.file_menu.add_command(label="Open Destination(s)", accelerator="Ctrl+K+O", command=lambda: self.show_ufd(source=False)) self.master.bind("<Control-k>o", lambda event: self.show_ufd(source=False)) self.file_menu.add_separator() self.file_menu.add_command(label="Help / Commands", command=self.show_help) self.file_menu.add_command(label="About", command=self.show_about) #Settings menu: self.settings_menu.add_checkbutton(label="Show Hidden Files & Folders", variable=self.settings_show_hidden, onvalue=True, offvalue=False) self.settings_menu.add_checkbutton( label="Include Files in Tree", variable=self.settings_include_files, onvalue=True, offvalue=False) self.settings_menu.add_separator() self.settings_menu.add_checkbutton( label="Ask Overwrite", variable=self.settings_ask_overwrite, onvalue=True, offvalue=False) self.settings_menu.add_checkbutton(label="Rename Duplicates", variable=self.settings_rename_dupes, onvalue=True, offvalue=False) self.settings_menu.add_separator() self.settings_menu.add_checkbutton(label="Multiselect", variable=self.settings_multiselect, onvalue=True, offvalue=False) self.settings_menu.add_checkbutton(label="Select Folders", variable=self.settings_select_dirs, onvalue=True, offvalue=False) self.settings_menu.add_checkbutton(label="Select Files", variable=self.settings_select_files, onvalue=True, offvalue=False) self.main_menu.add_separator() #Menu commands: self.main_menu.add_command(label="Swap Selected", command=self.swap_selected) self.master.bind("<Control-s>", lambda event: self.swap_selected()) self.main_menu.add_command(label="Clear Selected", command=self.clear_selected) self.master.bind("<Control-x>", lambda event: self.clear_selected()) self.main_menu.add_command(label="Clear All", command=self.clear_all) self.master.bind("<Control-Shift-X>", lambda event: self.clear_all()) self.main_menu.add_separator() self.main_menu.add_command(label="COPY", command=lambda: self._submit(copy=True)) self.master.bind("<Control-Shift-Return>", lambda event: self._submit(copy=True)) self.main_menu.add_command(label="MOVE", command=lambda: self._submit(copy=False)) self.master.bind("<Control-Return>", lambda event: self._submit(copy=False)) # Body: self.paneview = PanedWindow(self.master, sashwidth=7, bg="#cccccc", bd=0, orient="vertical") self.top_pane = PanedWindow(self.paneview) self.bottom_pane = PanedWindow(self.paneview) self.paneview.add(self.top_pane) self.paneview.add(self.bottom_pane) self.label_source = Label(self.top_pane, text="Source(s):") self.label_dest = Label(self.bottom_pane, text="Destination(s):") self.y_scrollbar_source = Scrollbar(self.top_pane, orient="vertical") self.x_scrollbar_source = Scrollbar(self.top_pane, orient="horizontal") self.y_scrollbar_dest = Scrollbar(self.bottom_pane, orient="vertical") self.x_scrollbar_dest = Scrollbar(self.bottom_pane, orient="horizontal") self.list_box_source = Listbox( self.top_pane, selectmode="extended", yscrollcommand=self.y_scrollbar_source.set, xscrollcommand=self.x_scrollbar_source.set) self.list_box_dest = Listbox(self.bottom_pane, selectmode="extended", yscrollcommand=self.y_scrollbar_dest.set, xscrollcommand=self.x_scrollbar_dest.set) self.x_scrollbar_source.config(command=self.list_box_source.xview) self.y_scrollbar_source.config(command=self.list_box_source.yview) self.x_scrollbar_dest.config(command=self.list_box_dest.xview) self.y_scrollbar_dest.config(command=self.list_box_dest.yview) # Layout: self.master.rowconfigure(0, weight=1) self.master.columnconfigure(0, weight=1) self.top_pane.rowconfigure(1, weight=1) self.top_pane.columnconfigure(0, weight=1) self.bottom_pane.rowconfigure(1, weight=1) self.bottom_pane.columnconfigure(0, weight=1) self.paneview.paneconfigure(self.top_pane, minsize=100) self.paneview.paneconfigure(self.bottom_pane, minsize=100) self.paneview.grid(row=0, column=0, sticky="nsew") self.label_source.grid(row=0, column=0, sticky="w") self.list_box_source.grid(row=1, column=0, sticky="nsew") self.y_scrollbar_source.grid(row=1, column=1, sticky="ns") self.x_scrollbar_source.grid(row=2, column=0, sticky="ew") self.label_dest.grid(row=0, column=0, sticky="w", columnspan=2) self.list_box_dest.grid(row=1, column=0, sticky="nsew") self.y_scrollbar_dest.grid(row=1, column=1, sticky="ns") self.x_scrollbar_dest.grid(row=2, column=0, sticky="ew") def __str__(self): """Return own address""" return f"CopyTo-MoveTo @ {hex(id(self))}" def init_settings(self): """Called on startup, loads, parses, and returns json settings.""" if exists(self.appdata_path): with open(self.appdata_path, "r") as settings_file: settings_json = settings_file.read() settings = loads(settings_json) return settings else: return None def settings_exclusives(self, *args): """ Callback assigned to settings that are mutually exclusive, to prevent logical/runtime errors or unexpected behavior. """ if args[0] == "PY_VAR2": if self.settings_ask_overwrite.get() == 1: self.settings_rename_dupes.set(0) return elif args[0] == "PY_VAR3": if self.settings_rename_dupes.get() == 1: self.settings_ask_overwrite.set(0) return def settings_mutuals(self, *args): """ Prevent select folders & select files from being disabled concurrently If both are unselected, reselect the one we didn't just deselect on. """ if self.settings_select_dirs.get() == 0 \ and self.settings_select_files.get() == 0: if args[0] == "PY_VAR5": self.settings_select_files.set(1) elif args[0] == "PY_VAR6": self.settings_select_dirs.set(1) def master_close(self, event=None): """ Similar to utils.toplevel_close(). writes settings to the disk as json. """ settings = { "geometry": self.master.geometry(), "show_hidden": self.settings_show_hidden.get(), "include_files": self.settings_include_files.get(), "ask_overwrite": self.settings_ask_overwrite.get(), "rename_dupes": self.settings_rename_dupes.get(), "multiselect": self.settings_multiselect.get(), "select_dirs": self.settings_select_dirs.get(), "select_files": self.settings_select_files.get(), } settings_json = dumps(settings) if not exists(self.appdata_dir): mkdir(self.appdata_dir) with open(self.appdata_path, "w+") as settings_file: settings_file.write(settings_json) if self.dialog_showing.get() == 1: self.ufd.cancel() self.master.destroy() def toplevel_close(self, dialog, boolean): """ This callback flips the value for a given toplevel_showing boolean to false, before disposing of the toplevel. """ boolean.set(0) dialog.destroy() def swap_selected(self): """Swap list entries between source & destination""" source_selection = list(self.list_box_source.curselection()) dest_selection = list(self.list_box_dest.curselection()) for i in reversed(source_selection): item = self.list_box_source.get(i) self.list_box_source.delete(i) self.list_box_dest.insert("0", item) for i in reversed(dest_selection): item = self.list_box_dest.get(i) self.list_box_dest.delete(i) self.list_box_source.insert("0", item) def clear_selected(self): """Removes selected (highlighted) item(s) from a given listbox""" source_selection = list(self.list_box_source.curselection()) dest_selection = list(self.list_box_dest.curselection()) if source_selection: for i in reversed(source_selection): self.list_box_source.delete(i) self.list_box_source.selection_set(source_selection[0]) if dest_selection: for i in reversed(dest_selection): self.list_box_dest.delete(i) self.list_box_dest.selection_set(dest_selection[0]) def clear_all(self): """Clears both listboxes in the main UI, resetting the form.""" self.list_box_source.delete(0, "end") self.list_box_dest.delete(0, "end") def handled(fn): """Filesystem operations are wrapped here for error handling""" @wraps(fn) def inner(self, *args, **kwargs): try: fn(self, *args, **kwargs) return True except (PermissionError, FileNotFoundError) as err: self.skipped_err.append(f"{err.args[1]}:\n" + (" => ".join(args))) return False return inner @handled def _copy(self, path, destination): """Wrapper for shutil.copy2() || shutil.copytree()""" if isfile(path): copy2(path, destination) else: copytree(path, destination) @handled def _move(self, path, destination): """Wrapper for shutil.move()""" move(path, destination) @handled def _delete(self, path): """Wrapper for os.remove() || shutil.rmtree()""" if isfile(path): remove(path) elif isdir(path): rmtree(path) def disabled_ui(fn): """Menubar is disabled during operations""" @wraps(fn) def inner(self, *args, **kwargs): self.main_menu.entryconfig("File", state="disabled") self.main_menu.entryconfig("Settings", state="disabled") self.main_menu.entryconfig("Clear Selected", state="disabled") self.main_menu.entryconfig("Clear All", state="disabled") self.main_menu.entryconfig("COPY", state="disabled") self.main_menu.entryconfig("MOVE", state="disabled") fn(self, *args, **kwargs) self.main_menu.entryconfig("File", state="normal") self.main_menu.entryconfig("Settings", state="normal") self.main_menu.entryconfig("Clear Selected", state="normal") self.main_menu.entryconfig("Clear All", state="normal") self.main_menu.entryconfig("COPY", state="normal") self.main_menu.entryconfig("MOVE", state="normal") return inner def _submit(self, copy): """Thread/wrapper for submit() so we don't block the UI during operations""" self.thread = Thread(target=self.submit, args=(copy, ), daemon=True) self.thread.start() @disabled_ui def submit(self, copy): """ Move or copy each item in the origin list to the path in the destination list. Supports no more than one destination directory where copy == False. Ask Overwrite and Rename Dupes will alter the way we handle existing data standing in the way. By default, duplicates are renamed with an index. A messagebox can complain to the user if shutil raises a PermissionError, and the operation is skipped. """ if (self.list_box_dest.size() > 1) and not copy: messagebox.showwarning( "Invalid Operation", "Move operation only supports a single destination directory.") return sources = self.list_box_source.get(0, "end") destinations = self.list_box_dest.get(0, "end") self.skipped_err = [] for j, destination in enumerate(destinations): if isfile(destination): self.skipped_err.append(f"Invalid destination: {destination}") continue for i, source in enumerate(sources): self.progress(i, j) (_, filename) = split(source) future_destination = join(destination + sep, filename) if exists(future_destination): if not self.settings_ask_overwrite.get() \ and not self.settings_rename_dupes.get(): if not self._delete(future_destination): continue if self.settings_ask_overwrite.get(): if self.ask_overwrite(future_destination): if not self._delete(future_destination): continue else: continue if self.settings_rename_dupes.get(): future_destination = self.name_dupe(future_destination) if copy: if not self._copy(source, future_destination): continue else: if not self._move(source, future_destination): continue self.list_box_source.delete(0, "end") self.list_box_dest.delete(0, "end") if self.skipped_err: messagebox.showerror(title="Error(s)", message="\n\n".join(self.skipped_err)) @staticmethod def name_dupe(path): """ Renames the file or directory until it doesn't exist in the destination with that name anymore, by appending the filename with an index wrapped in parenthesis. (Windows platforms) file.txt => file (1).txt => file (2).txt """ if system() != "Windows": raise OSError("For use with Windows filesystems.") path_ = path (root, filename) = split(path_) if isdir(path_): title = filename ext = None else: (title, ext) = splitext(filename) filecount = 0 while exists(path_): filecount += 1 new_title = title + " (" + str(filecount) + ")" if ext: new_title = new_title + ext path_ = join(root, new_title) return path_ def ask_overwrite(self, future_destination): """Messagebox result returned as truth value""" return messagebox.askyesno( title="Path Conflict", message=f"Overwrite:\n\n{future_destination}?\n\n" \ f"YES - Overwrite\nNO - Skip" ) def progress(self, i, j): """ Visualize operands in GUI during operations i = current source operand index j = current destination operand index """ for y, _ in enumerate(self.list_box_source.get(0, "end")): if y != i: self.list_box_source.itemconfigure(y, bg="#FFFFFF", fg="#000000") else: self.list_box_source.itemconfigure(y, bg="#cccccc", fg="#000000") for x, _ in enumerate(self.list_box_dest.get(0, "end")): if x != j: self.list_box_dest.itemconfigure(x, bg="#FFFFFF", fg="#000000") else: self.list_box_dest.itemconfigure(x, bg="#cccccc", fg="#000000") self.master.update() #Toplevels: def show_about(self): """ Displays a static dialog that doesn't allow additional instances of itself to be created while showing. """ if self.about_showing.get() == 0: self.about_showing.set(1) try: with open(f"{dirname(__file__)}/about.txt", "r") as aboutfile: about_info = aboutfile.read() except FileNotFoundError: messagebox.showerror("Error", "File not found") self.about_showing.set(0) return else: self.about = Toplevel() self.about.title("About") self.about.iconbitmap(f"{dirname(__file__)}/icon.ico") self.about.geometry("600x400") self.about.resizable(0, 0) self.about.update_idletasks() (width_offset, height_offset) = Ufd.get_offset(self.about) self.about.geometry(f"+{width_offset-75}+{height_offset-75}") self.about.update_idletasks() self.about_message = Label( self.about, text=about_info, justify="left", wraplength=(self.about.winfo_width() - 25)) self.about_message.grid(sticky="nsew") self.about.protocol( "WM_DELETE_WINDOW", lambda: self.toplevel_close( self.about, self.about_showing)) def show_help(self): """ Displays a scrollable dialog that doesn't allow additional instances of itself to be created while showing. """ if self.help_showing.get() == 0: self.help_showing.set(1) try: with open(f"{dirname(__file__)}/help.txt", "r") as helpfile: help_info = helpfile.read() except FileNotFoundError: messagebox.showerror("Error", "File not found") self.help_showing.set(0) return else: self.help_window = Toplevel() self.help_window.title("Help") self.help_window.iconbitmap(f"{dirname(__file__)}/icon.ico") self.help_window.geometry("500x300") self.help_window.update_idletasks() (width_offset, height_offset) = Ufd.get_offset(self.help_window) self.help_window.geometry( f"+{width_offset+75}+{height_offset-75}") self.help_window.update_idletasks() self.message_y_scrollbar = Scrollbar(self.help_window, orient="vertical") self.help_text = Text( self.help_window, wrap="word", yscrollcommand=self.message_y_scrollbar.set) self.help_window.rowconfigure(0, weight=1) self.help_window.columnconfigure(0, weight=1) self.help_text.grid(row=0, column=0, sticky="nsew") self.message_y_scrollbar.grid(row=0, column=1, sticky="nse") self.message_y_scrollbar.config(command=self.help_text.yview) self.help_text.insert("end", help_info) self.help_text.config(state="disabled") self.help_window.protocol( "WM_DELETE_WINDOW", lambda: self.toplevel_close( self.help_window, self.help_showing)) def show_ufd(self, source=True): """ Display Ufd w/ appropriate kwargs => Populate GUI w/ result""" if self.dialog_showing.get() == 0: self.dialog_showing.set(1) self.ufd = Ufd(title="Add Items", show_hidden=self.settings_show_hidden.get(), include_files=self.settings_include_files.get(), multiselect=self.settings_multiselect.get(), select_dirs=self.settings_select_dirs.get(), select_files=self.settings_select_files.get(), unix_delimiter=False, stdout=False) for result in self.ufd(): if source: self.list_box_source.insert("end", result) else: self.list_box_dest.insert("end", result) self.dialog_showing.set(0)
input_user = StringVar() input_type = 0 def join_recv(*args): print(exit_flag.get(), recv_thread.is_alive()) if exit_flag.get() and recv_thread.is_alive(): print('Attempting to Join recv_thread') recv_thread.join() print('Thread joined succesfully!') exit_flag = BooleanVar() exit_flag.set(False) exit_flag.trace('w', join_recv) ### Function Definitions ### def console(message, sender=None, recipient=None, end='\n', level=0): global text if level: # TODO: Set color formatting on [sender] if level == 1: color = 'Green' # Successful Action elif level == 2: color = 'Yellow' # Unsuccessful action elif level == 3: color = 'Red' # Server Critical Issue elif level == 4: color = 'blue' # Private Message # TODO: set bold formatting on [sender] if not sender: sender = 'LURKle' if not recipient:
class MainFrame(Frame): def set_saved_title(self, fpath): fname = split(fpath)[-1].replace('.json', '') self.master.title('{} - {}'.format(fname, _title)) def set_unsaved_title(self, *args): if len(roller_groups) < 1: return if self.autosave.get(): self.save_config(self.fpath) return title = self.master.title() if title == _title: title = '{} - {}'.format('Unsaved', title) if '*' not in title: title = '*' + title self.master.title(title) def __init__(self, master): Frame.__init__(self, master) self.master = master self.use_random_org = BooleanVar() self.allow_odd = IntVar() self.always_on_top = BooleanVar() self.autosave = BooleanVar() self.use_random_org.trace('w', self.set_unsaved_title) self.allow_odd.trace('w', self.set_unsaved_title) self.always_on_top.trace('w', self.set_unsaved_title) self.set_defaults() self.menubar = Menu(master) self.filemenu = Menu(self.menubar, tearoff=0, postcommand=maintain_group_indices) self.filemenu.add_command(label='New', underline=0, command=self.reset_default_group, accelerator='Ctrl+N') self.filemenu.add_command(label='Load', underline=3, command=self.load_config, accelerator='Ctrl+D') self.filemenu.add_command( label='Save', underline=1, command=lambda: self.save_config(fpath=self.fpath), accelerator='Ctrl+S') self.filemenu.add_command(label='Save as...', underline=4, command=self.save_config, accelerator='Ctrl+Shift+S') self.editmenu = Menu(self.menubar, tearoff=0) self.editmenu.add_checkbutton(label='Use random.org', underline=0, variable=self.use_random_org) self.editmenu.add_checkbutton(label='Allow odd dice', underline=6, variable=self.allow_odd, command=self.toggle_odd, onvalue=1, offvalue=2) self.editmenu.add_separator() # ------------------ self.editmenu.add_checkbutton(label='Always on top', underline=10, variable=self.always_on_top, command=self.pin) self.editmenu.add_checkbutton(label='Autosave', underline=4, variable=self.autosave, command=self.toggle_autosave) self.editmenu.add_separator() # ------------------ self.editmenu.add_command(label='Repeat last action', underline=0, accelerator='Ctrl+R') self.menubar.add_cascade(label='File', underline=0, menu=self.filemenu) self.menubar.add_cascade(label='Edit', underline=0, menu=self.editmenu) self.menubar.config(relief='flat') master.config(menu=self.menubar) self.reset_default_group() self.bind_all('<Control-n>', lambda e: self.reset_default_group()) self.bind_all('<Control-d>', lambda e: self.load_config()) self.bind_all('<Control-s>', lambda e: self.save_config(fpath=self.fpath)) self.bind_all('<Control-Shift-S>', lambda e: self.save_config()) def ask_proceed(self): if '*' in self.master.title(): if not askyesno( 'Unsaved changes!', 'There are unsaved changes!\r\nWould you like to proceed anyway?' ): return False return True def pin(self): self.master.wm_attributes('-topmost', self.always_on_top.get()) def toggle_odd(self): for group in roller_groups: for roller in group.rollers: roller.die_faces_spin.interval = self.allow_odd.get() num = roller.die_faces.get() if num % 2 != 0: roller.die_faces.set(num - 1) def toggle_autosave(self): if self.autosave.get(): self.save_config(self.fpath) else: self.set_unsaved_title() def set_defaults(self): self.master.title(_title) self.fpath = '' self.use_random_org.set(False) self.allow_odd.set(2) self.always_on_top.set(False) self.autosave.set(False) def reset_default_group(self): if self.ask_proceed(): self.autosave.set(False) self.clear_groups() self.set_defaults() self.create_group(0, 1) @staticmethod def clear_groups(): temp_groups = list(roller_groups) for group in temp_groups: group.remove_group(override=True) def create_group(self, index, rollers): default_group = RollerGroup(self, index) for i in range(rollers): default_group.rollers.append(Roller(default_group, i)) roller_groups.append(default_group) def load_config(self): autosave = False self.autosave.set(autosave) if not self.ask_proceed(): return fpath = askopenfilename(filetypes=[('JSON', '*.json'), ('All', '*.*')], defaultextension='.json') if not fpath or not isfile(fpath): return self.fpath = fpath self.clear_groups() with open(fpath, 'r') as f: group_dict = load(f) try: settings_dict = group_dict.pop('settings') autosave = (settings_dict['autosave']) self.use_random_org.set(settings_dict['use_random_org']) self.allow_odd.set(settings_dict['allow_odd']) self.always_on_top.set(settings_dict['always_on_top']) except KeyError: pass g = 0 for group_name, group_settings in group_dict.items(): self.create_group(g, len(group_settings['rollers'])) group = roller_groups[g] group.name.set(group_name) group.index = group_settings['index'] r = 0 h = 0 for roller_name, roller_settings in group_settings[ 'rollers'].items(): roller = group.rollers[r] roller.name.set(roller_name) for attr, value in roller_settings.items(): try: getattr(roller, attr).set(value) except AttributeError: setattr(roller, attr, value) roller.reset(loading=True) h = len(roller.history) - 1 r += 1 group.navigate_history(desired_index=h) g += 1 roller_groups.sort(key=lambda x: x.index) maintain_group_indices() for group in roller_groups: group.rollers.sort(key=lambda x: x.index) group.maintain_roller_indices() for roller in group.rollers: roller.apply_modifiers() maintain_tabstops() self.pin() self.autosave.set(autosave) self.set_saved_title(fpath) def save_config(self, fpath=''): if not fpath: fpath = asksaveasfilename(filetypes=[('JSON', '*.json'), ('All', '*.*')], defaultextension='.json') if not fpath: if '*' in self.master.title(): self.autosave.set(False) return self.fpath = fpath d1 = {} d1['settings'] = { 'use_random_org': self.use_random_org.get(), 'allow_odd': self.allow_odd.get(), 'always_on_top': self.always_on_top.get(), 'autosave': self.autosave.get() } for group in roller_groups: group.maintain_roller_indices() d2 = {} d2['index'] = group.index d2['rollers'] = {} for roller in group.rollers: name = roller.name.get() while name in d2['rollers']: name += '!' d2['rollers'][name] = { 'index': roller.index, 'history': roller.history, 'dice_qty': roller.dice_qty.get(), 'die_faces': roller.die_faces.get(), 'modifier': roller.modifier.get(), 'finalmod': roller.finalmod.get() } name = group.name.get() if name in d1: name += '!' d1[name] = d2 with open(fpath, 'w') as f: f.write(dumps(d1, indent=2, separators=(',', ': '))) self.set_saved_title(fpath)
def __init__(self, master, par=False): """ GUI for selecting default parameters - will write parameters to file \ of users choosing. :type master: Tk :param master: Tkinter window :type par: EQcorrscanParameters :param par: Default parameters to start-up with. """ from tkinter import Label, Button, Entry, DoubleVar, StringVar, IntVar from tkinter import BooleanVar, OptionMenu, Checkbutton import tkMessageBox from eqcorrscan.utils import parameters from obspy import UTCDateTime import warnings # Set the default par, only if they don't already exist. if not par: par = parameters.EQcorrscanParameters([''], 2, 10, 4, 100, 2, '1900-01-01', '2300-01-01', '', 'seishub', 4, False, '', 'jpg', False, 8, 'MAD', 6) # Callback functions for all variables (ugly) def update_template_names(*args): par.template_names = [ name.strip() for name in template_names.get().split(',') ] template_names.set(', '.join(par.template_names)) def update_lowcut(*args): par.lowcut = lowcut.get() lowcut.set(par.lowcut) def update_highcut(*args): par.highcut = highcut.get() if par.highcut >= 0.5 * par.samp_rate: msg = ('Highcut must be less than the Nyquist, setting to ' + str((par.samp_rate / 2.0) - 1)) tkMessageBox.showwarning(title="Nyquist error", message=msg) par.highcut = (par.samp_rate / 2.0) - 1 highcut.set(par.highcut) def update_filt_order(*args): par.filt_order = filt_order.get() filt_order.set(par.filt_order) def update_samp_rate(*args): par.samp_rate = samp_rate.get() if par.highcut >= 0.5 * par.samp_rate: msg = ('Highcut must be less than the Nyquist, setting to ' + str((par.samp_rate / 2.0) - 1)) tkMessageBox.showwarning(title="Nyquist error", message=msg) par.highcut = (par.samp_rate / 2.0) - 1 highcut.set(par.highcut) samp_rate.set(par.samp_rate) def update_debug(*args): par.debug = debug.get() debug.set(par.debug) def update_startdate(*args): par.startdate = UTCDateTime(startdate.get()) startdate.set(str(par.startdate)) def update_enddate(*args): par.enddate = UTCDateTime(enddate.get()) enddate.set(str(par.enddate)) def update_archive(*args): par.archive = archive.get() archive.set(par.archive) def update_arc_type(*args): par.arc_type = arc_type.get() arc_type.set(par.arc_type) def update_cores(*args): par.cores = cores.get() cores.set(par.cores) def update_plotvar(*args): par.plotvar = plotvar.get() plotvar.set(par.plotvar) def update_plot_format(*args): par.plot_format = plot_format.get() plot_format.set(par.plot_format) def update_tempdir(*args): par.tempdir = tempdir.get() tempdir.set(par.tempdir) def update_threshold(*args): par.threshold = threshold.get() threshold.set(par.threshold) def update_threshold_type(*args): par.threshold_type = threshold_type.get() threshold_type.set(par.threshold_type) def update_plotdir(*args): par.plotdir = plotdir.get() plotdir.set(par.plotdir) def update_trigger_interval(*args): par.trigger_interval = trigger_interval.get() trigger_interval.set(par.trigger_interval) # Set some grid parameters nrows = 25 ncolumns = 3 self.master = master master.title("EQcorrscan parameter setup") self.label = Label(master, text="Alpha GUI for default setup") self.label.grid(column=0, columnspan=ncolumns, row=0) # Set up parameter input self.t_names_label = Label(master, text="Template names", anchor='e') self.t_names_label.grid(column=0, row=1, sticky='e') template_names = StringVar() template_names.set(', '.join(par.template_names)) self.t_names_box = Entry(master, bd=2, textvariable=template_names) self.t_names_box.grid(column=1, row=1) template_names.trace("w", update_template_names) self.t_names_lookup = Button( master, text="Lookup", command=lambda: self.get_template_names(par)) self.t_names_lookup.grid(column=2, row=1) self.lowcut_label = Label(master, text="Lowcut (Hz)", anchor='e') self.lowcut_label.grid(column=0, row=2, sticky='e') lowcut = DoubleVar() lowcut.set(par.lowcut) self.lowcut_box = Entry(master, bd=2, textvariable=lowcut) self.lowcut_box.grid(column=1, row=2) lowcut.trace("w", update_lowcut) self.highcut_label = Label(master, text="Highcut (Hz)", anchor='e') self.highcut_label.grid(column=0, row=3, sticky='e') highcut = DoubleVar() highcut.set(par.highcut) self.highcut_box = Entry(master, bd=2, textvariable=highcut) self.highcut_box.grid(column=1, row=3) highcut.trace("w", update_highcut) self.filt_order_label = Label(master, text="Filter order") self.filt_order_label.grid(column=0, row=4, sticky='e') filt_order = DoubleVar() filt_order.set(par.filt_order) self.filt_order_box = Entry(master, bd=2, textvariable=filt_order) self.filt_order_box.grid(column=1, row=4) filt_order.trace("w", update_filt_order) self.samp_rate_label = Label(master, text="Sample rate (Hz)") self.samp_rate_label.grid(column=0, row=5, sticky='e') samp_rate = DoubleVar() samp_rate.set(par.samp_rate) self.samp_rate_box = Entry(master, bd=2, textvariable=samp_rate) self.samp_rate_box.grid(column=1, row=5) samp_rate.trace("w", update_samp_rate) self.debug_label = Label(master, text="Debug") self.debug_label.grid(column=0, row=6, sticky='e') debug = IntVar() debug.set(par.debug) self.debug_box = Entry(master, bd=2, textvariable=debug) self.debug_box.grid(column=1, row=6) debug.trace("w", update_debug) self.startdate_label = Label(master, text="Start date (yyyy-mm-dd)") self.startdate_label.grid(column=0, row=6, sticky='e') startdate = StringVar() startdate.set(par.startdate) self.startdate_box = Entry(master, bd=2, textvariable=startdate) self.startdate_box.grid(column=1, row=6) startdate.trace("w", update_startdate) self.enddate_label = Label(master, text="End date (yyyy-mm-dd)") self.enddate_label.grid(column=0, row=8, sticky='e') enddate = StringVar() enddate.set(par.enddate) self.enddate_box = Entry(master, bd=2, textvariable=enddate) self.enddate_box.grid(column=1, row=8) enddate.trace("w", update_enddate) self.archive_label = Label(master, text="Archive") self.archive_label.grid(column=0, row=9, sticky='e') archive = StringVar() archive.set(par.archive) self.archive_box = Entry(master, bd=2, textvariable=archive) self.archive_box.grid(column=1, row=9) archive.trace("w", update_archive) self.archive_lookup = Button(master, text="Lookup", command=lambda: self.get_archive(par)) self.archive_lookup.grid(column=2, row=9) self.arc_type_label = Label(master, text="Archive type") self.arc_type_label.grid(column=0, row=10, sticky='e') arc_type = StringVar() arc_type.set(par.arc_type) self.arc_type_box = OptionMenu(master, arc_type, "seishub", "fdsn", "day_vols") self.arc_type_box.grid(column=1, row=10, sticky='w,e') arc_type.trace("w", update_arc_type) self.cores_label = Label(master, text="Number of cores") self.cores_label.grid(column=0, row=11, sticky='e') cores = IntVar() cores.set(par.cores) self.cores_box = Entry(master, bd=2, textvariable=cores) self.cores_box.grid(column=1, row=11) cores.trace("w", update_cores) self.plotvar_label = Label(master, text="Plotting on/off") self.plotvar_label.grid(column=0, row=12, sticky='e') plotvar = BooleanVar() plotvar.set(par.plotvar) self.plotvar_box = Checkbutton(master, text='Plot on', var=plotvar, onvalue=True, offvalue=False) self.plotvar_box.grid(column=1, row=12) plotvar.trace("w", update_plotvar) self.plotdir_label = Label(master, text="Plot directory") self.plotdir_label.grid(column=0, row=13, sticky='e') plotdir = StringVar() plotdir.set(par.plotdir) self.plotdir_box = Entry(master, bd=2, textvariable=plotdir) self.plotdir_box.grid(column=1, row=13) plotdir.trace("w", update_plotdir) self.plotdir_lookup = Button(master, text="Lookup", command=lambda: self.get_plotdir(par)) self.plotdir_lookup.grid(column=2, row=13) self.plot_format_label = Label(master, text="Plot format") self.plot_format_label.grid(column=0, row=14, sticky='e') plot_format = StringVar() plot_format.set(par.plot_format) self.plot_format_box = OptionMenu(master, plot_format, "jpg", "eps", "pdf", "png") self.plot_format_box.grid(column=1, row=14, sticky='w,e') plot_format.trace("w", update_plot_format) self.tempdir_label = Label(master, text="Temporary directory") self.tempdir_label.grid(column=0, row=15, sticky='e') tempdir = StringVar() tempdir.set(par.tempdir) self.tempdir_box = Entry(master, bd=2, textvariable=tempdir) self.tempdir_box.grid(column=1, row=15) tempdir.trace("w", update_tempdir) self.tempdir_lookup = Button(master, text="Lookup", command=lambda: self.get_tempdir(par)) self.tempdir_lookup.grid(column=2, row=15) self.threshold_label = Label(master, text="Threshold") self.threshold_label.grid(column=0, row=16, sticky='e') threshold = DoubleVar() threshold.set(par.threshold) self.threshold_box = Entry(master, bd=2, textvariable=threshold) self.threshold_box.grid(column=1, row=16) threshold.trace("w", update_threshold) self.threshold_type_label = Label(master, text="Threshold type") self.threshold_type_label.grid(column=0, row=17, sticky='e') threshold_type = StringVar() threshold_type.set(par.threshold_type) self.threshold_type_box = OptionMenu(master, threshold_type, "MAD", "absolute", "av_chan_corr") self.threshold_type_box.grid(column=1, row=17, sticky='w,e') threshold_type.trace("w", update_threshold_type) self.trigger_interval_label = Label(master, text="Minimum trigger " + "interval (s)") self.trigger_interval_label.grid(column=0, row=18, sticky='e') trigger_interval = DoubleVar() trigger_interval.set(par.trigger_interval) self.trigger_interval_box = Entry(master, bd=2, textvariable=trigger_interval) self.trigger_interval_box.grid(column=1, row=18) trigger_interval.trace("w", update_trigger_interval) # End of user editable section, now we have read/write buttons self.read_button = Button(master, text="Read parameters", command=lambda: self.read_par(master)) self.read_button.grid(column=0, row=nrows - 2, sticky='w,e') self.write_button = Button(master, text="Write parameters", command=lambda: self.write_par(par)) self.write_button.grid(column=1, row=nrows - 2, sticky='w,e')
class BIDSFile(FileInfo): """ BIDSFiles are the main files which will contain the data that is to be converted to BIDS format. For KIT data this is the .con file. For Elekta data this is the .fif file. """ def __init__(self, id_=None, file=None, settings=dict(), parent=None): super(BIDSFile, self).__init__(id_, file, parent) self._settings = settings self._create_vars() if 'emptyroom' in self.file.lower(): self.is_empty_room.set(True) def _create_vars(self): # TODO: Fix # This is called multiple times... FileInfo._create_vars(self) self.run = StringVar(value='1') self.run.trace("w", self.validate) self.task = OptionsVar(options=['None']) self.task.trace("w", self._update_tasks) self.is_junk = BooleanVar() self.is_empty_room = BooleanVar() self.is_empty_room.trace("w", self.propagate_emptyroom_data) self.has_empty_room = BooleanVar() self.hpi = list() self.loaded = False self.extra_data = dict() # event info: a list of lists. The sub-lists contains the event number # and the event description self.event_info = list() # channel info: key - channel name (?), value - something self.channel_info = dict() self.raw = None self.container = None # Set all BIDS files to be saved by default self.requires_save = True def load_data(self): pass def validate(self, validate_container=True, *args): """ Check whether the file is valid (ie. contains all the required info for BIDS exporting). """ self.valid = self.check_valid() if self.container is not None and validate_container: self.container.validate() def check_valid(self): """ Go over all the required settings and determine whether the file is ready to be exported to the bids format. """ is_valid = super(BIDSFile, self).check_valid() # if empty room or junk we consider them good if self.is_empty_room.get() or self.is_junk.get(): return is_valid is_valid &= self.run.get() != '' is_valid &= (self.hpi != list()) return is_valid def get_event_data(self): return ['', ''] def propagate_emptyroom_data(self, *args): """ Callback to propagate the empty room state This is used to tell the container object that the empty room status of this file has changed and change the 'has empty room' state of any other files in the same folder (for KIT). """ if self.container is not None: emptyroom_set = self.container.autodetect_emptyroom() if not emptyroom_set: self.is_empty_room.set(False) self.associated_tab.is_emptyroom_info.value = self.is_empty_room # noqa def _update_tasks(self, *args): """Update the EntryChoice that contains the task options""" if self.associated_tab is not None: self.associated_tab.task_info.value = self.task def __getstate__(self): data = super(BIDSFile, self).__getstate__() data['run'] = self.run.get() # run data['tsk'] = self.task.get() # task # marker coils: data['hpi'] = [hpi.file for hpi in self.hpi] data['ier'] = self.is_empty_room.get() # is empty room data? data['her'] = self.has_empty_room.get() # has empty room data? return data def __setstate__(self, state): super(BIDSFile, self).__setstate__(state) self.run.set(state.get('run', 0)) task = state.get('tsk', '') self.task.options = [task] self.task.set(task) # support old and new format hpi storage self.hpi = state.get('hpi', list()) self.is_empty_room.set(state.get('ier', False)) self.has_empty_room.set(state.get('her', False))