def _create_widgets(self): Label(self, text='Example Entry:') Entry(self, textvariable=self.entry_var) button = Button(self, text='Show Dialog') button.configure(command=self.show_dialog_for(button)) button = Button( self, text='Show For This Button', ) button.configure(command=show_dialog_for(button)) def get_widget_text(widget: Entry): return widget.get() table = NewTable(self, headers=( ('Test', get_widget_text), ('The', get_widget_text), ('Table', get_widget_text), )) widgets_ = [] for row in range(4): for col in range(3): widgets_.append(Entry(table.table)) widgets_[-1].insert(0, 'Row {}; Col {}'.format(row, col)) table.cell_widgets = widgets_
def save(event=None): name = name_entry.get().strip() if not mailbox: # new mailbox i = self.b_add.grid_info()['row'] self.b_add.grid_configure(row=i + 1) c = Checkbutton(self.frame) c.state(('selected',)) c.grid(row=i, column=0, pady=4, padx=(4, 0)) l = Label(self.frame, text=name) l.grid(row=i, column=1, padx=4, pady=4) b_edit = Button(self.frame, image=self.im_edit, width=1, command=lambda m=name: self.mailbox_info(m)) b_edit.grid(row=i, column=2, padx=4, pady=4) b_del = Button(self.frame, image=self.im_del, width=1, command=lambda m=name: self.del_mailbox(m)) b_del.grid(row=i, column=3, padx=4, pady=4) self.mailboxes[name] = [c, l, b_edit, b_del] elif name != mailbox: # change name of mailbox os.remove(os.path.join(LOCAL_PATH, mailbox)) c, l, b_edit, b_del = self.mailboxes[mailbox] del(self.mailboxes[mailbox]) l.configure(text=name) b_edit.configure(command=lambda m=name: self.mailbox_info(m)) b_del.configure(command=lambda m=name: self.del_mailbox(m)) self.mailboxes[name] = [c, l, b_edit, b_del] encrypt(name, self.pwd, server_entry.get().strip(), login_entry.get().strip(), password_entry.get().strip(), folder_entry.get().strip()) top.destroy()
def update_button(self, button: ttk.Button, image: "ImageTk", node_draw: NodeDraw): logging.debug("update button(%s): %s", button, node_draw) self.hide_pickers() button.configure(image=image) button.image = image self.app.canvas.mode = GraphMode.NODE self.app.canvas.node_draw = node_draw
class Page1(Frame): def on_recommend_btn_click(self): selected_index = self.item_lst_box.curselection() user_preferences = [ self.item_lst_box.get(indx) for indx in selected_index ] print(f'User preferences are {user_preferences}') if len(user_preferences) > 3: messagebox.showwarning( "Warning", "Currently doesnt support for than 3 preferences") else: self.business_logic.set_preferences(user_preferences) Page2(self.master, self).show() def get_appliances(self): selected = self.radio_btn_var.get() print(f'Selected option is {selected}') if selected in (0, 1): self.business_logic.set_choice(selected) for index, item in enumerate(self.business_logic.items[ self.business_logic.selected_choice]): self.item_lst_box.insert(index, item) self.recommend_btn.configure(state=ACTIVE) else: self.recommend_btn.configure(state=DISABLED) self.item_lst_box.delete(0, 'end') def __init__(self, master): super().__init__(master, relief='flat', borderwidth=40) self.business_logic = MLLogic() self.radio_btn_var = IntVar() self.master.title(self.business_logic.title) self.pack(fill=None, expand=False) Label(self, text="Select the type of appliances you want to buy").grid( row=1, columnspan=2, sticky='NW') for index, appliance_type in enumerate( self.business_logic.appliance_choices): rdo_btn = Radiobutton(self, text=appliance_type, variable=self.radio_btn_var, value=index, command=self.get_appliances) rdo_btn.select() rdo_btn.grid(row=index + 2, column=2) Label(self, text="Preferences").grid(row=7, columnspan=2, sticky='NW') self.item_lst_box = Listbox(self, selectmode='multiple') self.item_lst_box.delete(0, 'end') self.item_lst_box.grid(row=7, column=2) self.recommend_btn = Button(self, text='Recommend', command=self.on_recommend_btn_click) self.recommend_btn.grid(row=9, column=2, sticky='NW') self.recommend_btn.configure(state=DISABLED)
def update_button( self, button: ttk.Button, node_draw: NodeDraw, type_enum: NodeTypeEnum, image: PhotoImage, ) -> None: logging.debug("update button(%s): %s", button, node_draw) button.configure(image=image) button.image = image self.app.canvas.node_draw = node_draw if type_enum == NodeTypeEnum.NODE: self.current_node = node_draw elif type_enum == NodeTypeEnum.NETWORK: self.current_network = node_draw
class DestinationFrame(LabelFrame): def __init__(self, root, model: ConfigUIModel): super().__init__(root, text="Destination", padding=10) self.pack(side=LEFT, anchor=NW, fill=BOTH, expand=True) self.model = model self.txt_dest = Entry(self, textvariable=self.model.var_dest) self.txt_dest.pack(fill=X) self.chk_wrap = Checkbutton(self, text="Create Date Wrapper", variable=self.model.var_wrap, onvalue=True, offvalue=False) self.chk_wrap.pack() self.btn_dest_browse = Button(self, text="Browse", command=self.set_dest) self.btn_dest_browse.pack() def manage(): self.model.var_managed.set(True) self.btn_make_managed = Button(self, text="Manage This Folder", command=manage) self.btn_make_managed.pack(side=BOTTOM) self.model.var_managed.trace_add("write", self.managed_changed) def managed_changed(self, *args): val = self.model.var_managed.get() if val: self.chk_wrap.configure(state="disabled") self.btn_make_managed.configure(text="Already Managed", state="disabled") else: self.chk_wrap.configure(state="normal") self.btn_make_managed.configure(text="Manage This Folder", state="normal") def set_dest(self): dest = filedialog.askdirectory(title="Choose Destination") if dest is not None: self.model.var_dest.set(dest) self.model.var_managed.set(MetaRecord.is_managed(dest))
def _create_widgets(self): """Create widgets""" self.table_frame = Frame(self) self.scrollable_canvas = Canvas(self.table_frame) self.x_scroll = Scrollbar( self, orient=HORIZONTAL, command=self.scrollable_canvas.xview) self.y_scroll = Scrollbar( self.table_frame, orient=VERTICAL, command=self.scrollable_canvas.yview) self.scrollable_canvas.configure(yscrollcommand=self.y_scroll.set, xscrollcommand=self.x_scroll.set) self.table = Frame(self.scrollable_canvas) for header_text in self.column_headers: widget = Frame(self.table) button = Button(widget, text=header_text) button.configure( command=lambda button=button: self._sort_command(button)) self._create_data_widgets()
def update_button( self, button: ttk.Button, image: "ImageTk", node_draw: NodeDraw, type_enum, image_enum, ): logging.debug("update button(%s): %s", button, node_draw) self.hide_pickers() button.configure(image=image) button.image = image self.app.canvas.mode = GraphMode.NODE self.app.canvas.node_draw = node_draw if type_enum == NodeTypeEnum.NODE: self.node_enum = image_enum elif type_enum == NodeTypeEnum.NETWORK: self.network_enum = image_enum
def initUI(self): self.parent.title("GUI Controls Test") self.style = Style() self.style.theme_use("default") self.pack(fill=BOTH, expand=1) #-------------------------------------------- #--------------------create menus------------ menuBar = Menu(self.parent) mnuFile = Menu(menuBar, tearoff=0) menuBar.add_cascade(label="File", menu=mnuFile) mnuFile.add_command(label="Open", command=self.mnuOpenFileClick) mnuFile.add_command(label="Save", command=self.mnuSaveFileClick) mnuFile.add_separator() mnuFile.add_command(label="Exit", command=self.exitButtonClick) mnuCustomers = Menu(menuBar, tearoff=0) menuBar.add_cascade(label="Loan Processing", menu=mnuCustomers) mnuCustomers.add_command(label="Loan Calculator", command=self.loanCalcButtonClick) mnuCustomers.add_separator() mnuCustomers.add_command(label="Provide Feedback", command=self.mnuShowFeedbackClick) self.parent.config(menu=menuBar) #-------------------------------------------- xpos = 30 ypos = 40 xpos2 = xpos + 90 #------------styling---------------------------------- style = Style() style.configure("Exit.TButton", foreground="red", background="white") style.configure("MainButton.TButton", foreground="yellow", background="red") #----------------------------------------------------- testButton = Button(self, text="Get StudentID", command=self.btnGetStudentIDClick) testButton.configure(style="MainButton.TButton") testButton.place(x=xpos, y=ypos) self.txtID = Entry(self, text="", foreground="#ff0000", background="light blue", font="Arial 9") # Arial 12 bold italic self.txtID.place(x=xpos2, y=ypos) self.txtID.configure(state="readonly") ypos += 30 btnLoanCalc = Button(self, text="Loan Calculator", command=self.loanCalcButtonClick) btnLoanCalc.configure(style="Exit.TButton") btnLoanCalc.place(x=xpos, y=ypos) ypos += 30 exitButton = Button(self, text="Exit", command=self.exitButtonClick) exitButton.configure(style="Exit.TButton") exitButton.place(x=xpos, y=ypos)
class P300Window(object): def __init__(self, master: Tk): self.master = master master.title('P300 speller') #Parameters self.imagesize = 125 self.images_folder_path = '../utils/images/' #use utils/char_generator to generate any image you want self.flash_image_path = '../utils/images/flash_images/einstein.jpg' self.number_of_rows = 6 self.number_of_columns = 6 #make sure you have 6 x 6 amount of images in the images_folder_path self.flash_mode = 2 #single element #1 for columns and rows; currently is NOT working yet; if I have time, will revisit self.flash_duration = 100 #soa self.break_duration = 125 #iti self.trials = 6 #number of letters self.delay = 2500 #interval between trial self.letter_idx = 0 #did not include numbers yet! self.random_letter = random.choices( string.ascii_lowercase, k=self.trials) #randomize [self.trials] number letters self.word = ''.join(self.random_letter) # Variables self.usable_images = [] self.image_labels = [] self.flash_sequence = [] self.flash_image = None self.sequence_number = 0 self.lsl_output = None self.running = 0 #for pause self.image_frame = Frame(self.master) self.image_frame.grid(row=0, column=0, rowspan=self.number_of_rows, columnspan=self.number_of_columns) self.start_btn_text = StringVar() self.start_btn_text.set('Start') self.start_btn = Button(self.master, textvariable=self.start_btn_text, command=self.start) self.start_btn.grid(row=self.number_of_rows + 3, column=self.number_of_columns - 1) self.pause_btn = Button(self.master, text='Pause', command=self.pause) self.pause_btn.grid(row=self.number_of_rows + 3, column=self.number_of_columns - 4) #-4 for center self.pause_btn.configure(state='disabled') self.close_btn = Button(self.master, text='Close', command=master.quit) self.close_btn.grid(row=self.number_of_rows + 3, column=0) fontStyle = tkFont.Font(family="Courier", size=40) self.output = Text(root, height=1, font=fontStyle) self.output.tag_configure("red", foreground="red") self.output.tag_configure("green", foreground="green") self.output.configure(width=10) self.output.insert("end", " ") self.output.grid(row=self.number_of_rows + 2, column=self.number_of_columns - 4) self.outputlabel = Label(root, text="Output: ", font=fontStyle) self.outputlabel.grid(row=self.number_of_rows + 2, column=self.number_of_columns - 5) self.targetlabel = Label(root, text="Target: ", font=fontStyle) self.targetlabel.grid(row=self.number_of_rows + 1, column=self.number_of_columns - 5) self.show_highlight_letter(0) # Initialization self.show_images() self.create_flash_sequence() self.lsl_output = self.create_lsl_output() def open_images(self): self.usable_images = [] self.highlight_letter_images = [] letter_images = sorted( glob.glob( os.path.join(self.images_folder_path, 'letter_images/*.png'))) #currently, still did not flash number yet! number_images = sorted( glob.glob( os.path.join(self.images_folder_path, 'number_images/*.png'))) letter_highlight_images = sorted( glob.glob( os.path.join(self.images_folder_path, 'letter_highlight_images/*.png'))) number_highlight_images = sorted( glob.glob( os.path.join(self.images_folder_path, 'number_highlight_images/*.png'))) for number_image in number_images: letter_images.append(number_image) #print("Paths: ", letter_images) min_number_of_images = self.number_of_columns * self.number_of_rows if len(letter_images) < min_number_of_images: print('To few images in folder: ' + self.images_folder_path) return # Convert and resize images for image_path in letter_images: image = Image.open(image_path) resized = image.resize((self.imagesize, self.imagesize), Image.BICUBIC) Tkimage = ImageTk.PhotoImage(resized) self.usable_images.append(Tkimage) # Convert and resize images for image_path in letter_highlight_images: image = Image.open(image_path) resized = image.resize((self.imagesize, self.imagesize), Image.BICUBIC) Tkimage = ImageTk.PhotoImage(resized) self.highlight_letter_images.append(Tkimage) flash_img = Image.open(self.flash_image_path) flash_img_res = flash_img.resize((self.imagesize, self.imagesize), Image.BICUBIC) self.flash_image = ImageTk.PhotoImage(flash_img_res) def show_images(self): self.open_images() if self.usable_images == []: print('No images opened') return num_rows = self.number_of_rows num_cols = self.number_of_columns # Arrange images for r in range(0, num_rows): for c in range(0, num_cols): current_image = self.usable_images[r * num_cols + c] label = Label(self.image_frame, image=current_image) label.image = current_image label.grid(row=r, column=c) self.image_labels.append(label) def create_lsl_output(self): """Creates an LSL Stream outlet""" info = StreamInfo(name='LetterMarkerStream', type='LetterFlashMarkers', channel_count=1, channel_format='int8', nominal_srate=IRREGULAR_RATE, source_id='lettermarker_stream', handle=None) return StreamOutlet(info) #for sending the predicted classes def create_flash_sequence(self): self.flash_sequence = [] num_rows = self.number_of_rows num_cols = self.number_of_columns maximum_number = num_rows * num_cols flash_sequence = [] for i in range(10000): seq = list(range(maximum_number)) #generate 0 to maximum_number random.shuffle(seq) #shuffle flash_sequence.extend(seq) self.flash_sequence = flash_sequence def start(self): self.read_lsl_marker() self.running = 1 letter = self.word[0] image_index = string.ascii_lowercase.index(letter) self.highlight_image(image_index) self.start_btn.configure(state='disabled') self.pause_btn.configure(state='normal') def pause(self): self.running = 0 self.start_btn_text.set('Resume') self.start_btn.configure(state='normal') self.pause_btn.configure(state='disabled') def start_flashing(self): if self.sequence_number == len( self.flash_sequence ): #stop flashing if all generated sequence number runs out print('All elements had flashed - run out of juice') self.running = 0 self.sequence_number = 0 return if self.running == 0: print('Flashing paused at sequence number ' + str(self.sequence_number)) return result = self.marker_result() if (result): print("Marker received: ", result[0][0]) receive = result[0][0] else: receive = 0 element_to_flash = self.flash_sequence[self.sequence_number] letter = self.word[self.letter_idx] image_index = string.ascii_lowercase.index(letter) #pushed markers to LSL stream timestamp = local_clock() print("Letter: ", image_index, " Element flash: ", [element_to_flash + 1], timestamp) self.lsl_output.push_sample([element_to_flash + 1], timestamp) # add 1 to prevent 0 in markers self.flash_single_element(element_to_flash) if not (receive): self.master.after(self.break_duration, self.start_flashing) else: if ((image_index + 1) == receive): self.output.insert("end", self.pos_to_char(receive), "green") else: self.output.insert("end", self.pos_to_char(receive), "red") self.letter_idx += 1 if (self.letter_idx == len(self.word)): return letter = self.word[self.letter_idx] image_index = string.ascii_lowercase.index(letter) self.master.after(self.break_duration, self.highlight_target, image_index) self.sequence_number = self.sequence_number + 1 #change flash position def pos_to_char(self, pos): return chr(pos - 1 + 97) def highlight_target(self, image_index): self.show_highlight_letter(self.letter_idx) self.highlight_image(image_index) def change_image(self, label, img): label.configure(image=img) label.image = img def highlight_image(self, element_no): self.change_image(self.image_labels[element_no], self.highlight_letter_images[element_no]) self.master.after(self.delay, self.unhighlight_image, element_no) def unhighlight_image(self, element_no): self.change_image(self.image_labels[element_no], self.usable_images[element_no]) self.master.after(self.flash_duration, self.start_flashing) def show_highlight_letter(self, pos): fontStyle = tkFont.Font(family="Courier", size=40) fontStyleBold = tkFont.Font(family="Courier bold", size=40) text = Text(root, height=1, font=fontStyle) text.tag_configure("bold", font=fontStyleBold) text.tag_configure("center", justify='center') for i in range(0, len(self.word)): if (i != pos): text.insert("end", self.word[i]) else: text.insert("end", self.word[i], "bold") text.configure(state="disabled", width=10) text.tag_add("center", "1.0", "end") text.grid(row=self.number_of_rows + 1, column=self.number_of_columns - 4) def flash_row_or_col(self, rc_number): num_rows = self.number_of_rows num_cols = self.number_of_columns if rc_number < num_rows: for c in range(0, num_cols): #flash row cur_idx = rc_number * num_cols + c self.change_image(self.image_labels[cur_idx], self.flash_image) else: current_column = rc_number - num_rows for r in range(0, num_rows): #flash column cur_idx = current_column + r * num_cols self.change_image(self.image_labels[cur_idx], self.flash_image) self.master.after(self.flash_duration, self.unflash_row_or_col, rc_number) def unflash_row_or_col(self, rc_number): num_rows = self.number_of_rows num_cols = self.number_of_columns if rc_number < num_rows: for c in range(0, num_cols): #flash row cur_idx = rc_number * num_cols + c self.change_image(self.image_labels[cur_idx], self.usable_images[cur_idx]) else: current_column = rc_number - num_rows for r in range(0, num_rows): #flash column cur_idx = current_column + r * num_cols self.change_image(self.image_labels[cur_idx], self.usable_images[cur_idx]) def flash_single_element(self, element_no): self.change_image(self.image_labels[element_no], self.flash_image) self.master.after(self.flash_duration, self.unflash_single_element, element_no) def unflash_single_element(self, element_no): self.change_image(self.image_labels[element_no], self.usable_images[element_no]) def marker_result(self): marker, timestamp = self.inlet_marker.pull_chunk() return marker def read_lsl_marker(self): print("looking for a Markers stream...") marker_streams = resolve_byprop('name', 'ResultMarkerStream') if marker_streams: self.inlet_marker = StreamInlet(marker_streams[0]) marker_time_correction = self.inlet_marker.time_correction() print("Found Markers stream")
class UploadPage(Frame): def __init__(self, parent, controller): Frame.__init__(self, parent) self.controller = controller self.outputState = False self.sem = threading.Semaphore() self.lock = 1 frame1 = Frame(self, relief=RAISED, borderwidth=1) frame1.pack(fill=BOTH, expand=True) frame2 = Frame(self, relief=RAISED, borderwidth=1) frame2.pack(fill=BOTH, expand=True) self.outputBox = Text(self, height=15, width=40) self.outputBox.tag_config('error', background="yellow", foreground="red") self.vsb = Scrollbar(self, orient="vertical", command=self.outputBox.yview) self.outputBox.configure(yscrollcommand=self.vsb.set, state="disabled") self.vsb.pack(side="right", fill="y") self.outputBox.pack(side="left", fill="both", expand=True) self.filePath_entry = Entry(frame1, width=35) self.filePath_label = Label(frame1, text="Blueprints .zip File Path:") self.fileButton = Button(frame1, text="Upload Blueprints", width=30) self.fileButton.configure(command=self.threader_upload) self.returnButton = Button( frame2, text="Go Back", command=lambda: self.controller.show_frame("MainPage")) self.filePath_label.pack(padx=5, pady=5) self.filePath_entry.pack(padx=5, pady=5) self.fileButton.pack(padx=5, pady=5) self.returnButton.pack(padx=5, pady=10) def threader_upload(self): if self.lock == 1: self.lock = 0 thread = threading.Thread(target=self.upload_blueprints) thread.daemon = True thread.start() return def upload_blueprints(self): """ Uploads blueprints (downloaded with this tool) to vRA server Creates: services.json -JSON file with all Catalog Services info *used to check fo service duplicates blueprintLog.txt -TEXT file with blueprints' names *used when uploading blueprints to sort into proper service categories """ if len(self.filePath_entry.get()) == 0: self.lock = 1 return blueprintID = [] file = self.filePath_entry.get() show_output(self, "Reading blueprintLog.txt") try: with open("blueprintLog.txt", 'r') as f: blueprintID = f.read().splitlines() except IOError: show_output( self, "blueprintLog.txt not found, blueprints will not be sorted", error=True) if file[-4:] != ".zip": file += ".zip" zipPath = "\'" + os.path.dirname(os.path.realpath(__file__)) + "\\" importBlueprints = "cloudclient.bat vra content import --path {}\' --resolution SKIP --precheck WARN"\ .format(zipPath + file) # TODO: Need to find a way to ensure all blueprints were imported uploadRep = int(len(blueprintID) / 25) if uploadRep == 0: uploadRep = 1 for x in range(0, uploadRep): cloud_client_run(self, importBlueprints, "Importing Blueprints to vRA ({}/{})".format( x + 1, uploadRep), newLog=True) close_output(self) show_output(self, "\nUpload Complete") self.lock = 1 return
class AutoCorrectConfig(Frame): """Configuration window for autocorrect.""" def __init__(self, master, app, **kwargs): Frame.__init__(self, master, padding=4, **kwargs) self.rowconfigure(2, weight=1) self.columnconfigure(0, weight=1) self.columnconfigure(1, weight=1) self.tree = Treeview(self, columns=('replace', 'by'), show='', selectmode='browse') scroll_x = AutoScrollbar(self, orient='horizontal', command=self.tree.xview) scroll_y = AutoScrollbar(self, orient='vertical', command=self.tree.yview) self.tree.configure(xscrollcommand=scroll_x.set, yscrollcommand=scroll_y.set) self.reset() self.replace = StringVar(self) self.by = StringVar(self) add_trace(self.replace, 'write', self._trace_replace) add_trace(self.by, 'write', self._trace_by) b_frame = Frame(self) self.b_add = Button(b_frame, text=_('New'), command=self.add) self.b_rem = Button(b_frame, text=_('Delete'), command=self.remove) self.b_add.state(('disabled', )) self.b_rem.state(('disabled', )) self.b_add.pack(pady=4, fill='x') self.b_rem.pack(pady=4, fill='x') Button(b_frame, text=_('Reset'), command=self.reset).pack(pady=8, fill='x') Label(self, text=_('Replace')).grid(row=0, column=0, sticky='w', pady=4) Label(self, text=_('By')).grid(row=0, column=1, sticky='w', pady=4) Entry(self, textvariable=self.replace).grid(row=1, column=0, sticky='ew', pady=4, padx=(0, 4)) Entry(self, textvariable=self.by).grid(row=1, column=1, sticky='ew', pady=4) self.tree.grid(row=2, columnspan=2, sticky='ewsn', pady=(4, 0)) scroll_x.grid(row=3, columnspan=2, sticky='ew', pady=(0, 4)) scroll_y.grid(row=2, column=2, sticky='ns', pady=(4, 0)) b_frame.grid(row=1, rowspan=2, padx=(4, 0), sticky='nw', column=3) self.tree.bind('<<TreeviewSelect>>', self._on_treeview_select) def _trace_by(self, *args): key = self.replace.get().strip() val = self.by.get().strip() self.by.set(val) if key in self.tree.get_children(''): if val != self.tree.set(key, 'by'): self.b_add.state(('!disabled', )) else: self.b_add.state(('disabled', )) else: self.b_add.state(('!disabled', )) if not val: self.b_add.state(('disabled', )) def _trace_replace(self, *args): key = self.replace.get().strip() val = self.by.get().strip() self.replace.set(key) if not key: self.b_add.state(('disabled', )) self.b_rem.state(('disabled', )) else: self.b_add.state(('!disabled', )) sel = self.tree.selection() if key in self.tree.get_children(''): if key not in sel: self.tree.selection_set(key) self.b_add.configure(text=_('Replace')) self.b_rem.state(('!disabled', )) if val != self.tree.set(key, 'by'): self.b_add.state(('!disabled', )) else: self.b_add.state(('disabled', )) else: self.b_rem.state(('disabled', )) self.b_add.configure(text=_('New')) if sel: self.tree.selection_remove(*sel) if not val: self.b_add.state(('disabled', )) def _on_treeview_select(self, event): sel = self.tree.selection() if sel: key, val = self.tree.item(sel[0], 'values') self.replace.set(key) self.by.set(val) def reset(self): self.tree.delete(*self.tree.get_children('')) keys = list(AUTOCORRECT.keys()) keys.sort() for key in keys: self.tree.insert('', 'end', key, values=(key, AUTOCORRECT[key])) def add(self): key = self.replace.get().strip() val = self.by.get().strip() if key in self.tree.get_children(''): self.tree.item(key, values=(key, val)) elif key and val: self.tree.insert('', 'end', key, values=(key, val)) def remove(self): key = self.replace.get() if key in self.tree.get_children(''): self.tree.delete(key) def ok(self): keys = self.tree.get_children('') AUTOCORRECT.clear() for key in keys: AUTOCORRECT[key] = self.tree.set(key, 'by')
class Window(Frame): def __init__(self): super().__init__() self.initUI() def initUI(self): self.master.title("UDPSendListenerGUI") self.pack(fill=BOTH, expand=True) self.master.protocol("WM_DELETE_WINDOW", self.on_closing) self.columnconfigure(1, weight=1) self.columnconfigure(3, pad=7) self.rowconfigure(3, weight=1) self.rowconfigure(5, pad=7) self.is_connect = False self.client_ip_label = Label(self, text="Client IP") self.client_ip_label.grid(row=0, column=0, padx=10, sticky=E + W + S + N) self.client_ip_entry = Entry(self) self.client_ip_entry.grid(row=0, column=1, padx=10, sticky=E + W + S + N) self.client_port_label = Label(self, text="Client Port") self.client_port_label.grid(row=0, column=2, padx=10, sticky=E + W + S + N) self.client_port_entry = Entry(self, width=5) self.client_port_entry.grid(row=0, column=3, padx=10, sticky=E + W + S + N) self.local_port_label = Label(self, text="Local Port") self.local_port_label.grid(row=0, column=4, padx=10, sticky=E + W + S + N) self.local_port_entry = Entry(self, width=5) self.local_port_entry.grid(row=0, column=5, padx=10, sticky=E + W + S + N) self.connect_button = Button(self, text="Connect", command=self.connect) self.connect_button.grid(row=0, column=6, padx=10, sticky=E + W + S + N) self.received_label = Label(self, text="Received data") self.received_label.grid(row=1, column=0, padx=10, sticky=E + W + S + N) self.received_text = Text(self, height=10) self.received_text.grid(row=2, column=0, columnspan=7, padx=10, sticky=E + W + S + N) self.sent_label = Label(self, text="Sent data") self.sent_label.grid(row=3, column=0, padx=10, sticky=E + W + S + N) self.sent_text = Text(self, height=10) self.sent_text.grid(row=4, column=0, columnspan=7, padx=10, sticky=E + W + S + N) self.send1_entry = Entry(self) self.send1_entry.grid(row=5, column=0, padx=10, columnspan=5, sticky=E + W + S + N) self.send1_button = Button(self, text="Send 1", command=self.send_1, state=DISABLED) self.send1_button.grid(row=5, column=5, padx=10, columnspan=2, sticky=E + W + S + N) self.send2_entry = Entry(self) self.send2_entry.grid(row=6, column=0, padx=10, columnspan=5, sticky=E + W + S + N) self.send2_button = Button(self, text="Send 2", command=self.send_2, state=DISABLED) self.send2_button.grid(row=6, column=5, padx=10, columnspan=2, sticky=E + W + S + N) self.send3_entry = Entry(self) self.send3_entry.grid(row=7, column=0, padx=10, columnspan=5, sticky=E + W + S + N) self.send3_button = Button(self, text="Send 3", command=self.send_3, state=DISABLED) self.send3_button.grid(row=7, column=5, padx=10, columnspan=2, sticky=E + W + S + N) self.cfg_file = "udp.ini" try: config = configparser.ConfigParser() config.read_file(open(self.cfg_file)) self.client_ip = config['SETTINGS']['client_ip'] self.client_port = config['SETTINGS']['client_port'] self.local_port = config['SETTINGS']['local_port'] self.client_ip_entry.insert(END, self.client_ip) self.client_port_entry.insert(END, self.client_port) self.local_port_entry.insert(END, self.local_port) self.send1_entry.insert(END, config['SETTINGS']['send1']) self.send2_entry.insert(END, config['SETTINGS']['send2']) self.send3_entry.insert(END, config['SETTINGS']['send3']) except: pass def get_ip_x(self): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: s.connect(('10.255.255.255', 1)) IP = s.getsockname()[0] except: IP = '127.0.0.1' finally: s.close() return IP def thread_function(self): while True: if not self.is_connect: del self.sock_server del self.sock_client break else: data, addr = self.sock_server.recvfrom(1024) self.received_text.insert(END, data) def connect(self): self.client_ip = self.client_ip_entry.get() self.client_port = self.client_port_entry.get() self.local_port = self.local_port_entry.get() try: self.sock_server.close() except: pass if not self.is_connect and self.client_ip and self.client_port and self.local_port: self.sock_client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock_server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock_server.bind((self.get_ip_x(), int(self.local_port_entry.get()))) self.is_connect = True self.x = threading.Thread(target=self.thread_function, daemon=True) self.x.start() self.send1_button.configure(state=NORMAL) self.send2_button.configure(state=NORMAL) self.send3_button.configure(state=NORMAL) self.client_ip_entry.configure(state=DISABLED) self.client_port_entry.configure(state=DISABLED) self.local_port_entry.configure(state=DISABLED) self.connect_button['text'] = "Close" else: self.send1_button.configure(state=DISABLED) self.send2_button.configure(state=DISABLED) self.send3_button.configure(state=DISABLED) self.client_ip_entry.configure(state=NORMAL) self.client_port_entry.configure(state=NORMAL) self.local_port_entry.configure(state=NORMAL) self.is_connect = False self.connect_button['text'] = "Connect" def send_1(self): msg = self.send1_entry.get() if msg: self.sent_text.insert(END, msg) self.sock_server.sendto(bytes(msg, encoding='utf-8'), ( bytes(self.client_ip_entry.get(), encoding='utf-8'), int(self.client_port_entry.get()))) def send_2(self): msg = self.send2_entry.get() if msg: self.sent_text.insert(END, msg) self.sock_server.sendto(bytes(msg, encoding='utf-8'), ( bytes(self.client_ip_entry.get(), encoding='utf-8'), int(self.client_port_entry.get()))) def send_3(self): msg = self.send3_entry.get() if msg: self.sent_text.insert(END, msg) self.sock_server.sendto(bytes(msg, encoding='utf-8'), ( bytes(self.client_ip_entry.get(), encoding='utf-8'), int(self.client_port_entry.get()))) def on_closing(self): config = configparser.ConfigParser() config['SETTINGS'] = {} config['SETTINGS']['client_ip'] = self.client_ip config['SETTINGS']['client_port'] = self.client_port config['SETTINGS']['local_port'] = self.local_port config['SETTINGS']['send1'] = self.send1_entry.get() config['SETTINGS']['send2'] = self.send2_entry.get() config['SETTINGS']['send3'] = self.send3_entry.get() with open(self.cfg_file, 'w') as configfile: config.write(configfile) self.master.destroy()
class P300Window(object): """All logic for the image flashing window. Args: master: Tkinter Toplevel element parent: Parent that opened the window config: ConfigParams instance """ def __init__(self, master: Toplevel, parent: MainWindow, config: ConfigParams): self.master = master self.parent = parent self.master.protocol('WM_DELETE_WINDOW', self.close_window) self.config = config self.running = 0 self.image_labels = [] self.sequence_number = 0 self.lsl_output = None self.usable_images = [] self.flash_sequence = [] self.image_frame = Frame(self.master) self.image_frame.grid(row=0, column=0, rowspan=self.config.number_of_rows.get(), columnspan=self.config.number_of_columns.get()) self.start_btn_text = StringVar() self.start_btn_text.set('Start') self.start_btn = Button(self.master, textvariable=self.start_btn_text, command=self.start) self.start_btn.grid(row=self.config.number_of_rows.get() + 1, column=self.config.number_of_columns.get() - 1) self.pause_btn = Button(self.master, text='Pause', command=self.pause) self.pause_btn.grid(row=self.config.number_of_rows.get() + 1, column=self.config.number_of_columns.get() - 2) self.pause_btn.configure(state='disabled') self.close_btn = Button(self.master, text='Close', command=self.close_window) self.close_btn.grid(row=self.config.number_of_rows.get() + 1, column=0) # Initialization self.show_images() self.create_flash_sequence() self.lsl_output = self.create_lsl_output() def open_images(self): self.usable_images = [] image_paths = glob.glob(os.path.join(self.config.images_folder_path.get(), '*.jpg')) png_images = glob.glob(os.path.join(self.config.images_folder_path.get(), '*.png')) for png_image in png_images: image_paths.append(png_image) min_number_of_images = self.config.number_of_columns.get() * self.config.number_of_rows.get() if len(image_paths) < min_number_of_images: self.parent.print_to_console('To few images in folder: ' + self.config.images_folder_path.get()) return # Convert and resize images for image_path in image_paths: image = Image.open(image_path) resized = image.resize((self.config.imagesize.get(), self.config.imagesize.get()), Image.ANTIALIAS) Tkimage = ImageTk.PhotoImage(resized) self.usable_images.append(Tkimage) flash_img = Image.open(self.config.flash_image_path.get()) flash_img_res = flash_img.resize((self.config.imagesize.get(), self.config.imagesize.get()), Image.ANTIALIAS) self.flash_image = ImageTk.PhotoImage(flash_img_res) def show_images(self): self.open_images() if self.usable_images == []: self.parent.print_to_console('No images opened') return num_rows = self.config.number_of_rows.get() num_cols = self.config.number_of_columns.get() # Arrange images for r in range(0, num_rows): for c in range(0, num_cols): current_image = self.usable_images[r * num_cols + c] label = Label(self.image_frame, image=current_image) label.image = current_image label.grid(row=r, column=c) self.image_labels.append(label) def create_lsl_output(self): """Creates an LSL Stream outlet""" info = StreamInfo(name=self.config.lsl_streamname.get(), type='P300_Marker', channel_count=1, channel_format='int8', nominal_srate=IRREGULAR_RATE, source_id='marker_stream', handle=None) if self.config.flash_mode == 1: info.desc().append_child_value('flash_mode', 'Row and Column') elif self.config.flash_mode == 2: info.desc().append_child_value('flash_mode', 'Single Value') info.desc().append_child_value('num_rows', str(self.config.number_of_rows.get())) info.desc().append_child_value('num_cols', str(self.config.number_of_columns.get())) return StreamOutlet(info) def create_flash_sequence(self): self.flash_sequence = [] num_rows = self.config.number_of_rows.get() num_cols = self.config.number_of_columns.get() sequence_length = 700 # ms distance_between_similar_elements = int((sequence_length / self.config.break_duration.get()) + 1) if self.config.flash_mode.get() == 1: self.parent.print_to_console('CAUTION: Row and Column flash mode currently uses only random samples!') self.flash_sequence = np.random.randint(0, num_rows + num_cols, 3000) elif self.config.flash_mode.get() == 2: flash_sequence = [] maximum_number = num_rows * num_cols if maximum_number * 0.7 < distance_between_similar_elements: self.parent.print_to_console('No sequence could be created because the break duration is too short') return number_buffer = deque(maxlen=distance_between_similar_elements) for _ in range(0, MAX_FLASHES): while True: new_number = np.random.randint(0, maximum_number, 1) if bool(number_buffer.count(new_number[0])) is False: number_buffer.append(new_number[0]) flash_sequence.append(new_number[0]) break self.flash_sequence = flash_sequence def start(self): self.running = 1 self.start_flashing() self.start_btn.configure(state='disabled') self.pause_btn.configure(state='normal') def pause(self): self.running = 0 self.start_btn_text.set('Resume') self.start_btn.configure(state='normal') self.pause_btn.configure(state='disabled') def start_flashing(self): if self.sequence_number == len(self.flash_sequence): self.parent.print_to_console('All elements flashed') self.running = 0 self.sequence_number = 0 return if self.running == 0: self.parent.print_to_console('Flashing paused at sequence number ' + str(self.sequence_number)) return element_to_flash = self.flash_sequence[self.sequence_number] self.sequence_number = self.sequence_number + 1 timestamp = local_clock() print([element_to_flash + 1], timestamp) self.lsl_output.push_sample([element_to_flash + 1], timestamp) # add 1 to prevent 0 in markers if self.config.flash_mode.get() == 1: self.flash_row_or_col(element_to_flash) elif self.config.flash_mode.get() == 2: self.flash_single_element(element_to_flash) self.master.after(self.config.break_duration.get(), self.start_flashing) def change_image(self, label, img): label.configure(image=img) label.image = img def flash_row_or_col(self, rc_number): num_rows = self.config.number_of_rows.get() num_cols = self.config.number_of_columns.get() if rc_number < num_rows: for c in range(0, num_cols): cur_idx = rc_number * num_cols + c self.change_image(self.image_labels[cur_idx], self.flash_image) else: current_column = rc_number - num_rows for r in range(0, num_rows): cur_idx = current_column + r * num_cols self.change_image(self.image_labels[cur_idx], self.flash_image) self.master.after(self.config.flash_duration.get(), self.unflash_row_or_col, rc_number) def unflash_row_or_col(self, rc_number): num_rows = self.config.number_of_rows.get() num_cols = self.config.number_of_columns.get() if rc_number < num_rows: for c in range(0, num_cols): cur_idx = rc_number * num_cols + c self.change_image(self.image_labels[cur_idx], self.usable_images[cur_idx]) else: current_column = rc_number - num_rows for r in range(0, num_rows): cur_idx = current_column + r * num_cols self.change_image(self.image_labels[cur_idx], self.usable_images[cur_idx]) def flash_single_element(self, element_no): self.change_image(self.image_labels[element_no], self.flash_image) self.master.after(self.config.flash_duration.get(), self.unflash_single_element, element_no) def unflash_single_element(self, element_no): self.change_image(self.image_labels[element_no], self.usable_images[element_no]) def close_window(self): self.parent.enable_all_widgets() self.master.destroy()
def listchans(self, index=None, tagsearch="", archived=0, ob=Channel.displayname, so=asc): self.center = Frame(root, bg=config.bgcolor, width=50, height=40, padx=3, pady=3) # layout all of the main containers root.grid_rowconfigure(1, weight=1) root.grid_columnconfigure(0, weight=1) self.center.grid(row=1, sticky="nsew") channels = database.get_channels(archived, ob, so) if so == desc: so = asc else: so = desc tree = Treeview(self.center) sstring = Entry(self.center, width=config.textBoxWidth) sstring.bind( "<KeyRelease-Return>", lambda e: self.listchans(None, sstring.get(), archived, ob, so)) sstring.grid(column=0, row=0) if len(tagsearch) >= 1: sstring.focus() sstring.insert(0, tagsearch) searchbutton = Button(self.center, text="Search", command=lambda: self.listchans( None, sstring.get(), archived, ob, so)) searchbutton.grid(column=1, row=0) clearbutton = Button(self.center, text="Clear Search") clearbutton.configure(command=lambda: self.listchans(archived)) clearbutton.grid(column=3, row=0) tree["columns"] = ("one", "two") tree.column("#0", width=210, minwidth=10, stretch=YES) tree.column("one", width=350, minwidth=250, stretch=YES) tree.column("two", width=210, minwidth=10, stretch=YES) tree.heading("#0", text="Last Check", anchor=W, command=lambda: self.listchans(None, sstring.get( ), archived, Channel.lastcheck, so)) tree.heading("one", text="Channel Name", anchor=E, command=lambda: self.listchans(None, sstring.get( ), archived, Channel.displayname, so)) tree.heading("two", text="Last Published", command=lambda: self.listchans(None, sstring.get( ), archived, Channel.lastpub, so)) i = 0 tree.tag_configure('oddrow', background='#88DD88') tree.tag_configure('evenrow', background='#FFFFFF') tree.tag_configure('archivedodd', background="#88DD88", foreground="#aaaaaa") tree.tag_configure('archivedeven', background='#FFFFFF', foreground="#cccccc") for item in channels: foldername = "folder" + str(i) if i % 2 == 0: color = "evenrow" else: color = "oddrow" if item.archive == True: if i % 2 == 0: color = "archivedeven" else: color = "archivedodd" if tagsearch.lower() in str( item.displayname).lower() or tagsearch.lower() in str( item.dldir).lower() or tagsearch.lower() in str( item.yt_channelid).lower(): if item.lastpub == None: lastpub = "N/A" else: lastpub = time.ctime(item.lastpub) foldername = tree.insert("", "end", text=time.ctime(item.lastcheck), values=(item.displayname, lastpub), tags=(color, item.displayname, item.dldir, item.yt_channelid)) tree.insert(foldername, "end", text="Directory", values=(item.dldir, ), tags=(color)) tree.insert(foldername, "end", text="Last Published", values=(lastpub, ), tags=(color)) i = i + 1 vertscroll = Scrollbar(self.center) vertscroll.config(command=tree.yview) tree.config(yscrollcommand=vertscroll.set, height=20) vertscroll.grid(column=4, row=1, sticky='NSE') tree.bind("<Double-1>", self.item_selected) tree.grid(row=1, columnspan=4, sticky="NSEW") tree.focus(index) tree.selection_set(index) tree.see(index)
class SignInView(Frame): def __init__(self, master): super().__init__(master=master) self.layout_components() # Setup Callbacks self.show_register: Callable = None self.sign_in: Callable = None self.user_Entry.focus() def layout_components(self): self.pack(fill=tk.BOTH, expand=False, padx=PADX, pady=PADY) error_font = font.Font(family="Ariel", size=8) # Variables self.username = tk.StringVar() self.username.set("") self.password = tk.StringVar() self.password.set("") # Username Row user_Frame = Frame(self) user_Frame.pack(fill=tk.X) user_Label = Label(user_Frame, text="Username:"******"<FocusOut>", self.enable_sign_in) self.user_Entry.pack(fill=tk.X, padx=PADX, pady=PADY, expand=True) # Password Row pass_Frame = Frame(self) pass_Frame.pack(fill=tk.X) pass_Label = Label(pass_Frame, text="Password:"******"*") self.pass_Entry.bind("<FocusOut>", self.enable_sign_in) self.pass_Entry.pack(fill=tk.X, padx=PADX, pady=PADY, expand=True) # Error Row err_Frame = Frame(self) err_Frame.pack(fill=tk.X) self.err_Label = Label(err_Frame, text="", foreground="red", font=error_font) self.err_Label.pack(side=tk.LEFT, anchor="center", expand=True, padx=PADX, pady=PADY) # Button Row button_Frame = Frame(self) button_Frame.pack(fill=tk.X) # Cancel Button cncl_Button = Button(button_Frame, text="Cancel", command=self.cancel) cncl_Button.pack(side=tk.RIGHT, padx=PADX, pady=PADY, expand=False) # Sign in Button self.sgnn_Button = Button(button_Frame, text="Sign in", state="disabled", command=self._sign_in) self.sgnn_Button.pack(side=tk.RIGHT, padx=PADX, pady=PADY, expand=False) # Register Row register_Frame = Frame(self) register_Frame.pack(fill=tk.X) register_Button = Button(register_Frame, text="Register", command=self._show_register) register_Button.pack(side=tk.RIGHT, padx=PADX, pady=PADY, expand=False) register_Label = Label(register_Frame, text="Not a Member of ExchangeGram?") register_Label.pack(side=tk.RIGHT, padx=PADX, pady=PADY) def _sign_in(self): if self.sign_in is not None: self.sign_in() def enable_sign_in(self, event): if self.username.get() != "" and self.password.get() != "": self.sgnn_Button.configure(state="normal") else: self.sgnn_Button.configure(state="disabled") def failed_sign_in(self): self.err_Label.configure(text="Username/Password Incorrect") def cancel(self): self.username.set("") self.password.set("") self.user_Entry.focus() def _show_register(self): if self.show_register is not None: self.cancel() self.show_register() else: pass
class ErrorSurfaceFrame(LabelFrame): def __init__(self,parent): LabelFrame.__init__(self, parent, borderwidth=0) entryWidth = 7 xPad1 = 30 xPad2 = 5 self.errorXLowerLimitL = Label(self) self.errorXLowerLimitE = CustomEntry(self,width=entryWidth,justify="right") self.errorXLowerLimitL.grid(row=0,column=0,padx=(10,xPad2),pady=5,sticky="W") self.errorXLowerLimitE.grid(row=0,column=1,padx=(xPad2,xPad1),pady=5) self.errorXUpperLimitL = Label(self) self.errorXUpperLimitE = CustomEntry(self,width=entryWidth,justify="right") self.errorXUpperLimitL.grid(row=1,column=0,padx=(10,xPad2),pady=5,sticky="W") self.errorXUpperLimitE.grid(row=1,column=1,padx=(xPad2,xPad1),pady=5) self.errorYLowerLimitL = Label(self) self.errorYLowerLimitE = CustomEntry(self,width=entryWidth,justify="right") self.errorYLowerLimitL.grid(row=0,column=2,padx=(xPad1,xPad2),pady=5,sticky="W") self.errorYLowerLimitE.grid(row=0,column=3,padx=(xPad2,xPad1),pady=5) self.errorYUpperLimitL = Label(self) self.errorYUpperLimitE = CustomEntry(self,width=entryWidth,justify="right") self.errorYUpperLimitL.grid(row=1,column=2,padx=(xPad1,xPad2),pady=5,sticky="W") self.errorYUpperLimitE.grid(row=1,column=3,padx=(xPad2,xPad1),pady=5) self.errorResolutionL = Label(self,text="Resolution: ") self.errorResolutionE = CustomEntry(self,width=entryWidth,justify="right") self.errorResolutionE.insert(0,ERROR_SURFACE_DEFAULT_RESOLUTION) self.errorResolutionL.grid(row=0,column=4,padx=(xPad1,xPad2),pady=5,sticky="W") self.errorResolutionE.grid(row=0,column=5,padx=(xPad2,xPad1),pady=5,sticky="E") self.errorSurfaceB = Button(self,text=" Calculate error surface ") self.errorSurfaceB.grid(row=1,column=4,columnspan=2,padx=(xPad1,xPad1),sticky="EW") self.errorSurfaceB.configure(state=tkinter.ACTIVE) def update(self,xSymbol,ySymbol,xLL,xUL,yLL,yUL): self.xSymbol = xSymbol self.ySymbol = ySymbol self.errorXLowerLimitL.configure(text="Lower limit ("+self.xSymbol+"): ") self.errorXLowerLimitE.insertNew(xLL) self.errorXUpperLimitL.configure(text="Upper limit ("+self.xSymbol+"): ") self.errorXUpperLimitE.insertNew(xUL) self.errorYLowerLimitL.configure(text="Lower limit ("+self.ySymbol+"): ") self.errorYLowerLimitE.insertNew(yLL) self.errorYUpperLimitL.configure(text="Upper limit ("+self.ySymbol+"): ") self.errorYUpperLimitE.insertNew(yUL) def getSurfaceParameters(self): xLowerLimit = helper_functions.validateValue(self.errorXLowerLimitE.get(), self.xSymbol + " lower limit must be a positive number", "float", lowerBound=0) xUpperLimit = helper_functions.validateValue(self.errorXUpperLimitE.get(), self.xSymbol + " upper limit must be greater than the lower limit", "float", strictLowerBound=xLowerLimit) yLowerLimit = helper_functions.validateValue(self.errorYLowerLimitE.get(), self.ySymbol + " lower limit must be a positive number", "float", lowerBound=0) yUpperLimit = helper_functions.validateValue(self.errorYUpperLimitE.get(), self.ySymbol + " upper limit must be greater than the lower limit", "float", strictLowerBound=yLowerLimit) resolution = helper_functions.validateValue(self.errorResolutionE.get(), "Resolution must be " + str(ERROR_SURFACE_MIN_RESOLUTION) + " \u2264 x \u2264 " + str(ERROR_SURFACE_MAX_RESOLUTION), "int", lowerBound=ERROR_SURFACE_MIN_RESOLUTION, upperBound=ERROR_SURFACE_MAX_RESOLUTION) return [xLowerLimit,xUpperLimit,yLowerLimit,yUpperLimit,resolution] def clear(self): self.errorXLowerLimitE.insertNew("") self.errorXUpperLimitE.insertNew("") self.errorYLowerLimitE.insertNew("") self.errorYUpperLimitE.insertNew("")
class Example(Frame): def __init__(self, parent): Frame.__init__(self, parent) self.parent = parent self.initUI() def initUI(self): self.parent.title("") #self.style = Style() #self.style.theme_use("clam") #self.pack(fill=BOTH, expand = 1) self.quitbutton = Button(self, text="Quit", command=lambda: self.quit()) self.quitbutton.grid(row=3, column=1, pady=4) self.labelErrorPointer = Label(self, text="◀") self.labellist = [] self.entrylist = [] self.verifylist = [] self.misclist = [] self.optionCreate = "Create" self.optionUpcoming = "Upcoming" self.optionPast = "Past" self.prevmode = self.optionCreate self.curmode = self.optionCreate self.optionvar = tkinter.StringVar(self) self.optionvar.trace("w", self.permaloop) self.optionvar.set(self.optionCreate) self.option = OptionMenu(self, self.optionvar, self.optionCreate, self.optionUpcoming, self.optionPast) self.optionpostmodevar = tkinter.StringVar(self) self.optionpostmodevar.trace("w", self.permaloop) self.optionpostmodevar.set('url') self.optionpostmode = OptionMenu(self, self.optionpostmodevar, 'url', 'text') self.labelText = Label(self, text='Selftext:') self.entryText = Text(self) self.labelURL = Label(self, text='URL:') self.entryURL = Entry(self) self.entryURL.configure(width=60) self.sql = sqlite3.connect('sql.db') print('Loaded SQL Database') self.cur = self.sql.cursor() self.cur.execute( 'CREATE TABLE IF NOT EXISTS upcoming(ID TEXT, SUBREDDIT TEXT, TIME INT, TITLE TEXT, URL TEXT, BODY TEXT)' ) self.cur.execute( 'CREATE TABLE IF NOT EXISTS past(ID TEXT, SUBREDDIT TEXT, TIME INT, TITLE TEXT, URL TEXT, BODY TEXT, POSTLINK TEXT)' ) self.cur.execute( 'CREATE TABLE IF NOT EXISTS internal(NAME TEXT, ID INT)') print('Loaded Completed table') self.cur.execute('SELECT * FROM internal') f = self.cur.fetchone() if not f: print('Database is new. Adding ID counter') self.cur.execute('INSERT INTO internal VALUES(?, ?)', ['counter', 1]) self.idcounter = 1 else: self.idcounter = f[1] print('Current ID counter: ' + str(self.idcounter)) self.sql.commit() sw = self.parent.winfo_screenwidth() sh = self.parent.winfo_screenheight() w = 853 h = 480 x = (sw - w) / 2 y = (sh - h) / 2 self.parent.geometry('%dx%d+%d+%d' % (w, h, x, y - 50)) self.login() def login(self): try: self.quitbutton.grid_forget() self.quitbutton.grid(row=9000, column=0, columnspan=20) self.option.grid(row=1, column=0, columnspan=80, pady=8) self.updategui(fullclean=True) except praw.errors.InvalidUserPass: pass print('Invalid username or password') self.entryPassword.delete(0, 200) self.labelErrorPointer.grid(row=1, column=2) def permaloop(self, *args): self.curmode = self.optionvar.get() print('Was: ' + self.prevmode + ' | Now: ' + self.curmode) if self.curmode != self.prevmode: self.prevmode = self.curmode self.updategui(fullclean=True) else: self.updategui(False) def getTime(self, bool): timeNow = datetime.datetime.now(datetime.timezone.utc) timeUnix = timeNow.timestamp() if bool == False: return timeNow else: return timeUnix def addentrytobase(self, subreddit, title, url="", body="", mode="", ptime=""): curtime = round(self.getTime(True)) try: t = self.entryMo.get() + ' ' + self.entryDa.get( ) + ' ' + self.entryYr.get() + ' ' + self.entryHH.get( ) + ':' + self.entryMM.get() plandate = datetime.datetime.strptime(t, "%B %d %Y %H:%M") plandate = plandate.timestamp() except ValueError: print('Invalid Day') return False if mode == 'url': url = self.entryURL.get() body = "" if 'http://' not in url and 'https://' not in url: print('Please enter a proper URL') return False if mode == 'text': body = self.entryText.get("1.0", "end") url = "" if plandate < curtime: print('Please enter a time in the future') return False if not all(char in string.ascii_letters + string.digits + '_-' for char in subreddit): print('Subreddit contains invalid characters') return False if len(subreddit) == 0: print('You must enter a subreddit') return False if len(title) == 0: print('You must enter a title') return False if len(title) > 300: print('Title is too long. ' + str(len(title)) + '/300 char max') return False if len(body) > 15000: print('Body is too long. ' + str(len(body)) + '/15,000 char max') print('Timestamp:', plandate) self.cur.execute( 'INSERT INTO upcoming VALUES(?, ?, ?, ?, ?, ?)', [self.idcounter, subreddit, int(plandate), title, url, body]) self.idcounter += 1 self.cur.execute('UPDATE internal SET ID=? WHERE NAME=?', [self.idcounter, 'counter']) self.sql.commit() print('\nPost Saved!') print(self.idcounter, subreddit, self.timestamptoday(int(plandate))) print(title) print(url, body) print() self.entryText.delete("1.0", "end") self.entryURL.delete(0, 'end') self.entryTitle.delete(0, 'end') #self.updategui(halfclean=True) def timestamptoday(self, timestamp): d = datetime.datetime.fromtimestamp(timestamp) info = datetime.datetime.strftime(d, "%b %d %H:%M") return info def dropentryfrombase(self, ID): if '-' not in ID: try: ID = int(ID) l = [ID] except ValueError: print('You must enter a number') return else: if ID.count('-') == 1: try: ID = ID.replace(' ', '') ID = ID.split('-') ID[0] = int(ID[0]) ID[1] = int(ID[1]) if ID[1] > ID[0]: l = list(range(ID[0], ID[1] + 1)) else: return except ValueError: return for item in l: item = str(item) print('Dropping Item ' + item + ' from Upcoming') self.cur.execute('DELETE FROM upcoming WHERE ID=?', [item]) self.sql.commit() self.updategui(fullclean=True) def printbasetofile(self, db): filea = open(db + '.txt', 'w') if db == 'past': self.cur.execute('SELECT * FROM past') if db == 'upcoming': self.cur.execute('SELECT * FROM upcoming') f = self.cur.fetchall() print('Printed ' + db + ' unimpeded to file') for item in f: i = list(item) i[2] = self.timestamptoday(i[2]) i.remove('') print(str(i)[1:-1], file=filea) filea.close() def updategui(self, halfclean=False, fullclean=False): if self.curmode == self.optionCreate: try: print(self.optionpostmodevar.get()) if self.optionpostmodevar.get() == 'url': self.entryText.delete("1.0", 'end') self.labelText.grid_forget() self.entryText.grid_forget() self.labelURL.grid(row=8, column=0, columnspan=30) self.entryURL.grid(row=9, column=0, columnspan=12, pady=10) if self.optionpostmodevar.get() == 'text': self.entryURL.delete(0, 'end') self.labelURL.grid_forget() self.entryURL.grid_forget() self.labelText.grid(row=8, column=0, columnspan=30) self.entryText.configure(width=40, height=8) self.entryText.grid(row=9, column=0, columnspan=12) except AttributeError: pass if fullclean == True: print('Cleaning GUI') for item in self.labellist: item.grid_forget() for item in self.entrylist: item.grid_forget() for item in self.verifylist: item.grid_forget() for item in self.misclist: item.grid_forget() self.labellist = [] self.entrylist = [] self.verifylist = [] self.misclist = [] if self.curmode == self.optionCreate: self.newrowindex = 6 self.labelSubreddit = Label(self, text="Subreddit: /r/") self.labelTitle = Label(self, text="Post title: ") self.entrySubreddit = Entry(self) self.entryTitle = Entry(self) self.labelHH = Label(self, text="Schedule time (Local timezone):") nowlist = datetime.datetime.strftime(datetime.datetime.now(), "%B %d %Y %H %M").split() self.entryMo = Spinbox(self, width=9, values=('January', 'February', 'March', 'April', 'May', 'June', 'July', \ 'August', 'September', 'October', 'November', 'December')) self.entryMo.delete(0, 'end') self.entryMo.insert(0, nowlist[0]) self.entryDa = Spinbox(self, width=2, from_=1, to=31) self.entryDa.delete(0, 'end') self.entryDa.insert(0, nowlist[1]) self.entryYr = Spinbox(self, width=4, from_=2014, to=2500) self.entryYr.delete(0, 'end') self.entryYr.insert(0, nowlist[2]) self.entryHH = Spinbox(self, from_=0, to=23, width=2) self.entryHH.delete(0, 'end') self.entryHH.insert(0, nowlist[3]) self.entryMM = Spinbox(self, from_=0, to=59, width=2) self.entryMM.delete(0, 'end') self.entryMM.insert(0, nowlist[4]) self.buttonAddentry = Button(self, text='Save', command=lambda: self.addentrytobase(self.entrySubreddit.get(), self.entryTitle.get(),\ mode=self.optionpostmodevar.get())) self.misclist.append(self.labelSubreddit) self.misclist.append(self.entrySubreddit) self.misclist.append(self.labelHH) self.misclist.append(self.entryHH) self.misclist.append(self.entryMM) self.misclist.append(self.entryMo) self.misclist.append(self.entryDa) self.misclist.append(self.entryYr) self.misclist.append(self.labelTitle) self.misclist.append(self.entryTitle) self.misclist.append(self.buttonAddentry) self.misclist.append(self.optionpostmode) self.misclist.append(self.labelText) self.misclist.append(self.entryText) self.misclist.append(self.labelURL) self.misclist.append(self.entryURL) self.labelSubreddit.grid(row=2, column=0, sticky="e") self.labelTitle.grid(row=3, column=0, sticky="e") self.entrySubreddit.grid(row=2, column=1, columnspan=3, sticky="w") self.entryTitle.grid(row=3, column=1, columnspan=3, sticky="w") self.entryMo.grid(row=4, column=1, sticky="e") self.entryDa.grid(row=4, column=2) self.entryYr.grid(row=4, column=3) self.labelHH.grid(row=4, column=0, sticky="se", pady=5) self.entryHH.grid(row=5, column=1, sticky="e") self.entryMM.grid(row=5, column=2, sticky="w") self.optionpostmode.grid(row=6, column=0, columnspan=20, pady=10) self.buttonAddentry.grid(row=200, column=0, columnspan=20) if self.curmode == self.optionUpcoming: self.cur.execute('SELECT * FROM upcoming') dobutton = True if self.curmode == self.optionPast: self.cur.execute('SELECT * FROM past') dobutton = False if self.curmode == self.optionPast or self.curmode == self.optionUpcoming: self.listboxId = Listbox(self) self.listboxId.configure(width=118, height=20, font=("Courier 8")) self.misclist.append(self.listboxId) self.listboxScroller = Scrollbar(self, orient='horizontal', command=self.listboxId.xview) self.listboxScroller.grid(row=4, column=0, columnspan=900) self.listboxId.grid(row=3, column=0, columnspan=10) self.listboxId.configure( xscrollcommand=self.listboxScroller.set) self.misclist.append(self.listboxScroller) self.buttonPrinter = Button(self, text="Print to .txt file") if self.curmode == self.optionPast: self.buttonPrinter.configure( command=lambda: self.printbasetofile('past')) if self.curmode == self.optionUpcoming: self.buttonPrinter.configure( command=lambda: self.printbasetofile('upcoming')) self.buttonPrinter.grid(row=6, column=0, columnspan=90) self.misclist.append(self.buttonPrinter) if dobutton == True: self.entryDelete = Entry(self) self.buttonDelete = Button( self, text="Delete Item: ", command=lambda: self.dropentryfrombase(self.entryDelete .get())) self.buttonDelete.grid(row=5, column=0, sticky='e') self.entryDelete.grid(row=5, column=1, sticky='w') self.misclist.append(self.entryDelete) self.misclist.append(self.buttonDelete) fetched = self.cur.fetchall() for item in fetched: info = self.timestamptoday(item[2]) if item[4] == '': infx = item[5] if item[5] == '': infx = item[4] if self.curmode == self.optionPast: infy = '.' + item[6] else: infy = '' self.listboxId.insert('end', \ item[0] + '.'*(6 - len(item[0])) \ + item[1][:10] + '.'*(12 - len(item[1][:10])) \ + info + '.'*(15 - len(info[:14])) \ + item[3][:18] + '.'*(20 - len(item[3][:14])) \ + infx[:45] + '.'*(47-len(infx[:45])) \ + infy) def morerows(self, label, columnm, columnn, limit, *args): self.redditlabel = Label(self, text=label) self.redditlabel.grid(row=self.newrowindex, column=columnm, sticky="e") self.labellist.append(self.redditlabel) self.redditentry = Entry(self) self.redditentry.grid(row=self.newrowindex, column=columnn, columnspan=9) self.entrylist.append(self.redditentry) self.newrowindex += 1 if self.newrowindex >= limit: self.morerowbutton.grid_forget() print(self.newrowindex)
class Example(Frame): def __init__(self, parent): Frame.__init__(self, parent) self.parent = parent self.initUI() def initUI(self): self.parent.title("") # self.style = Style() # self.style.theme_use("clam") # self.pack(fill=BOTH, expand = 1) self.quitbutton = Button(self, text="Quit", command=lambda: self.quit()) self.quitbutton.grid(row=3, column=1, pady=4) self.labelErrorPointer = Label(self, text="◀") self.labellist = [] self.entrylist = [] self.verifylist = [] self.misclist = [] self.optionCreate = "Create" self.optionUpcoming = "Upcoming" self.optionPast = "Past" self.prevmode = self.optionCreate self.curmode = self.optionCreate self.optionvar = tkinter.StringVar(self) self.optionvar.trace("w", self.permaloop) self.optionvar.set(self.optionCreate) self.option = OptionMenu(self, self.optionvar, self.optionCreate, self.optionUpcoming, self.optionPast) self.optionpostmodevar = tkinter.StringVar(self) self.optionpostmodevar.trace("w", self.permaloop) self.optionpostmodevar.set("url") self.optionpostmode = OptionMenu(self, self.optionpostmodevar, "url", "text") self.labelText = Label(self, text="Selftext:") self.entryText = Text(self) self.labelURL = Label(self, text="URL:") self.entryURL = Entry(self) self.entryURL.configure(width=60) self.sql = sqlite3.connect("sql.db") print("Loaded SQL Database") self.cur = self.sql.cursor() self.cur.execute( "CREATE TABLE IF NOT EXISTS upcoming(ID TEXT, SUBREDDIT TEXT, TIME INT, TITLE TEXT, URL TEXT, BODY TEXT)" ) self.cur.execute( "CREATE TABLE IF NOT EXISTS past(ID TEXT, SUBREDDIT TEXT, TIME INT, TITLE TEXT, URL TEXT, BODY TEXT, POSTLINK TEXT)" ) self.cur.execute("CREATE TABLE IF NOT EXISTS internal(NAME TEXT, ID INT)") print("Loaded Completed table") self.cur.execute("SELECT * FROM internal") f = self.cur.fetchone() if not f: print("Database is new. Adding ID counter") self.cur.execute("INSERT INTO internal VALUES(?, ?)", ["counter", 1]) self.idcounter = 1 else: self.idcounter = f[1] print("Current ID counter: " + str(self.idcounter)) self.sql.commit() sw = self.parent.winfo_screenwidth() sh = self.parent.winfo_screenheight() w = 853 h = 480 x = (sw - w) / 2 y = (sh - h) / 2 self.parent.geometry("%dx%d+%d+%d" % (w, h, x, y - 50)) self.login() def login(self): try: self.quitbutton.grid_forget() self.quitbutton.grid(row=9000, column=0, columnspan=20) self.option.grid(row=1, column=0, columnspan=8, pady=8) self.updategui(fullclean=True) except praw.errors.InvalidUserPass: pass print("Invalid username or password") self.entryPassword.delete(0, 200) self.labelErrorPointer.grid(row=1, column=2) def permaloop(self, *args): self.curmode = self.optionvar.get() print("Was: " + self.prevmode + " | Now: " + self.curmode) if self.curmode != self.prevmode: self.prevmode = self.curmode self.updategui(fullclean=True) else: self.updategui(False) def getTime(self, bool): timeNow = datetime.datetime.now(datetime.timezone.utc) timeUnix = timeNow.timestamp() if bool == False: return timeNow else: return timeUnix def addentrytobase(self, subreddit, title, url="", body="", mode="", ptime=""): curtime = round(self.getTime(True)) try: t = ( self.entryMo.get() + " " + self.entryDa.get() + " " + self.entryYr.get() + " " + self.entryHH.get() + ":" + self.entryMM.get() ) plandate = datetime.datetime.strptime(t, "%B %d %Y %H:%M") plandate = plandate.timestamp() except ValueError: print("Invalid Day") return False if mode == "url": url = self.entryURL.get() body = "" if "http://" not in url and "https://" not in url: print("Please enter a proper URL") return False if mode == "text": body = self.entryText.get("1.0", "end") url = "" if plandate < curtime: print("Please enter a time in the future") return False if not all(char in string.ascii_letters + string.digits + "_-" for char in subreddit): print("Subreddit contains invalid characters") return False if len(subreddit) == 0: print("You must enter a subreddit") return False if len(title) == 0: print("You must enter a title") return False if len(title) > 300: print("Title is too long. " + str(len(title)) + "/300 char max") return False if len(body) > 15000: print("Body is too long. " + str(len(body)) + "/15,000 char max") print("Timestamp:", plandate) self.cur.execute( "INSERT INTO upcoming VALUES(?, ?, ?, ?, ?, ?)", [self.idcounter, subreddit, int(plandate), title, url, body], ) self.idcounter += 1 self.cur.execute("UPDATE internal SET ID=? WHERE NAME=?", [self.idcounter, "counter"]) self.sql.commit() print("Post Saved!") self.entryText.delete("1.0", "end") self.entryURL.delete(0, "end") self.entryTitle.delete(0, "end") # self.updategui(halfclean=True) def dropentryfrombase(self, ID): try: ID = int(ID) except ValueError: print("You must enter a number") return print("Dropping Item " + str(ID) + " from Upcoming") self.cur.execute("DELETE FROM upcoming WHERE ID=?", [ID]) self.sql.commit() self.updategui(fullclean=True) def printbasetofile(self, db): filea = open(db + ".txt", "w") if db == "past": self.cur.execute("SELECT * FROM past") if db == "upcoming": self.cur.execute("SELECT * FROM upcoming") f = self.cur.fetchall() print("Printed " + db + " unimpeded to file") for item in f: i = list(item) d = datetime.datetime.fromtimestamp(i[2]) i[2] = datetime.datetime.strftime(d, "%b %d %H:%M") i.remove("") print(str(i)[1:-1], file=filea) filea.close() def updategui(self, halfclean=False, fullclean=False): if self.curmode == self.optionCreate: try: print(self.optionpostmodevar.get()) if self.optionpostmodevar.get() == "url": self.entryText.delete("1.0", "end") self.labelText.grid_forget() self.entryText.grid_forget() self.labelURL.grid(row=8, column=0, columnspan=30) self.entryURL.grid(row=9, column=0, columnspan=12, pady=10) if self.optionpostmodevar.get() == "text": self.entryURL.delete(0, "end") self.labelURL.grid_forget() self.entryURL.grid_forget() self.labelText.grid(row=8, column=0, columnspan=30) self.entryText.configure(width=40, height=8) self.entryText.grid(row=9, column=0, columnspan=12) except AttributeError: pass if fullclean == True: print("Cleaning GUI") for item in self.labellist: item.grid_forget() for item in self.entrylist: item.grid_forget() for item in self.verifylist: item.grid_forget() for item in self.misclist: item.grid_forget() self.labellist = [] self.entrylist = [] self.verifylist = [] self.misclist = [] if self.curmode == self.optionCreate: self.newrowindex = 6 self.labelSubreddit = Label(self, text="Subreddit: /r/") self.labelTitle = Label(self, text="Post title: ") self.entrySubreddit = Entry(self) self.entryTitle = Entry(self) self.labelHH = Label(self, text="Schedule time (Local timezone):") nowlist = datetime.datetime.strftime(datetime.datetime.now(), "%B %d %Y %H %M").split() self.entryMo = Spinbox( self, width=9, values=( "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", ), ) self.entryMo.delete(0, "end") self.entryMo.insert(0, nowlist[0]) self.entryDa = Spinbox(self, width=2, from_=1, to=31) self.entryDa.delete(0, "end") self.entryDa.insert(0, nowlist[1]) self.entryYr = Spinbox(self, width=4, from_=2014, to=2500) self.entryYr.delete(0, "end") self.entryYr.insert(0, nowlist[2]) self.entryHH = Spinbox(self, from_=0, to=23, width=2) self.entryHH.delete(0, "end") self.entryHH.insert(0, nowlist[3]) self.entryMM = Spinbox(self, from_=0, to=59, width=2) self.entryMM.delete(0, "end") self.entryMM.insert(0, nowlist[4]) self.buttonAddentry = Button( self, text="Save", command=lambda: self.addentrytobase( self.entrySubreddit.get(), self.entryTitle.get(), mode=self.optionpostmodevar.get() ), ) self.misclist.append(self.labelSubreddit) self.misclist.append(self.entrySubreddit) self.misclist.append(self.labelHH) self.misclist.append(self.entryHH) self.misclist.append(self.entryMM) self.misclist.append(self.entryMo) self.misclist.append(self.entryDa) self.misclist.append(self.entryYr) self.misclist.append(self.labelTitle) self.misclist.append(self.entryTitle) self.misclist.append(self.buttonAddentry) self.misclist.append(self.optionpostmode) self.misclist.append(self.labelText) self.misclist.append(self.entryText) self.misclist.append(self.labelURL) self.misclist.append(self.entryURL) self.labelSubreddit.grid(row=2, column=0, sticky="e") self.labelTitle.grid(row=3, column=0, sticky="e") self.entrySubreddit.grid(row=2, column=1, columnspan=3, sticky="w") self.entryTitle.grid(row=3, column=1, columnspan=3, sticky="w") self.entryMo.grid(row=4, column=1, sticky="e") self.entryDa.grid(row=4, column=2) self.entryYr.grid(row=4, column=3) self.labelHH.grid(row=4, column=0, sticky="se", pady=5) self.entryHH.grid(row=5, column=1, sticky="e") self.entryMM.grid(row=5, column=2, sticky="w") self.optionpostmode.grid(row=6, column=0, columnspan=20, pady=10) self.buttonAddentry.grid(row=200, column=0, columnspan=20) if self.curmode == self.optionUpcoming: self.cur.execute("SELECT * FROM upcoming") dobutton = True if self.curmode == self.optionPast: self.cur.execute("SELECT * FROM past") dobutton = False if self.curmode == self.optionPast or self.curmode == self.optionUpcoming: self.listboxId = Listbox(self) self.listboxId.configure(width=118, height=20, font=("Courier 8")) self.misclist.append(self.listboxId) self.listboxScroller = Scrollbar(self, orient="horizontal", command=self.listboxId.xview) self.listboxScroller.grid(row=4, column=0, columnspan=900) self.listboxId.grid(row=3, column=0, columnspan=10) self.listboxId.configure(xscrollcommand=self.listboxScroller.set) self.misclist.append(self.listboxScroller) self.buttonPrinter = Button(self, text="Print to .txt file") if self.curmode == self.optionPast: self.buttonPrinter.configure(command=lambda: self.printbasetofile("past")) if self.curmode == self.optionUpcoming: self.buttonPrinter.configure(command=lambda: self.printbasetofile("upcoming")) self.buttonPrinter.grid(row=6, column=0, columnspan=90) self.misclist.append(self.buttonPrinter) if dobutton == True: self.entryDelete = Entry(self) self.buttonDelete = Button( self, text="Delete Item: ", command=lambda: self.dropentryfrombase(self.entryDelete.get()) ) self.buttonDelete.grid(row=5, column=0, sticky="e") self.entryDelete.grid(row=5, column=1, sticky="w") self.misclist.append(self.entryDelete) self.misclist.append(self.buttonDelete) fetched = self.cur.fetchall() for item in fetched: d = datetime.datetime.fromtimestamp(item[2]) info = datetime.datetime.strftime(d, "%b %d %H:%M") if item[4] == "": infx = item[5] if item[5] == "": infx = item[4] if self.curmode == self.optionPast: infy = "." + item[6] else: infy = "" self.listboxId.insert( "end", item[0] + "." * (6 - len(item[0])) + item[1][:10] + "." * (12 - len(item[1][:10])) + info + "." * (15 - len(info[:14])) + item[3][:18] + "." * (20 - len(item[3][:14])) + infx[:45] + "." * (47 - len(infx[:45])) + infy, ) def morerows(self, label, columnm, columnn, limit, *args): self.redditlabel = Label(self, text=label) self.redditlabel.grid(row=self.newrowindex, column=columnm, sticky="e") self.labellist.append(self.redditlabel) self.redditentry = Entry(self) self.redditentry.grid(row=self.newrowindex, column=columnn, columnspan=9) self.entrylist.append(self.redditentry) self.newrowindex += 1 if self.newrowindex >= limit: self.morerowbutton.grid_forget() print(self.newrowindex)
class Application(tkinter.Tk): def __init__(self): tkinter.Tk.__init__(self) # Menu Setup menuBar = Menu(self) fileMenu = Menu(menuBar, tearoff=False) fileMenu.add_command(label="Open...", command=self.loadSequence) fileMenu.add_command(label="Close", command=self.closeSequence) fileMenu.add_separator() fileMenu.add_command(label="Exit", command=self.destroy) aboutMenu = Menu(menuBar, tearoff=False) aboutMenu.add_command(label="Help", command=self.showHelp) aboutMenu.add_command(label="About...", command=self.showAbout) menuBar.add_cascade(label="File", menu=fileMenu) menuBar.add_cascade(label="About", menu=aboutMenu) # Window Setup self.title("Rotostitch " + __version__) self.config(menu=menuBar) self.iconbitmap(default=os.path.join(RESOURCE_DIR, "rotostitch-icon.ico")) masterFrame = Frame(self) masterFrame.pack(expand=1, fill=Tkc.BOTH, padx=2, pady=2) self.status = StatusBar(self) self.status.pack(anchor=Tkc.W, fill=Tkc.X, side=Tkc.BOTTOM) # Image review panels frame imgFrame = Frame(masterFrame, borderwidth=2, relief=Tkc.GROOVE) imgFrame.pack(expand=1, fill=Tkc.BOTH) imgFrame.columnconfigure(0, weight=1) imgFrame.columnconfigure(1, weight=0) imgFrame.columnconfigure(2, weight=1) imgFrame.rowconfigure(0, weight=1) imgFrame.rowconfigure(1, weight=0, pad=3) # Creation options frame settingsFrame = Frame(masterFrame, borderwidth=2, relief=Tkc.GROOVE) settingsFrame.pack(fill=Tkc.X) settingsFrame.columnconfigure(1, weight=1, pad=4) create = Button(masterFrame, text="Create", command=self.merge) create.pack(anchor='se', pady=2) self.previewStart = ZoomImage(imgFrame, width=200, height=200, borderwidth=2, relief=Tkc.RIDGE, cursor="crosshair") self.previewStart.grid(row=0, column=0, sticky=Tkc.NSEW) self.previewEnd = ZoomImage(imgFrame, width=200, height=200, borderwidth=2, relief=Tkc.RIDGE) self.previewEnd.grid(row=0, column=2, sticky=Tkc.NSEW) self.previewStart.bind("<Button>", self._startPreviewClicked) self.previewStart.bind("<<Dragged>>", self._previewDragged) self.previewEnd.bind("<<Dragged>>", self._previewDragged) # Binding just the previews to the MouseWheel event should work but doesn't. # The workaround is to bind everything to the mousewheel event # and filter it for just our previews in our callback... self.bind_all("<MouseWheel>", self.previewsScrollZoom) zoomFrame = Frame(imgFrame) zoomFrame.grid(row=0, column=1) self.zoomInImg = PhotoImage(file=os.path.join(RESOURCE_DIR, "plus.gif")) zoomIn = Button(zoomFrame, image=self.zoomInImg, command=self.previewsZoomIn) zoomIn.pack() self.zoomResetImg = PhotoImage(file=os.path.join(RESOURCE_DIR, "refresh.gif")) zoomReset = Button(zoomFrame, image=self.zoomResetImg, command=self.previewsResetZoom) zoomReset.pack() self.zoomOutImg = PhotoImage(file=os.path.join(RESOURCE_DIR, "minus.gif")) zoomOut = Button(zoomFrame, image=self.zoomOutImg, command=self.previewsZoomOut) zoomOut.pack() self.differenceImg = PhotoImage(file=os.path.join(RESOURCE_DIR, "difference.gif")) self.differenceBtn = ToggleButton(imgFrame, image=self.differenceImg) self.differenceBtn.grid(row=1, column=1) self.differenceBtn.bind("<Button1-ButtonRelease>", self.toggleDifference) startSpinFrame = Frame(imgFrame) startSpinFrame.grid(row=1, column=0) endSpinFrame = Frame(imgFrame) endSpinFrame.grid(row=1, column=2) startLabel = Label(startSpinFrame, text="Start Frame:") startLabel.pack(side=Tkc.LEFT) self.startSpin = Spinbox(startSpinFrame) self.startSpin.pack() self.startSpin.changedCallback = self.updateStartPreview endLabel = Label(endSpinFrame, text="End Frame:") endLabel.pack(side=Tkc.LEFT) self.endSpin = Spinbox(endSpinFrame) self.endSpin.pack() self.endSpin.changedCallback = self.updateEndPreview widthHeightFrame = Frame(settingsFrame) widthHeightFrame.grid(row=0, column=1, columnspan=2, sticky=Tkc.E+Tkc.W) widthLabel = Label(settingsFrame, text="Width:") widthLabel.grid(row=0, column=0, sticky=Tkc.W) self.activePic = PhotoImage(file=os.path.join(RESOURCE_DIR, "go.gif")) self.widthSetButton = Button(widthHeightFrame, text="Set", command=self.activateSetWidth, image=self.activePic, compound=Tkc.LEFT) self.widthSetButton.grid(row=0, column=1, sticky=Tkc.W) heightLabel = Label(widthHeightFrame, text="Height:") heightLabel.grid(row=0, column=2, padx=10, sticky=Tkc.E) self.unactivePic = PhotoImage(file=os.path.join(RESOURCE_DIR, "stop.gif")) self.heightSetButton = Button(widthHeightFrame, text="Set", command=self.activateSetHeight, image=self.unactivePic, compound=Tkc.LEFT) self.heightSetButton.grid(row=0, column=3, sticky=Tkc.W) rotationLabel = Label(settingsFrame, text="Rotation:") rotationLabel.grid(row=1, column=0, sticky=Tkc.W) rotFrame = Frame(settingsFrame) rotFrame.grid(row=1, column=1, sticky=Tkc.W) self.rotVar = IntVar() self.rotVar.set(1) rotLeft = Radiobutton(rotFrame, text="Counter Clockwise", value=1, variable=self.rotVar) rotLeft.pack(side=Tkc.LEFT, padx=4) rotRight = Radiobutton(rotFrame, text="Clockwise", value=2, variable=self.rotVar) rotRight.pack(padx=4) outputLabel = Label(settingsFrame, text="Save As:") outputLabel.grid(row=2, column=0, sticky=Tkc.W) self.outputPathVar = StringVar() outputEntry = Entry(settingsFrame, textvariable=self.outputPathVar) outputEntry.grid(row=2, column=1, sticky=Tkc.EW) self.outputImg = PhotoImage(file=os.path.join(RESOURCE_DIR, "folder.gif")) outputSearch = Button(settingsFrame, image=self.outputImg, command=self.setSavePath) outputSearch.grid(row=2, column=2, sticky=Tkc.W) # Object variables self.sequenceLoaded = False self.currentSequence = None self.startImage = None self.endImage = None self.differenceOn = False self.overlayTag = "OverlayItems" self.settingWidth = True self.settingHeight = False self.width = {'start': Coord(), 'end': Coord()} self.height = {'start1': Coord(), 'end1': Coord(), 'start2': Coord(), 'end2': Coord()} def showAbout(self): pass def showHelp(self): pass def loadSequence(self): path = filedialog.askopenfilename(title="Select image from image sequence...") if path and os.path.isfile(path): self.status.showText("Loading sequence: '{}'".format(path)) s = self.currentSequence = FrameSequence(path) self.sequenceLoaded = True self.setupForSequence() self.status.showText("Finished loading: '{}'".format(''.join([s.name, "#" * s.frameDigits, s.ext]))) else: self.status.showText("No sequence at: '{}'".format(path)) def closeSequence(self): self.currentSequence = None self.sequenceLoaded = False self.previewStart.delete(self.overlayTag) self.width = {'start': Coord(), 'end': Coord()} self.height = {'start1': Coord(), 'end1': Coord(), 'start2': Coord(), 'end2': Coord()} self.previewStart.reset() self.previewEnd.reset() self.startSpin.reset() self.endSpin.reset() def setSavePath(self): path = filedialog.asksaveasfilename(title="Select save location...") if path: self.outputPathVar.set(path) def setupForSequence(self): s = self.currentSequence if self.differenceOn: self.differenceBtn.setSelected(False) self.differenceOn = False start = s.start end = s.end self.setStartPreview(start) self.setEndPreview(end) self.startSpin.min(start) self.startSpin.set(start) self.endSpin.max(end) self.endSpin.set(end) self.startSpin.max(end) self.endSpin.min(start) def setStartPreview(self, frame): self.startImage = self.currentSequence.image(frame) if self.startImage: if self.startImage.mode != "RGBA": self.startImage = self.startImage.convert("RGBA") self._refreshStartPreview() def _refreshStartPreview(self): im = self.startImage.copy() if self.differenceOn: endIm = self.previewEnd.getImage() im = ImageChops.difference(im, endIm) im.putalpha(255) self.previewStart.setImage(im) def setEndPreview(self, frame): self.endImage = self.currentSequence.image(frame) if self.endImage: if self.endImage.mode != "RGBA": self.endImage = self.endImage.convert("RGBA") self._refreshEndPreview() def _refreshEndPreview(self): im = self.endImage if self.differenceOn: dif = ImageChops.difference(self.startImage, im) dif.putalpha(255) self.previewStart.setImage(dif) self.previewEnd.setImage(im) def updateStartPreview(self): i = self.startSpin.get() self.endSpin.min(i) if self.sequenceLoaded: self.setStartPreview(i) def updateEndPreview(self): i = self.endSpin.get() self.startSpin.max(i) if self.sequenceLoaded: self.setEndPreview(i) def previewsScrollZoom(self, event): w = self.winfo_containing(event.x_root, event.y_root) if w == self.previewStart or w == self.previewEnd: center = (event.x_root - w.winfo_rootx(), event.y_root - w.winfo_rooty()) if event.delta < 0: self.adjustZoom("out", center) else: self.adjustZoom("in", center) def previewsZoomIn(self): self.adjustZoom("in") def previewsZoomOut(self): self.adjustZoom("out") def adjustZoom(self, direction, center=None): if self.sequenceLoaded: x, y = center if center else (self.previewStart.size[0] / 2, self.previewStart.size[1] / 2) if direction == "in": self.previewStart.zoomIn(center) self.previewEnd.zoomIn(center) self.previewStart.scale(self.overlayTag, x, y, 2.0, 2.0) elif direction == "out": self.previewStart.zoomOut(center) self.previewEnd.zoomOut(center) self.previewStart.scale(self.overlayTag, x, y, .5, .5) self.drawOverlay() def previewsResetZoom(self): self.previewStart.resetZoom() self.previewEnd.resetZoom() self.drawOverlay() def _previewDragged(self, event): if event.widget == self.previewStart: self.previewEnd.moveImage(event.x, event.y) else: self.previewStart.moveImage(event.x, event.y) self.previewStart.move(self.overlayTag, -event.x, -event.y) def toggleDifference(self, event): if not self.differenceBtn.selected(): self.differenceOn = True else: self.differenceOn = False self.setDifference() def setDifference(self): if self.sequenceLoaded: if self.differenceOn: startIm = self.previewStart.getImage() endIm = self.previewEnd.getImage() dif = ImageChops.difference(startIm, endIm) dif.putalpha(255) self.previewStart.setImage(dif) self.differenceOn = True else: self.previewStart.setImage(self.startImage) self.differenceOn = False def activateSetWidth(self): self.widthSetButton.configure(image=self.activePic) self.heightSetButton.configure(image=self.unactivePic) self.settingWidth = True self.settingHeight = False def activateSetHeight(self): self.widthSetButton.configure(image=self.unactivePic) self.heightSetButton.configure(image=self.activePic) self.settingWidth = False self.settingHeight = True def _startPreviewClicked(self, event): if self.sequenceLoaded and (event.num == 1 or event.num == 3): x, y = self.previewStart.screenToWorld((event.x, event.y)) if self.settingWidth: self._setWidth(event.num, x, y) elif self.settingHeight: self._setHeight(event.num, x, y, event.state) def _setWidth(self, button, x, y): if button == 1: self.width['start'].set(x, y) else: self.width['end'].set(x, y) self.drawOverlay() def _setHeight(self, button, x, y, mod): shift = 0x0001 if button == 1: if mod & shift == shift: self.height['start2'].set(x, y) else: self.height['start1'].set(x, y) else: if mod & shift == shift: self.height['end2'].set(x, y) else: self.height['end1'].set(x, y) self.drawOverlay() def drawOverlay(self): prev = self.previewStart # Draw the width line and center line x, y = prev.worldToScreen(self.width['start'].get()) # Width line and dot start point x2, y2 = prev.worldToScreen(self.width['end'].get()) # Width line and dot end point x3 = (x + x2) / 2 # Rotation center line x __, y3 = prev.worldToScreen((0, 0)) # Rotation center line top y __, y4 = prev.worldToScreen(prev.getImage().size) # Rotation center line bottom y prev.delete(self.overlayTag) prev.create_line(x3, y3, x3, y4, tags=self.overlayTag, fill="blue") prev.create_line(x, y, x2, y2, tags=self.overlayTag, fill="red") prev.create_oval(x - 2, y - 2, x + 3, y + 3, tags=self.overlayTag, fill="green") prev.create_oval(x2 - 2, y2 - 2, x2 + 3, y2 + 3, tags=self.overlayTag, fill="green") # Draw the height lines x, y = prev.worldToScreen(self.height['start1'].get()) x2, y2 = prev.worldToScreen(self.height['end1'].get()) prev.create_line(x, y, x2, y2, tags=self.overlayTag, fill="violet") prev.create_oval(x - 2, y - 2, x + 3, y + 3, tags=self.overlayTag, fill="green") prev.create_oval(x2 - 2, y2 - 2, x2 + 3, y2 + 3, tags=self.overlayTag, fill="green") x, y = prev.worldToScreen(self.height['start2'].get()) x2, y2 = prev.worldToScreen(self.height['end2'].get()) prev.create_line(x, y, x2, y2, tags=self.overlayTag, fill="cyan") prev.create_oval(x - 2, y - 2, x + 3, y + 3, tags=self.overlayTag, fill="green") prev.create_oval(x2 - 2, y2 - 2, x2 + 3, y2 + 3, tags=self.overlayTag, fill="green") def merge(self): path = self.outputPathVar.get() if self.sequenceLoaded and path != "": self.status.showProgress() start = self.startSpin.get() end = self.endSpin.get() s = self.currentSequence rotationCenter = int((self.width['start'].x + self.width['end'].x) / 2) height = s.frameSize[1] frameCount = end - start self.status.setProgressMax(frameCount) h = self.height len1 = abs(h['start1'].y - h['end1'].y) len2 = abs(h['start2'].y - h['end2'].y) if len1 > len2: adjustmentRatio = len1/len2 else: adjustmentRatio = len2/len1 crossSection = abs(self.width['start'].x - self.width['end'].x) idealWidth = crossSection * math.pi * adjustmentRatio arcLen = (1.0 / frameCount) * idealWidth stripWidth = int(round(arcLen)) width = stripWidth * frameCount mergeIm = Image.new("RGB", (width, height), None) if self.rotVar.get() == 1: x = width - stripWidth dx = -1 else: x = 0 dx = 1 self.status.setProgress(0) for fnum in range(start, end): frame = s.image(fnum) if frame: frameStrip = frame.crop((rotationCenter, 0, rotationCenter + stripWidth, height)) mergeIm.paste(frameStrip, (x, 0)) x += (dx * stripWidth) self.status.setProgress(fnum) mergeIm = mergeIm.resize((int(idealWidth), height), Image.ANTIALIAS) try: mergeIm.save(path) except IOError: messagebox.showerror("Sorry...", message="Unable to save the final image.") self.status.showText("Finished merging. Unable to save to '{}'".format(path)) self.status.showText("Finished merging. Saved to '{}'".format(path))
class P300Window(object): def __init__(self, master: Tk): self.master = master master.title('P300 speller') #Parameters self.imagesize = 125 self.images_folder_path = '../utils/images/' #use utils/char_generator to generate any image you want self.flash_image_path = '../utils/images/flash_images/einstein.jpg' self.number_of_rows = 6 self.number_of_columns = 6 #make sure you have 6 x 6 amount of images in the images_folder_path self.flash_mode = 2 #single element #1 for columns and rows; currently is NOT working yet; if I have time, will revisit self.flash_duration = 100 #flash duration self.break_duration = 150 #isi self.trials = 6 #number of letters self.delay = 1000 #interval between trial self.letter_idx = 0 # Param for creating sequence self.num_sequence = 100 #Parameter for random # self.number_of_symbols =36 # self.fashed_per_iteration = 2 # self.number_of_iterations = 6 # self.stimuli_per_iteration = self.number_of_symbols * self.fashed_per_iteration / self.number_of_iterations #did not include numbers yet! self.random_letter = random.choices( string.ascii_lowercase, k=self.trials) #randomize [self.trials] number letters self.word = ''.join(self.random_letter) # Variables self.usable_images = [] self.image_labels = [] self.flash_sequence = [] self.flash_image = None self.sequence_number = 0 self.lsl_output = None # self.running = 0 #for pause self.image_frame = Frame(self.master) self.image_frame.grid(row=0, column=0, rowspan=self.number_of_rows, columnspan=self.number_of_columns) self.start_btn_text = StringVar() self.start_btn_text.set('Start') self.start_btn = Button(self.master, textvariable=self.start_btn_text, command=self.start) self.start_btn.grid(row=self.number_of_rows + 3, column=self.number_of_columns - 1) # self.pause_btn = Button(self.master, text='Pause', command=self.pause) # self.pause_btn.grid(row=self.number_of_rows + 3, column=self.number_of_columns - 4) #-4 for center # self.pause_btn.configure(state='disabled') self.close_btn = Button(self.master, text='Close', command=master.quit) self.close_btn.grid(row=self.number_of_rows + 3, column=0) fontStyle = tkFont.Font(family="Courier", size=40) self.output = Text(root, height=1, font=fontStyle) self.output.tag_configure("red", foreground="red") self.output.tag_configure("green", foreground="green") self.output.configure(width=10) self.output.insert("end", " ") self.output.grid(row=self.number_of_rows + 2, column=self.number_of_columns - 4) self.outputlabel = Label(root, text="Output: ", font=fontStyle) self.outputlabel.grid(row=self.number_of_rows + 2, column=self.number_of_columns - 5) self.targetlabel = Label(root, text="Target: ", font=fontStyle) self.targetlabel.grid(row=self.number_of_rows + 1, column=self.number_of_columns - 5) self.show_highlight_letter(0) # Initialization self.show_images() self.create_flash_sequence() self.lsl_output = self.create_lsl_output() def open_images(self): self.usable_images = [] self.highlight_letter_images = [] letter_images = sorted( glob.glob( os.path.join(self.images_folder_path, 'letter_images/*.png'))) #currently, still did not flash number yet! number_images = sorted( glob.glob( os.path.join(self.images_folder_path, 'number_images/*.png'))) letter_highlight_images = sorted( glob.glob( os.path.join(self.images_folder_path, 'letter_highlight_images/*.png'))) number_highlight_images = sorted( glob.glob( os.path.join(self.images_folder_path, 'number_highlight_images/*.png'))) for number_image in number_images: letter_images.append(number_image) #print("Paths: ", letter_images) min_number_of_images = self.number_of_columns * self.number_of_rows if len(letter_images) < min_number_of_images: print('To few images in folder: ' + self.images_folder_path) return # Convert and resize images for image_path in letter_images: image = Image.open(image_path) resized = image.resize((self.imagesize, self.imagesize), Image.BICUBIC) Tkimage = ImageTk.PhotoImage(resized) self.usable_images.append(Tkimage) # Convert and resize images for image_path in letter_highlight_images: image = Image.open(image_path) resized = image.resize((self.imagesize, self.imagesize), Image.BICUBIC) Tkimage = ImageTk.PhotoImage(resized) self.highlight_letter_images.append(Tkimage) flash_img = Image.open(self.flash_image_path) flash_img_res = flash_img.resize((self.imagesize, self.imagesize), Image.BICUBIC) self.flash_image = ImageTk.PhotoImage(flash_img_res) def show_images(self): self.open_images() if self.usable_images == []: print('No images opened') return num_rows = self.number_of_rows num_cols = self.number_of_columns # Arrange images for r in range(0, num_rows): for c in range(0, num_cols): current_image = self.usable_images[r * num_cols + c] label = Label(self.image_frame, image=current_image) label.image = current_image label.grid(row=r, column=c) self.image_labels.append(label) def create_lsl_output(self): """Creates an LSL Stream outlet""" info = StreamInfo(name='LetterMarkerStream', type='LetterFlashMarkers', channel_count=1, channel_format='int8', nominal_srate=IRREGULAR_RATE, source_id='lettermarker_stream', handle=None) return StreamOutlet(info) #for sending the predicted classes # def create_flash_sequence_old(self): # num_rows = 6 # num_cols = 6 # maximum_number = num_rows * num_cols # number_count = np.array([0] * maximum_number) # seq = [] # next_number = [random.randint(0, maximum_number)] # count = 1 # while (len(seq) / 6 < 100): # if (len(seq) > 0): # neighbor_list = seq[-count:] #the same set, with size of -1 to -6 # left_list = [x-1 for x in neighbor_list] # neighbor of each neighbor in neighbor_list # right_list = [x+1 for x in neighbor_list] # up_list = [x-6 for x in neighbor_list] # bot_list = [x+6 for x in neighbor_list] # combine_list =[] # combine_list.extend(left_list) # combine_list.extend(right_list) # combine_list.extend(up_list) # combine_list.extend(bot_list) # #should not be same element as previous set (*2), and should not be same element in the combine_list # if (next_number not in seq[-CONCURRENT_ELEMENTS*2:] and next_number not in combine_list): # seq.extend(next_number) # count = ((count) % 6) + 1 #this count makes sure we have one set of 6 # else: # seq.extend(next_number) # left_over = np.argwhere(number_count == np.argmin(number_count)) # selectMax = len(left_over) - 1 # next_number = left_over[random.randint(0, selectMax)] # self.flash_sequence = seq def find_replacement(self, nextelem, seq): length = CONCURRENT_ELEMENTS if len( self.flash_sequence) > CONCURRENT_ELEMENTS - 1 else len( self.flash_sequence) for i in range(1, length + 1): prev_seq = self.flash_sequence[-i:][0] for j in range(len(prev_seq)): previous_seq_elem = self.flash_sequence[-i:][0][j] prev_seq_without_prevelem = [ x for x in prev_seq if x != previous_seq_elem ] nl_prev = self.get_neighbors(prev_seq_without_prevelem) nl_current = self.get_neighbors(seq) if (nextelem not in prev_seq and #a nextelem not in nl_prev and #b nextelem not in self.flash_sequence[-i - 1:][0] and #c1 left consecutive nextelem not in self.flash_sequence[-i + 1:][ 0] #c2 right consecutive and previous_seq_elem not in seq and #a previous_seq_elem not in nl_current and #b previous_seq_elem not in self.flash_sequence[-1:][0]): #c self.flash_sequence[-i:][0][j] = nextelem seq.append(previous_seq_elem) return seq print("Can't swap..Restarting everything...") create_flash_sequence() def get_neighbors(self, seq): right = [x + 1 for x in seq] left = [x - 1 for x in seq] top = [x - 6 for x in seq] bottom = [x + 6 for x in seq] neighbors = list(chain(right, left, top, bottom)) return neighbors def create_flash_sequence(self): num_rows = self.number_of_rows num_cols = self.number_of_columns total = num_rows * num_cols num_sequence = self.num_sequence li = list(range(total)) while len(self.flash_sequence) < num_sequence: seq = [] failcount = 0 if not li: #if li is exhausted li = list(range(36)) while len(seq) < CONCURRENT_ELEMENTS: nextelem = random.choice(li) if len(seq) > 0: nl = self.get_neighbors(seq) #prevent stuck if failcount > 20: seq = self.find_replacement(nextelem, seq) li.remove(nextelem) failcount = 0 elif nextelem not in nl and nextelem not in seq: if len(self.flash_sequence): if nextelem not in self.flash_sequence[-1:][ 0]: #0 remove the outer list [[]] becomes [] seq.append(nextelem) li.remove(nextelem) else: failcount += 1 else: seq.append(nextelem) li.remove(nextelem) else: failcount += 1 else: #first element, just insert if len(self.flash_sequence): if nextelem not in self.flash_sequence[-1:][0]: seq.append(nextelem) li.remove(nextelem) else: seq.append(nextelem) li.remove(nextelem) self.flash_sequence.append(seq) self.flash_sequence = list(chain(*self.flash_sequence)) def start(self): if not (TEST_UI): self.read_lsl_marker() self.running = 1 letter = self.word[0] image_index = string.ascii_lowercase.index(letter) self.highlight_image(image_index) self.start_btn.configure(state='disabled') self.pause_btn.configure(state='normal') # def pause(self): # self.running = 0 # self.start_btn_text.set('Resume') # self.start_btn.configure(state='normal') # self.pause_btn.configure(state='disabled') # def check_pause(self): # if self.running == 0: # print('Flashing paused at sequence number ' + str(self.sequence_number)) # return def check_sequence_end(self): if self.sequence_number == len( self.flash_sequence ): #stop flashing if all generated sequence number runs out print('All elements had flashed - run out of juice') self.running = 0 self.sequence_number = 0 return def get_marker_result(self): result = self.marker_result() if (result): print("Marker received: ", result[0][0]) receive = result[0][0] else: receive = 0 return receive def output_letter(self, receive, image_index): # receive = [9,10,13] if not receive: if FLASH_CONCURRENT: self.master.after(self.break_duration, self.start_concurrent_flashing) else: self.master.after(self.break_duration, self.start_flashing) elif isinstance(receive, int): if (image_index + 1) == receive: self.output.insert("end", self.pos_to_char(receive), "green") else: self.output.insert("end", self.pos_to_char(receive), "red") self.letter_idx += 1 if self.letter_idx == len(self.word): return letter = self.word[self.letter_idx] image_index = string.ascii_lowercase.index(letter) self.master.after(self.break_duration, self.highlight_target, image_index) else: if FLASH_CONCURRENT: self.candidate_flash_sequence(receive) self.master.after(self.break_duration, self.start_concurrent_flashing) else: self.master.after(self.break_duration, self.start_flashing) def candidate_flash_sequence(self, candidates): print("got candidate") num_rows = self.number_of_rows num_col = self.number_of_columns current_elements = self.flash_sequence[self.sequence_number:self. sequence_number + CONCURRENT_ELEMENTS] next_elements = self.flash_sequence[self.sequence_number + CONCURRENT_ELEMENTS:self. sequence_number + CONCURRENT_ELEMENTS * 2] # Step 1 check if the candidate is included in the next list # Step 2 check if there is multiple candidate in the next list ~ else just continue count_contain_candidate = 0 for target_candidate in candidates: if target_candidate in next_elements: count_contain_candidate += 1 if count_contain_candidate > 1 or count_contain_candidate == 0: # Step 3 check previous i length of sequence to know lease used candidate where i = candidate length start_check_index = 0 previous_flashes_count = [] if CONCURRENT_ELEMENTS * len(candidates) > self.sequence_number: start_check_index = self.sequence_number - CONCURRENT_ELEMENTS * len( candidates) - 1 for target_candidate in candidates: previous_flashes = self.flash_sequence[start_check_index:self. sequence_number - 1] previous_flashes_count.append([ target_candidate, previous_flashes.count(target_candidate) ]) least_index = np.argmin(previous_flashes_count, axis=0)[1] choosen_candidate = previous_flashes_count[least_index][0] # generate new flash_sequence new_sequence = [choosen_candidate] li = list(range(num_rows * num_col)) print("new_sequence1 :", new_sequence) #generate sequence while len(new_sequence) < 6: nextelem = random.choice(li) print("nextelem :", nextelem) nl = self.get_neighbors(new_sequence) #random until get the flasshes sequence while (nextelem in nl or nextelem in current_elements or nextelem in next_elements): nextelem = random.choice(li) print("nextelem :", nextelem) new_sequence.append(nextelem) print("new_sequence2: ", new_sequence) print("self.sequence_number: ", self.sequence_number) # update flashing sequences print(self.sequence_number) self.flash_sequence = self.flash_sequence[ 0:self.sequence_number + CONCURRENT_ELEMENTS] + new_sequence + self.flash_sequence[ self.sequence_number + CONCURRENT_ELEMENTS:] # Step 4 generate new list where # 1 no duplicate # 2 no multiple candidate # 3 no neighbors # 4 no previous list if possible (maybe all candidate is in the previous) # 5 no next list if possible (maybe all candidate is in the next) # 6 use the least use candidate def start_concurrent_flashing(self): self.check_sequence_end() # self.check_pause() receive = self.get_marker_result() element_to_flash = self.flash_sequence[self.sequence_number:self. sequence_number + CONCURRENT_ELEMENTS] letter = self.word[self.letter_idx] image_index = string.ascii_lowercase.index(letter) #pushed markers to LSL stream print("Letter: ", image_index, " Element flash: ", [x + 1 for x in element_to_flash]) for e in element_to_flash: self.lsl_output.push_sample([e + 1 ]) # add 1 to prevent 0 in markers self.flash_multiple_elements(element_to_flash) self.output_letter(receive, image_index) self.sequence_number = self.sequence_number + CONCURRENT_ELEMENTS #change flash position def start_flashing(self): self.check_sequence_end() # self.check_pause() receive = self.get_marker_result() element_to_flash = self.flash_sequence[self.sequence_number] letter = self.word[self.letter_idx] image_index = string.ascii_lowercase.index(letter) #pushed markers to LSL stream print("Letter: ", image_index, " Element flash: ", [element_to_flash + 1]) self.lsl_output.push_sample([element_to_flash + 1 ]) # add 1 to prevent 0 in markers self.flash_single_element(element_to_flash) self.output_letter(receive, image_index) self.sequence_number = self.sequence_number + 1 #change flash position def pos_to_char(self, pos): return chr(pos + 97) def highlight_target(self, image_index): self.show_highlight_letter(self.letter_idx) self.highlight_image(image_index) def change_image(self, label, img): label.configure(image=img) label.image = img def highlight_image(self, element_no): self.change_image(self.image_labels[element_no], self.highlight_letter_images[element_no]) self.master.after(self.delay, self.unhighlight_image, element_no) def unhighlight_image(self, element_no): self.change_image(self.image_labels[element_no], self.usable_images[element_no]) if (FLASH_CONCURRENT): self.master.after(self.flash_duration, self.start_concurrent_flashing) else: self.master.after(self.flash_duration, self.start_flashing) def show_highlight_letter(self, pos): fontStyle = tkFont.Font(family="Courier", size=40) fontStyleBold = tkFont.Font(family="Courier bold", size=40) text = Text(root, height=1, font=fontStyle) text.tag_configure("bold", font=fontStyleBold) text.tag_configure("center", justify='center') for i in range(0, len(self.word)): if (i != pos): text.insert("end", self.word[i]) else: text.insert("end", self.word[i], "bold") text.configure(state="disabled", width=10) text.tag_add("center", "1.0", "end") text.grid(row=self.number_of_rows + 1, column=self.number_of_columns - 4) def flash_row_or_col(self, rc_number): num_rows = self.number_of_rows num_cols = self.number_of_columns if rc_number < num_rows: for c in range(0, num_cols): #flash row cur_idx = rc_number * num_cols + c self.change_image(self.image_labels[cur_idx], self.flash_image) else: current_column = rc_number - num_rows for r in range(0, num_rows): #flash column cur_idx = current_column + r * num_cols self.change_image(self.image_labels[cur_idx], self.flash_image) self.master.after(self.flash_duration, self.unflash_row_or_col, rc_number) def unflash_row_or_col(self, rc_number): num_rows = self.number_of_rows num_cols = self.number_of_columns if rc_number < num_rows: for c in range(0, num_cols): #flash row cur_idx = rc_number * num_cols + c self.change_image(self.image_labels[cur_idx], self.usable_images[cur_idx]) else: current_column = rc_number - num_rows for r in range(0, num_rows): #flash column cur_idx = current_column + r * num_cols self.change_image(self.image_labels[cur_idx], self.usable_images[cur_idx]) def flash_multiple_elements(self, element_array): for element_no in element_array: self.change_image(self.image_labels[element_no], self.flash_image) self.master.after(self.flash_duration, self.unflash_multiple_elements, element_array) def unflash_multiple_elements(self, element_array): for element_no in element_array: self.change_image(self.image_labels[element_no], self.usable_images[element_no]) def flash_single_element(self, element_no): self.change_image(self.image_labels[element_no], self.flash_image) self.master.after(self.flash_duration, self.unflash_single_element, element_no) def unflash_single_element(self, element_no): self.change_image(self.image_labels[element_no], self.usable_images[element_no]) def marker_result(self): if not (TEST_UI): marker, timestamp = self.inlet_marker.pull_chunk() return marker else: return 0 def read_lsl_marker(self): print("looking for a Markers stream...") marker_streams = resolve_byprop('name', 'ResultMarkerStream') if marker_streams: self.inlet_marker = StreamInlet(marker_streams[0]) marker_time_correction = self.inlet_marker.time_correction() print("Found Markers stream")
class Example(Frame): def __init__(self, parent): Frame.__init__(self, parent) self.parent = parent self.initUI() def initUI(self): self.parent.title("") #self.style = Style() #self.style.theme_use("clam") #self.pack(fill=BOTH, expand = 1) self.quitbutton = Button(self, text="Quit", command= lambda: self.quit()) self.quitbutton.grid(row=3, column=1, pady=4) self.labelErrorPointer = Label(self, text="◀") self.labellist = [] self.entrylist = [] self.verifylist = [] self.misclist = [] self.optionCreate = "Create" self.optionUpcoming = "Upcoming" self.optionPast = "Past" self.prevmode = self.optionCreate self.curmode = self.optionCreate self.optionvar = tkinter.StringVar(self) self.optionvar.trace("w",self.permaloop) self.optionvar.set(self.optionCreate) self.option = OptionMenu(self, self.optionvar, self.optionCreate, self.optionUpcoming, self.optionPast) self.optionpostmodevar = tkinter.StringVar(self) self.optionpostmodevar.trace("w",self.permaloop) self.optionpostmodevar.set('url') self.optionpostmode = OptionMenu(self, self.optionpostmodevar, 'url', 'text') self.labelText = Label(self, text='Selftext:') self.entryText = Text(self) self.labelURL = Label(self, text='URL:') self.entryURL = Entry(self) self.entryURL.configure(width=60) self.sql = sqlite3.connect('sql.db') print('Loaded SQL Database') self.cur = self.sql.cursor() self.cur.execute('CREATE TABLE IF NOT EXISTS upcoming(ID TEXT, SUBREDDIT TEXT, TIME INT, TITLE TEXT, URL TEXT, BODY TEXT)') self.cur.execute('CREATE TABLE IF NOT EXISTS past(ID TEXT, SUBREDDIT TEXT, TIME INT, TITLE TEXT, URL TEXT, BODY TEXT, POSTLINK TEXT)') self.cur.execute('CREATE TABLE IF NOT EXISTS internal(NAME TEXT, ID INT)') print('Loaded Completed table') self.cur.execute('SELECT * FROM internal') f = self.cur.fetchone() if not f: print('Database is new. Adding ID counter') self.cur.execute('INSERT INTO internal VALUES(?, ?)', ['counter', 1]) self.idcounter = 1 else: self.idcounter = f[1] print('Current ID counter: ' + str(self.idcounter)) self.sql.commit() sw = self.parent.winfo_screenwidth() sh = self.parent.winfo_screenheight() w=853 h=480 x = (sw - w) / 2 y = (sh - h) / 2 self.parent.geometry('%dx%d+%d+%d' % (w, h, x, y-50)) self.login() def login(self): try: self.quitbutton.grid_forget() self.quitbutton.grid(row=9000, column=0, columnspan=20) self.option.grid(row=1,column=0,columnspan=80,pady=8) self.updategui(fullclean=True) except praw.errors.InvalidUserPass: pass print('Invalid username or password') self.entryPassword.delete(0,200) self.labelErrorPointer.grid(row=1, column=2) def permaloop(self, *args): self.curmode = self.optionvar.get() print('Was: ' + self.prevmode + ' | Now: ' + self.curmode) if self.curmode != self.prevmode: self.prevmode = self.curmode self.updategui(fullclean=True) else: self.updategui(False) def getTime(self, bool): timeNow = datetime.datetime.now(datetime.timezone.utc) timeUnix = timeNow.timestamp() if bool is False: return timeNow else: return timeUnix def addentrytobase(self, subreddit, title, url="", body="", mode="", ptime=""): curtime = round(self.getTime(True)) try: t = self.entryMo.get() + ' ' + self.entryDa.get() + ' ' + self.entryYr.get() + ' ' + self.entryHH.get() + ':' + self.entryMM.get() plandate = datetime.datetime.strptime(t, "%B %d %Y %H:%M") plandate = plandate.timestamp() except ValueError: print('Invalid Day') return False if mode == 'url': url = self.entryURL.get() body = "" if 'http://' not in url and 'https://' not in url: print('Please enter a proper URL') return False if mode == 'text': body = self.entryText.get("1.0", "end") url = "" if plandate < curtime: print('Please enter a time in the future') return False if not all(char in string.ascii_letters+string.digits+'_-' for char in subreddit): print('Subreddit contains invalid characters') return False if len(subreddit) == 0: print('You must enter a subreddit') return False if len(title) == 0: print('You must enter a title') return False if len(title) > 300: print('Title is too long. ' + str(len(title)) + '/300 char max') return False if len(body) > 15000: print('Body is too long. ' + str(len(body)) + '/15,000 char max') print('Timestamp:', plandate) self.cur.execute('INSERT INTO upcoming VALUES(?, ?, ?, ?, ?, ?)', [self.idcounter, subreddit, int(plandate), title, url, body]) self.idcounter += 1 self.cur.execute('UPDATE internal SET ID=? WHERE NAME=?', [self.idcounter, 'counter']) self.sql.commit() print('\nPost Saved!') print(self.idcounter, subreddit, self.timestamptoday(int(plandate))) print(title) print(url, body) print() self.entryText.delete("1.0", "end") self.entryURL.delete(0, 'end') self.entryTitle.delete(0, 'end') #self.updategui(halfclean=True) def timestamptoday(self, timestamp): d = datetime.datetime.fromtimestamp(timestamp) info = datetime.datetime.strftime(d, "%b %d %H:%M") return info def dropentryfrombase(self, ID): if '-' not in ID: try: ID = int(ID) l = [ID] except ValueError: print('You must enter a number') return else: if ID.count('-') == 1: try: ID = ID.replace(' ', '') ID = ID.split('-') ID[0] = int(ID[0]) ID[1] = int(ID[1]) if ID[1] > ID[0]: l = list(range(ID[0], ID[1]+1)) else: return except ValueError: return for item in l: item = str(item) print('Dropping Item ' + item + ' from Upcoming') self.cur.execute('DELETE FROM upcoming WHERE ID=?', [item]) self.sql.commit() self.updategui(fullclean=True) def printbasetofile(self, db): filea = open(db + '.txt', 'w') if db == 'past': self.cur.execute('SELECT * FROM past') if db == 'upcoming': self.cur.execute('SELECT * FROM upcoming') f = self.cur.fetchall() print('Printed ' + db + ' unimpeded to file') for item in f: i = list(item) i[2] = self.timestamptoday(i[2]) i.remove('') print(str(i)[1:-1], file=filea) filea.close() def updategui(self, halfclean=False, fullclean=False): if self.curmode == self.optionCreate: try: print(self.optionpostmodevar.get()) if self.optionpostmodevar.get() == 'url': self.entryText.delete("1.0", 'end') self.labelText.grid_forget() self.entryText.grid_forget() self.labelURL.grid(row=8, column=0, columnspan=30) self.entryURL.grid(row=9, column=0, columnspan=12, pady=10) if self.optionpostmodevar.get() == 'text': self.entryURL.delete(0, 'end') self.labelURL.grid_forget() self.entryURL.grid_forget() self.labelText.grid(row=8, column=0, columnspan=30) self.entryText.configure(width=40, height=8) self.entryText.grid(row=9, column=0, columnspan=12) except AttributeError: pass if fullclean is True: print('Cleaning GUI') for item in self.labellist: item.grid_forget() for item in self.entrylist: item.grid_forget() for item in self.verifylist: item.grid_forget() for item in self.misclist: item.grid_forget() self.labellist = [] self.entrylist = [] self.verifylist = [] self.misclist = [] if self.curmode == self.optionCreate: self.newrowindex = 6 self.labelSubreddit = Label(self, text="Subreddit: /r/") self.labelTitle = Label(self, text="Post title: ") self.entrySubreddit = Entry(self) self.entryTitle = Entry(self) self.labelHH = Label(self, text="Schedule time (Local timezone):") nowlist = datetime.datetime.strftime(datetime.datetime.now(), "%B %d %Y %H %M").split() self.entryMo = Spinbox(self, width=9, values=('January', 'February', 'March', 'April', 'May', 'June', 'July', \ 'August', 'September', 'October', 'November', 'December')) self.entryMo.delete(0,'end') self.entryMo.insert(0, nowlist[0]) self.entryDa = Spinbox(self, width=2, from_=1, to=31) self.entryDa.delete(0,'end') self.entryDa.insert(0, nowlist[1]) self.entryYr = Spinbox(self, width=4, from_=2014, to=2500) self.entryYr.delete(0,'end') self.entryYr.insert(0, nowlist[2]) self.entryHH = Spinbox(self, from_=0, to=23, width=2) self.entryHH.delete(0,'end') self.entryHH.insert(0, nowlist[3]) self.entryMM = Spinbox(self, from_=0, to=59, width=2) self.entryMM.delete(0,'end') self.entryMM.insert(0, nowlist[4]) self.buttonAddentry = Button(self, text='Save', command=lambda: self.addentrytobase(self.entrySubreddit.get(), self.entryTitle.get(),\ mode=self.optionpostmodevar.get())) self.misclist.append(self.labelSubreddit) self.misclist.append(self.entrySubreddit) self.misclist.append(self.labelHH) self.misclist.append(self.entryHH) self.misclist.append(self.entryMM) self.misclist.append(self.entryMo) self.misclist.append(self.entryDa) self.misclist.append(self.entryYr) self.misclist.append(self.labelTitle) self.misclist.append(self.entryTitle) self.misclist.append(self.buttonAddentry) self.misclist.append(self.optionpostmode) self.misclist.append(self.labelText) self.misclist.append(self.entryText) self.misclist.append(self.labelURL) self.misclist.append(self.entryURL) self.labelSubreddit.grid(row=2, column=0, sticky="e") self.labelTitle.grid(row=3, column=0, sticky="e") self.entrySubreddit.grid(row=2, column=1, columnspan=3, sticky="w") self.entryTitle.grid(row=3, column=1, columnspan=3, sticky="w") self.entryMo.grid(row=4, column=1,sticky="e") self.entryDa.grid(row=4, column=2) self.entryYr.grid(row=4, column=3) self.labelHH.grid(row=4, column=0, sticky="se", pady=5) self.entryHH.grid(row=5, column=1, sticky="e") self.entryMM.grid(row=5, column=2, sticky="w") self.optionpostmode.grid(row=6, column=0, columnspan=20, pady=10) self.buttonAddentry.grid(row=200, column=0, columnspan=20) if self.curmode == self.optionUpcoming: self.cur.execute('SELECT * FROM upcoming') dobutton = True if self.curmode == self.optionPast: self.cur.execute('SELECT * FROM past') dobutton = False if self.curmode == self.optionPast or self.curmode == self.optionUpcoming: self.listboxId = Listbox(self) self.listboxId.configure(width=118, height=20, font=("Courier 8")) self.misclist.append(self.listboxId) self.listboxScroller = Scrollbar(self, orient='horizontal', command=self.listboxId.xview) self.listboxScroller.grid(row=4, column=0, columnspan=900) self.listboxId.grid(row=3, column=0, columnspan=10) self.listboxId.configure(xscrollcommand=self.listboxScroller.set) self.misclist.append(self.listboxScroller) self.buttonPrinter = Button(self, text="Print to .txt file") if self.curmode == self.optionPast: self.buttonPrinter.configure(command=lambda: self.printbasetofile('past')) if self.curmode == self.optionUpcoming: self.buttonPrinter.configure(command=lambda: self.printbasetofile('upcoming')) self.buttonPrinter.grid(row = 6, column=0, columnspan=90) self.misclist.append(self.buttonPrinter) if dobutton is True: self.entryDelete = Entry(self) self.buttonDelete = Button(self, text="Delete Item: ", command=lambda: self.dropentryfrombase(self.entryDelete.get())) self.buttonDelete.grid(row=5, column=0, sticky='e') self.entryDelete.grid(row=5, column=1, sticky='w') self.misclist.append(self.entryDelete) self.misclist.append(self.buttonDelete) fetched = self.cur.fetchall() for item in fetched: info = self.timestamptoday(item[2]) if item[4] == '': infx = item[5] if item[5] == '': infx = item[4] if self.curmode == self.optionPast: infy = '.' + item[6] else: infy = '' self.listboxId.insert('end', \ item[0] + '.'*(6 - len(item[0])) \ + item[1][:10] + '.'*(12 - len(item[1][:10])) \ + info + '.'*(15 - len(info[:14])) \ + item[3][:18] + '.'*(20 - len(item[3][:14])) \ + infx[:45] + '.'*(47-len(infx[:45])) \ + infy) def morerows(self, label, columnm, columnn, limit, *args): self.redditlabel = Label(self,text=label) self.redditlabel.grid(row=self.newrowindex,column=columnm, sticky="e") self.labellist.append(self.redditlabel) self.redditentry = Entry(self) self.redditentry.grid(row=self.newrowindex,column=columnn, columnspan=9) self.entrylist.append(self.redditentry) self.newrowindex += 1 if self.newrowindex >= limit: self.morerowbutton.grid_forget() print(self.newrowindex)
class GUI(Tk): """This app allows for easy editing of circular datatrees""" dataset: dict[str, dict[str, list[str]]] = dict() datapth = Path(__file__).with_name('dataset.json') imgcanvas: Canvas image: Image imagetk: PhotoImage cnvimg: int saveBtn: Button dataScrl: SFrame dataFrm: Frame datadict: dict[Entry, dict[Entry, list[Entry]]] imgargs: tuple[StringVar, StringVar, StringVar, StringVar] def __init__(self): Tk.__init__(self) self.protocol("WM_DELETE_WINDOW", self.on_exit) wd = (self.winfo_screenwidth() // 2) ht = (self.winfo_screenheight() // 2) self.geometry(f'{wd * 1.5:.0f}' f'x{ht * 1.5:.0f}' f'+{wd - wd * 0.75:.0f}' f'+{ht - ht * 0.75:.0f}') self.columnconfigure(1, weight=1) self.rowconfigure(1, weight=1) self.option_add('*font', 'Ebrima 12') self.option_add('*Entry.width', 15) s = Style() s.configure('TButton', font='Ebrima 12') s.configure('sm.TButton', width=6, font='Ebrima 8') self.datadict = dict() self.build() def on_exit(self): self.quit() self.destroy() def build(self): # data buttons dataBtns = LFrame(master=self, text="Dataset") dataBtns.grid(column=0, row=0) self.buildDataBtns(dataBtns) # data self.dataScrl = SFrame(master=self) self.dataScrl.grid(column=0, row=1, sticky='nsew') self.buildData() # image buttons imgBtns = LFrame(master=self, text="Image") imgBtns.grid(column=0, row=2, pady=3) self.buildImgBtns(imgBtns) # canvas cnvfrm = SFrame(master=self, padding=3) cnvfrm.grid(column=1, row=0, rowspan=3, sticky='nsew') self.buildCanvas(cnvfrm) # ___ _ _ _____ _____ ___ _ _ ___ # | _ ) | | |_ _|_ _/ _ \| \| / __| # | _ \ |_| | | | | || (_) | .` \__ \ # |___/\___/ |_| |_| \___/|_|\_|___/ # ___ ___ _________ # / _ \/ _ /_ __/ _ | # / // / __ |/ / / __ | # /____/_/ |_/_/ /_/ |_| def buildDataBtns(self, parent): loadBtn = Button(master=parent, text="Load", command=self.loadData) loadBtn.grid(column=0, row=0, padx=3) clearBtn = Button(master=parent, text="Clear", command=self.clearData) clearBtn.grid(column=1, row=0, padx=3) saveBtn = Button(master=parent, text="Save", command=self.saveData) saveBtn.grid(column=2, row=0, padx=3) def loadData(self): with self.datapth.open('r') as f: self.dataset = json.load(f) self.datadict.clear() for w in self.dataFrm.winfo_children(): w.destroy() for inr, sub in self.dataset.items(): mfrm, k = self.addInner(inr) for mid, items in sub.items(): ofrm, v = self.addMid(mfrm, k, mid) for out in items: self.addOuter(ofrm, k, v, out) def clearData(self): self.dataset.clear() self.datadict.clear() for w in self.dataScrl.winfo_children(): w.destroy() self.buildData() def saveData(self): self.getData() with self.datapth.open('w') as f: json.dump(self.dataset, f, indent=4) # ______ ______ _________ # / _/ |/ / _ |/ ___/ __/ # _/ // /|_/ / __ / (_ / _/ # /___/_/ /_/_/ |_\___/___/ def buildImgBtns(self, parent): def lblent(txt, var, col, row) -> None: lfrm = LFrame(master=parent, text=txt, font='Ebrima 8') lfrm.grid(column=col, row=row, padx=3) ent = Entry(master=lfrm, textvariable=var) ent.pack() cmapnm = StringVar() offset = StringVar() ffamily = StringVar() fsize = StringVar() self.imgargs = (cmapnm, offset, ffamily, fsize) cmapnm.set('hsv') offset.set('20') ffamily.set('Ebrima') fsize.set('8, 7, 6.5') lblent("Colormap Name", cmapnm, 0, 0) lblent("Color Offset", offset, 1, 0) lblent("Font Family", ffamily, 0, 1) lblent("Font Size (inner, mid, outer)", fsize, 1, 1) drawBtn = Button(master=parent, text="Draw", command=self.drawImg) drawBtn.grid(column=0, row=2, padx=3) self.saveBtn = Button(master=parent, text="Save", command=self.saveImg, state='disabled') self.saveBtn.grid(column=1, row=2, padx=3) def drawImg(self): self.getData() cm, off, fam, sz = self.imgargs self.image = DataChart( dataset=self.dataset, colormap_name=cm.get(), offset=float(off.get()), fontfamily=fam.get(), fontsize=[float(n) for n in split(r', ?| ', sz.get())]) self.imagetk = PhotoImage(self.image) self.imgcanvas.itemconfigure(self.cnvimg, image=self.imagetk) self.saveBtn.configure(state='normal') def saveImg(self): saveas = Askfile(parent=self, title="Save image as...", filetypes=[('PNG Image (*.png)', '*.png'), ('JPG Image (*.jpg)', '*.jpg')], defaultextension='.png') if saveas: file = Path(saveas) if file.suffix == '.jpg': self.image.convert('RGB').save(file) else: self.image.save(file) # ___ _ _____ _ # | \ /_\_ _/_\ # | |) / _ \| |/ _ \ # |___/_/ \_\_/_/ \_\ def buildData(self): self.dataFrm = Frame(self.dataScrl) self.dataFrm.grid(column=0, row=0) addBtn = Button(master=self.dataScrl, text="Add Category", command=self.addInner) addBtn.grid(column=0, row=1, sticky='w', padx=3, pady=3) self.addInner() def addInner(self, name: str = None) -> O[tuple[Frame, Entry]]: def remInner(): frm.destroy() self.datadict.pop(Entry) frm = Frame(master=self.dataFrm) frm.grid() remBtn = Button(master=frm, text='Delete', style='sm.TButton', command=remInner) remBtn.grid(column=0, row=0, sticky='ne', pady=3) ent = Entry() lfrm = LFrame(master=frm, labelwidget=ent, bd=3, padx=5, pady=5) lfrm.grid(column=1, row=0, sticky='w') subfrm = Frame(master=lfrm) subfrm.grid(column=0, row=0) addBtn = Button(master=lfrm, text="Add Subcategory", command=(lambda a=(subfrm, ent): self.addMid(*a))) addBtn.grid(column=0, row=1, sticky='w', padx=3, pady=3) self.datadict[ent] = dict() self.dataScrl.redraw() if name: ent.insert(0, name) return (subfrm, ent) else: self.addMid(subfrm, ent) def addMid(self, parent: Frame, key: Entry, name: str = None) -> O[tuple[Frame, Entry]]: def remMid(): frm.destroy() self.datadict[key].pop(ent) frm = Frame(master=parent) frm.grid() remBtn = Button(master=frm, text='Delete', style='sm.TButton', command=remMid) remBtn.grid(column=0, row=0, sticky='ne', pady=3) ent = Entry() lfrm = LFrame(master=frm, labelwidget=ent, bd=3, padx=5, pady=5) lfrm.grid(column=1, row=0, sticky='w') subfrm = Frame(master=lfrm) subfrm.grid(column=0, row=0) addBtn = Button( master=lfrm, text="Add Item", command=(lambda a=(subfrm, key, ent): self.addOuter(*a))) addBtn.grid(column=0, row=1, sticky='w', padx=3, pady=3) self.datadict[key][ent] = list() self.dataScrl.redraw() if name: ent.insert(0, name) return (subfrm, ent) else: self.addOuter(subfrm, key, ent) def addOuter(self, parent: Frame, key: Entry, val: Entry, name: str = '') -> None: def remOuter(): frm.destroy() i = self.datadict[key][val].index(ent) self.datadict[key][val].pop(i) frm = Frame(master=parent, pady=1) frm.grid() remBtn = Button(master=frm, text='Delete', style='sm.TButton', command=remOuter) remBtn.grid(column=0, row=0, sticky='ne', pady=3) ent = Entry(master=frm) ent.insert(0, name) ent.grid(column=1, row=0, sticky='w', padx=3, pady=3) self.datadict[key][val].append(ent) self.dataScrl.redraw() def getData(self): self.dataset = { inr.get(): { mid.get(): [out.get() for out in items] for mid, items in sub.items() } for inr, sub in self.datadict.items() } # ___ _ _ ___ ___ ___ # / __| /_\ | \| \ \ / /_\ / __| # | (__ / _ \| .` |\ V / _ \\__ \ # \___/_/ \_\_|\_| \_/_/ \_\___/ def buildCanvas(self, parent): self.imgcanvas = Canvas(master=parent, width=1440, height=1440) self.imgcanvas.pack() self.cnvimg = self.imgcanvas.create_image(0, 0, anchor='nw') self.image = None self.imagetk = None
class Sudoku(Tk): def __init__(self, file=None): Tk.__init__(self, className="Sudoku-Tk") self.title("Sudoku-Tk") self.resizable(0, 0) self.protocol("WM_DELETE_WINDOW", self.quitter) cst.set_icon(self) self.columnconfigure(3, weight=1) # --- style bg = '#dddddd' activebg = '#efefef' pressedbg = '#c1c1c1' lightcolor = '#ededed' darkcolor = '#cfcdc8' bordercolor = '#888888' focusbordercolor = '#5E5E5E' disabledfg = '#999999' disabledbg = bg button_style_config = {'bordercolor': bordercolor, 'background': bg, 'lightcolor': lightcolor, 'darkcolor': darkcolor} button_style_map = {'background': [('active', activebg), ('disabled', disabledbg), ('pressed', pressedbg)], 'lightcolor': [('pressed', darkcolor)], 'darkcolor': [('pressed', lightcolor)], 'bordercolor': [('focus', focusbordercolor)], 'foreground': [('disabled', disabledfg)]} style = Style(self) style.theme_use(cst.STYLE) style.configure('TFrame', background=bg) style.configure('TLabel', background=bg) style.configure('TScrollbar', gripcount=0, troughcolor=pressedbg, **button_style_config) style.map('TScrollbar', **button_style_map) style.configure('TButton', **button_style_config) style.map('TButton', **button_style_map) style.configure('TCheckutton', **button_style_config) style.map('TCheckutton', **button_style_map) self.option_add('*Toplevel.background', bg) self.option_add('*Menu.background', bg) self.option_add('*Menu.activeBackground', activebg) self.option_add('*Menu.activeForeground', "black") self.configure(bg=bg) style.configure("bg.TFrame", background="grey") style.configure("case.TFrame", background="white") style.configure("case.TLabel", background="white", foreground="black") style.configure("case_init.TFrame", background="lightgrey") style.configure("case_init.TLabel", background="lightgrey", foreground="black") style.configure("erreur.TFrame", background="white") style.configure("erreur.TLabel", background="white", foreground="red") style.configure("solution.TFrame", background="white") style.configure("solution.TLabel", background="white", foreground="blue") style.configure("pause.TLabel", foreground="grey", background='white') # --- images self.im_erreur = open_image(cst.ERREUR) self.im_pause = open_image(cst.PAUSE) self.im_restart = open_image(cst.RESTART) self.im_play = open_image(cst.PLAY) self.im_info = open_image(cst.INFO) self.im_undo = open_image(cst.UNDO) self.im_redo = open_image(cst.REDO) self.im_question = open_image(cst.QUESTION) # --- timer self.chrono = [0, 0] self.tps = Label(self, text=" %02i:%02i" % tuple(self.chrono), font="Arial 16") self.debut = False # la partie a-t-elle commencée ? self.chrono_on = False # le chrono est-il en marche ? # --- buttons self.b_pause = Button(self, state="disabled", image=self.im_pause, command=self.play_pause) self.b_restart = Button(self, state="disabled", image=self.im_restart, command=self.recommence) self.b_undo = Button(self, image=self.im_undo, command=self.undo) self.b_redo = Button(self, image=self.im_redo, command=self.redo) # --- tooltips self.tooltip_wrapper = TooltipWrapper(self) self.tooltip_wrapper.add_tooltip(self.b_pause, _("Pause game")) self.tooltip_wrapper.add_tooltip(self.b_restart, _("Restart game")) self.tooltip_wrapper.add_tooltip(self.b_undo, _("Undo")) self.tooltip_wrapper.add_tooltip(self.b_redo, _("Redo")) # --- numbers frame_nb = Frame(self, style='bg.TFrame', width=36) self.progression = [] for i in range(1, 10): self.progression.append(Progression(frame_nb, i)) self.progression[-1].pack(padx=1, pady=1) # --- level indication frame = Frame(self) frame.grid(row=0, columnspan=5, padx=(30, 10), pady=10) Label(frame, text=_("Level") + ' - ', font="Arial 16").pack(side='left') self.label_level = Label(frame, font="Arial 16", text=_("Unknown")) self.label_level.pack(side='left') self.level = "unknown" # puzzle level # --- frame contenant la grille de sudoku self.frame_puzzle = Frame(self, style="bg.TFrame") self.frame_pause = Frame(self, style="case.TFrame") self.frame_pause.grid_propagate(False) self.frame_pause.columnconfigure(0, weight=1) self.frame_pause.rowconfigure(0, weight=1) Label(self.frame_pause, text='PAUSE', style='pause.TLabel', font='Arial 30 bold').grid() # --- placement frame_nb.grid(row=1, column=6, sticky='en', pady=0, padx=(0, 30)) self.frame_puzzle.grid(row=1, columnspan=5, padx=(30, 15)) self.tps.grid(row=2, column=0, sticky="e", padx=(30, 10), pady=30) self.b_pause.grid(row=2, column=1, sticky="w", padx=2, pady=30) self.b_restart.grid(row=2, column=2, sticky="w", padx=2, pady=30) self.b_undo.grid(row=2, column=3, sticky="e", pady=30, padx=2) self.b_redo.grid(row=2, column=4, sticky="w", pady=30, padx=(2, 10)) # --- menu menu = Menu(self, tearoff=0) menu_nouveau = Menu(menu, tearoff=0) menu_levels = Menu(menu_nouveau, tearoff=0) menu_levels.add_command(label=_("Easy"), command=self.new_easy) menu_levels.add_command(label=_("Medium"), command=self.new_medium) menu_levels.add_command(label=_("Difficult"), command=self.new_difficult) menu_nouveau.add_cascade(label=_("Level"), menu=menu_levels) menu_nouveau.add_command(label=_("Generate a puzzle"), command=self.genere_grille, accelerator="Ctrl+G") menu_nouveau.add_command(label=_("Empty grid"), command=self.grille_vide, accelerator="Ctrl+N") menu_ouvrir = Menu(menu, tearoff=0) menu_ouvrir.add_command(label=_("Game"), command=self.import_partie, accelerator="Ctrl+O") menu_ouvrir.add_command(label=_("Puzzle"), command=self.import_grille, accelerator="Ctrl+Shift+O") menu_game = Menu(menu, tearoff=0) menu_game.add_command(label=_("Restart"), command=self.recommence) menu_game.add_command(label=_("Solve"), command=self.resolution) menu_game.add_command(label=_("Save"), command=self.sauvegarde, accelerator="Ctrl+S") menu_game.add_command(label=_("Export"), command=self.export_impression, accelerator="Ctrl+E") menu_game.add_command(label=_("Evaluate level"), command=self.evaluate_level) menu_language = Menu(menu, tearoff=0) self.langue = StringVar(self) self.langue.set(cst.LANGUE[:2]) menu_language.add_radiobutton(label="Français", variable=self.langue, value="fr", command=self.translate) menu_language.add_radiobutton(label="English", variable=self.langue, value="en", command=self.translate) menu_help = Menu(menu, tearoff=0) menu_help.add_command(label=_("Help"), command=self.aide, accelerator='F1') menu_help.add_command(label=_("About"), command=self.about) menu.add_cascade(label=_("New"), menu=menu_nouveau) menu.add_cascade(label=_("Open"), menu=menu_ouvrir) menu.add_cascade(label=_("Game"), menu=menu_game) menu.add_cascade(label=_("Language"), menu=menu_language) menu.add_command(label=_("Statistics"), command=self.show_stat) menu.add_cascade(label=_("Help"), menu=menu_help) self.configure(menu=menu) # --- clavier popup self.clavier = None # --- cases self.nb_cases_remplies = 0 self.blocs = np.zeros((9, 9), dtype=object) for i in range(9): for j in range(9): self.blocs[i, j] = Case(self.frame_puzzle, i, j, self.update_nbs, width=50, height=50) px, py = 1, 1 if i % 3 == 2 and i != 8: py = (1, 3) if j % 3 == 2 and j != 8: px = (1, 3) self.blocs[i, j].grid(row=i, column=j, padx=px, pady=py) self.blocs[i, j].grid_propagate(0) # --- undo/redo stacks self._undo_stack = [] self._redo_stack = [] # --- raccourcis clavier et actions de la souris self.bind("<Button>", self.edit_case) self.bind("<Control-z>", lambda e: self.undo()) self.bind("<Control-y>", lambda e: self.redo()) self.bind("<Control-s>", lambda e: self.sauvegarde()) self.bind("<Control-e>", lambda e: self.export_impression()) self.bind("<Control-o>", lambda e: self.import_partie()) self.bind("<Control-Shift-O>", lambda e: self.import_grille()) self.bind("<Control-n>", lambda e: self.grille_vide()) self.bind("<Control-g>", lambda e: self.genere_grille()) self.bind("<FocusOut>", self.focus_out) self.bind("<F1>", self.aide) # --- open game if file: try: self.load_sudoku(file) except FileNotFoundError: one_button_box(self, _("Error"), _("The file %(file)r does not exist.") % file, image=self.im_erreur) except (KeyError, EOFError, UnpicklingError): try: self.load_grille(file) except Exception: one_button_box(self, _("Error"), _("This file is not a valid sudoku file."), image=self.im_erreur) elif exists(cst.PATH_SAVE): self.load_sudoku(cst.PATH_SAVE) remove(cst.PATH_SAVE) @property def level(self): return self._level @level.setter def level(self, level): self._level = level self.label_level.configure(text=_(level.capitalize())) def update_nbs(self, nb, delta): self.progression[nb - 1].nb += delta def reset_nbs(self): for p in self.progression: p.nb = 0 def evaluate_level(self): grille = Grille() for i in range(9): for j in range(9): val = self.blocs[i, j].get_val() if val: grille.ajoute_init(i, j, val) self.level = difficulte_grille(grille) def show_stat(self): """ show best times """ def reset(): """ reset best times """ for level in ["easy", "medium", "difficult"]: CONFIG.set("Statistics", level, "") top.destroy() if self.chrono_on: self.play_pause() top = Toplevel(self) top.transient(self) top.columnconfigure(1, weight=1) top.resizable(0, 0) top.title(_("Statistics")) top.grab_set() Label(top, text=_("Best times"), font="Sans 12 bold").grid(row=0, columnspan=2, padx=30, pady=10) for i, level in enumerate(["easy", "medium", "difficult"]): Label(top, text=_(level.capitalize()), font="Sans 10 bold").grid(row=i + 1, column=0, padx=(20, 4), pady=4, sticky="e") tps = CONFIG.get("Statistics", level) if tps: tps = int(tps) m = tps // 60 s = tps % 60 Label(top, text=" %i min %i s" % (m, s), font="Sans 10").grid(row=i + 1, column=1, sticky="w", pady=4, padx=(4, 20)) Button(top, text=_("Close"), command=top.destroy).grid(row=4, column=0, padx=(10, 4), pady=10) Button(top, text=_("Clear"), command=reset).grid(row=4, column=1, padx=(4, 10), pady=10) def new_easy(self): nb = np.random.randint(1, 101) fichier = join(cst.PUZZLES_LOCATION, "easy", "puzzle_easy_%i.txt" % nb) self.import_grille(fichier) self.level = "easy" def new_medium(self): nb = np.random.randint(1, 101) fichier = join(cst.PUZZLES_LOCATION, "medium", "puzzle_medium_%i.txt" % nb) self.import_grille(fichier) self.level = "medium" def new_difficult(self): nb = np.random.randint(1, 101) fichier = join(cst.PUZZLES_LOCATION, "difficult", "puzzle_difficult_%i.txt" % nb) self.import_grille(fichier) self.level = "difficult" def translate(self): """ changement de la langue de l'interface """ one_button_box(self, _("Information"), _("The language setting will take effect after restarting the application"), image=self.im_info) CONFIG.set("General", "language", self.langue.get()) def focus_out(self, event): """ met en pause si la fenêtre n'est plus au premier plan """ try: if not self.focus_get() and self.chrono_on: self.play_pause() except KeyError: # erreur déclenchée par la présence d'une tkMessagebox if self.chrono_on: self.play_pause() def stacks_reinit(self): """efface l'historique des actions""" self._undo_stack.clear() self._redo_stack.clear() self.b_undo.configure(state="disabled") self.b_redo.configure(state="disabled") def stacks_modif(self, action): """Record action and clear redo stack.""" self._undo_stack.append(action) self.b_undo.configure(state="normal") self.b_redo.configure(state="disabled") self._redo_stack.clear() def about(self): if self.chrono_on: self.play_pause() About(self) def aide(self, event=None): if self.chrono_on: self.play_pause() Aide(self) def quitter(self): rep = _("Yes") if self.debut: rep = two_button_box(self, _("Confirmation"), _("Do you want to interrupt the current puzzle?"), _("Yes"), _("No"), image=self.im_question) if rep == _("Yes"): if self.debut: self.save(cst.PATH_SAVE) self.destroy() def undo(self): if self._undo_stack and self.chrono_on: self.b_redo.configure(state="normal") i, j, val_prec, pos_prec, modifs, val, pos = self._undo_stack.pop(-1) self._redo_stack.append((i, j, val_prec, pos_prec, modifs, val, pos)) if not self._undo_stack: self.b_undo.configure(state="disabled") if self.blocs[i, j].get_val(): self.modifie_nb_cases_remplies(-1) self.update_nbs(self.blocs[i, j].get_val(), -1) self.blocs[i, j].efface_case() if val_prec: self.modifie_nb_cases_remplies(self.blocs[i, j].edit_chiffre(val_prec)) if not self.test_case(i, j, val): self.update_grille(i, j, val) else: for nb in pos_prec: v = int(nb) self.modifie_nb_cases_remplies(self.blocs[i, j].edit_possibilite(v)) self.test_possibilite(i, j, v) for k, l in modifs: self.blocs[k, l].edit_possibilite(val) def redo(self): if self._redo_stack and self.chrono_on: self.b_undo.configure(state="normal") i, j, val_prec, pos_prec, modifs, val, pos = self._redo_stack.pop(-1) self._undo_stack.append((i, j, val_prec, pos_prec, modifs, val, pos)) if not self._redo_stack: self.b_redo.configure(state="disabled") val_prec = self.blocs[i, j].get_val() if val_prec: self.modifie_nb_cases_remplies(-1) self.update_nbs(val_prec, -1) self.blocs[i, j].efface_case() if val: self.modifie_nb_cases_remplies(self.blocs[i, j].edit_chiffre(val)) if not self.test_case(i, j, val_prec): self.update_grille(i, j, val_prec) else: for nb in pos: v = int(nb) self.modifie_nb_cases_remplies(self.blocs[i, j].edit_possibilite(v)) self.test_possibilite(i, j, v) def restart(self, m=0, s=0): """ réinitialise le chrono et les boutons """ self.chrono = [m, s] self.chrono_on = False self.debut = False self.tps.configure(text=" %02i:%02i" % tuple(self.chrono)) self.b_undo.configure(state="disabled") self.b_pause.configure(state="disabled", image=self.im_pause) self.b_redo.configure(state="disabled") self.b_restart.configure(state="disabled") self.stacks_reinit() self.frame_pause.place_forget() def play_pause(self): """ Démarre le chrono s'il était arrêté, le met en pause sinon """ if self.debut: if self.chrono_on: self.chrono_on = False self.b_pause.configure(image=self.im_play) self.b_redo.configure(state="disabled") self.b_undo.configure(state="disabled") self.tooltip_wrapper.set_tooltip_text(self.b_pause, _("Resume game")) self.frame_pause.place(in_=self.frame_puzzle, x=0, y=0, anchor='nw', relwidth=1, relheight=1) elif self.nb_cases_remplies != 81: self.chrono_on = True self.b_pause.configure(image=self.im_pause) self.tps.after(1000, self.affiche_chrono) if self._undo_stack: self.b_undo.configure(state="normal") if self._redo_stack: self.b_redo.configure(state="normal") self.tooltip_wrapper.set_tooltip_text(self.b_pause, _("Pause game")) self.frame_pause.place_forget() def affiche_chrono(self): """ Met à jour l'affichage du temps """ if self.chrono_on: self.chrono[1] += 1 if self.chrono[1] == 60: self.chrono[0] += 1 self.chrono[1] = 0 self.tps.configure(text=" %02i:%02i" % tuple(self.chrono)) self.tps.after(1000, self.affiche_chrono) def modifie_nb_cases_remplies(self, nb): self.nb_cases_remplies += nb def edit_case(self, event): if event.num in [1, 3]: if not self.debut and self.nb_cases_remplies != 81: self.debut = True self.b_pause.configure(state="normal") self.b_restart.configure(state="normal") self.play_pause() if str(event.widget) != "." and self.chrono_on: if self.clavier: self.clavier.quitter() ref = self.blocs[0, 0].winfo_parent() case = event.widget.grid_info().get("in", None) if str(case) == ref: case = event.widget try: if case.is_modifiable(): if event.num == 1: self.clavier = Clavier(self, case, "val") elif event.num == 3: self.clavier = Clavier(self, case, "possibilite") self.clavier.display("+%i+%i" % (case.winfo_rootx() - 25, case.winfo_rooty() + 50)) except AttributeError: if self.clavier: self.clavier.quitter() elif self.clavier: self.clavier.quitter() def test_case(self, i, j, val_prec=0): """ Teste si la valeur de la case est en contradiction avec celles des autres cases de la ligne / colonne / bloc et renvoie True s'il y a une erreur.""" val = self.blocs[i, j].get_val() a, b = i // 3, j // 3 error = False if val: if ((self.blocs[i, :] == val).sum() > 1 or (self.blocs[:, j] == val).sum() > 1 or (self.blocs[3 * a: 3 * (a + 1), 3 * b: 3 * (b + 1)] == val).sum() > 1): # erreur ! self.blocs[i, j].affiche_erreur() error = True if val_prec: # a number was removed, remove obsolete errors line = self.blocs[i, :] == val_prec column = self.blocs[:, j] == val_prec bloc = self.blocs[3 * a: 3 * (a + 1), 3 * b: 3 * (b + 1)] == val_prec if line.sum() == 1: self.blocs[i, line.argmax()].no_error() self.test_case(i, line.argmax()) if column.sum() == 1: self.blocs[column.argmax(), j].no_error() self.test_case(column.argmax(), j) if bloc.sum() == 1: x, y = divmod(bloc.argmax(), 3) self.blocs[3 * a + x, 3 * b + y].no_error() self.test_case(3 * a + x, 3 * b + y) return error def test_possibilite(self, i, j, val): """ Teste si la possibilité val de la case est en contradiction avec les valeurs des autres cases de la ligne / colonne / bloc """ a, b = i // 3, j // 3 if ((self.blocs[i, :] == val).sum() > 0 or (self.blocs[:, j] == val).sum() > 0 or (self.blocs[3 * a: 3 * (a + 1), 3 * b: 3 * (b + 1)] == val).sum() > 0): # erreur ! self.blocs[i, j].affiche_erreur_possibilite(val) def test_remplie(self): """ Test si la grille est remplie """ if self.nb_cases_remplies == 81: grille = Grille() for i in range(9): for j in range(9): val = self.blocs[i, j].get_val() if val: grille.ajoute_init(i, j, val) sol = grille.solve() if type(sol) == np.ndarray: self.play_pause() self.frame_pause.place_forget() one_button_box(self, _("Information"), _("You solved the puzzle in %(min)i minutes and %(sec)i secondes.") % {"min": self.chrono[0], "sec": self.chrono[1]}, image=self.im_info) if self.level != "unknown": best = CONFIG.get("Statistics", self.level) current = self.chrono[0] * 60 + self.chrono[1] if best: best = int(best) if current < best: CONFIG.set("Statistics", self.level, str(current)) else: CONFIG.set("Statistics", self.level, str(current)) self.b_pause.configure(state="disabled") self.debut = False else: i, j = sol[1] if self.blocs[i, j].get_val(): self.blocs[i, j].affiche_erreur() one_button_box(self, _("Information"), _("There is a mistake."), image=self.im_info) def update_grille(self, i, j, val_prec=0): """ Enlève les possibilités devenues impossibles suite à l'ajout d'une valeur dans la case (i, j) """ val = self.blocs[i, j].get_val() modif = [] a, b = i // 3, j // 3 if val_prec: x, y = divmod(val_prec - 1, 3) for k, (line, column, bloc) in enumerate(zip(self.blocs[i, :], self.blocs[:, j], self.blocs[3 * a: 3 * (a + 1), 3 * b: 3 * (b + 1)].flatten())): # works because if line is bloc then pos1 is pos3 and both are edited at once pos1 = line.get_possibilites() pos2 = column.get_possibilites() pos3 = bloc.get_possibilites() if val in pos1: self.blocs[i, k].edit_possibilite(val) modif.append((i, k)) if val in pos2: self.blocs[k, j].edit_possibilite(val) modif.append((k, j)) if val in pos3: m, n = divmod(k, 3) self.blocs[3 * a + m, 3 * b + n].edit_possibilite(val) modif.append((3 * a + m, 3 * b + n)) if val_prec: if val_prec in pos1: self.blocs[i, k].pas_erreur(x, y) self.test_possibilite(i, k, val_prec) if val_prec in pos2: self.blocs[k, j].pas_erreur(x, y) self.test_possibilite(k, j, val_prec) if val_prec in pos3: m, n = divmod(k, 3) m, n = 3 * a + m, 3 * b + n if m != i and n != j: self.blocs[m, n].pas_erreur(x, y) self.test_possibilite(m, n, val_prec) return modif def set_clavier(self, c): self.clavier = c def grille_vide(self): rep = _("Yes") if self.debut: rep = two_button_box(self, _("Confirmation"), _("Do you want to abandon the current puzzle?"), _("Yes"), _("No"), self.im_question) if rep == _("Yes"): self.nb_cases_remplies = 0 self.restart() self.level = "unknown" self.reset_nbs() for i in range(9): for j in range(9): self.blocs[i, j].set_modifiable(True) self.blocs[i, j].efface_case() def genere_grille(self): """ Génère une nouvelle grille """ if self.chrono_on: self.play_pause() rep = _("Yes") if self.debut: rep = two_button_box(self, _("Confirmation"), _("Do you want to abandon the current puzzle?"), _("Yes"), _("No"), self.im_question) if rep == _("Yes"): self.configure(cursor="watch") self.update() rep2 = _("Retry") while rep2 == _("Retry"): grille = genere_grille() diff = difficulte_grille(grille) nb = grille.nb_cases_remplies() self.configure(cursor="") rep2 = two_button_box(self, _("Information"), _("The generated puzzle contains %(nb)i numbers and its level is %(difficulty)s.") % ({"nb": nb, "difficulty": _(diff.capitalize())}), _("Play"), _("Retry"), image=self.im_info) if rep2 == _("Play"): self.level = diff self.affiche_grille(grille.get_sudoku()) def recommence(self): if self.chrono_on: self.play_pause() rep = _("Yes") if self.debut: rep = two_button_box(self, _("Confirmation"), _("Do you really want to start again?"), _("Yes"), _("No"), self.im_question) if rep == _("Yes"): self.reset_nbs() for i in range(9): for j in range(9): if self.blocs[i, j].is_modifiable(): if self.blocs[i, j].get_val(): self.nb_cases_remplies -= 1 self.blocs[i, j].efface_case() else: self.update_nbs(self.blocs[i, j].get_val(), 1) self.restart() elif self.debut: self.play_pause() def save(self, path): grille = np.zeros((9, 9), dtype=int) modif = np.zeros((9, 9), dtype=bool) possibilites = [] for i in range(9): possibilites.append([]) for j in range(9): grille[i, j] = self.blocs[i, j].get_val() modif[i, j] = self.blocs[i, j].is_modifiable() possibilites[i].append(self.blocs[i, j].get_possibilites()) with open(path, "wb") as fich: p = Pickler(fich) p.dump(grille) p.dump(modif) p.dump(possibilites) p.dump(self.chrono) p.dump(self.level) def sauvegarde(self): if self.chrono_on: self.play_pause() fichier = asksaveasfilename(initialdir=cst.INITIALDIR, defaultextension='.sudoku', filetypes=[('Sudoku', '*.sudoku')]) if fichier: self.save(fichier) def affiche_grille(self, grille): """ Affiche la grille """ self.nb_cases_remplies = 0 self.restart() self.reset_nbs() for i in range(9): for j in range(9): nb = grille[i, j] self.blocs[i, j].efface_case() if nb: self.blocs[i, j].set_modifiable(False) self.nb_cases_remplies += 1 self.blocs[i, j].edit_chiffre(nb) else: self.blocs[i, j].set_modifiable(True) def load_sudoku(self, file): with open(file, "rb") as fich: dp = Unpickler(fich) grille = dp.load() modif = dp.load() possibilites = dp.load() chrono = dp.load() self.level = dp.load() self.nb_cases_remplies = 0 self.reset_nbs() self.restart(*chrono) for i in range(9): for j in range(9): self.blocs[i, j].efface_case() if grille[i, j]: self.nb_cases_remplies += 1 self.blocs[i, j].edit_chiffre(grille[i, j]) else: for pos in possibilites[i][j]: self.blocs[i, j].edit_possibilite(pos) self.blocs[i, j].set_modifiable(modif[i, j]) def import_partie(self): """ importe une partie stockée dans un fichier .sudoku """ if self.chrono_on: self.play_pause() rep = _("Yes") if self.debut: rep = two_button_box(self, _("Confirmation"), _("Do you want to abandon the current puzzle?"), _("Yes"), _("No"), self.im_question) if rep == _("Yes"): fichier = askopenfilename(initialdir=cst.INITIALDIR, defaultextension='.sudoku', filetypes=[('Sudoku', '*.sudoku')]) if fichier: try: self.load_sudoku(fichier) except FileNotFoundError: one_button_box(self, _("Error"), _("The file %(file)r does not exist.") % fichier, image=self.im_erreur) except (KeyError, EOFError, UnpicklingError): one_button_box(self, _("Error"), _("This file is not a valid sudoku file."), image=self.im_erreur) elif self.debut: self.play_pause() def resolution_init(self): """ Résolution de la grille initiale (sans tenir compte des valeurs rentrées par l'utilisateur. """ grille = Grille() for i in range(9): for j in range(9): if not self.blocs[i, j].is_modifiable(): val = self.blocs[i, j].get_val() grille.ajoute_init(i, j, val) self.configure(cursor="watch") self.update() sol = grille.solve() self.configure(cursor="") if type(sol) == np.ndarray: for i in range(9): for j in range(9): val = self.blocs[i, j].get_val() if not val: self.blocs[i, j].edit_chiffre(sol[i, j]) self.blocs[i, j].affiche_solution() elif self.blocs[i, j].is_modifiable(): if val != sol[i, j]: self.blocs[i, j].edit_chiffre(sol[i, j]) self.blocs[i, j].affiche_erreur() self.restart() self.nb_cases_remplies = 81 elif sol[1]: i, j = sol[1] if self.blocs[i, j].get_val(): self.blocs[i, j].affiche_erreur() one_button_box(self, _("Error"), _("The grid is wrong. It cannot be solved."), image=self.im_erreur) else: one_button_box(self, _("Error"), _("Resolution failed."), image=self.im_erreur) def resolution(self): if self.chrono_on: self.play_pause() rep = two_button_box(self, _("Confirmation"), _("Do you really want to get the solution?"), _("Yes"), _("No"), image=self.im_question) if rep == _("Yes"): self.frame_pause.place_forget() grille = Grille() for i in range(9): for j in range(9): val = self.blocs[i, j].get_val() if val: grille.ajoute_init(i, j, val) self.configure(cursor="watch") self.update() sol = grille.solve() self.configure(cursor="") if type(sol) == np.ndarray: for i in range(9): for j in range(9): val = self.blocs[i, j].get_val() if not val: self.blocs[i, j].edit_chiffre(sol[i, j]) self.blocs[i, j].affiche_solution() self.restart() self.b_restart.configure(state="normal") self.nb_cases_remplies = 81 elif sol[1]: i, j = sol[1] if self.blocs[i, j].get_val(): self.blocs[i, j].affiche_erreur() i, j = 0, 0 while i < 9 and self.blocs[i, j].is_modifiable(): j += 1 if j == 9: i += 1 j = 0 if i < 9: # il y a au moins une case de type "initial" rep = two_button_box(self, _("Error"), _("The grid is wrong. It cannot be solved. Do you want the solution of the initial grid?"), _("Yes"), _("No"), image=self.im_erreur) if rep == _("Yes"): self.resolution_init() else: one_button_box(self, _("Error"), _("The grid is wrong. It cannot be solved."), image=self.im_erreur) else: one_button_box(self, _("Error"), _("Resolution failed."), image=self.im_erreur) def load_grille(self, file): gr = np.loadtxt(file, dtype=int) if gr.shape == (9, 9): self.affiche_grille(gr) self.level = "unknown" else: one_button_box(self, _("Error"), _("This is not a 9x9 sudoku grid."), image=self.im_erreur) def import_grille(self, fichier=None): """ importe une grille stockée dans un fichier txt sous forme de chiffres séparés par des espaces (0 = case vide) """ if self.chrono_on: self.play_pause() rep = _("Yes") if self.debut: rep = two_button_box(self, _("Confirmation"), _("Do you want to abandon the current puzzle?"), _("Yes"), _("No"), self.im_question) if rep == _("Yes"): if not fichier: fichier = askopenfilename(initialdir=cst.INITIALDIR, defaultextension='.txt', filetypes=[('Text', '*.txt'), ('Tous les fichiers', "*")]) if fichier: try: self.load_grille(fichier) except (ValueError, UnicodeDecodeError): one_button_box(self, _("Error"), _("The file does not have the right format. It should be a .txt file with cell values separated by one space. 0 means empty cell."), image=self.im_erreur) except FileNotFoundError: one_button_box(self, _("Error"), _("The file %(file)r does not exist.") % fichier, image=self.im_erreur) elif self.debut: self.play_pause() def export_impression(self): """ exporte la grille en image (pour pouvoir l'imprimer) """ if self.chrono_on: self.play_pause() fichier = asksaveasfilename(title=_("Export"), initialdir=cst.INITIALDIR, defaultextension='.png', filetypes=[('PNG', '*.png'), ('JPEG', 'jpg')]) if fichier: grille = np.zeros((9, 9), dtype=int) for i in range(9): for j in range(9): grille[i, j] = self.blocs[i, j].get_val() font = ImageFont.truetype("arial.ttf", 64) im = Image.new("RGB", (748, 748), "white") draw = ImageDraw.Draw(im) i = 0 l = 1 while i < 10: if i % 3 == 0: w = 4 else: w = 2 draw.line((l, 1, l, 748), width=w, fill="black") draw.line((1, l, 748, l), width=w, fill="black") l += 80 + w i += 1 for i in range(9): for j in range(9): if grille[i, j]: draw.text((26 + j * 82 + 2 * (j // 3), 10 + i * 82 + 2 * (i // 3)), " %i" % grille[i, j], fill="black", font=font) del draw im.save(fichier)
class P300Window(object): def __init__(self, master: Tk): self.master = master master.title('P300 speller') #Parameters self.imagesize = 125 self.images_folder_path = '../utils/images/' #use utils/char_generator to generate any image you want self.flash_image_path = '../utils/images/flash_images/einstein.jpg' self.number_of_rows = 6 self.number_of_columns = 6 #make sure you have 6 x 6 amount of images in the images_folder_path self.lsl_streamname = 'P300_stream' self.flash_mode = 2 #single element #1 for columns and rows; currently is NOT working yet; if I have time, will revisit self.flash_duration = 100 #soa self.break_duration = 250 #iti self.set_of_repetition = 12 self.number_of_flashes_per_repetition = 10 self.total_repetitions_per_trial = self.set_of_repetition * self.number_of_flashes_per_repetition #30 repetitions per letters self.trials = 6 #number of letters self.delay = 2500 #interval between trial self.letter_idx = 0 #did not include numbers yet! self.random_letter = random.choices( string.ascii_lowercase, k=self.trials) #randomize [self.trials] number letters self.word = ''.join(self.random_letter) # Variables self.usable_images = [] self.image_labels = [] self.flash_sequence = [] self.flash_image = None self.sequence_number = 0 self.lsl_output = None self.running = 0 #for pause self.image_frame = Frame(self.master) self.image_frame.grid(row=0, column=0, rowspan=self.number_of_rows, columnspan=self.number_of_columns) self.start_btn_text = StringVar() self.start_btn_text.set('Start') self.start_btn = Button(self.master, textvariable=self.start_btn_text, command=self.start) self.start_btn.grid(row=self.number_of_rows + 2, column=self.number_of_columns - 1) self.pause_btn = Button(self.master, text='Pause', command=self.pause) self.pause_btn.grid(row=self.number_of_rows + 2, column=self.number_of_columns - 4) #-4 for center self.pause_btn.configure(state='disabled') self.close_btn = Button(self.master, text='Close', command=master.quit) self.close_btn.grid(row=self.number_of_rows + 2, column=0) self.show_highlight_letter(0) # Initialization self.show_images() self.create_flash_sequence() self.lsl_output = self.create_lsl_output() def open_images(self): self.usable_images = [] self.highlight_letter_images = [] letter_images = sorted( glob.glob( os.path.join(self.images_folder_path, 'letter_images/*.png'))) #currently, still did not flash number yet! number_images = sorted( glob.glob( os.path.join(self.images_folder_path, 'number_images/*.png'))) letter_highlight_images = sorted( glob.glob( os.path.join(self.images_folder_path, 'letter_highlight_images/*.png'))) number_highlight_images = sorted( glob.glob( os.path.join(self.images_folder_path, 'number_highlight_images/*.png'))) for number_image in number_images: letter_images.append(number_image) #print("Paths: ", letter_images) min_number_of_images = self.number_of_columns * self.number_of_rows if len(letter_images) < min_number_of_images: print('To few images in folder: ' + self.images_folder_path) return # Convert and resize images for image_path in letter_images: image = Image.open(image_path) resized = image.resize((self.imagesize, self.imagesize), Image.BICUBIC) Tkimage = ImageTk.PhotoImage(resized) self.usable_images.append(Tkimage) # Convert and resize images for image_path in letter_highlight_images: image = Image.open(image_path) resized = image.resize((self.imagesize, self.imagesize), Image.BICUBIC) Tkimage = ImageTk.PhotoImage(resized) self.highlight_letter_images.append(Tkimage) flash_img = Image.open(self.flash_image_path) flash_img_res = flash_img.resize((self.imagesize, self.imagesize), Image.BICUBIC) self.flash_image = ImageTk.PhotoImage(flash_img_res) def show_images(self): self.open_images() if self.usable_images == []: print('No images opened') return num_rows = self.number_of_rows num_cols = self.number_of_columns # Arrange images for r in range(0, num_rows): for c in range(0, num_cols): current_image = self.usable_images[r * num_cols + c] label = Label(self.image_frame, image=current_image) label.image = current_image label.grid(row=r, column=c) self.image_labels.append(label) def create_lsl_output(self): """Creates an LSL Stream outlet""" info = StreamInfo(name=self.lsl_streamname, type='Markers', channel_count=1, channel_format='int8', nominal_srate=IRREGULAR_RATE, source_id='marker_stream', handle=None) if self.flash_mode == 1: info.desc().append_child_value('flash_mode', 'Row and Column') elif self.flash_mode == 2: info.desc().append_child_value('flash_mode', 'Single Value') info.desc().append_child_value('num_rows', str(self.number_of_rows)) info.desc().append_child_value('num_cols', str(self.number_of_columns)) return StreamOutlet(info) def create_flash_sequence(self): self.flash_sequence = [] num_rows = self.number_of_rows num_cols = self.number_of_columns if self.flash_mode == 1: print( 'CAUTION: Row and Column flash mode currently uses only random samples!' ) self.flash_sequence = np.random.randint( 0, num_rows + num_cols, 3000) #3000 should be enough elif self.flash_mode == 2: maximum_number = num_rows * num_cols for i in range(len(self.word)): for j in range(self.set_of_repetition): #generate three sets seq = list( range(maximum_number)) #generate 0 to maximum_number random.shuffle(seq) #shuffle index = string.ascii_lowercase.index(self.word[i]) allowed_values = list(range(0, maximum_number)) allowed_values.remove( index ) #reduce number of flashed by first excluding the actual letter #print("Index: ", index) while (len(seq) > self.number_of_flashes_per_repetition ): #cut down array until there is only 10 values choice = random.choice(allowed_values) if choice in seq: seq.remove(choice) #make sure no repeating element in consecutive order #the consecutive happens when we combine the next array, thus we simply check the tail of seq and head of the array try: if seq[0] == flash_sequence[-1]: seq[0], seq[-6] = seq[-6], seq[ 0] #swap the consecutive to some other places, here i choose -6 except NameError: flash_sequence = [] flash_sequence.extend(seq) self.flash_sequence = flash_sequence def start(self): self.running = 1 letter = self.word[0] image_index = string.ascii_lowercase.index(letter) self.highlight_image(image_index) self.start_btn.configure(state='disabled') self.pause_btn.configure(state='normal') self.master.quit def pause(self): self.running = 0 self.start_btn_text.set('Resume') self.start_btn.configure(state='normal') self.pause_btn.configure(state='disabled') def start_flashing(self): if self.sequence_number == len( self.flash_sequence ): #stop flashing if all generated sequence number runs out print('All elements had flashed - run out of juice') self.running = 0 self.sequence_number = 0 return if self.running == 0: print('Flashing paused at sequence number ' + str(self.sequence_number)) return element_to_flash = self.flash_sequence[self.sequence_number] letter = self.word[self.letter_idx] image_index = string.ascii_lowercase.index(letter) #pushed markers to LSL stream timestamp = local_clock() if (element_to_flash == image_index): print("Pushed to the LSL: ", "Marker: ", [2], "; Timestamp: ", timestamp, "Seq: ", self.sequence_number, "Target letter - [Letter#]: ", self.letter_idx, " [Image index]: ", image_index, " [Character]: ", letter, "Flash element: ", element_to_flash) self.lsl_output.push_sample([2], timestamp) #2 for targets else: #print("Pushed to the LSL: ", "Marker: ", [1], "; Timestamp: ", timestamp) self.lsl_output.push_sample([1], timestamp) #1 for non-targets #flashing if self.flash_mode == 1: self.flash_row_or_col(element_to_flash) elif self.flash_mode == 2: self.flash_single_element(element_to_flash) if (self.letter_idx < len(self.word)): if ( (self.sequence_number + 1) % self.total_repetitions_per_trial == 0 ): #every self.repetitions, change letter (0 - 29, 30 - 59, 60 - 89 etc.) self.letter_idx += 1 if (self.letter_idx == len(self.word)): return letter = self.word[self.letter_idx] image_index = string.ascii_lowercase.index(letter) self.master.after(self.break_duration, self.highlight_target, image_index) else: self.master.after(self.break_duration, self.start_flashing) self.sequence_number = self.sequence_number + 1 #change flash position def highlight_target(self, image_index): self.show_highlight_letter(self.letter_idx) self.highlight_image(image_index) def change_image(self, label, img): label.configure(image=img) label.image = img def highlight_image(self, element_no): self.change_image(self.image_labels[element_no], self.highlight_letter_images[element_no]) self.master.after(self.delay, self.unhighlight_image, element_no) def unhighlight_image(self, element_no): self.change_image(self.image_labels[element_no], self.usable_images[element_no]) self.master.after(self.flash_duration, self.start_flashing) def show_highlight_letter(self, pos): fontStyle = tkFont.Font(family="Courier", size=40) fontStyleBold = tkFont.Font(family="Courier bold", size=40) text = Text(root, height=1, font=fontStyle) text.tag_configure("bold", font=fontStyleBold) text.tag_configure("center", justify='center') for i in range(0, len(self.word)): if (i != pos): text.insert("end", self.word[i]) else: text.insert("end", self.word[i], "bold") text.configure(state="disabled", width=10) text.tag_add("center", "1.0", "end") text.grid(row=self.number_of_rows + 1, column=self.number_of_columns - 4) def flash_row_or_col(self, rc_number): num_rows = self.number_of_rows num_cols = self.number_of_columns if rc_number < num_rows: for c in range(0, num_cols): #flash row cur_idx = rc_number * num_cols + c self.change_image(self.image_labels[cur_idx], self.flash_image) else: current_column = rc_number - num_rows for r in range(0, num_rows): #flash column cur_idx = current_column + r * num_cols self.change_image(self.image_labels[cur_idx], self.flash_image) self.master.after(self.flash_duration, self.unflash_row_or_col, rc_number) def unflash_row_or_col(self, rc_number): num_rows = self.number_of_rows num_cols = self.number_of_columns if rc_number < num_rows: for c in range(0, num_cols): #flash row cur_idx = rc_number * num_cols + c self.change_image(self.image_labels[cur_idx], self.usable_images[cur_idx]) else: current_column = rc_number - num_rows for r in range(0, num_rows): #flash column cur_idx = current_column + r * num_cols self.change_image(self.image_labels[cur_idx], self.usable_images[cur_idx]) def flash_single_element(self, element_no): self.change_image(self.image_labels[element_no], self.flash_image) self.master.after(self.flash_duration, self.unflash_single_element, element_no) def unflash_single_element(self, element_no): self.change_image(self.image_labels[element_no], self.usable_images[element_no])
def _create_header_button(self, text) -> Button: button = Button(self.table, text=text) button.configure(command=lambda index=len( self.__headers): self.sort_data(index)) return button
class GUI(Tk): def __init__(self, master=None): Tk.__init__(self, master) self.b1 = Button(self, text="Start A*", command=self.start_alg) self.b1.grid(row=0, column=0) self.b1.configure(state="disabled") self.load_thread = threading.Thread(target=self.initImage) self.load_thread.start() def initImage(self): """ Loads the image from the file, using the appropriate function. """ if LAB_PATH is None: print("Generating labyrinth...") self.labyrinth = maze(*MAZE_SIZE, size=SIZE) self.im = lab_to_im(self.labyrinth) else: print("Reading labyrinth from {}...".format(LAB_PATH)) if LAB_PATH.endswith("map"): load_fun = load_from_map_file else: load_fun = load_from_img self.labyrinth, self.im = load_fun(LAB_PATH) self.pix = self.im.load() print("Read!") self.b1.configure(state="normal") def setImage(self, im): im2 = im.resize(self.newsize, Image.NEAREST) self.image = ImageTk.PhotoImage(im2) self.panel.configure(image=self.image) def gui_callback(self, queue, path=None): """ Draws the A* queue, and eventually the path computed. """ path = path or [] queue = queue or [] for node in queue: x, y = [int(x) for x in node[1]] # convert numpy.int32 to int h = self.heur_goal((x, y)) v = h / self.max_heur r = int(v * 255) g = int((1 - v) * 255) self.pix[x, y] = r, g, 0 for node in path: x, y = node self.pix[x, y] = 0, 0, 255 start = self.labyrinth.start self.pix[start[0], start[1]] = 255, 0, 0 self.setImage(self.im) def start_alg(self): children = list(self.children.values()) for child in children: child.destroy() # removes the button self.panel = Label(self) self.panel.pack() self.newsize = tuple([int(i * SCALE) for i in self.im.size]) self.geometry("{}x{}+200+200".format(*self.newsize)) self.update() threading.Thread(target=self.compute).start() # start the computation def compute(self): if not self.labyrinth.start or not self.labyrinth.goal: raise ValueError("Start or goal not found") self.children_gen = NeighborsGeneratorJPS(self.labyrinth) self.heur = heur_diag self.heur_goal = self.heur(self.labyrinth.goal, self.labyrinth.start) self.max_heur = int(self.heur_goal(self.labyrinth.start)) print("Start detected:\t{}".format(self.labyrinth.start)) print("Goal detected:\t{}".format(self.labyrinth.goal)) print("Starting search...") eq_to_goal = lambda p: np.array_equal(p, self.labyrinth.goal) c_time = time.perf_counter() path, *_, info = a_star(self.labyrinth.start, eq_to_goal, self.heur_goal, self.children_gen, self.gui_callback) c_time = (time.perf_counter() - c_time) if path: path, cost = self.reconstruct_path(path) else: cost = float("inf") # print(path) print("Search ended") print("Time:", round(c_time, 2), "s") print("Nodes searched:", info.nodes) print("Maximum list size:", info.maxl) print("Path cost:", cost) if path is None: print("Path not found") else: print("Found path of", len(path), "nodes") print("Path generation completed") print("*" * 100) if PATH_DELAY_TIME: for i in range(len(path)): self.gui_callback(None, path[:i]) time.sleep(PATH_DELAY_TIME) else: self.gui_callback(None, path) def reconstruct_path(self, path): """ Reconstructs the complete path inserting nodes which were "jumped" by the generating function. This is possible because JPS generates jump only in straight or diagonal lines. For example: [(0,0),(4,4)] -> [(0,0),(1,1),(2,2),(3,3),(4,4)]. """ def pairwise(iterable): a, b = it.tee(iterable) next(b, None) return zip(a, b) cost = 0 expanded_path = [path[0]] sqrt_2 = 2 ** 0.5 for cur_node, next_node in pairwise(path): cur_node = np.array(cur_node) next_node = np.array(next_node) direction = normalize(next_node - cur_node) cost_unit = sqrt_2 if np.all(direction) else 1 while not np.array_equal(cur_node, next_node): cur_node = tuple(int(x) for x in cur_node + direction) expanded_path.append(cur_node) cost += cost_unit return expanded_path, cost
class MainWindow(mp.Process): """Defines the main control window and all its control logic. As Tkinter only allows to create root windwo (Tk()) in the main process, this is implemented as its own subprocess that will open the window with a call to self.run() Args: connector_dict: Dictionary create by calling multiprocessing.Manger().dict() message_q: Queue that will be polled every few seconds. Elements in queue will be plotted to the internal text field. start_analysis_e: Event signaling if analysis can be started connected_e: Event signaling that LSL streams are connected ready_for_connection_e: Event signaling that LSL streams have been selected in GUI save_e: Event signaling that data should be saved. """ def __init__(self, connector_dict: Dict, message_q: mp.Queue, start_recording_e: mp.Event, start_analysis_e: mp.Event, connected_e: mp.Event, ready_for_connection_e: mp.Event, save_e: mp.Event): super().__init__() self.connector_dict = connector_dict self.message_q = message_q self.start_recording_e = start_recording_e self.start_analysis_e = start_analysis_e self.ready_for_connection_e = ready_for_connection_e self.connected_e = connected_e self.save_e = save_e self.master = None # Parameters self.eeg_stream = None self.eeg_streams_dict = None self.marker_stream = None self.marker_streams_dict = None self.channel_select = None self.update_interval = None self.record_time = None self.y_min = None self.y_max = None self.save_filename = None self.filter_check = None self.squared_check = None self.connected = None # Widgets self.eeg_stream_label = None self.eeg_stream_combobox = None self.eeg_stream_button = None self.marker_stream_label = None self.marker_stream_combobox = None self.marker_stream_button = None self.filter_checkbutton_label = None self.filter_checkbutton = None self.connect_button = None self.seperator = None self.start_recording_btn = None self.record_time_label = None self.Separator_2 = None self.update_interval_label = None self.update_interval_combobox = None self.start_analysis_btn = None self.channel_select_label = None self.channel_select_combobox = None self.squared_label = None self.squared_checkbtn = None self.update_ylim_btn = None self.y_min_label = None self.y_min_entry = None self.y_max_label = None self.y_max_entry = None self.seperator = None self.save_label = None self.save_entry = None self.save_btn = None self.text_console = None def build_main_window(self): # Hack to make tkinter work in other process than main from tkinter import Tk, StringVar, Text, HORIZONTAL, EW, IntVar from tkinter.ttk import Separator, Combobox, Button, Label, Entry, Checkbutton self.master = Tk() # Parameters self.eeg_stream = StringVar() self.eeg_streams_dict = {} self.marker_stream = StringVar() self.marker_streams_dict = {} self.channel_select = IntVar() self.channel_select.set(0) self.update_interval = IntVar() self.update_interval.set(0) self.record_time = StringVar() self.record_time.set("00:00 minutes recorded") self.y_min = IntVar() self.y_min.set(-10) self.y_max = IntVar() self.y_max.set(10) self.save_filename = StringVar() self.save_filename.set("1") self.filter_check = IntVar() self.filter_check.set(1) self.squared_check = IntVar() self.squared_check.set(0) self.connected = False self.print_from_queue() # Widgets self.eeg_stream_label = Label(self.master, text='EEG LSL-stream:') self.eeg_stream_label.grid(row=0, column=0) self.eeg_stream_combobox = Combobox(self.master, textvariable=self.eeg_stream) self.eeg_stream_combobox.configure(state='disabled') self.eeg_stream_combobox.grid(row=0, column=1) self.eeg_stream_button = Button( self.master, text='Refresh', command=lambda: self.find_streams('EEG', self.eeg_stream_combobox, self.eeg_streams_dict, self. eeg_stream)) self.eeg_stream_button.grid(row=0, column=2) self.marker_stream_label = Label(self.master, text='Marker LSL-stream:') self.marker_stream_label.grid(row=1, column=0) self.marker_stream_combobox = Combobox(self.master, textvariable=self.marker_stream) self.marker_stream_combobox.configure(state='disabled') self.marker_stream_combobox.grid(row=1, column=1) self.marker_stream_button = Button( self.master, text='Refresh', command=lambda: self.find_streams( 'P300_Marker', self.marker_stream_combobox, self. marker_streams_dict, self.marker_stream)) self.marker_stream_button.grid(row=1, column=2) self.filter_checkbutton_label = Label( self.master, text='Filter (Butter, Order 4, Cutoff: 1, 30):') self.filter_checkbutton_label.grid(row=2, column=0) self.filter_checkbutton = Checkbutton(self.master, variable=self.filter_check, text='') self.filter_checkbutton.grid(row=2, column=1) self.connect_button = Button(self.master, text='Connect', command=self.connect_streams) self.connect_button.grid(row=2, column=2) self.connect_button.configure(state='disabled') self.seperator = Separator(self.master, orient=HORIZONTAL) self.seperator.grid(row=3, column=0, columnspan=3, sticky=EW) self.start_recording_btn = Button(self.master, text='Start recoding', command=self.start_recording) self.start_recording_btn.grid(row=4, column=2) self.start_recording_btn.configure(state='disabled') self.record_time_label = Label(self.master, textvariable=self.record_time) self.record_time_label.grid(row=4, column=1) self.Separator_2 = Separator(self.master) self.Separator_2.grid(row=5, column=0, columnspan=3, sticky=EW) self.update_interval_label = Label(self.master, text='Update interval (seconds):') self.update_interval_label.grid(row=6, column=0) self.update_interval_combobox = Combobox( self.master, textvariable=self.update_interval) self.update_interval_combobox.bind('<<ComboboxSelected>>', self.update_connector_dict) self.update_interval_combobox.grid(row=6, column=1) self.update_interval_combobox['values'] = list(range(10)) self.update_interval_combobox.configure(state='disabled') self.start_analysis_btn = Button(self.master, text='Start analysis', command=self.start_analysis) self.start_analysis_btn.grid(row=6, column=2) self.start_analysis_btn.configure(state='disabled') self.channel_select_label = Label(self.master, text='Channel to display:') self.channel_select_label.grid(row=7, column=0) self.channel_select_combobox = Combobox( self.master, textvariable=self.channel_select) self.channel_select_combobox.bind('<<ComboboxSelected>>', self.update_connector_dict) self.channel_select_combobox.grid(row=7, column=1) self.channel_select_combobox.configure(state='disabled') self.squared_label = Label(self.master, text='squared') self.squared_label.grid(row=8, column=0) self.squared_checkbtn = Checkbutton(self.master, variable=self.squared_check) self.squared_checkbtn.grid(row=8, column=1) self.squared_checkbtn.configure(state='disabled') self.update_ylim_btn = Button(self.master, text='Update', command=self.update_connector_dict) self.update_ylim_btn.grid(row=8, column=2) self.update_ylim_btn.configure(state='disabled') self.y_min_label = Label(self.master, text='Y min:') self.y_min_label.grid(row=9, column=0) self.y_min_entry = Entry(self.master, textvariable=self.y_min) self.y_min_entry.grid(row=9, column=1) self.y_min_entry.configure(state='disabled') self.y_max_label = Label(self.master, text='Y max:') self.y_max_label.grid(row=10, column=0) self.y_max_entry = Entry(self.master, textvariable=self.y_max) self.y_max_entry.grid(row=10, column=1) self.y_max_entry.configure(state='disabled') self.seperator = Separator(self.master, orient=HORIZONTAL) self.seperator.grid(row=11, column=0, columnspan=3, sticky=EW) self.save_label = Label(self.master, text='Filename:') self.save_label.grid(row=12, column=0) self.save_entry = Entry(self.master, textvariable=self.save_filename) self.save_entry.grid(row=12, column=1) self.save_entry.configure(state='disabled') self.save_btn = Button(self.master, text='Save', command=self.save) self.save_btn.grid(row=12, column=2) self.save_btn.configure(state='disabled') self.text_console = Text(self.master) self.text_console.grid(row=15, column=0, rowspan=3, columnspan=3) self.text_console.configure(state='disabled') def save(self): self.update_connector_dict() self.save_e.set() def update_channel_select(self): num_channels = self.connector_dict['number of channels'] self.channel_select_combobox['values'] = list(range(num_channels)) def start_recording(self): self.connect_button.configure(state='disabled') self.start_recording_btn.configure(state='disabled') self.channel_select_combobox.configure(state='normal') self.update_interval_combobox.configure(state='normal') self.y_min_entry.configure(state='normal') self.y_max_entry.configure(state='normal') self.update_ylim_btn.configure(state='normal') self.squared_checkbtn.configure(state='normal') self.update_channel_select() self.update_recording_time() self.start_recording_e.set() self.start_analysis_btn.configure(state='normal') def update_recording_time(self): num_samples = self.connector_dict['sample count'] samplerate = self.connector_dict['samplerate'] number_of_seconds = int(num_samples / samplerate) minutes = number_of_seconds // 60 remaining_seconds = number_of_seconds % 60 result_string = '{:02}:{:02} minutes recorded'.format( minutes, remaining_seconds) self.record_time.set(result_string) self.master.after(1000, self.update_recording_time) def start_analysis(self): self.start_analysis_btn.configure(state='disabled') self.save_btn.configure(state='normal') self.save_entry.configure(state='normal') self.update_connector_dict() self.start_analysis_e.set() def find_streams(self, stream_type, widget, stream_dict, var): stream_dict.clear() timeout = 3 self.print_to_console('Searching for ' + stream_type + ' streams... (timeout = ' + str(timeout) + ' seconds)') streams = resolve_byprop('type', stream_type, timeout=timeout) if not streams: self.print_to_console('No stream found.') return widget.configure(state='normal') stream_list = [] for stream in streams: stream_dict[stream.name()] = stream stream_list.append(stream.name()) widget['values'] = stream_list if len(streams) >= 1: var.set(streams[0].name()) self.print_to_console(str(len(streams)) + ' Stream(s) found!') self.test_if_two_streams() def test_if_two_streams(self): if self.eeg_stream.get() is not '' and self.marker_stream.get( ) is not '': self.connect_button.configure(state='normal') else: self.connect_button.configure(state='disabled') def print_to_console(self, text_to_print): text_to_print = str(text_to_print) self.text_console.configure(state='normal') self.text_console.insert('end', text_to_print + '\n') self.text_console.configure(state='disabled') def print_from_queue(self): if not self.message_q.empty(): self.print_to_console(self.message_q.get()) self.master.after(500, self.print_from_queue) # noinspection PyUnusedLocal def update_connector_dict(self, event=None): self.connector_dict['update interval'] = self.update_interval.get() self.connector_dict['channel select'] = self.channel_select.get() self.connector_dict['eeg streamname'] = self.eeg_stream.get() self.connector_dict['marker streamname'] = self.marker_stream.get() self.connector_dict['y lim'] = [self.y_min.get(), self.y_max.get()] self.connector_dict['savefile'] = self.save_filename.get() self.connector_dict['filter'] = self.filter_check.get() self.connector_dict['squared'] = self.squared_check.get() def connect_streams(self): self.eeg_stream_combobox.configure(state='disabled') self.eeg_stream_button.configure(state='disabled') self.marker_stream_combobox.configure(state='disabled') self.marker_stream_button.configure(state='disabled') self.filter_checkbutton.configure(state='disabled') self.connect_button.configure(state='disabled') self.update_connector_dict() self.ready_for_connection_e.set() self.connected_e.wait() self.start_recording_btn.configure(state='normal') def run(self): self.build_main_window() self.master.mainloop()
class Update(ThemedTk): def __init__(self): ThemedTk.__init__(self, theme='black') self.title('Обновление F_Reference_H') self.geometry('500x140') x = (self.winfo_screenwidth() - self.winfo_reqwidth()) / 2 y = (self.winfo_screenheight() - self.winfo_reqheight()) / 2 self.wm_geometry("+%d+%d" % (x - 150, y)) self.resizable(width=False, height=False) self.iconphoto(True, PhotoImage(file='settings/ico/ico_main.png')) flow_hack_png = Image.open(f'settings/ico/mini_flowhack.png') flow_hack_png = ImageTk.PhotoImage(flow_hack_png) self.frame = Frame(self) self.frame.place(relwidth=1, relheight=1) flow_1 = Label(self.frame, image=flow_hack_png, cursor='heart') flow_1.bind('<Button-1>', lambda no_matter: webopen(VK)) flow_1.place(relx=.09, rely=.085, anchor='center') flow_2 = Label(self.frame, image=flow_hack_png, cursor='heart') flow_2.bind('<Button-1>', lambda no_matter: webopen(VK)) flow_2.place(relx=.91, rely=.085, anchor='center') self.lbl_done = Label(self.frame, text='ОБНОВЛЕНИЕ', font=('Times New Roman', 12, 'bold italic')) self.lbl_done.place(relx=.5, rely=.1, anchor='c') self.lable_second = Label( self.frame, text='Нам понадобится интернет!\nМы всё сделаем сами, это не ' 'займё много времени!', font=('Times New Roman', 10, 'bold italic'), justify='center') self.lable_second.place(relx=.5, rely=.35, anchor='c') self.btn_update = Button(self.frame, text='Обновить', cursor='hand1', command=self.updater_window) self.btn_update.place(relx=.5, rely=.65, anchor='c') self.license = Label( self.frame, cursor='hand1', text='Нажимая "Обновить" вы принимаете лицензионное соглашение', font=('Times New Roman', 10, 'bold italic'), foreground='black') self.license.bind('<Button-1>', lambda no_matter: webopen(SAIT)) self.license.place(relx=.5, rely=.92, anchor='c') self.mainloop() def updater_window(self): try: if name == 'nt': with open(NAME_FILE, "wb") as f: response = requests.get(URL_FILE, stream=True) total_length = int(response.headers.get('content-length')) self.btn_update.configure( text=f'Размер: {total_length / 1024 / 1024:0.2f} Mb') self.btn_update.update() if total_length is None: f.write(response.content) else: dl = 0 for data in response.iter_content(chunk_size=4096): dl += len(data) f.write(data) done = int(50 * dl / total_length) self.license.configure( text=f'\r{"=" * done}{" " * (50 - done)}') self.license.update() sys.stdout.flush() self.lbl_done.configure(text='Готово!', foreground='#DA9958') self.btn_update.configure(text='Открыть программу', command=open_app) elif name == 'posix': showerror('Error', 'К сожалению на Linux пока нельзя обновиться') self.btn_update.configure(text='Закрыть', command=open_app) except requests.exceptions.ConnectionError: showerror( 'Error', 'Произошла ошибка!\n\nПохоже, что у вас отсутствует ' 'подключение к интернету!\n\nЕсли не получается решить ' 'проблему, то напишите мне в блоке "Обратная связь" ')
class AppRow(MagicSession, Frame): """ Row for each app in mixer. handles refreshing the gui if session is changed external. handles user input and changing session volume/mute. """ def __init__(self, root_frame_instance): super().__init__(volume_callback=self.update_volume, mute_callback=self.update_mute, state_callback=self.update_state) self.root_frame_instance = root_frame_instance # ______________ DISPLAY NAME ______________ self.app_name = self.magic_root_session.app_exec print(f":: new session: {self.app_name}") # ______________ CREATE FRAME ______________ # super(MagicSession, self).__init__(root_frame_instance) Frame.__init__(self, root_frame_instance) # _______________ NAME LABEL _______________ self.name_label = Label(self, text=self.app_name, font=("Consolas", 12, "italic")) # _____________ VOLUME SLIDER _____________ self.volume_slider_state = DoubleVar() self.volume_slider = Scale(self, variable=self.volume_slider_state, command=self._slide_volume, from_=0, to=100, takefocus=False, orient=HORIZONTAL) # set initial: self.volume_slider_state.set(self.volume * 100) # ______________ MUTE BUTTON ______________ self.mute_button_state = StringVar() self.mute_button = Button(self, style="", textvariable=self.mute_button_state, command=self._toogle_mute, takefocus=False) # set initial: self.update_mute(self.mute) # _____________ SESSION STATUS _____________ self.status_line = Frame(self, style="", width=6) # set initial: self.update_state(self.state) # ________________ SEPARATE ________________ self.separate = Separator(self, orient=HORIZONTAL) # ____________ ARRANGE ELEMENTS ____________ # set column[1] to take the most space # and make all others as small as possible: self.columnconfigure(1, weight=1) # grid self.name_label.grid(row=0, column=0, columnspan=2, sticky="EW") self.mute_button.grid(row=1, column=0) self.volume_slider.grid(row=1, column=1, sticky="EW", pady=10, padx=20) self.separate.grid(row=2, column=0, columnspan=3, sticky="EW", pady=10) self.status_line.grid(row=0, rowspan=2, column=2, sticky="NS") # _____________ DISPLAY FRAME _____________ self.pack(pady=0, padx=15, fill='x') def update_volume(self, new_volume): """ when volume is changed externally (see callback -> AudioSessionEvents -> OnSimpleVolumeChanged ) """ # compare if the windows callback is because we set the slider: # if so drop update since we made the change # and all is already up to date - this will prevent lagg print(f"{self.app_name} volume: {new_volume}") self.volume_slider_state.set(new_volume * 100) def update_mute(self, new_mute): """ when mute state is changed by user or through other app """ if new_mute: icon = "🔈" self.mute_button.configure(style="Muted.TButton") else: icon = "🔊" self.mute_button.configure(style="Unmuted.TButton") # .set is a method of tkinters variables # it will change the button text print(f"{self.app_name} mute: {icon}") self.mute_button_state.set(icon) def update_state(self, new_state): """ when status changed (see callback -> AudioSessionEvents -> OnStateChanged) """ print(f"{self.app_name} state: {new_state}") if new_state == AudioSessionState.Inactive: # AudioSessionStateInactive self.status_line.configure(style="Inactive.TFrame") elif new_state == AudioSessionState.Active: # AudioSessionStateActive self.status_line.configure(style="Active.TFrame") elif new_state == AudioSessionState.Expired: # AudioSessionStateExpired self.status_line.configure(style="TFrame") """when session expires""" print(f":: closed session: {self.app_name}") self.destroy() def _slide_volume(self, value): """ when slider moved by user """ new_volume = float(value) / 100 # check if new user value really is new: (ttk bug) if self.volume != new_volume: # since self.volume is true data through windows # it will generally differ, but 1.0 == 1 print(f"with pycaw: {self.app_name} volume: {new_volume}") self.volume = new_volume def _toogle_mute(self): """ when mute button pressed """ new_mute = self.toggle_mute() self.update_mute(new_mute)
class Register(Frame): def __init__(self, master): super().__init__(master=master) self.layout_components() # Setup Callbacks self.show_sign_in: Callable = None self.sign_in: Callable = None self.search_email: Callable = None self.search_username: Callable = None self.search_password: Callable = None self.register: Callable = None self.username_valid: bool = False self.email_valid: bool = False self.password_valid: bool = False self.passcnfm_valid: bool = False # Refocus to Email Entry self.email_Entry.focus() def layout_components(self): self.pack(fill=tk.BOTH, expand=False, padx=PADX, pady=PADY) error_font = font.Font(family="Ariel", size=8) # Variables self.username = tk.StringVar() self.username.set("") self.email = tk.StringVar() self.email.set("") self.password = tk.StringVar() self.password.set("") self.passcnfm = tk.StringVar() self.passcnfm.set("") # Email Row email_Frame = Frame(self) email_Frame.pack(fill=tk.X) email_Label = Label(email_Frame, text="Email:", width=LABEL_WIDTH) email_Label.pack(side=tk.LEFT, padx=PADX, pady=PADY) self.email_Entry = Entry(email_Frame, width=ENTRY_WIDTH, textvariable=self.email) self.email_Entry.pack(fill=tk.X, padx=PADX, pady=PADY, expand=True) self.email_Entry.bind("<FocusOut>", self._validate_email) # Email Error Row email_errFrame = Frame(self) email_errFrame.pack(fill=tk.X) self.email_errLabel = Label(email_errFrame, text="", foreground="red", font=error_font) self.email_errLabel.pack(side=tk.LEFT, anchor="center", expand=True, padx=PADX, pady=PADY) # Username Row user_Frame = Frame(self) user_Frame.pack(fill=tk.X) user_Label = Label(user_Frame, text="Username:"******"<FocusOut>", self._validate_username) # Username Error Row user_errFrame = Frame(self) user_errFrame.pack(fill=tk.X) self.user_errLabel = Label(user_errFrame, text="", foreground="red", font=error_font) self.user_errLabel.pack(side=tk.LEFT, anchor="center", expand=True, padx=PADX, pady=PADY) # Original Password Row pass_Frame = Frame(self) pass_Frame.pack(fill=tk.X) pass_Label = Label(pass_Frame, text="Password:"******"*") self.pass_Entry.pack(fill=tk.X, padx=PADX, pady=PADY, expand=True) self.pass_Entry.bind("<FocusOut>", self._validate_password) # Confirming Password Row pass_cnfmFrame = Frame(self) pass_cnfmFrame.pack(fill=tk.X) pass_cnfmLabel = Label(pass_cnfmFrame, text="Confirm:", width=LABEL_WIDTH) pass_cnfmLabel.pack(side=tk.LEFT, padx=PADX, pady=PADY) self.pass_cnfmEntry = Entry(pass_cnfmFrame, width=ENTRY_WIDTH, textvariable=self.passcnfm, show="*") self.pass_cnfmEntry.pack(fill=tk.X, padx=PADX, pady=PADY, expand=True) self.pass_cnfmEntry.bind("<FocusOut>", self._validate_password) # Password Error Row pass_errFrame = Frame(self) pass_errFrame.pack(fill=tk.X) self.pass_errLabel = Label(pass_errFrame, text="", foreground="red", font=error_font) self.pass_errLabel.pack(side=tk.LEFT, anchor="center", expand=True, padx=PADX, pady=PADY) # Button Row button_Frame = Frame(self) button_Frame.pack(fill=tk.X) # Cancel Button cncl_Button = Button(button_Frame, text="Cancel", command=self.cancel) cncl_Button.pack(side=tk.RIGHT, padx=PADX, pady=PADY, expand=False) # Register Button self.register_Button = Button(button_Frame, text="Register", state="disabled", command=self._register) self.register_Button.pack(side=tk.RIGHT, padx=PADX, pady=PADY, expand=False) # View Password Button self.view_pass_Button = Button(button_Frame, text="View Password", command=self.view_password) self.view_pass_Button.pack(side=tk.LEFT, padx=PADX, pady=PADY) # Go Back Button Row gbck_Frame = Frame(self) gbck_Frame.pack(fill=tk.X) gbck_Label = Label(gbck_Frame, text="Have an Account? Go Ahead and ") gbck_Label.pack(side=tk.LEFT, padx=PADX, pady=PADY, expand=False) gbck_Button = Button(gbck_Frame, text="Sign In", command=self._show_sign_in) gbck_Button.pack(side=tk.RIGHT, padx=PADX, pady=PADY, expand=False) def cancel(self): self.email.set("") self.username.set("") self.password.set("") self.passcnfm.set("") self.email_Entry.focus() self.email_errLabel.configure(text="") self.user_errLabel.configure(text="") self.pass_errLabel.configure(text="") def view_password(self): self.pass_Entry.configure(show="") self.pass_cnfmEntry.configure(show="") self.view_pass_Button.configure(text="Hide Password", command=self.hide_password) def hide_password(self): self.pass_Entry.configure(show="*") self.pass_cnfmEntry.configure(show="*") self.view_pass_Button.configure(text="View Passwrod", command=self.view_password) def _show_sign_in(self): if self.show_sign_in is not None: self.show_sign_in() self.cancel() def _register(self): if self.register is not None: self.register() def _validate_email(self, event): email = self.email.get() if len(email) == 0: self.email_errLabel.configure(text="Email Must not be Empty...") self.email_valid = False elif not validate_email(email): self.email_errLabel.configure(text="Email Format Invalide...") self.email_valid = False elif self.search_email is not None and not self.search_email(email): self.email_errLabel.configure(text="Email Already Registered...") self.email_valid = False else: self.email_errLabel.configure(text="") self.email_valid = True self.enable_register() def _validate_username(self, event): username = self.username.get() if len(username) == 0: self.user_errLabel.configure(text="Username Must not be Empty...") self.username_valid = False elif self.search_username is not None and not self.search_username( username): self.username_valid = False else: self.user_errLabel.configure(text="") self.username_valid = True self.enable_register() def _validate_password(self, event): password = self.password.get() passcnfm = self.passcnfm.get() if len(password) == 0: self.pass_errLabel.configure(text="Password Must Not be Empty...") self.password_valid = False elif len(password) < 8: self.pass_errLabel.configure( text="Password Must be Longer than 8 Characters...") self.password_valid = False elif password != passcnfm: self.pass_errLabel.configure(text="Password Must Match... ") self.passcnfm_valid = False elif self.search_password is not None and not self.search_password( password): self.pass_errLabel.configure(text="") self.password_valid = False else: self.pass_errLabel.configure(text="") self.passcnfm_valid = True self.password_valid = True self.enable_register() def enable_register(self): if (self.email_valid and self.username_valid and self.password_valid and self.passcnfm_valid): self.register_Button.configure(state="normal") def failed_register(self): self.email.set("") self.username.set("") self.pass_errLabel.configure(text="Email or Username Already used") self.email_Entry.focus()
class Timer(Tk): """ Chronométre de temps de travail pour plus d'efficacité """ def __init__(self): Tk.__init__(self, className="WorkHourGlass") self.on = False # is the timer on? if not CONFIG.options("Tasks"): CONFIG.set("Tasks", _("Work"), CMAP[0]) # colors self.background = { _("Work"): CONFIG.get("Work", "bg"), _("Break"): CONFIG.get("Break", "bg"), _("Rest"): CONFIG.get("Rest", "bg") } self.foreground = { _("Work"): CONFIG.get("Work", "fg"), _("Break"): CONFIG.get("Break", "fg"), _("Rest"): CONFIG.get("Rest", "fg") } # window configuration if PL[0] == "w": self.iconbitmap(ICON_WIN, default=ICON_WIN) else: self.icon = PhotoImage(master=self, file=ICON) self.iconphoto(True, self.icon) self.title("WorkHourGlass") self.protocol("WM_DELETE_WINDOW", self.exit) self.rowconfigure(1, weight=1) self.columnconfigure(0, weight=1) self.columnconfigure(1, weight=1) self.minsize(181, 190) self.geometry("200x190+%i+%i" % ((self.winfo_screenwidth() - 200) // 2, (self.winfo_screenheight() - 190) // 2)) self.configure(background=self.background[_("Work")]) # style self.style = Style(self) self.style.theme_use(STYLE) self.style.configure('fen.TLabel', foreground=self.foreground[_("Work")], background=self.background[_("Work")]) # nombre de séquence de travail effectuées d'affilée (pour # faire des pauses plus longues tous les 4 cycles) self.nb_cycles = 0 self.pomodori = IntVar(self, 0) # images self.im_go = PhotoImage(master=self, file=GO) self.im_stop = PhotoImage(master=self, file=STOP) self.im_plus = PhotoImage(master=self, file=PLUS) self.im_moins = PhotoImage(master=self, file=MOINS) self.im_params = PhotoImage(master=self, file=PARAMS) self.im_tomate = PhotoImage(master=self, file=TOMATE) self.im_graph = PhotoImage(master=self, file=GRAPH) # tasks list tasks_frame = Frame(self) tasks_frame.grid(row=3, column=0, columnspan=3, sticky="wnse") tasks = [t.capitalize() for t in CONFIG.options("Tasks")] self.task = StringVar(self, tasks[0]) self.menu_tasks = Menu(tasks_frame, tearoff=False) for task in tasks: self.menu_tasks.add_radiobutton(label=task, value=task, variable=self.task) self.menu_tasks.add_command(label=_("New task"), image=self.im_plus, compound="left", command=self.add_task) self.menu_tasks.add_command(label=_("Remove task"), image=self.im_moins, compound="left", command=self.del_task) self.menu_tasks.add_command(label=_("Statistics"), image=self.im_graph, compound="left", command=self.display_stats) self.choose_task = Menubutton(tasks_frame, textvariable=self.task, menu=self.menu_tasks) Label(tasks_frame, text=_("Task: "), font="CMU\ Sans\ Serif\ Demi\ Condensed 12", width=6, anchor="e").pack(side="left") self.choose_task.pack(side="right", fill="x") # display self.tps = [CONFIG.getint("Work", "time"), 0] # time: min, sec self.activite = StringVar(self, _("Work")) self.titre = Label(self, textvariable=self.activite, font='CMU\ Sans\ Serif\ Demi\ Condensed 14', style='fen.TLabel', anchor="center") self.titre.grid(row=0, column=0, columnspan=2, sticky="we") self.temps = Label( self, text="{0:02}:{1:02}".format(self.tps[0], self.tps[1]), font="%s %i" % (CONFIG.get( "General", "font"), CONFIG.getint("General", "fontsize")), style='fen.TLabel', anchor="center") self.temps.grid(row=1, column=0, columnspan=2, sticky="nswe", pady=(0, 10)) self.aff_pomodori = Label(self, textvariable=self.pomodori, image=self.im_tomate, compound="left", style='fen.TLabel', font='CMU\ Sans\ Serif\ Demi\ Condensed 14') self.aff_pomodori.grid(row=2, columnspan=2, sticky="e", padx=20) # buttons self.b_go = Button(self, image=self.im_go, command=self.go) self.b_go.grid(row=4, column=0, sticky="ew") self.b_params = Button(self, image=self.im_params, command=self.params) self.b_params.grid(row=4, column=1, sticky="ew") # --- make window sticky self.update_idletasks() e = EWMH() try: for w in e.getClientList(): if w.get_wm_name() == self.title(): e.setWmState(w, 1, '_NET_WM_STATE_STICKY') e.display.flush() except ewmh.display.error.BadWindow: pass def set_config(self): self.background = { _("Work"): CONFIG.get("Work", "bg"), _("Break"): CONFIG.get("Break", "bg"), _("Rest"): CONFIG.get("Rest", "bg") } self.foreground = { _("Work"): CONFIG.get("Work", "fg"), _("Break"): CONFIG.get("Break", "fg"), _("Rest"): CONFIG.get("Rest", "fg") } act = self.activite.get() self.configure(background=self.background[act]) self.style.configure('fen.TLabel', foreground=self.foreground[act], background=self.background[act]) self.temps.configure(font="%s %i" % (CONFIG.get("General", "font"), CONFIG.getint("General", "fontsize"))) def add_task(self): def ajoute(event=None): task = nom.get() if task and not CONFIG.has_option("Tasks", task): index = len(CONFIG.options("Tasks")) self.menu_tasks.insert_radiobutton(index, label=task, value=task, variable=self.task) CONFIG.set("Tasks", task, CMAP[index % len(CMAP)]) top.destroy() with open(PATH_CONFIG, "w") as file: CONFIG.write(file) self.menu_tasks.invoke(index) else: nom.delete(0, "end") top = Toplevel(self) top.title(_("New task")) top.transient(self) top.grab_set() nom = Entry(top, width=20) nom.grid(row=0, columnspan=2, sticky="ew") nom.focus_set() nom.bind('<Key-Return>', ajoute) Button(top, text=_("Cancel"), command=top.destroy).grid(row=1, column=0) Button(top, text=_("Ok"), command=ajoute).grid(row=1, column=1) top.wait_window(top) def del_task(self): """ Suppression de tâches """ def supprime(): rep = askyesno(_("Confirmation"), _("Are you sure you want to delete these tasks?")) if rep: for i in range(len(boutons) - 1, -1, -1): # l'ordre de parcours permet de supprimer les derniers # éléments en premier afin de ne pas modifier les index des # autres éléments lors des suppressions task = tasks[i] if "selected" in boutons[i].state(): # suppression de la tâche de la liste des tâches CONFIG.remove_option("Tasks", task) tasks.remove(task) # suppression de l'entrée correspondante dans le menu self.menu_tasks.delete(i) if not tasks: CONFIG.set("Tasks", _("Work"), CMAP[0]) tasks.append(_("Work")) self.menu_tasks.insert_radiobutton( 0, label=_("Work"), value=_("Work"), variable=self.task) if self.task.get() == task: self.task.set(tasks[0]) # suppression des stats associées chemin = PATH_STATS + "_" + "_".join(task.split(" ")) if os.path.exists(chemin): os.remove(chemin) top.destroy() with open(PATH_CONFIG, "w") as file: CONFIG.write(file) else: top.destroy() tasks = [t.capitalize() for t in CONFIG.options("Tasks")] top = Toplevel(self) top.title(_("Remove task")) top.transient(self) top.grab_set() style = Style(top) style.theme_use(STYLE) boutons = [] for i, task in enumerate(tasks): boutons.append(Checkbutton(top, text=task)) boutons[-1].grid(row=i, columnspan=2, sticky="w") Button(top, text=_("Cancel"), command=top.destroy).grid(row=i + 1, column=0) Button(top, text=_("Delete"), command=supprime).grid(row=i + 1, column=1) def stats(self): """ Enregistre la durée de travail (en min) effectuée ce jour pour la tâche qui vient d'être interrompue. Seul les pomodori complets sont pris en compte. """ # TODO: translate, correct date/time format pom = self.pomodori.get() if pom: # la tâche en cours a été travaillée, il faut enregistrer les stats date = dt.date.today() task = self.task.get() chemin = PATH_STATS + "_" + "_".join(task.split(" ")) if not os.path.exists(chemin): with open(chemin, 'w') as fich: fich.write( "# tâche : %s\n# jour\tmois\tannée\ttemps de travail (min)\n" % task) with open(chemin, 'r') as fich: stats = fich.readlines() if len(stats) > 2: last = stats[-1][:10], stats[-1][:-1].split("\t")[-1] else: last = "", 0 if last[0] != date.strftime("%d\t%m\t%Y"): with open(chemin, 'a') as fich: fich.write("%s\t%i\n" % (date.strftime("%d\t%m\t%Y"), pom * CONFIG.getint("Work", "time"))) else: # un nombre a déjà été enregistré plus tôt dans la journée # il faut les additioner with open(chemin, 'w') as fich: fich.writelines(stats[:-1]) fich.write( "%s\t%i\n" % (date.strftime("%d\t%m\t%Y"), pom * CONFIG.getint("Work", "time") + int(last[1]))) def display_stats(self): """ affiche les statistiques """ plt.figure("Statistiques") tasks = [t.capitalize() for t in CONFIG.options("Tasks")] coul = [CONFIG.get("Tasks", task) for task in tasks] stats_x = [] stats_y = [] demain = dt.date.today().toordinal() + 1 min_x = demain # récupération des données no_data = True for i, task in enumerate(tasks): chemin = PATH_STATS + "_" + "_".join(task.split(" ")) if os.path.exists(chemin): no_data = False stat = loadtxt(chemin, dtype=int) if len(stat.shape) == 1: stat = stat.reshape(1, 4) x = [ dt.date(an, mois, jour).toordinal() for jour, mois, an in stat[:, :3] ] y = stat[:, -1] / 60 # temps de travail min_x = min(x[0], min_x) stats_x.append(x) stats_y.append(y) else: # la taĉhe n'a jamais été travaillée stats_x.append([demain - 1]) stats_y.append(array([0])) # plots xx = arange(min_x, demain, dtype=float) yy0 = zeros_like(xx) # pour empiler les stats if not no_data: for (i, task), x, y in zip(enumerate(tasks), stats_x, stats_y): ax0 = plt.subplot(111) plt.ylabel(_("time (h)")) plt.xlabel(_("date")) yy = array([], dtype=int) # comble les trous par des 0 # ainsi, les jours où une tâche n'a pas été travaillée correspondent # à des 0 sur le graph xxx = arange(min_x, x[0]) yy = concatenate((yy, zeros_like(xxx, dtype=int))) for j in range(len(x) - 1): xxx = arange(x[j], x[j + 1]) yy = concatenate((yy, [y[j]], zeros(len(xxx) - 1, dtype=int))) xxx = arange(x[-1], demain) yy = concatenate((yy, [y[-1]], zeros(len(xxx) - 1, dtype=int))) plt.bar(xx - 0.4, yy, bottom=yy0, width=0.8, label=task, color=coul[i]) yy0 += yy axx = array( [int(xt) for xt in ax0.get_xticks() if xt.is_integer()]) ax0.set_xticks(axx) ax0.set_xticklabels( [dt.date.fromordinal(i).strftime("%x") for i in axx]) plt.gcf().autofmt_xdate() ax0.set_xlim(min_x - 0.5, demain - 0.5) lgd = plt.legend(fontsize=10) lgd.draggable() plt.subplots_adjust(top=0.95) max_y = yy0.max() ax0.set_ylim(0, max_y + 0.1 * max_y) plt.show() def go(self): if self.on: self.on = False if self.activite.get() == _("Work"): # rep = askyesno(title=_("Confirmation"), # message=_("You should not interrupt your work if you want to be efficient. Do you still want to suspend the timer?"), # icon="warning") # else: # rep = True self.stop() # if rep: # self.b_go.configure(image=self.im_go) # else: # self.on = True # self.affiche() else: self.on = True self.choose_task.config(state="disabled") self.b_go.configure(image=self.im_stop) self.after(1000, self.affiche) def stop(self, confirmation=True): """ Arrête le décompte du temps et le réinitialise, demande une confirmation avant de le faire si confirmation=True """ self.on = False if confirmation: rep = askyesno( title=_("Confirmation"), message=_( "Are you sure you want to give up the current session?")) else: rep = True if rep: self.stats() self.pomodori.set(0) self.nb_cycles = 0 self.b_go.configure(image=self.im_go) self.tps = [CONFIG.getint("Work", "time"), 0] self.temps.configure( text="{0:02}:{1:02}".format(self.tps[0], self.tps[1])) act = _("Work") self.activite.set(act) self.style.configure('fen.TLabel', background=self.background[act], foreground=self.foreground[act]) self.configure(background=self.background[act]) self.choose_task.config(state="normal") else: self.on = True self.affiche() def ting(self): """ joue le son marquant le changement de période """ if not CONFIG.getboolean("Sound", "mute", fallback=False): if PL[0] == "w": Popen([ "powershell", "-c", '(New-Object Media.SoundPlayer "%s").PlaySync();' % (CONFIG.get("Sound", "beep")) ]) else: Popen([ CONFIG.get("Sound", "player"), CONFIG.get("Sound", "beep") ]) def affiche(self): if self.on: self.tps[1] -= 1 if self.tps[1] == 0: if self.tps[0] == 0: self.ting() if self.activite.get() == _("Work"): self.pomodori.set(self.pomodori.get() + 1) self.nb_cycles += 1 if self.nb_cycles % 4 == 0: # pause longue self.activite.set(_("Rest")) self.tps = [CONFIG.getint("Rest", "time"), 0] else: # pause courte self.activite.set(_("Break")) self.tps = [CONFIG.getint("Break", "time"), 0] else: self.activite.set(_("Work")) self.tps = [CONFIG.getint("Work", "time"), 0] act = self.activite.get() self.style.configure('fen.TLabel', background=self.background[act], foreground=self.foreground[act]) self.configure(background=self.background[act]) elif self.tps[1] == -1: self.tps[0] -= 1 self.tps[1] = 59 self.temps.configure( text="{0:02}:{1:02}".format(self.tps[0], self.tps[1])) self.after(1000, self.affiche) def params(self): on = self.on self.on = False self.b_go.configure(image=self.im_go) p = Params(self) self.wait_window(p) if on: self.on = True self.choose_task.config(state="disabled") self.b_go.configure(image=self.im_stop) self.after(1000, self.affiche) def exit(self): self.stats() plt.close() self.destroy()
class DownloadPage(Frame): def __init__(self, parent, controller): Frame.__init__(self, parent) self.controller = controller self.outputState = False self.sem = threading.Semaphore() self.lock = 1 frame1 = Frame(self, relief=RAISED, borderwidth=1) frame1.pack(fill=BOTH, expand=True) frame2 = Frame(self, relief=RAISED, borderwidth=1) frame2.pack(fill=BOTH, expand=True) frame3 = Frame(self, relief=RAISED, borderwidth=1) frame3.pack(fill=BOTH, expand=True) frame4 = Frame(self, relief=RAISED, borderwidth=1) frame4.pack(fill=BOTH, expand=True) self.outputBox = Text(self, height=15, width=40) self.outputBox.tag_config('error', background="yellow", foreground="red") self.vsb = Scrollbar(self, orient="vertical", command=self.outputBox.yview) self.outputBox.configure(yscrollcommand=self.vsb.set, state="disabled") self.vsb.pack(side="right", fill="y") self.outputBox.pack(side="left", fill="both", expand=True) self.oneBPName_entry = Entry(frame2, width=35) self.listBPPath_entry = Entry(frame3, width=35) self.oneBPNAme_label = Label(frame2, text="Blueprint Name:") self.listBPPath_label = Label(frame3, text="Blueprint List File Path:") self.allButton = Button(frame1, text="Download All Blueprints", width=30) self.allButton.configure(command=self.threader_all) self.oneButton = Button(frame2, text="Download One Blueprint", width=30) self.oneButton.configure(command=self.threader_one) self.listButton = Button(frame3, text="Download Blueprints From List", width=30) self.listButton.configure(command=self.threader_list) self.returnButton = Button( frame4, text="Go Back", command=lambda: self.controller.show_frame("MainPage")) self.oneBPNAme_label.pack(padx=5, pady=5) self.listBPPath_label.pack(padx=5, pady=5) self.oneBPName_entry.pack(padx=5, pady=5) self.listBPPath_entry.pack(padx=5, pady=5) self.allButton.pack(padx=5, pady=5) self.oneButton.pack(padx=5, pady=5) self.listButton.pack(padx=5, pady=5) self.returnButton.pack(padx=5, pady=10) # Starts thread for background process (also keeps lock so only one option may run at a time) def threader_all(self): if self.lock == 1: self.lock = 0 thread = threading.Thread(target=self.download_all_blueprints) thread.daemon = True thread.start() return def threader_one(self): if self.lock == 1: self.lock = 0 thread = threading.Thread(target=self.download_one_blueprint) thread.daemon = True thread.start() return def threader_list(self): if self.lock == 1: self.lock = 0 thread = threading.Thread(target=self.download_blueprints) thread.daemon = True thread.start() return def download_all_blueprints(self): """ Downloads all published blueprints from vRA Creates: output.json -JSON file with all published blueprints info (name, id, type, etc.) *used to get all blueprint names to add to a vRA package blueprintLog.txt -TEXT file with blueprints' names *used when uploading blueprints to sort into proper service categories pkg.json -JSON file with all packages in vRA (name, info, etc.) *used to get the pkgId to download (with all blueprints contained) .zip of blueprints -ZIP file with all downloaded blueprints """ packageName = "vRAScriptPackage" packageID = "" dirPath = "\'" + os.path.dirname(os.path.realpath(__file__)) + "\\" getList = "cloudclient.bat vra content list --format JSON --export {}output.json\'".format( dirPath) makePackage = "cloudclient.bat vra package create --name {} --ids ".format( packageName) getPkgID = "cloudclient.bat vra package list --format JSON --export {}pkg.json\'".format( dirPath) blueprintLog = [] cloud_client_run(self, getList, "Getting list of all blueprints in vRA", newLog=True) show_output(self, "Saving list of all blueprints as \'output.json\'") data = json.load(open('output.json')) show_output(self, "Creating list of blueprints") start_output(self, "Creating list of blueprints to download") for blueprint in data: makePackage += blueprint['id'] + "," blueprintLog.append(blueprint['name']) close_output(self) show_output(self, "Saving list of blueprints names as \'blueprintLog.txt\'") with open("blueprintLog.txt", 'w') as f: for BP in blueprintLog: f.write(BP + "\n") f.close() cloud_client_run(self, makePackage[:-1], "Making package of useful blueprints from vRA") # vRA sometimes takes a second or two to create package and update list time.sleep(5) cloud_client_run( self, getPkgID, "Getting blueprint packages from vRA as \'pkg.json\'") # Gets package ID to download (Can't download with package name) start_output(self, "Sorting packages and retrieving one for download") data = json.load(open('pkg.json')) for pkg in data: if pkg['name'] == packageName: packageID = str(pkg['id']) close_output(self) dirPath = "\'" + os.path.dirname(os.path.realpath(__file__)) + "\'" BPDownload = "cloudclient.bat vra package export --path {} --pkgId {}".format( dirPath, packageID) deletePkg = "cloudclient.bat vra package delete --pkgId {}".format( packageID) cloud_client_run(self, BPDownload, "Downloading blueprints") cloud_client_run(self, deletePkg, "Deleting package in vRA") show_output(self, "\nDownload Complete") self.lock = 1 return def download_one_blueprint(self): """ Downloads single inputted blueprint Creates: output.json -JSON file with all published blueprints info (name, id, type, etc.) *used to get all blueprint names to add to a vRA package blueprintLog.txt -TEXT file with blueprints' names *used when uploading blueprints to sort into proper service categories pkg.json -JSON file with all packages in vRA (name, info, etc.) *used to get the pkgId to download (with all blueprints contained) .zip of blueprints -ZIP file with all downloaded blueprints """ if len(self.oneBPName_entry.get()) == 0: self.lock = 1 return dirPath = "\'" + os.path.dirname(os.path.realpath(__file__)) + "\\" getList = "cloudclient.bat vra content list --format JSON --export {}output.json\'".format( dirPath) BPID = None cloud_client_run(self, getList, "Getting list of all blueprints in vRA", newLog=True) show_output(self, "Saving list of all blueprints as \'output.json\'") data = json.load(open('output.json')) start_output(self, "Searching for {}".format(self.oneBPName_entry.get())) for BP in data: if BP['name'].lower() == self.oneBPName_entry.get().lower(): BPID = BP['id'] close_output(self) with open("blueprintLog.txt", 'w') as f: f.write(self.oneBPName_entry.get() + "\n") f.close() dirPath = "\'" + os.path.dirname(os.path.realpath(__file__)) + "\'" if BPID is not None: contentBP = "cloudclient.bat vra content export --path {} --id {}".format( dirPath, BPID) cloud_client_run( self, contentBP, "Downloading {}".format(self.oneBPName_entry.get())) show_output(self, "\nDownload Complete") else: show_output(self, "\"{}\" not found in vRA\n".format( self.oneBPName_entry.get()), error=True) self.lock = 1 return def download_blueprints(self): """ Downloads blueprints from a text file Creates: output.json -JSON file with all published blueprints info (name, id, type, etc.) *used to get all blueprint names to add to a vRA package blueprintLog.txt -TEXT file with blueprints' names *used when uploading blueprints to sort into proper service categories pkg.json -JSON file with all packages in vRA (name, info, etc.) *used to get the pkgId to download (with all blueprints contained) .zip of blueprints -ZIP file with all downloaded blueprints """ if len(self.listBPPath_entry.get()) == 0: self.lock = 1 return packageName = "vRAScriptPackage" packageID = "" dirPath = "\'" + os.path.dirname(os.path.realpath(__file__)) + "\\" getList = "cloudclient.bat vra content list --format JSON --export {}output.json\'".format( dirPath) makePackage = "cloudclient.bat vra package create --name {} --ids ".format( packageName) getPkgID = "cloudclient.bat vra package list --format JSON --export {}pkg.json\'".format( dirPath) blueprintLog = [] show_output(self, "Reading in list") try: with open(self.listBPPath_entry.get(), 'r') as f: blueprintsToGet = f.read().splitlines() f.close() blueprintsToGet = [element.lower() for element in blueprintsToGet] except IOError: show_output(self, "File not found", error=True) self.lock = 1 return cloud_client_run(self, getList, "Getting list of all blueprints in vRA", newLog=True) show_output(self, "Saving list of all blueprints as \'output.json\'") data = json.load(open('output.json')) start_output(self, "Creating package list of blueprints to download") for blueprint in data: if str(blueprint['name']).lower() in blueprintsToGet: blueprintsToGet.remove(str(blueprint['name']).lower()) blueprintLog.append(blueprint['name']) makePackage += blueprint['id'] + "," close_output(self) cloud_client_run(self, makePackage[:-1], "Making package of useful blueprints from vRA") show_output( self, "Saving list of useful blueprints names as \'blueprintLog.txt\'") with open("blueprintLog.txt", 'w') as f: for BP in blueprintLog: f.write(BP + "\n") f.close() # vRA sometimes takes a second or two to create package and update list time.sleep(5) cloud_client_run( self, getPkgID, "Getting blueprint packages from vRA as \'pkg.json\'") start_output(self, "Sorting packages and retrieving one for download") data = json.load(open('pkg.json')) for pkg in data: if pkg['name'] == packageName: packageID = str(pkg['id']) close_output(self) path = "\'" + os.path.dirname(os.path.realpath(__file__)) + "\'" BPDownload = "cloudclient.bat vra package export --path {} --pkgId {}".format( path, packageID) deletePkg = "cloudclient.bat vra package delete --pkgId {}".format( packageID) cloud_client_run(self, BPDownload, "Downloading blueprints") cloud_client_run(self, deletePkg, "Deleting package in vRA") if len(blueprintsToGet) != 0: show_output( self, "Following blueprints either misspelled or are not in vRA:") for missing in blueprintsToGet: show_output(self, "-{}".format(str(missing)), error=True) show_output(self, "\nDownload Complete") self.lock = 1 return
class ErrorSurfaceFrame(LabelFrame): def __init__(self,parent): LabelFrame.__init__(self, parent, borderwidth=0) entryWidth = 7 xPad1 = 30 xPad2 = 5 self.errorXLowerLimitL = Label(self) self.errorXLowerLimitE = CustomEntry(self,width=entryWidth,justify="right") self.errorXLowerLimitL.grid(row=0,column=0,padx=(10,xPad2),pady=5,sticky="W") self.errorXLowerLimitE.grid(row=0,column=1,padx=(xPad2,xPad1),pady=5) self.errorXUpperLimitL = Label(self) self.errorXUpperLimitE = CustomEntry(self,width=entryWidth,justify="right") self.errorXUpperLimitL.grid(row=1,column=0,padx=(10,xPad2),pady=5,sticky="W") self.errorXUpperLimitE.grid(row=1,column=1,padx=(xPad2,xPad1),pady=5) self.errorYLowerLimitL = Label(self) self.errorYLowerLimitE = CustomEntry(self,width=entryWidth,justify="right") self.errorYLowerLimitL.grid(row=0,column=2,padx=(xPad1,xPad2),pady=5,sticky="W") self.errorYLowerLimitE.grid(row=0,column=3,padx=(xPad2,xPad1),pady=5) self.errorYUpperLimitL = Label(self) self.errorYUpperLimitE = CustomEntry(self,width=entryWidth,justify="right") self.errorYUpperLimitL.grid(row=1,column=2,padx=(xPad1,xPad2),pady=5,sticky="W") self.errorYUpperLimitE.grid(row=1,column=3,padx=(xPad2,xPad1),pady=5) self.errorResolutionL = Label(self,text="Resolution: ") self.errorResolutionE = CustomEntry(self,width=entryWidth,justify="right") self.errorResolutionE.insert(0,ERROR_SURFACE_DEFAULT_RESOLUTION) self.errorResolutionL.grid(row=0,column=4,padx=(xPad1,xPad2),pady=5,sticky="W") self.errorResolutionE.grid(row=0,column=5,padx=(xPad2,xPad1),pady=5,sticky="E") self.errorSurfaceB = Button(self,text=" Calculate error surface ") self.errorSurfaceB.grid(row=1,column=4,columnspan=2,padx=(xPad1,xPad1),sticky="EW") self.errorSurfaceB.configure(state=tkinter.ACTIVE) def update(self,xSymbol,ySymbol,xLL,xUL,yLL,yUL): self.xSymbol = xSymbol self.ySymbol = ySymbol self.errorXLowerLimitL.configure(text="Lower limit ("+self.xSymbol+"): ") self.errorXLowerLimitE.insertNew(xLL) self.errorXUpperLimitL.configure(text="Upper limit ("+self.xSymbol+"): ") self.errorXUpperLimitE.insertNew(xUL) self.errorYLowerLimitL.configure(text="Lower limit ("+self.ySymbol+"): ") self.errorYLowerLimitE.insertNew(yLL) self.errorYUpperLimitL.configure(text="Upper limit ("+self.ySymbol+"): ") self.errorYUpperLimitE.insertNew(yUL) def getSurfaceParameters(self): xLowerLimit = helper_functions.validateValue(self.errorXLowerLimitE.get(), self.xSymbol + " lower limit must be a positive number", "float", lowerBound=0) xUpperLimit = helper_functions.validateValue(self.errorXUpperLimitE.get(), self.xSymbol + " upper limit must be greater than the lower limit", "float", strictLowerBound=xLowerLimit) yLowerLimit = helper_functions.validateValue(self.errorYLowerLimitE.get(), self.ySymbol + " lower limit must be a positive number", "float", lowerBound=0) yUpperLimit = helper_functions.validateValue(self.errorYUpperLimitE.get(), self.ySymbol + " upper limit must be greater than the lower limit", "float", strictLowerBound=yLowerLimit) resolution = helper_functions.validateValue(self.errorResolutionE.get(), "Resolution must be " + str(ERROR_SURFACE_MIN_RESOLUTION) + " \u2264 x \u2264 " + str(ERROR_SURFACE_MAX_RESOLUTION), "int", lowerBound=ERROR_SURFACE_MIN_RESOLUTION, upperBound=ERROR_SURFACE_MAX_RESOLUTION) return [xLowerLimit,xUpperLimit,yLowerLimit,yUpperLimit,resolution] def clear(self): self.errorXLowerLimitE.insertNew("") self.errorXUpperLimitE.insertNew("") self.errorYLowerLimitE.insertNew("") self.errorYUpperLimitE.insertNew("")
class Root(Frame): ''' The root window ''' def __init__(self,parent,csvpath="",rosterpath=""): ''' Initilization of the window, assigning height centering the window, and starting the interface. ''' self.queue = Queue() self.parent = parent self.interface = GuiInterface() self.loadWindow = None self.remember = False self.initialized = False self.csvpathh = csvpath self.rosterpathh = rosterpath self.outpathh = "" self.teamsizeh = "" self.startMainUI() def centerWindow(self,notself=None): ''' This centers the window into place if notself is set, then it centers the notself window @param: notself - TKobject ''' if notself != None: #notself is primarly for progressbar sw = self.parent.winfo_screenwidth() sh = self.parent.winfo_screenheight() x = (sw - self.w/2) / 2 y = (sh - self.h/2) / 2 notself.geometry('%dx%d+%d+%d' % (self.w/1.8,self.h/1.8, x,y)) else: sw = self.parent.winfo_screenwidth() sh = self.parent.winfo_screenheight() x = (sw - self.w) / 2 y = (sh - self.h) / 2 self.parent.geometry('%dx%d+%d+%d' % (self.w,self.h, x ,y)) def startWindow(self): ''' This method starts/creates the window for the UI ''' Frame.__init__(self, self.parent, background="white") self.style = Style() self.style.theme_use("default") self.pack(fill=BOTH, expand=1) if(not self.initialized): self.centerWindow() else: self.parent.geometry('%dx%d' % (self.w,self.h)) self.initialized = True def resetWindow(self): ''' Resets the window ''' if(self.initialized): self.destroy() if(self.loadWindow != None): self.loadWindow.destroy() self.startWindow() def startMainUI(self): ''' Starting the main UI takes some work, this creates the buttons labels and entrys. Also puts them into place, and adds function calls to the buttons ''' #RESETING WINDOW self.h = 290 self.w = 600 self.resetWindow() self.parent.title("Input") #CREATING CSV FRAME csvFrame = Frame(self) csvFrame.pack(fill=X, side=TOP) csvLabel = Label(csvFrame, text="Path to csv:", background="white") csvLabel.pack(side=LEFT, padx=15, pady=10) self.csvEntry = Entry(csvFrame, width=30) self.csvEntry.insert(0,self.csvpathh) self.csvEntry.pack(side=LEFT, padx=35, pady=10) csvButton = Button(csvFrame, command=self.csvstartfilebrowser, text="Browse...") csvButton.pack(side=LEFT, padx=10, pady=10) #DONE CSV FRAME #CREATING ROSTER FRAME rosterFrame = Frame(self) rosterFrame.pack(fill=X, side=TOP) rosterLabel = Label(rosterFrame, text="Path to roster:", background="white") rosterLabel.pack(side=LEFT, padx=17, pady=10) self.rosterEntry = Entry(rosterFrame, width=30) self.rosterEntry.insert(0,self.rosterpathh) self.rosterEntry.pack(side=LEFT, padx=15, pady=10) rosterButton = Button(rosterFrame, command=self.rosterstartfilebrowser, text="Browse...") rosterButton.pack(side=LEFT, padx=28, pady=10) #DONE ROSTER FRAME #CREATING OUTPUT FRAME outputFrame = Frame(self) outputFrame.pack(fill=X, side=TOP) outputLabel = Label(outputFrame, text="Path to output:", background="white") outputLabel.pack(side=LEFT, padx=15, pady=10) self.outputEntry = Entry(outputFrame, width=30) self.outputEntry.insert(0,self.outpathh) self.outputEntry.pack(side=LEFT, padx=15, pady=10) outputButton = Button(outputFrame, command=self.outputstartfilebrowser, text="Browse...") outputButton.pack(side=LEFT, padx=28, pady=10) #DONE OUTPUT FRAME #CREATING TEAMSIZE FRAME teamsizeFrame= Frame(self) teamsizeFrame.pack(fill=X, side=TOP) teamsizeLabel = Label(teamsizeFrame, text="Team size:", background="white") teamsizeLabel.pack(side=LEFT, padx=15, pady=10) self.teamsizeEntry = Entry(teamsizeFrame, width=5) self.teamsizeEntry.insert(0,self.teamsizeh) self.teamsizeEntry.pack(side=LEFT, padx=43, pady=10) #DONE TEAMSIZE FRAME #CREATING BOTTOM BUTTONS frame = Frame(self, borderwidth=1) frame.pack(fill=BOTH, expand=True) self.pack(fill=BOTH, expand=True) exitButton = Button(self,text="Exit",command=self.parent.destroy) exitButton.pack(side=RIGHT, padx=5, pady=5) self.submitButton = Button(self,text="Submit",command=self.submitFiles) self.submitButton.pack(side=RIGHT) #DONE BOTTOM BUTTONS def optionUI(self): ''' This creates the option window which presents the user with the generated teams and their options ''' #RESETING WINDOW self.h = 400 self.w = 800 self.resetWindow() self.parent.title("Options") #CREATING SCROLL AREA scrollFrame = Frame(self) scrollFrame.pack(fill=X, side=TOP) self.teamlisting = Listbox(scrollFrame, width=self.w, height=18, \ selectmode=MULTIPLE) count = 1 for team in self.interface.teams: teamstring = "Team: " + str(count) teamstring += " score: " + "%.4f " % team.rating teamstring += " members: " for student in team.members: teamstring += student.name + " | " count += 1 self.teamlisting.insert(END, teamstring) self.teamlisting.pack(padx=5, pady=5) #DONE SCROLL AREA #This will enable double-clicking self.teamlisting.bind('<Double-1>', lambda x: self.inspectTeamUI(self.teamlisting.curselection())) #CREATING BOTTOM BUTTONS frame = Frame(self, borderwidth=1) frame.pack(fill=BOTH, expand=True) self.pack(fill=BOTH, expand=True) backButton = Button(self,text="Back",command=self.startMainUI) backButton.pack(side=LEFT, padx=5, pady=5) exitButton = Button(self,text="Exit",command=lambda: self.parent.destroy() and exit()) exitButton.pack(side=RIGHT, padx=5, pady=5) saveButton = Button(self,text="Save",command=self.interface.writeFile) saveButton.pack(side=RIGHT) rerunButton = Button(self,text="Rerun",command=self.reRun) rerunButton.pack(side=RIGHT, padx=5, pady=5) shuffleTeamsButton = Button(self,text="Shuffle Selected",command=self.shuffleSelected) shuffleTeamsButton.pack(side=RIGHT) swappingMembersButton = Button(self,text="Swap Members",command=self.memberSwap) swappingMembersButton.pack(side=RIGHT,padx=5, pady=5) emailscreenButton = Button(self,text="Email Team(s)",command=self.emailScreen) emailscreenButton.pack(side=RIGHT) #DONE BOTTOM BUTTONS def inspectTeamUI(self,selection): ''' This page will allow the user to see info on the team that was double clicked. ''' #RESETING WINDOW self.h = 400 self.w = 800 self.resetWindow() self.parent.title("About This Team") #CREATING SCROLL AREA scrollFrame = Frame(self) scrollFrame.pack(fill=X, side=TOP) self.inspectedTeamStudentListing = Listbox(scrollFrame, width=self.w, height=9) self.inspectedTeamScheduleListing = Listbox(scrollFrame, width=self.w, height=9) if selection: inspectedTeamIndex = selection[0] inspectedTeam = self.interface.teams[inspectedTeamIndex] for student in inspectedTeam.members: studentstring = "Name: " + student.name studentstring += " |Languages: " + str(student.filters.get("Languages")[0]).strip('[]') studentstring += " |Pref. Teammates: " + str(student.filters.get("Teammates")[0]).strip('[]') self.inspectedTeamStudentListing.insert(END, studentstring) studentstring = "Schedule for " + student.name + " = " for time_and_day in student.filters.get("Schedule")[0]: studentstring += str(time_and_day.name) + " " + str(time_and_day.times) + " | " self.inspectedTeamScheduleListing.insert(END, studentstring) else: self.inspectedTeamStudentListing.insert(END, "Please try again") self.inspectedTeamStudentListing.pack(padx=5, pady=5) self.inspectedTeamScheduleListing.pack(padx=5, pady=5) #DONE SCROLL AREA #CREATING BOTTOM BUTTONS frame = Frame(self, borderwidth=1) frame.pack(fill=BOTH, expand=True) self.pack(fill=BOTH, expand=True) closeButton = Button(self,text="Close",command=self.optionUI) closeButton.pack(side=RIGHT,padx=5, pady=5) #DONE BOTTOM BUTTONS def memberSwapUI(self,indexes): ''' This creates the window which allows the user to swap individual members and reweigh teams. @param: indexes = int[] ''' #RESETING WINDOW self.h = 400 self.w = 800 self.resetWindow() self.parent.title("Swapping Members") #CREATING SCROLL AREA scrollFrame = Frame(self) scrollFrame.pack(fill=X, side=TOP) self.teamlisting1 = Listbox(scrollFrame, width=self.w, height=9) self.teamlisting2 = Listbox(scrollFrame, width=self.w, height=9) count = 1 team = self.interface.teams[indexes[0]] for student in team.members: teamstring = "" teamstring += student.name self.teamlisting1.insert(END, teamstring) count += 1 team = self.interface.teams[indexes[1]] for student in team.members: teamstring = "" teamstring += student.name self.teamlisting2.insert(END, teamstring) count += 1 self.teamlisting1.pack(padx=5, pady=5) self.teamlisting2.pack(padx=5, pady=5) #DONE SCROLL AREA #CREATING BOTTOM BUTTONS frame = Frame(self, borderwidth=1) frame.pack(fill=BOTH, expand=True) self.pack(fill=BOTH, expand=True) backButton = Button(self,text="Back",command=lambda: self.swapSizeCheck(indexes)) backButton.pack(side=LEFT, padx=5, pady=5) exitButton = Button(self,text="Exit",command=self.parent.destroy) exitButton.pack(side=RIGHT, padx=5, pady=5) swapButton = Button(self,text="Swap Team",command= lambda: self.switchTeams(indexes)) swapButton.pack(side=RIGHT,padx=5, pady=5) #DONE BOTTOM BUTTONS def emailScreen(self): ''' This starts the email login screen ''' if(len(self.teamlisting.curselection()) < 1): messagebox.showinfo("Error","Please select one or more teams") return if(self.remember): self.emailTeams() return self.emailWindow = Toplevel(self.parent) self.centerWindow(self.emailWindow) #CREATING EMAIL FRAME emailFrame = Frame(self.emailWindow) emailFrame.pack(fill=X, side=TOP) emailLabel = Label(emailFrame, text="Email address:", background="white") emailLabel.pack(side=LEFT, padx=15, pady=10) self.emailEntry = Entry(emailFrame, width=20) self.emailEntry.insert(0,"") self.emailEntry.pack(side=LEFT, padx=43, pady=10) #EMAIL FRAME DONE #CREATING PASSWORD FRAME passwordFrame = Frame(self.emailWindow) passwordFrame.pack(fill=X, side=TOP) passwordLabel = Label(passwordFrame, text="Password:"******"white") passwordLabel.pack(side=LEFT, padx=17, pady=10) self.passwordEntry = Entry(passwordFrame, width=20, show="*") self.passwordEntry.insert(0,"") self.passwordEntry.pack(side=LEFT, padx=65, pady=10) #PASSWORD FRAME DONE #CREATING REMEMBER FRAME rememberFrame = Frame(self.emailWindow) rememberFrame.pack(fill=X, side=TOP) rememberLabel = Label(rememberFrame, text="Remember Username/Password", background="white") rememberLabel.pack(side=LEFT, padx=15, pady=10) self.rememberCheck = Checkbutton(rememberFrame) self.rememberCheck.pack(side=LEFT, padx=15, pady=10) #REMEMBER FRAME DONE #CREATING BOTTOM BUTTONS frame = Frame(self.emailWindow, borderwidth=1) frame.pack(fill=BOTH, expand=True) exitButton = Button(self.emailWindow,text="Cancel",command=self.emailWindow.destroy) exitButton.pack(side=RIGHT, padx=5, pady=5) submitButton = Button(self.emailWindow,text="Submit",command=self.emailTeams) submitButton.pack(side=RIGHT,padx=5, pady=5) #DONE BOTTOM BUTTONS def emailTeams(self): ''' This invokes emailing the selected teams ''' success = True if(not self.remember): selection = self.teamlisting.curselection() email = self.emailEntry.get() password = self.passwordEntry.get() if(email == "" or password == ""): messagebox.showinfo("Error","Cannot leave fields empty") return if(len(self.rememberCheck.state()) != 0 and self.rememberCheck.state()[0] == "selected"): self.remember = True success = self.interface.sendEmail(selection,email,password,True) else: success = self.interface.sendEmail(selection,email,password) else: success = self.interface.sendEmail(self.teamlisting.curselection()) if not success: self.remember = False messagebox.showinfo("Error","Sending the email was unsuccessful, check your email and password") return if success: messagebox.showinfo("Success","Email was sent successfully") self.emailWindow.destroy() return def loadingScreen(self): ''' This starts the loading screen and disables all buttons ''' for i in self.winfo_children(): if Button == type(i): i.configure(state=DISABLED) self.loadWindow = Toplevel(self.parent) loadingstring = "Please wait while we run the algorithm" loadinglabel = Label(self.loadWindow, text=loadingstring, background="white") progressbar = Progressbar(self.loadWindow, orient= "horizontal", \ length=300, mode="indeterminate") progressbar.pack(pady=self.h/10) loadinglabel.pack() self.centerWindow(self.loadWindow) self.loadWindow.title("Wait") progressbar.start() def memberSwap(self): ''' This will setup the call for memberSwapUI and check for improper/missing selections ''' indexes = [] selection = self.teamlisting.curselection() for i in selection: indexes.append(i) if len(indexes) == 2: self.memberSwapUI(indexes); else: messagebox.showinfo("Error","Please select 2 teams") def switchTeams(self, indexes): ''' Puts selected members into the other team in the memberSwapUI @param: indexes = int[] ''' student1 = self.teamlisting1.curselection() student2 = self.teamlisting2.curselection() if student1: if len(self.interface.teams[indexes[1]].members) < self.interface.teams[indexes[1]].maxsize: student = self.interface.teams[indexes[0]].members[int(student1[0])] newTeam = self.interface.teams[indexes[1]] oldTeam = self.interface.teams[indexes[0]] newTeam.insertStudent(student) oldTeam.remStudent(student) self.memberSwapUI(indexes) else: messagebox.showinfo("Max Capacity", "Group is at maximum capacity") if student2: if len(self.interface.teams[indexes[0]].members) < self.interface.teams[indexes[0]].maxsize: student = self.interface.teams[indexes[1]].members[int(student2[0])] newTeam = self.interface.teams[indexes[0]] oldTeam = self.interface.teams[indexes[1]] newTeam.insertStudent(student) oldTeam.remStudent(student) self.memberSwapUI(indexes) else: messagebox.showinfo("Max Capacity", "Group is at maximum capacity") def swapSizeCheck(self,indexes): ''' This is a check to make sure before you back up from the memberSwapUI that the sizes are still correct @param: indexes = int[] ''' if len(self.interface.teams[indexes[0]].members) < self.interface.teams[indexes[0]].maxsize \ or len(self.interface.teams[indexes[1]].members) < self.interface.teams[indexes[1]].maxsize: if messagebox.askokcancel("WARNING", "Warning: A group is shorthanded. You sure you want to proceed?"): for index in indexes: self.interface.algorithm.weightCalc(self.interface.teams[index]) self.optionUI(); else: for index in indexes: self.interface.algorithm.weightCalc(self.interface.teams[index]) self.optionUI(); def shuffleSelected(self): ''' This is a wrapper function that shuffles the selected teams ''' #Gets selected values indexes = [] selection = self.teamlisting.curselection() for i in selection: indexes.append(i) self.interface.reShuffleSelectedTeams(indexes) self.optionUI() def reRun(self): ''' A wrapper function to rerun the algorithm ''' thread = ThreadedTask(self.queue,\ self.interface.reShuffleAll) thread.start() ThreadedTask(self.queue,self.loadingScreen).start() self.checkThread(thread,self.optionUI) def submitFiles(self): ''' Checks the validity of the entry feilds for After checks it runs our python script. ''' csvtext = self.csvEntry.get() teamsize = self.teamsizeEntry.get() rostertext = self.rosterEntry.get() outputtext = self.outputEntry.get() #Checking existance of paths and extensions if(not os.path.exists(csvtext) and csvtext[-4:] != ".csv"): messagebox.showinfo("Error","Not a CSV or the file does not exist") return #Checking existance of paths and extensions if(not os.path.exists(rostertext) and rostertext[-4:] != ".txt"): messagebox.showinfo("Error","Not a roster or the file does not exist") return #Checking existance of path if(not os.path.exists(outputtext)): messagebox.showinfo("Error","Directory dosen't exists for output") return #Checking if the string is an int and in range if(not self.testNumber(teamsize)): messagebox.showinfo("Error","Please enter a positive integer for teamsize(2,5)") return self.csvpathh = csvtext self.rosterpathh = rostertext self.outpathh = outputtext self.teamsizeh = teamsize self.interface.setOutputPath(outputtext) self.submitButton.configure(state=DISABLED) runalgorithm = lambda: self.interface.runGeneral(\ rostertext,csvtext,int(teamsize)) thread1 = ThreadedTask(self.queue,runalgorithm) thread2 = ThreadedTask(self.queue,self.loadingScreen) thread2.start() thread1.start() self.checkThread(thread1,self.optionUI) def checkThread(self,thread,function): ''' This function checks to see if the given thread is dead, if it is not, it recalls a new checkThread. After the thread is dead, it calls the given function @param: thread - ThreadedTask functoin - a function ''' if thread.is_alive(): self.parent.after(1000, lambda: self.checkThread(thread,function)) else: function() def testNumber(self,i,minimum=0,maximum=5): ''' Checks if i is an integer and between a certain range @param: i (string) minimum (optional int) maximum (optional int) ''' try: i = int(i) return (i >= minimum) and (i <= maximum) except: return False def csvstartfilebrowser(self): ''' Starts the filebrowser for the csv file ''' currdir = os.getcwd() fileopt = [('csv files', '*.csv')] directo = filedialog.askopenfilename(parent=self, filetypes=fileopt, \ initialdir=currdir, title="Select file") #clearning and setting csventry self.csvEntry.delete(0,'end') self.csvEntry.insert(0,str(directo)) def rosterstartfilebrowser(self): ''' Starts the filebrowser for the text file ''' currdir = os.getcwd() fileopt = [('text files', '*.txt')] directo = filedialog.askopenfilename(parent=self, filetypes=fileopt, \ initialdir=currdir, title="Select file") #clearning and setting rosterentry self.rosterEntry.delete(0,'end') self.rosterEntry.insert(0,str(directo)) def outputstartfilebrowser(self): ''' Starts the filebrowser for the output ''' currdir = os.getcwd() directo = filedialog.askdirectory(parent=self,\ initialdir=currdir, title="Select file") #clearning and setting outputentry self.outputEntry.delete(0,'end') self.outputEntry.insert(0,str(directo))