class TimerDemo: """Controller""" def __init__(self): self.root = Tk() self.root.title('Timer') left = (self.root.winfo_screenwidth() - WIDTH) // 2 top = (self.root.winfo_screenheight() - HEIGHT) // 2 self.root.geometry(f'{WIDTH}x{HEIGHT}+{left}+{top}') self.root.resizable(False, False) # variables self.count_start = IntVar() self.count_start.trace_add('write', self.count_start_changed) self.count = IntVar() self.count.trace_add('write', self.count_changed) self.time = StringVar() self.timer_id = None self.ui = TimerUi(self) self.count_start.set(DEFAULT_COUNT) def run(self): self.root.mainloop() def count_changed(self, *args): self.draw_time() def count_start_changed(self, *args): # Countdown中には呼ばれないようにすること self.count.set(self.count_start.get()) def draw_time(self): count = self.count.get() m, s = count // 60, count % 60 self.time.set(f'{m:0>2}:{s:0>2}') def start_timer(self): if self.count.get() == 0: self.count_start_changed() self.ui.state_started() self.timer_id = self.root.after(1000, self.countdown) def stop_timer(self): if self.timer_id: self.root.after_cancel(self.timer_id) self.ui.state_stopped() def countdown(self): self.count.set(self.count.get() - 1) if self.count.get() > 0: self.timer_id = self.root.after(1000, self.countdown) # recursive else: self.root.bell() self.ui.state_stopped()
class UITk(UI): """TODO doc for UITk""" def __init__(self, rogue): """Constructor for UITk""" super().__init__(rogue) self.window = Tk() self.screen = StringVar() self.draw_from_rogue() self.label = Label(self.window, textvariable=self.screen, fg="white", bg="black", font=("monospace", 11)) self.label.focus_set() self.label.pack() def start_ui(self): """TODO docs""" self.window.mainloop() def on_timer_end(self, timer, callback): """TODO docs""" return self.window.after(timer, callback) def cancel_timer(self, timer): """TODO docs""" if timer is not None: self.window.after_cancel(timer) def on_key_press(self, callback): """TODO docs""" self.label.bind("<Key>", callback) def draw(self, string): """Draw on the screen the provided string""" self.screen.set(string) def read_rogue(self): """Return the string on the rogue process screen""" return self.rb.get_screen_string() def draw_from_rogue(self): """Draw on the screen whats on rogue""" self.draw(self.read_rogue()) def draw_log(self, string): pass
class CollocationsView: _BACKGROUND_COLOUR = "#FFF" # white def __init__(self): self.queue = q.Queue() self.model = CollocationsModel(self.queue) self.top = Tk() self._init_top(self.top) self._init_menubar() self._init_widgets(self.top) self.load_corpus(self.model.DEFAULT_CORPUS) self.after = self.top.after(POLL_INTERVAL, self._poll) def _init_top(self, top): top.geometry("550x650+50+50") top.title("NLTK Collocations List") top.bind("<Control-q>", self.destroy) top.protocol("WM_DELETE_WINDOW", self.destroy) top.minsize(550, 650) def _init_widgets(self, parent): self.main_frame = Frame( parent, dict(background=self._BACKGROUND_COLOUR, padx=1, pady=1, border=1)) self._init_corpus_select(self.main_frame) self._init_results_box(self.main_frame) self._init_paging(self.main_frame) self._init_status(self.main_frame) self.main_frame.pack(fill="both", expand=True) def _init_corpus_select(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) self.var = StringVar(innerframe) self.var.set(self.model.DEFAULT_CORPUS) Label( innerframe, justify=LEFT, text=" Corpus: ", background=self._BACKGROUND_COLOUR, padx=2, pady=1, border=0, ).pack(side="left") other_corpora = list(self.model.CORPORA.keys()).remove( self.model.DEFAULT_CORPUS) om = OptionMenu(innerframe, self.var, self.model.DEFAULT_CORPUS, command=self.corpus_selected, *self.model.non_default_corpora()) om["borderwidth"] = 0 om["highlightthickness"] = 1 om.pack(side="left") innerframe.pack(side="top", fill="x", anchor="n") def _init_status(self, parent): self.status = Label( parent, justify=LEFT, relief=SUNKEN, background=self._BACKGROUND_COLOUR, border=0, padx=1, pady=0, ) self.status.pack(side="top", anchor="sw") def _init_menubar(self): self._result_size = IntVar(self.top) menubar = Menu(self.top) filemenu = Menu(menubar, tearoff=0, borderwidth=0) filemenu.add_command(label="Exit", underline=1, command=self.destroy, accelerator="Ctrl-q") menubar.add_cascade(label="File", underline=0, menu=filemenu) editmenu = Menu(menubar, tearoff=0) rescntmenu = Menu(editmenu, tearoff=0) rescntmenu.add_radiobutton( label="20", variable=self._result_size, underline=0, value=20, command=self.set_result_size, ) rescntmenu.add_radiobutton( label="50", variable=self._result_size, underline=0, value=50, command=self.set_result_size, ) rescntmenu.add_radiobutton( label="100", variable=self._result_size, underline=0, value=100, command=self.set_result_size, ) rescntmenu.invoke(1) editmenu.add_cascade(label="Result Count", underline=0, menu=rescntmenu) menubar.add_cascade(label="Edit", underline=0, menu=editmenu) self.top.config(menu=menubar) def set_result_size(self, **kwargs): self.model.result_count = self._result_size.get() def _init_results_box(self, parent): innerframe = Frame(parent) i1 = Frame(innerframe) i2 = Frame(innerframe) vscrollbar = Scrollbar(i1, borderwidth=1) hscrollbar = Scrollbar(i2, borderwidth=1, orient="horiz") self.results_box = Text( i1, font=Font(family="courier", size="16"), state="disabled", borderwidth=1, yscrollcommand=vscrollbar.set, xscrollcommand=hscrollbar.set, wrap="none", width="40", height="20", exportselection=1, ) self.results_box.pack(side="left", fill="both", expand=True) vscrollbar.pack(side="left", fill="y", anchor="e") vscrollbar.config(command=self.results_box.yview) hscrollbar.pack(side="left", fill="x", expand=True, anchor="w") hscrollbar.config(command=self.results_box.xview) # there is no other way of avoiding the overlap of scrollbars while using pack layout manager!!! Label(i2, text=" ", background=self._BACKGROUND_COLOUR).pack(side="left", anchor="e") i1.pack(side="top", fill="both", expand=True, anchor="n") i2.pack(side="bottom", fill="x", anchor="s") innerframe.pack(side="top", fill="both", expand=True) def _init_paging(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) self.prev = prev = Button( innerframe, text="Previous", command=self.previous, width="10", borderwidth=1, highlightthickness=1, state="disabled", ) prev.pack(side="left", anchor="center") self.next = next = Button( innerframe, text="Next", command=self.__next__, width="10", borderwidth=1, highlightthickness=1, state="disabled", ) next.pack(side="right", anchor="center") innerframe.pack(side="top", fill="y") self.reset_current_page() def reset_current_page(self): self.current_page = -1 def _poll(self): try: event = self.queue.get(block=False) except q.Empty: pass else: if event == CORPUS_LOADED_EVENT: self.handle_corpus_loaded(event) elif event == ERROR_LOADING_CORPUS_EVENT: self.handle_error_loading_corpus(event) self.after = self.top.after(POLL_INTERVAL, self._poll) def handle_error_loading_corpus(self, event): self.status["text"] = "Error in loading " + self.var.get() self.unfreeze_editable() self.clear_results_box() self.freeze_editable() self.reset_current_page() def handle_corpus_loaded(self, event): self.status["text"] = self.var.get() + " is loaded" self.unfreeze_editable() self.clear_results_box() self.reset_current_page() # self.next() collocations = self.model.next(self.current_page + 1) self.write_results(collocations) self.current_page += 1 def corpus_selected(self, *args): new_selection = self.var.get() self.load_corpus(new_selection) def previous(self): self.freeze_editable() collocations = self.model.prev(self.current_page - 1) self.current_page = self.current_page - 1 self.clear_results_box() self.write_results(collocations) self.unfreeze_editable() def __next__(self): self.freeze_editable() collocations = self.model.next(self.current_page + 1) self.clear_results_box() self.write_results(collocations) self.current_page += 1 self.unfreeze_editable() def load_corpus(self, selection): if self.model.selected_corpus != selection: self.status["text"] = "Loading " + selection + "..." self.freeze_editable() self.model.load_corpus(selection) def freeze_editable(self): self.prev["state"] = "disabled" self.next["state"] = "disabled" def clear_results_box(self): self.results_box["state"] = "normal" self.results_box.delete("1.0", END) self.results_box["state"] = "disabled" def fire_event(self, event): # Firing an event so that rendering of widgets happen in the mainloop thread self.top.event_generate(event, when="tail") def destroy(self, *e): if self.top is None: return self.top.after_cancel(self.after) self.top.destroy() self.top = None def mainloop(self, *args, **kwargs): if in_idle(): return self.top.mainloop(*args, **kwargs) def unfreeze_editable(self): self.set_paging_button_states() def set_paging_button_states(self): if self.current_page == -1 or self.current_page == 0: self.prev["state"] = "disabled" else: self.prev["state"] = "normal" if self.model.is_last_page(self.current_page): self.next["state"] = "disabled" else: self.next["state"] = "normal" def write_results(self, results): self.results_box["state"] = "normal" row = 1 for each in results: self.results_box.insert( str(row) + ".0", each[0] + " " + each[1] + "\n") row += 1 self.results_box["state"] = "disabled"
class Game: def __init__(self): self.in_game = False self.add = False self.job = None self.score = 0 self.keys = { 'j': 'Down', 'k': 'Up', 'h': 'Left', 'l': 'Right', } self.root = Tk() self.root.title('Python Snake') self.canvas = Canvas(self.root, width=WIDTH + 250, height=HEIGHT, bg=BG_SIDE) self.canvas.grid() self.score_label = Label(self.canvas, width=8, height=2, font='Impact 36', bg=BG_SIDE) self.score_label.place(x=WIDTH + 10, y=100) self.score_label.configure(text='Очки:\n0') self.hint_label = Label(self.canvas, width=11, height=7, font='Impact 36', bg=BG_SIDE) self.hint_label.place(x=WIDTH + 10, y=200) self.canvas.focus_set() self.draw_field() self.snake = Snake(self.canvas, SEG) self.apple = None self.create_apple() self.in_menu() self.root.protocol("WM_DELETE_WINDOW", self.on_close) def run(self): self.root.mainloop() def in_menu(self): self.in_game = False self.canvas.bind("<KeyPress>", self.menu_keypress_handler) self.hint_label.configure(text='Управление:\n' 'Q: Выход\n' 'I: Режим игры\n') def menu_keypress_handler(self, event): if event.keysym == 'i': self.game_mode() if event.keysym == 'q': self.root.destroy() def game_mode(self): self.canvas.bind("<KeyPress>", self.game_keypress_handler) self.in_game = True self.job = self.root.after(100, self.main_loop) self.hint_label.configure(text='Управление:\n' 'H: Влево\n' 'J: Вниз\n' 'K: Вверх\n' 'L: Вправо\n' 'Esc: Меню\n') def game_keypress_handler(self, event): if event.keysym in self.keys: self.snake.change_direction(self.keys[event.keysym]) if event.keysym == 'Escape': self.root.after_cancel(self.job) self.in_menu() def snake_intersection(self): head = self.canvas.coords(self.snake.segments[-1].instance) return any( self.canvas.coords(segment.instance) == head for segment in self.snake.segments[:-1]) def main_loop(self): if self.in_game: self.snake.move(self.add) if self.add: self.delete_apple() self.create_apple() self.update_score() self.add = False x1, y1, x2, y2 = self.canvas.coords( self.snake.segments[-1].instance) if x2 > WIDTH or x1 < 0 or y1 < 0 or y2 > HEIGHT or self.snake_intersection( ): self.in_game = False if self.apple in self.canvas.find_overlapping( x1 + 1, y1 + 1, x2 - 1, y2 - 1): self.add = True self.job = self.root.after(100, self.main_loop) def delete_apple(self): self.canvas.delete(self.apple) self.apple = None def create_apple(self): while not self.apple or any(self.apple in self.canvas.find_overlapping( *self.canvas.coords(x.instance)) for x in self.snake.segments): if self.apple: self.canvas.delete(self.apple) x = SEG * randint(1, WIDTH // SEG - 1) y = SEG * randint(1, HEIGHT // SEG - 1) self.apple = self.canvas.create_oval(x, y, x + SEG, y + SEG, fill='red') def draw_field(self): self.canvas.create_rectangle(0, 0, WIDTH, HEIGHT, fill=BG_FIELD) def update_score(self): self.score += 1 self.score_label.configure(text=f'Очки:\n{self.score}') def on_close(self): if self.job: self.root.after_cancel(self.job) self.root.destroy()
class ShiftApp(): ### In-progress Shift application ### def __init__(self, db, job_name): self.db = db self.events = {} self.job_name = job_name self.start_time = time.time() self.break_start = 0 self.break_time = 0 self.id = self.db.add_shift(self.job_name, self.start_time, None, self.break_time, None) self.tasks = {} self.task_index = [] self.cur_task = 0 self.root = Tk() self.root.title(f'Shift - {job_name}') self.root.resizable(False, False) self.root.overrideredirect(0) self.tm1_selection = StringVar(self.root) self.tm2_selection = StringVar(self.root) self.container = ttk.Frame(self.root) self.frame = ttk.Frame(self.container) self.job_label = ttk.Label(self.frame, text=f'Job: {job_name}') self.elapsed_time_label = ttk.Label(self.frame, width=18) self.task_frame = ttk.Frame(self.frame) self.task_label = ttk.Label(self.task_frame, text="Tasks:") self.tm1_options, self.tm2_options = ('All Job Tasks', 'Shift Tasks Only'), () self.task_menu1 = ttk.OptionMenu(self.frame, self.tm1_selection, self.tm1_options[0], *self.tm1_options) self.task_entry = ttk.Entry(self.frame) self.task_list = Listbox(self.task_frame, selectmode=SINGLE, width=20, height=12, relief='sunken') vbar = ttk.Scrollbar(self.task_frame, orient='vertical', command=self.task_list.yview) self.task_list.config(yscrollcommand=vbar.set) self.new_task_button = ttk.Button(self.frame, text="New Task", command=self.new_task) self.button_frame = ttk.Frame(self.frame) self.pause_button = ttk.Button(self.button_frame, text='Pause', command=self.toggle_break, width=7) # TODO: Add info button self.cancel_button = ttk.Button(self.frame, text='Cancel', command=self.cancel_prompt) self.notes = ScrolledText(self.frame, undo=True, width=60, height=15, relief='sunken') self.report_job_button = ttk.Button(self.frame, text='Prior Shifts', command=self.launch_report_edit) self.save_button = ttk.Button(self.frame, text='Stop and Save', command=self.end_prompt) self.job_label.grid(column=2, columnspan=2, row=1, sticky=W, pady=5) self.elapsed_time_label.grid(column=4, row=1, sticky=E) self.task_menu1.grid(column=1, row=1, sticky=(E, W), padx=(0, 15)) self.task_entry.grid(column=1, row=2, sticky=(E, W), padx=(0, 15)) self.task_list.grid(column=1, row=1, sticky=(N, S)) vbar.grid(column=2, row=1, sticky=(N, S)) self.task_frame.grid(column=1, row=3, sticky=(N, S), pady=(5, 5)) self.new_task_button.grid(column=1, row=4, sticky=W) self.button_frame.grid(column=2, columnspan=2, row=2, sticky=W) self.pause_button.grid(column=2, row=1, sticky=W) self.cancel_button.grid(column=4, row=2, sticky=E) self.notes.grid(column=2, columnspan=3, row=3, pady=(5, 5)) self.report_job_button.grid(column=2, row=4, sticky=W) self.save_button.grid(column=4, row=4, sticky=E) self.frame.grid(column=0, row=0, padx=5, pady=5) self.container.grid(column=0, row=0) self.root.protocol("WM_DELETE_WINDOW", self.cancel_prompt) self.task_menu1.bind("<ButtonRelease-1>", self.filter_tasks) # filter_tasks clears notes self.task_entry.bind("<KeyRelease>", self.filter_tasks) self.task_list.bind("<ButtonRelease-1>", self.focus_task) self.task_list.bind("<Double-Button-1>", self.copy_task_title) self.notes.bind("<Command-Z>", self.edit_undo) self.time_counter() self.auto_save() self.get_tasks() def edit_undo(self, event=None): try: self.notes.edit_undo() except TclError as e: print(e) def get_tasks(self, event=None, **kwargs): ## Gets set of filtered tasks from db ## if "Job" in self.tm1_selection.get(): kwargs["job_name"] = self.job_name else: kwargs["shift_id"] = self.id tasks = self.db.report_tasks(**kwargs) self.tasks.clear() for task in tasks: self.tasks[task["id"]] = task self.task_list.delete(0, END) self.cur_task = 0 self.task_index = [] self.task_list.insert(END, "Shift Notes") self.task_index.append(None) for _, task in self.tasks.items(): self.task_list.insert(END, task['title']) self.task_index.append(task["id"]) def filter_tasks(self, event=None): # TODO: set focus to 'Shift Notes' search_term = self.task_entry.get() if search_term in ('', ' '): search_term = None self.get_tasks(search_term=search_term) notes = self.db.get_shift(self.id)["notes"] self.notes.delete("0.0", END) self.notes.insert(END, notes) def new_task(self): task_title = self.task_entry.get() if task_title not in ('', ' '): task = self.db.add_task(self.id, self.job_name, task_title) self.task_entry.delete(0, END) self.get_tasks() self.task_list.activate(self.task_index.index(task["id"])) def focus_task( self, event=None): ## displays notes for the currently selected task ## if self.task_list.curselection(): new_cur = self.task_list.curselection()[0] if new_cur == self.cur_task: return notes = self.notes.get(0.0, END).strip('\n') if self.cur_task: # if the notes being displayed belong to a task task_id = self.task_index[self.cur_task] self.tasks[task_id]["notes"] = notes else: self.db.update_shift(self.id, notes=notes) if new_cur: # if the new notes to be displayed belong to a task task_id = self.task_index[new_cur] notes = self.tasks[task_id]["notes"] else: shift = self.db.report_shifts(shift_id=self.id)[0] notes = shift["notes"] self.notes.delete('0.0', END) self.notes.insert(END, notes) self.cur_task = new_cur def copy_task_title( self, event=None ): ## Copies title from current selection in task_list to task_entry ## cur = self.task_list.curselection()[0] if cur: task = self.tasks[self.task_index[cur]] self.task_entry.delete(0, END) self.task_entry.insert(0, task["title"]) self.filter_tasks() def time_counter(self): if not self.break_start: elapsed_time = time.gmtime(time.time() - self.start_time - self.break_time) elapsed_hours = time.strftime('%H', elapsed_time) elapsed_minutes = time.strftime('%M', elapsed_time) elapsed_seconds = time.strftime('%S', elapsed_time) self.elapsed_time_label[ 'text'] = f'Elapsed Time: {elapsed_hours}:{elapsed_minutes}:{elapsed_seconds}' self.events['time_counter'] = self.root.after(200, self.time_counter) def auto_save(self): self.save_update() self.events['auto_save'] = self.root.after(5000, self.auto_save) def save_update( self): ## Commits current state for shift and tasks to db ## if self.break_start: break_time = self.break_time + (time.time() - self.break_start) else: break_time = self.break_time end_time = time.time() notes = self.notes.get(0.0, END).strip('\n') if self.cur_task: # if the notes being displayed belong to a task task_id = self.task_index[self.cur_task] self.tasks[task_id]["notes"] = notes notes = None self.db.update_shift(self.id, end_time=end_time, break_time=break_time, notes=notes) for _, task in self.tasks.items(): self.db.update_task(**task) def toggle_break(self): if not self.break_start: self.break_start = time.time() self.job_label['text'] = f'{self.job_name} - Paused' self.pause_button['text'] = 'Resume' else: self.break_time += time.time() - self.break_start self.break_start = 0 self.job_label['text'] = self.job_name self.pause_button['text'] = 'Pause' def end_prompt(self): popup = PopConfirm("Save and exit shift?", self.end_shift) popup.root.mainloop() def cancel_prompt(self): popup = PopConfirm("Cancel and delete shift?", self.cancel_shift) popup.root.mainloop() def end_shift(self): self.save_update() self.db.complete_shift(self.id) self.close() def cancel_shift(self): self.db.remove_shift(self.id) self.close() def launch_report_edit(self): self.report_edit_window = ReportEditApp(self.db, job_name=self.job_name) self.report_edit_window.root.mainloop() def close(self): for val in self.events.values(): self.root.after_cancel(val) self.root.destroy()
class puzzle(): # x is number of rows, y = number of columns def __init__(self, x=3, y=3): #name of the window self.root = Tk() self.root.title('Number Puzzle') self.root.iconphoto(False, PhotoImage(file='info.png')) #name of canvas = 'puzz' self.puzz = Canvas(self.root, width=720, height=550, bg='#cccccc') self.puzz.pack() self.ids = {} self.timer = [0, 0] self.timejob = None #after function self.timeshuffle = None #after function self.shufflepoints = 0 #counting shuffles self.x = x self.y = y self.grids = {} self.allowables = {} # this gives the allowable swap self.__grid_creator__() self.__clock_creator__() #self.__timer_clock__() self.__get_allowables__() self.root.mainloop() ##get allowables swap positions def __get_allowables__(self): for i in self.allowables: if i % self.y != 0: self.allowables[i].append(i - 1) if i % self.y != self.y - 1: self.allowables[i].append(i + 1) if i >= self.y: self.allowables[i].append(i - self.y) if i // self.y != self.x - 1: self.allowables[i].append(i + self.y) #refreshes the time_text def __timer_clock__(self): self.timer[1] += 1 #getting mis and secs if self.timer[1] >= 60: self.timer[1] = 0 self.timer[0] += 1 #converting them into displayanle format if self.timer[1] <= 9: secs = '0' + str(self.timer[1]) else: secs = str(self.timer[1]) if self.timer[0] <= 9: mins = '0' + str(self.timer[0]) else: mins = str(self.timer[0]) timer = mins + ':' + secs #time over condition if self.timer[0] >= 59: self.__time_stopper__("Stop") else: self.puzz.itemconfig(self.ids['clock_time'], text=timer, fill='#000000') self.timejob = self.root.after(1000, self.__timer_clock__) def __time_stopper__(self, msg): self.root.after_cancel(self.timejob) self.timejob = None self.puzz.itemconfig(self.ids['clock_time'], text=msg, fill='#ff4444') def __clock_creator__(self): ##for creating clock and other button #name used for clock_text = 'clock_time' and box is = 'clock_box' self.ids['clock_box'] = self.puzz.create_rectangle(520, 300, 700, 350, fill='#eeeeee', tag='clock_box') self.ids['clock_time'] = self.puzz.create_text(610, 325, text='Timer', font='tester 20', tag='clock_time') self.ids['shuffle_box'] = self.puzz.create_rectangle(520, 200, 700, 250, fill='#eeeeee', tag='shuffle_box') self.ids['shuffle_text'] = self.puzz.create_text(610, 225, text='shuffle', font='tester 20', tag='shuffle_text') self.puzz.tag_bind(self.ids['shuffle_box'], "<Enter>", lambda event: self.Entered('shuffle_box')) self.puzz.tag_bind(self.ids['shuffle_box'], "<Leave>", lambda event: self.Leaved('shuffle_box')) self.puzz.tag_bind(self.ids['shuffle_text'], "<Enter>", lambda event: self.Entered('shuffle_box')) self.puzz.tag_bind(self.ids['shuffle_text'], "<Leave>", lambda event: self.Leaved('shuffle_box')) self.puzz.tag_bind(self.ids['shuffle_box'], "<Button-1>", self.__shuffler__) self.puzz.tag_bind(self.ids['shuffle_text'], "<Button-1>", self.__shuffler__) def clock_reset(self): self.timer[0] = 0 self.timer[1] = 0 def __grid_creator__(self): #getting row and column number for i in range(self.x): for j in range(self.y): self.grids[i * self.y + j] = [i, j] #for getting the coordinates for i in self.grids: x1 = self.grids[i][1] * 500 / self.y + 20 x1 = floor(x1) x2 = x1 + 500 / self.y - 5 x2 = floor(x2) y1 = self.grids[i][0] * 500 / self.x + 20 y1 = floor(y1) y2 = y1 + 500 / self.x - 5 y2 = floor(y2) self.grids[i].append(x1) self.grids[i].append(y1) self.grids[i].append(x2) self.grids[i].append(y2) bname = 'slot_' + str(i) tname = 'text_' + str(i) self.ids[bname] = self.puzz.create_rectangle(self.grids[i][2], self.grids[i][3], self.grids[i][4], self.grids[i][5], tags=bname, fill='#eeeeee') self.ids[tname] = self.puzz.create_text( (self.grids[i][2] + self.grids[i][4]) / 2, (self.grids[i][3] + self.grids[i][5]) / 2, text=i, tags=tname, font='tester 20') bname = 'slot_' + str(i) tname = 'text_' + str(i) self.event_binder(bname, tname) self.grids[i].append(i) self.allowables[i] = [] # with the last box made empty self.puzz.itemconfig(self.ids['slot_' + str(self.x * self.y - 1)], fill='', outline='') self.puzz.delete('text_' + str(self.x * self.y - 1)) #to bind the slots with the Enter and leave events and click def event_binder(self, x, y): self.puzz.tag_bind(self.ids[x], "<Enter>", lambda event: self.Entered(x)) self.puzz.tag_bind(self.ids[x], "<Leave>", lambda event: self.Leaved(x)) self.puzz.tag_bind(self.ids[y], "<Enter>", lambda event: self.Entered(x)) self.puzz.tag_bind(self.ids[y], "<Leave>", lambda event: self.Leaved(x)) self.puzz.tag_bind(self.ids[x], "<Button-1>", lambda event: self.clicked(x)) self.puzz.tag_bind(self.ids[y], "<Button-1>", lambda event: self.clicked(x)) #when cursor hovers over the slot def Entered(self, x): if x != 'slot_' + str(self.x * self.y - 1): self.puzz.itemconfig(self.ids[x], fill='#ffffff') else: self.puzz.itemconfig(self.ids['slot_' + str(self.x * self.y - 1)], fill='', outline='') #when cursor leaved the slot def Leaved(self, x): if x != 'slot_' + str(self.x * self.y - 1): self.puzz.itemconfig(self.ids[x], fill='#eeeeee') else: self.puzz.itemconfig(self.ids['slot_' + str(self.x * self.y - 1)], fill='', outline='') # notices the click and decides if the swapper fuction is to be called def clicked(self, x): cid = int(x[5:]) #it is the actual name of the box that is clicked on cpos = self.grids[cid][6] #it is its position on the canvas bpos = self.grids[self.x * self.y - 1][6] # it is postion of blank in canvas if cpos in self.allowables[bpos]: self.swapper(cid) #swappes the clicked slot with the blank slot def swapper(self, x): ##print(x,self.grids[x][6], '\t',self.grids[self.x*self.y-1][6]) ccoor = self.puzz.coords('slot_' + str(x)) bcoor = self.puzz.coords('slot_' + str(self.x * self.y - 1)) #now moving the slots # and changing their data respectively #blank moving left if ccoor[0] < bcoor[0]: self.puzz.move(self.ids['slot_' + str(self.x * self.y - 1)], -100, 0) self.puzz.move(self.ids['slot_' + str(x)], 100, 0) self.puzz.move(self.ids['text_' + str(x)], 100, 0) self.grids[self.x * self.y - 1][6] = self.grids[self.x * self.y - 1][6] - 1 self.grids[x][6] = self.grids[x][6] + 1 elif ccoor[1] < bcoor[1]: self.puzz.move(self.ids['slot_' + str(self.x * self.y - 1)], 0, -100) self.puzz.move(self.ids['slot_' + str(x)], 0, 100) self.puzz.move(self.ids['text_' + str(x)], 0, 100) self.grids[self.x * self.y - 1][6] = self.grids[self.x * self.y - 1][6] - self.y self.grids[x][6] = self.grids[x][6] + self.y elif ccoor[0] > bcoor[0]: self.puzz.move(self.ids['slot_' + str(self.x * self.y - 1)], 100, 0) self.puzz.move(self.ids['slot_' + str(x)], -100, 0) self.puzz.move(self.ids['text_' + str(x)], -100, 0) self.grids[self.x * self.y - 1][6] = self.grids[self.x * self.y - 1][6] + 1 self.grids[x][6] = self.grids[x][6] - 1 elif ccoor[1] > bcoor[1]: self.puzz.move(self.ids['slot_' + str(self.x * self.y - 1)], 0, 100) self.puzz.move(self.ids['slot_' + str(x)], 0, -100) self.puzz.move(self.ids['text_' + str(x)], 0, -100) self.grids[self.x * self.y - 1][6] = self.grids[self.x * self.y - 1][6] + self.y self.grids[x][6] = self.grids[x][6] - self.y ###checking if all the slots are in positions self.__checker__() def __shuffler__(self, x): #print('shuffle was called') self.timer = [0, 0] try: self.__time_stopper__('wait') except: self.__timer_clock__() self.__time_stopper__('wait') #current poition of blanks self.grids[24][6] #getting available swapsposition self.allowables[self.grids[24][6]] if self.timeshuffle == None: self.shuffler2() def shuffler2(self): if self.shufflepoints <= 10: ch = [] for i in self.allowables[self.grids[24][6]]: for j in range(25): if self.grids[j][6] == i: ch.append(j) ch = choice(ch) ch = 'slot_' + str(ch) self.clicked(ch) ##print(self.timeshuffle) self.shufflepoints += 1 self.timeshuffle = self.root.after(100, self.shuffler2) else: self.root.after_cancel(self.timeshuffle) self.shufflepoints = 0 self.timeshuffle = None self.__timer_clock__() def __checker__(self): 'checking if the grid has been restored' if self.timeshuffle == None: correct = 0 for i in range(25): if i == self.grids[i][6]: correct += 1 if correct == 25: #print('completed') #converting them into displayanle format if self.timer[1] <= 9: secs = '0' + str(self.timer[1]) else: secs = str(self.timer[1]) if self.timer[0] <= 9: mins = '0' + str(self.timer[0]) else: mins = str(self.timer[0]) timer = mins + ':' + secs try: self.__time_stopper__(timer) except: pass def __reset__(self, x): pass #del game #print('game deleted') #still working on it def __del__(self): pass
class ConcordanceSearchView(object): _BACKGROUND_COLOUR='#FFF' #white #Colour of highlighted results _HIGHLIGHT_WORD_COLOUR='#F00' #red _HIGHLIGHT_WORD_TAG='HL_WRD_TAG' _HIGHLIGHT_LABEL_COLOUR='#C0C0C0' # dark grey _HIGHLIGHT_LABEL_TAG='HL_LBL_TAG' #Percentage of text left of the scrollbar position _FRACTION_LEFT_TEXT=0.30 def __init__(self): self.queue = q.Queue() self.model = ConcordanceSearchModel(self.queue) self.top = Tk() self._init_top(self.top) self._init_menubar() self._init_widgets(self.top) self.load_corpus(self.model.DEFAULT_CORPUS) self.after = self.top.after(POLL_INTERVAL, self._poll) def _init_top(self, top): top.geometry('950x680+50+50') top.title('NLTK Concordance Search') top.bind('<Control-q>', self.destroy) top.protocol('WM_DELETE_WINDOW', self.destroy) top.minsize(950,680) def _init_widgets(self, parent): self.main_frame = Frame(parent, dict(background=self._BACKGROUND_COLOUR, padx=1, pady=1, border=1)) self._init_corpus_select(self.main_frame) self._init_query_box(self.main_frame) self._init_results_box(self.main_frame) self._init_paging(self.main_frame) self._init_status(self.main_frame) self.main_frame.pack(fill='both', expand=True) def _init_menubar(self): self._result_size = IntVar(self.top) self._cntx_bf_len = IntVar(self.top) self._cntx_af_len = IntVar(self.top) menubar = Menu(self.top) filemenu = Menu(menubar, tearoff=0, borderwidth=0) filemenu.add_command(label='Exit', underline=1, command=self.destroy, accelerator='Ctrl-q') menubar.add_cascade(label='File', underline=0, menu=filemenu) editmenu = Menu(menubar, tearoff=0) rescntmenu = Menu(editmenu, tearoff=0) rescntmenu.add_radiobutton(label='20', variable=self._result_size, underline=0, value=20, command=self.set_result_size) rescntmenu.add_radiobutton(label='50', variable=self._result_size, underline=0, value=50, command=self.set_result_size) rescntmenu.add_radiobutton(label='100', variable=self._result_size, underline=0, value=100, command=self.set_result_size) rescntmenu.invoke(1) editmenu.add_cascade(label='Result Count', underline=0, menu=rescntmenu) cntxmenu = Menu(editmenu, tearoff=0) cntxbfmenu = Menu(cntxmenu, tearoff=0) cntxbfmenu.add_radiobutton(label='60 characters', variable=self._cntx_bf_len, underline=0, value=60, command=self.set_cntx_bf_len) cntxbfmenu.add_radiobutton(label='80 characters', variable=self._cntx_bf_len, underline=0, value=80, command=self.set_cntx_bf_len) cntxbfmenu.add_radiobutton(label='100 characters', variable=self._cntx_bf_len, underline=0, value=100, command=self.set_cntx_bf_len) cntxbfmenu.invoke(1) cntxmenu.add_cascade(label='Before', underline=0, menu=cntxbfmenu) cntxafmenu = Menu(cntxmenu, tearoff=0) cntxafmenu.add_radiobutton(label='70 characters', variable=self._cntx_af_len, underline=0, value=70, command=self.set_cntx_af_len) cntxafmenu.add_radiobutton(label='90 characters', variable=self._cntx_af_len, underline=0, value=90, command=self.set_cntx_af_len) cntxafmenu.add_radiobutton(label='110 characters', variable=self._cntx_af_len, underline=0, value=110, command=self.set_cntx_af_len) cntxafmenu.invoke(1) cntxmenu.add_cascade(label='After', underline=0, menu=cntxafmenu) editmenu.add_cascade(label='Context', underline=0, menu=cntxmenu) menubar.add_cascade(label='Edit', underline=0, menu=editmenu) self.top.config(menu=menubar) def set_result_size(self, **kwargs): self.model.result_count = self._result_size.get() def set_cntx_af_len(self, **kwargs): self._char_after = self._cntx_af_len.get() def set_cntx_bf_len(self, **kwargs): self._char_before = self._cntx_bf_len.get() def _init_corpus_select(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) self.var = StringVar(innerframe) self.var.set(self.model.DEFAULT_CORPUS) Label(innerframe, justify=LEFT, text=' Corpus: ', background=self._BACKGROUND_COLOUR, padx = 2, pady = 1, border = 0).pack(side='left') other_corpora = list(self.model.CORPORA.keys()).remove(self.model.DEFAULT_CORPUS) om = OptionMenu(innerframe, self.var, self.model.DEFAULT_CORPUS, command=self.corpus_selected, *self.model.non_default_corpora()) om['borderwidth'] = 0 om['highlightthickness'] = 1 om.pack(side='left') innerframe.pack(side='top', fill='x', anchor='n') def _init_status(self, parent): self.status = Label(parent, justify=LEFT, relief=SUNKEN, background=self._BACKGROUND_COLOUR, border=0, padx = 1, pady = 0) self.status.pack(side='top', anchor='sw') def _init_query_box(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) another = Frame(innerframe, background=self._BACKGROUND_COLOUR) self.query_box = Entry(another, width=60) self.query_box.pack(side='left', fill='x', pady=25, anchor='center') self.search_button = Button(another, text='Search', command=self.search, borderwidth=1, highlightthickness=1) self.search_button.pack(side='left', fill='x', pady=25, anchor='center') self.query_box.bind('<KeyPress-Return>', self.search_enter_keypress_handler) another.pack() innerframe.pack(side='top', fill='x', anchor='n') def search_enter_keypress_handler(self, *event): self.search() def _init_results_box(self, parent): innerframe = Frame(parent) i1 = Frame(innerframe) i2 = Frame(innerframe) vscrollbar = Scrollbar(i1, borderwidth=1) hscrollbar = Scrollbar(i2, borderwidth=1, orient='horiz') self.results_box = Text(i1, font=tkinter.font.Font(family='courier', size='16'), state='disabled', borderwidth=1, yscrollcommand=vscrollbar.set, xscrollcommand=hscrollbar.set, wrap='none', width='40', height = '20', exportselection=1) self.results_box.pack(side='left', fill='both', expand=True) self.results_box.tag_config(self._HIGHLIGHT_WORD_TAG, foreground=self._HIGHLIGHT_WORD_COLOUR) self.results_box.tag_config(self._HIGHLIGHT_LABEL_TAG, foreground=self._HIGHLIGHT_LABEL_COLOUR) vscrollbar.pack(side='left', fill='y', anchor='e') vscrollbar.config(command=self.results_box.yview) hscrollbar.pack(side='left', fill='x', expand=True, anchor='w') hscrollbar.config(command=self.results_box.xview) #there is no other way of avoiding the overlap of scrollbars while using pack layout manager!!! Label(i2, text=' ', background=self._BACKGROUND_COLOUR).pack(side='left', anchor='e') i1.pack(side='top', fill='both', expand=True, anchor='n') i2.pack(side='bottom', fill='x', anchor='s') innerframe.pack(side='top', fill='both', expand=True) def _init_paging(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) self.prev = prev = Button(innerframe, text='Previous', command=self.previous, width='10', borderwidth=1, highlightthickness=1, state='disabled') prev.pack(side='left', anchor='center') self.next = next = Button(innerframe, text='Next', command=self.__next__, width='10', borderwidth=1, highlightthickness=1, state='disabled') next.pack(side='right', anchor='center') innerframe.pack(side='top', fill='y') self.current_page = 0 def previous(self): self.clear_results_box() self.freeze_editable() self.model.prev(self.current_page - 1) def __next__(self): self.clear_results_box() self.freeze_editable() self.model.next(self.current_page + 1) def about(self, *e): ABOUT = ("NLTK Concordance Search Demo\n") TITLE = 'About: NLTK Concordance Search Demo' try: from tkinter.messagebox import Message Message(message=ABOUT, title=TITLE, parent=self.main_frame).show() except: ShowText(self.top, TITLE, ABOUT) def _bind_event_handlers(self): self.top.bind(CORPUS_LOADED_EVENT, self.handle_corpus_loaded) self.top.bind(SEARCH_TERMINATED_EVENT, self.handle_search_terminated) self.top.bind(SEARCH_ERROR_EVENT, self.handle_search_error) self.top.bind(ERROR_LOADING_CORPUS_EVENT, self.handle_error_loading_corpus) def _poll(self): try: event = self.queue.get(block=False) except q.Empty: pass else: if event == CORPUS_LOADED_EVENT: self.handle_corpus_loaded(event) elif event == SEARCH_TERMINATED_EVENT: self.handle_search_terminated(event) elif event == SEARCH_ERROR_EVENT: self.handle_search_error(event) elif event == ERROR_LOADING_CORPUS_EVENT: self.handle_error_loading_corpus(event) self.after = self.top.after(POLL_INTERVAL, self._poll) def handle_error_loading_corpus(self, event): self.status['text'] = 'Error in loading ' + self.var.get() self.unfreeze_editable() self.clear_all() self.freeze_editable() def handle_corpus_loaded(self, event): self.status['text'] = self.var.get() + ' is loaded' self.unfreeze_editable() self.clear_all() self.query_box.focus_set() def handle_search_terminated(self, event): #todo: refactor the model such that it is less state sensitive results = self.model.get_results() self.write_results(results) self.status['text'] = '' if len(results) == 0: self.status['text'] = 'No results found for ' + self.model.query else: self.current_page = self.model.last_requested_page self.unfreeze_editable() self.results_box.xview_moveto(self._FRACTION_LEFT_TEXT) def handle_search_error(self, event): self.status['text'] = 'Error in query ' + self.model.query self.unfreeze_editable() def corpus_selected(self, *args): new_selection = self.var.get() self.load_corpus(new_selection) def load_corpus(self, selection): if self.model.selected_corpus != selection: self.status['text'] = 'Loading ' + selection + '...' self.freeze_editable() self.model.load_corpus(selection) def search(self): self.current_page = 0 self.clear_results_box() self.model.reset_results() query = self.query_box.get() if (len(query.strip()) == 0): return self.status['text'] = 'Searching for ' + query self.freeze_editable() self.model.search(query, self.current_page + 1, ) def write_results(self, results): self.results_box['state'] = 'normal' row = 1 for each in results: sent, pos1, pos2 = each[0].strip(), each[1], each[2] if len(sent) != 0: if (pos1 < self._char_before): sent, pos1, pos2 = self.pad(sent, pos1, pos2) sentence = sent[pos1-self._char_before:pos1+self._char_after] if not row == len(results): sentence += '\n' self.results_box.insert(str(row) + '.0', sentence) word_markers, label_markers = self.words_and_labels(sent, pos1, pos2) for marker in word_markers: self.results_box.tag_add(self._HIGHLIGHT_WORD_TAG, str(row) + '.' + str(marker[0]), str(row) + '.' + str(marker[1])) for marker in label_markers: self.results_box.tag_add(self._HIGHLIGHT_LABEL_TAG, str(row) + '.' + str(marker[0]), str(row) + '.' + str(marker[1])) row += 1 self.results_box['state'] = 'disabled' def words_and_labels(self, sentence, pos1, pos2): search_exp = sentence[pos1:pos2] words, labels = [], [] labeled_words = search_exp.split(' ') index = 0 for each in labeled_words: if each == '': index += 1 else: word, label = each.split('/') words.append((self._char_before + index, self._char_before + index + len(word))) index += len(word) + 1 labels.append((self._char_before + index, self._char_before + index + len(label))) index += len(label) index += 1 return words, labels def pad(self, sent, hstart, hend): if hstart >= self._char_before: return sent, hstart, hend d = self._char_before - hstart sent = ''.join([' '] * d) + sent return sent, hstart + d, hend + d def destroy(self, *e): if self.top is None: return self.top.after_cancel(self.after) self.top.destroy() self.top = None def clear_all(self): self.query_box.delete(0, END) self.model.reset_query() self.clear_results_box() def clear_results_box(self): self.results_box['state'] = 'normal' self.results_box.delete("1.0", END) self.results_box['state'] = 'disabled' def freeze_editable(self): self.query_box['state'] = 'disabled' self.search_button['state'] = 'disabled' self.prev['state'] = 'disabled' self.next['state'] = 'disabled' def unfreeze_editable(self): self.query_box['state'] = 'normal' self.search_button['state'] = 'normal' self.set_paging_button_states() def set_paging_button_states(self): if self.current_page == 0 or self.current_page == 1: self.prev['state'] = 'disabled' else: self.prev['state'] = 'normal' if self.model.has_more_pages(self.current_page): self.next['state'] = 'normal' else: self.next['state'] = 'disabled' def fire_event(self, event): #Firing an event so that rendering of widgets happen in the mainloop thread self.top.event_generate(event, when='tail') def mainloop(self, *args, **kwargs): if in_idle(): return self.top.mainloop(*args, **kwargs)
class TkViewer(Viewer): """Graphical viewer using Tk.""" # size of a tile w_tile = 50 # width of a wall w_wall = 20 # size of a cell (including the walls) w = w_tile + w_wall # diameter of a pawn w_pawn = 4 * w_tile / 5 # length of a wall l_wall = 2 * w_tile + w_wall # the x offset before the board begins x_offset = 20 # the y offset before the board begins y_offset = 20 # the separation between the tiles and the unused walls y_separa = 10 # the width of the scoreboard scoreboard_width = 300 # the y distance between the walls on the scoreboard y_wall_scoreboard_offset = y_offset # the x distance between the walls on the scoreboard x_wall_scoreboard_offset = 25 # the x offset of the scoreboard x_scoreboard_offset = (scoreboard_width - 5 * w_wall - 4 * x_wall_scoreboard_offset) / 2 # the y offset of the scoreboard y_scoreboard_offset = y_offset / 2 # the fontsize used in the scoreboard scoreboard_font_size = 14 # the total x offset on the left left_off_x = x_offset + w_wall / 2 # the total y offset on the left left_off_y = y_offset + w_wall / 2 # the total x offset on the right right_off_x = left_off_x + w_tile * 9 + w_wall * 8 # the total y offset on the right right_off_y = left_off_y + w_tile * 9 + w_wall * 8 names = ('Blue', 'Red', 'Teal', 'Yellow') # player names pawn_colors = ('#3465A4', '#EF2929', '#008080', '#ffff00' ) # pawn player colors pawn_outlin = ('#C2D0E3', '#FABEBE', '#000000', '#000000') tile_color = '#EEEEEC' tile_backg = '#631919' wall_color = '#F1E2BE' wall_outli = ('#3465A4', '#EF2929', '#008080', '#ffff00') wall_backg = '#999999' # these defaults are all overwritten during the normal game flow. # it was hacked around so that we could type-hint without refactoring. canvas_width: int = 0 canvas_height: int = 0 canvas: Canvas = None # type: ignore tile_ids: List[List[int]] = [] bg_h_wall_ids: List[List[int]] = [] bg_v_wall_ids: List[List[int]] = [] h_wall_ids: List[List[int]] = [] v_wall_ids: List[List[int]] = [] scoreboard_wall_ids: List[List[int]] = [] pawn_ids: List[int] = [] scoreboard: Canvas = None # type: ignore wall_titles: List[int] = [] status: Label = None # type: ignore status_text: str = "" substatus_text: str = "" buttons: Frame = None # type: ignore action: Action = None # type: ignore player: int = None # type: ignore selection: TkObject = None # type: ignore # these are used during replays trace: Trace = None # type: ignore speed: float = 0 boards: List[Board] = [] ########################################################################### # Helper functions def get_tile_xy(self, i: XY, j: XY) -> Coordinates: y = self.y_offset + (i + .5) * self.w x = self.x_offset + (j + .5) * self.w return x, y def get_wall_xy(self, i: XY, j: XY) -> Coordinates: y = self.y_offset + (i + 1) * self.w x = self.x_offset + (j + 1) * self.w return x, y def get_wall_scoreboard_xy(self, player: int, wall_num: int) -> Coordinates: y = (self.scoreboard_font_size + self.y_scoreboard_offset + self.y_wall_scoreboard_offset + 0.5 * self.l_wall) y += (self.canvas_height / len(self.board.pawns)) * player if wall_num >= 5: y += self.l_wall + self.y_wall_scoreboard_offset x = (self.x_scoreboard_offset + (wall_num % 5 + 0.5) * self.w_wall + (wall_num % 5) * self.x_wall_scoreboard_offset) return x, y def get_object(self, x: XY, y: XY) -> TkObject: if (x < self.left_off_x) or (y < self.left_off_y) or ( x >= self.right_off_x) or (y >= self.right_off_y): return -1, -1, 'border' x_rest = (x - self.left_off_x) % self.w y_rest = (y - self.left_off_y) % self.w if x_rest < self.w_tile and y_rest < self.w_tile: column = int((x - self.left_off_x) / self.w) row = int((y - self.left_off_y) / self.w) return row, column, 'tile' if x_rest > self.w_tile > y_rest: column = int((x - self.left_off_x) / self.w) if (y - self.left_off_y) < (self.w + self.w_tile / 2): row = 0 elif (y - self.left_off_y) >= (self.w * 7 + self.w_tile / 2): row = 7 else: row = 1 + int( (y - (self.left_off_y + self.w + self.w_tile / 2)) / (self.w_tile + self.w_wall)) return row, column, 'bg_v_wall' else: row = int((y - self.left_off_y) / self.w) if (x - self.x_offset) < (self.w + self.w_tile / 2): column = 0 elif (x - self.x_offset) >= (self.w * 7 + self.w_tile / 2): column = 7 else: column = 1 + int( (x - (self.left_off_x + self.w + self.w_tile / 2)) / (self.w_tile + self.w_wall)) return row, column, 'bg_h_wall' ########################################################################### # UI construction def __init__(self) -> None: super().__init__() self.running = False self.root = Tk() self.barrier = threading.Event() self.barrier.clear() self.b_prev: Button = None # type: ignore self.b_play: Button = None # type: ignore self.b_next: Button = None # type: ignore self.is_playing: bool = False self.step: int = 0 self.after_id = None def init_viewer(self, board: Board) -> None: super().init_viewer(board) self.barrier.set() def run(self) -> None: """Launch the GUI.""" if self.running: return self.running = True self.barrier.wait() # Create UI self.canvas_width = 2 * self.x_offset + self.w * self.board.cols self.canvas_height = 2 * self.y_offset + self.w * self.board.rows # Root window self.root.title('Quoridor') self.root.resizable(False, False) self.root.bind_all('<Escape>', self.close) mainframe = Frame(self.root) mainframe.pack() # Canvas board self.canvas = Canvas(mainframe, width=self.canvas_width, height=self.canvas_height) self.canvas.pack(side=LEFT) # tile_ids is filled by _update_gui self.tile_ids = [[0] * self.board.cols for _ in range(self.board.rows)] wall_ids = [[0] * (self.board.cols - 1) for _ in range(self.board.rows - 1)] self.bg_h_wall_ids = wall_ids self.bg_v_wall_ids = deepcopy(wall_ids) self.h_wall_ids = deepcopy(wall_ids) self.v_wall_ids = deepcopy(wall_ids) self.scoreboard_wall_ids = [[ 0 for _ in range(self.board.starting_wall_count) ] for _ in range(len(self.board.pawns))] self.pawn_ids = [0 for _ in range(len(self.board.pawns))] for i in range(self.board.rows): for j in range(self.board.cols): x, y = self.get_tile_xy(i, j) self.canvas.create_rectangle(x - self.w_tile / 2, y - self.w_tile / 2, x + self.w_tile / 2, y + self.w_tile / 2, width=2, outline='grey') # Score board medfont = Font(size=self.scoreboard_font_size) self.scoreboard = Canvas(mainframe, width=self.scoreboard_width, height=self.canvas_height) self.scoreboard.pack(side=RIGHT) self.wall_titles = [ self.scoreboard.create_text(self.scoreboard_width / 2, self.y_scoreboard_offset, font=medfont, anchor='n'), self.scoreboard.create_text( self.scoreboard_width / 2, self.y_scoreboard_offset + self.canvas_height / len(self.board.pawns), font=medfont, anchor='n'), self.scoreboard.create_text( self.scoreboard_width / 2, self.y_scoreboard_offset + (self.canvas_height / len(self.board.pawns)) * 2, font=medfont, anchor='n'), self.scoreboard.create_text( self.scoreboard_width / 2, self.y_scoreboard_offset + (self.canvas_height / len(self.board.pawns)) * 3, font=medfont, anchor='n') ] self.scoreboard.itemconfigure(self.wall_titles[0], fill=self.pawn_colors[0], text='Walls of player 1') self.scoreboard.itemconfigure(self.wall_titles[1], fill=self.pawn_colors[1], text='Walls of player 2') self.scoreboard.itemconfigure(self.wall_titles[2], fill=self.pawn_colors[2], text='Walls of player 3') self.scoreboard.itemconfigure(self.wall_titles[3], fill=self.pawn_colors[3], text='Walls of player 4') # Status bar self.status = Label(self.root, height=2, justify=LEFT) self.status.pack(side=LEFT) self.status_text = "" self.substatus_text = "" self.buttons = Frame(self.root) self.buttons.pack(side=RIGHT) # Draw board self.draw_board(self.board) # Launch event loop try: self.root.mainloop() except KeyboardInterrupt: pass # ensure the main thread exits when closing viewer during human action self.root = None # type: ignore self.action = None # type: ignore # generate invalid action self.barrier.set() ########################################################################### # General UI def close(self, event: TkEvent = None) -> None: """Close the GUI.""" if self.root is not None: self.root.destroy() def set_status(self, new_status: str) -> None: """Set the first line of the status bar.""" s = self.status_text = new_status if self.substatus_text: s += '\n' + self.substatus_text self.status['text'] = s def set_substatus(self, new_substatus: str) -> None: """Set the second line of the status bar.""" self.substatus_text = new_substatus self.set_status(self.status_text) ########################################################################### # Viewer UI def playing(self, step: int, player: int) -> None: if self.root is None: return self.root.after_idle(self._playing, step, player) def _playing(self, step: int, player: int) -> None: # This method may only be called from the gui thread. self.set_status(f'Step {step}: {self.names[player]}\'s turn.') self.set_substatus("") def update(self, step: int, action: Action, player: int) -> None: self.board.play_action(action, player) if self.root is not None: self.root.after_idle(self.redraw_board, self.board) def draw_board(self, board: Board) -> None: # creating the background self.canvas.create_rectangle(self.x_offset, self.y_offset, self.x_offset + self.w * self.board.cols, self.y_offset + self.w * self.board.rows, fill=self.wall_backg, outline=self.wall_backg) # creating the walls (on the board) for i in range(board.rows - 1): for j in range(board.cols - 1): x, y = self.get_wall_xy(i, j) self.canvas.create_rectangle(x - self.l_wall / 2, y - self.w_wall / 2, x + self.l_wall / 2, y + self.w_wall / 2, fill=self.wall_backg, outline=self.wall_backg, tags=['bg_h_walls']) self.bg_h_wall_ids[i][j] = self.canvas.create_rectangle( x - self.l_wall / 2, y - self.w_wall / 2 + 2, x + self.l_wall / 2, y + self.w_wall / 2 - 3, tags=['bg_h_walls'], width=0) self.mark_object((i, j, 'bg_h_wall')) self.canvas.create_rectangle(x - self.w_wall / 2, y - self.l_wall / 2, x + self.w_wall / 2, y + self.l_wall / 2, fill=self.wall_backg, outline=self.wall_backg, tags=['bg_v_walls']) self.bg_v_wall_ids[i][j] = self.canvas.create_rectangle( x - self.w_wall / 2 + 2, y - self.l_wall / 2, x + self.w_wall / 2 - 3, y + self.l_wall / 2, tags=['bg_v_walls'], width=0) self.mark_object((i, j, 'bg_v_wall')) # creating the tiles for i in range(board.rows): for j in range(board.cols): x, y = self.get_tile_xy(i, j) self.canvas.create_rectangle(x - self.w_tile / 2, y - self.w_tile / 2, x + self.w_tile / 2, y + self.w_tile / 2, fill=self.tile_backg, tags=['tiles']) self.tile_ids[i][j] = self.canvas.create_rectangle( x - self.w_tile / 2, y - self.w_tile / 2, x + self.w_tile / 2, y + self.w_tile / 2, tags=['tiles']) # ensure coherent unselected appearance self.mark_object((i, j, 'tile')) # creating the wall slots (on the scoreboard) for player in range(self.board.player_count): i, j = self.board.pawns[player] x, y = self.get_tile_xy(i, j) self.pawn_ids[player] = self.canvas.create_oval( x - self.w_pawn / 2, y - self.w_pawn / 2, x + self.w_pawn / 2, y + self.w_pawn / 2, fill=self.pawn_colors[player], outline=self.pawn_outlin[player], width=2, tags=['pawns']) for wall_num in range(self.board.player_walls[player]): x, y = self.get_wall_scoreboard_xy(player, wall_num) self.scoreboard.create_rectangle(x - self.w_wall / 2, y - self.l_wall / 2, x + self.w_wall / 2, y + self.l_wall / 2, fill=self.wall_backg, outline=self.wall_backg) self.scoreboard_wall_ids[player][ wall_num] = self.scoreboard.create_rectangle( x - self.w_wall / 2 + 1, y - self.l_wall / 2 + 1, x + self.w_wall / 2, y + self.l_wall / 2, fill=self.wall_color, outline=self.wall_outli[player], width=2, tags=['scoreboard_walls']) for i, j in self.board.horiz_walls: x, y = self.get_wall_xy(i, j) self.h_wall_ids[i][j] = self.canvas.create_rectangle( x - self.l_wall / 2, y - self.w_wall / 2 + 2, x + self.l_wall / 2, y + self.w_wall / 2 - 3, fill=self.wall_color, width=2, tags=['h_walls']) for i, j in self.board.verti_walls: x, y = self.get_wall_xy(i, j) self.v_wall_ids[i][j] = self.canvas.create_rectangle( x - self.w_wall / 2 + 2, y - self.l_wall / 2, x + self.w_wall / 2 - 3, y + self.l_wall / 2, fill=self.wall_color, width=2, tags=['v_walls']) def redraw_board(self, board: Board) -> None: """Draw a board with all unselected tiles.""" self.canvas.delete('pawns') self.canvas.delete('h_walls') self.canvas.delete('v_walls') self.scoreboard.delete('scoreboard_walls') for i in range(board.rows): for j in range(board.cols): # ensure coherent unselected appearance self.mark_object((i, j, 'tile')) for player in range(self.board.player_count): i, j = self.board.pawns[player] x, y = self.get_tile_xy(i, j) self.pawn_ids[player] = self.canvas.create_oval( x - self.w_pawn / 2, y - self.w_pawn / 2, x + self.w_pawn / 2, y + self.w_pawn / 2, fill=self.pawn_colors[player], outline=self.pawn_outlin[player], width=2, tags=['pawns']) for wall_num in range(self.board.player_walls[player]): x, y = self.get_wall_scoreboard_xy(player, wall_num) self.scoreboard_wall_ids[player][ wall_num] = self.scoreboard.create_rectangle( x - self.w_wall / 2 + 1, y - self.l_wall / 2 + 1, x + self.w_wall / 2, y + self.l_wall / 2, fill=self.wall_color, outline=self.wall_outli[player], width=2, tags=['scoreboard_walls']) for i in range(board.rows - 1): for j in range(board.cols - 1): self.mark_object((i, j, 'bg_h_wall')) self.mark_object((i, j, 'bg_v_wall')) for i, j in self.board.horiz_walls: x, y = self.get_wall_xy(i, j) self.canvas.create_rectangle(x - self.l_wall / 2, y - self.w_wall / 2 + 2, x + self.l_wall / 2, y + self.w_wall / 2 - 3, fill=self.wall_color, width=2, tags=['h_walls']) for i, j in self.board.verti_walls: x, y = self.get_wall_xy(i, j) self.canvas.create_rectangle(x - self.w_wall / 2 + 2, y - self.l_wall / 2, x + self.w_wall / 2 - 3, y + self.l_wall / 2, fill=self.wall_color, width=2, tags=['v_walls']) def mark_object(self, selection: Tuple[int, int, str], style: str = 'unselected') -> None: """Mark tile as unselected, hover or moving.""" i, j, object_type = selection if object_type == 'tile': o = self.tile_ids[i][j] if style == 'unselected': self.canvas.itemconfigure(o, outline=self.tile_color, width=2) elif style == 'hover': self.canvas.itemconfigure(o, outline='#008000', width=3) else: assert False if object_type == 'bg_h_wall': o = self.bg_h_wall_ids[i][j] if style == 'unselected': self.canvas.tag_lower(o, 'tiles') self.canvas.itemconfigure(o, width=0) elif style == 'hover': self.canvas.tag_raise(o, 'tiles') self.canvas.itemconfigure(o, outline='#008000', width=3) else: assert False if object_type == 'bg_v_wall': o = self.bg_v_wall_ids[i][j] if style == 'unselected': self.canvas.tag_lower(o, 'tiles') self.canvas.itemconfigure(o, width=0) elif style == 'hover': self.canvas.tag_raise(o, 'tiles') self.canvas.itemconfigure(o, outline='#008000', width=3) else: assert False def put_wall(self, player: int) -> None: """Deduct a wall from player.""" self.scoreboard.delete( self.scoreboard_wall_ids[player][self.board.player_walls[player] - 1]) def finished(self, steps: int, winner: int, reason: str = "") -> None: if self.root is None: return self.root.after_idle(self._finished, steps, winner, reason) def _finished(self, step: int, winner: int, reason: str = "") -> None: # This method may only be called from the gui thread. if not winner: s = 'Draw game' elif winner > 0: s = f'{"/".join(n for p, n in enumerate(self.names) if p % 2 == 0)} has won' else: s = f'{"/".join(n for p, n in enumerate(self.names) if p % 2 == 1)} has won' s += f' after {step} steps.' self.set_status(s) if reason: self.set_substatus(reason) self.canvas.unbind('<Motion>') self.canvas.unbind('<Button-1>') ########################################################################### # Human player UI def play(self, percepts: Board, player: int, step: int, time_left: float) -> Optional[Action]: if self.root is None: return None self.player = player self.barrier.clear() self.root.after_idle(self._play_start) self.barrier.wait() return self.action def _play_start(self) -> None: """Configure GUI to accept user input.""" self.canvas.bind('<Leave>', self._play_leave) self.canvas.bind('<Motion>', self._play_motion) self.canvas.bind('<Button-1>', self._play_click) self._play_reset() def _play_reset(self, event: TkEvent = None) -> None: self.action = None # type: ignore self.selection = None # type: ignore self.set_substatus('Select a tile to move.') self.canvas.event_generate('<Motion>') def _play_leave(self, event: TkEvent) -> None: if event.state & 0x100: # leave event is also called on mouse click return if self.selection is not None: self.mark_object(self.selection) self.selection = None # type: ignore def _play_motion(self, event: TkEvent) -> None: self._play_leave(event) i, j, object_type = self.get_object(event.x, event.y) if i < 0 or i >= self.board.rows or j < 0 or j >= self.board.cols: return if object_type == 'tile' and self.board.can_move_here( i, j, self.player): self.selection = (i, j, object_type) self.mark_object(self.selection, 'hover') elif object_type == 'bg_h_wall' and self.board.is_wall_possible_here( (i, j), True) and (self.board.player_walls[self.player] > 0): self.selection = (i, j, object_type) self.mark_object(self.selection, 'hover') elif object_type == 'bg_v_wall' and self.board.is_wall_possible_here( (i, j), False) and (self.board.player_walls[self.player] > 0): self.selection = (i, j, object_type) self.mark_object(self.selection, 'hover') def _play_click(self, event: TkEvent) -> None: if self.selection is not None: i, j, object_type = self.selection if object_type == 'tile': self.set_substatus("") self.barrier.set() self.action = (MOVE, i, j) elif object_type == 'bg_h_wall': self.put_wall(self.player) self.set_substatus("") self.barrier.set() self.action = (WALL_H, i, j) elif object_type == 'bg_v_wall': self.put_wall(self.player) self.set_substatus("") self.barrier.set() self.action = (WALL_V, i, j) ########################################################################### # Replay UI def replay(self, trace: Trace, speed: float = 1.0, show_end: bool = False) -> None: """Replay a game given its saved trace. Attributes: trace -- trace of the game speed -- speed scale of the replay show_end -- start with the final state instead of the initial state """ self.trace = trace self.speed = speed # generate all boards to access them backwards self.boards = [trace.get_initial_board()] for player, action, t in trace.actions: b = self.boards[-1].clone() b.play_action(action, player) self.boards.append(b) if self.root is not None: self.root.after_idle(self._replay_gui, show_end) self._board = self.boards[0] self.barrier.set() self.run() def _replay_gui(self, show_end: bool) -> None: """Initialize replay UI.""" self.b_prev = Button(self.buttons, text='<', command=self._replay_prev) self.b_play = Button(self.buttons, text='Play', command=self._replay_play) self.b_next = Button(self.buttons, text='>', command=self._replay_next) self.b_prev.pack(side=LEFT) self.b_play.pack(side=LEFT) self.b_next.pack(side=LEFT) self.root.bind_all('<Left>', self._replay_prev) self.root.bind_all('<Right>', self._replay_next) self.root.bind_all('<Home>', self._replay_first) self.root.bind_all('<End>', self._replay_last) self.root.bind_all('<space>', self._replay_play) self.is_playing = False if show_end: # Only used when replaying right after a game, so use the hack to # keep the last played action on screen self._replay_goto(len(self.boards) - 1) else: self._replay_goto(0) def _replay_goto(self, step: int) -> None: """Update UI to show one step.""" self.step = step self._board = self.boards[step] self.redraw_board(self.board) if step == len(self.boards) - 1: self._finished(step, self.trace.winner, self.trace.reason) if self.is_playing: self.is_playing = False self.b_play['text'] = 'Play' else: player, _, t = self.trace.actions[step] self._playing(step, player) if self.is_playing: if self.speed < 0: step_time = -self.trace.actions[step][2] / self.speed else: step_time = self.speed self.after_id = self.root.after(int(step_time * 1000), self._replay_goto, step + 1) else: if not step: self.b_prev['state'] = DISABLED else: self.b_prev['state'] = NORMAL if step == len(self.boards) - 1: self.b_next['state'] = DISABLED else: self.b_next['state'] = NORMAL def _replay_next(self, event: TkEvent = None) -> None: if not self.is_playing and self.step < len(self.boards) - 1: self._replay_goto(self.step + 1) def _replay_prev(self, event: TkEvent = None) -> None: if not self.is_playing and self.step > 0: self._replay_goto(self.step - 1) def _replay_first(self, event: TkEvent = None) -> None: if not self.is_playing: self._replay_goto(0) def _replay_last(self, event: TkEvent = None) -> None: if not self.is_playing: self._replay_goto(len(self.boards) - 1) def _replay_play(self, event: TkEvent = None) -> None: if self.is_playing: self.root.after_cancel(self.after_id) self.is_playing = False self.b_play['text'] = 'Play' self._replay_goto(self.step) else: self.is_playing = True self.b_prev['state'] = DISABLED self.b_next['state'] = DISABLED self.b_play['text'] = 'Pause' if self.step < len(self.boards) - 1: self._replay_goto(self.step) else: self._replay_goto(0)
class HOPSWindow: def __init__(self, log=HOPSLog, name='HOPS - window', sizex=None, sizey=None, position=5): self.log = log self.root = Tk() self.mainloop_on = False self.exit = False self.name = name self.jobs = [] self.root.wm_title(name) if sizex and sizey: self.root.geometry('{0}x{1}'.format( int(self.root.winfo_screenwidth() / sizex), int(self.root.winfo_screenheight() / sizey))) self.hide() self.finalised = False self.position = position self.DISABLED = DISABLED self.NORMAL = NORMAL self.END = END self.RIGHT = RIGHT self.LEFT = LEFT self.TOP = TOP self.BOTTOM = BOTTOM self.BOTH = BOTH self.Y = Y self.HORIZONTAL = HORIZONTAL self.VERTICAL = VERTICAL # # create a canvas object and a vertical scrollbar for scrolling it # self.vscrollbar = Scrollbar(self.root, orient=VERTICAL) # self.vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE) # self.canvas = Canvas(self.root, bd=0, highlightthickness=0, # yscrollcommand=self.vscrollbar.set) # self.canvas.pack(side=LEFT, fill=BOTH, expand=TRUE) # self.vscrollbar.config(command=self.canvas.yview) # # # reset the view # self.canvas.xview_moveto(0) # self.canvas.yview_moveto(0) # # # create a frame inside the canvas which will be scrolled with it # self.interior = Frame(self.canvas) # self.interior_id = self.canvas.create_window(0, 0, window=self.interior, # anchor=NW) # # # track changes to the canvas and frame width and sync them, # # also updating the scrollbar # def _configure_interior(event): # # update the scrollbars to match the size of the inner frame # size = (self.interior.winfo_reqwidth(), self.interior.winfo_reqheight()) # self.canvas.config(scrollregion="0 0 %s %s" % size) # if self.interior.winfo_reqwidth() != self.canvas.winfo_width(): # # update the canvas's width to fit the inner frame # self.canvas.config(width=self.interior.winfo_reqwidth()) # # self.interior.bind('<Configure>', _configure_interior) # # def _configure_canvas(event): # if self.interior.winfo_reqwidth() != self.canvas.winfo_width(): # # update the inner frame's width to fill the canvas # self.canvas.itemconfigure(self.interior_id, width=self.canvas.winfo_width()) # # self.canvas.bind('<Configure>', _configure_canvas) # self.main_frame = self.interior self.main_frame = self.root self.widgets = [] self.root.protocol('WM_DELETE_WINDOW', self.close) def no_action(self): pass def update(self): self.root.update() def after(self, function): xx = self.root.after(5, function) self.jobs.append(xx) self.update_idletasks() def update_idletasks(self): self.root.update_idletasks() def finalise(self): if not self.finalised: self.update_idletasks() # print(self.interior.winfo_reqwidth(), self.interior.winfo_reqheight()) # self.canvas.config(width=self.interior.winfo_reqwidth(), height=self.interior.winfo_reqheight()) # self.canvas.itemconfigure(self.interior_id, width=self.interior.winfo_reqwidth(), height=self.interior.winfo_reqheight()) if self.position == 1: x = 0 y = 0 elif self.position == 2: x = (self.root.winfo_screenwidth() - self.root.winfo_reqwidth()) / 2 y = 0 elif self.position == 3: x = self.root.winfo_screenwidth() - self.root.winfo_reqwidth() y = 0 elif self.position == 4: x = 0 y = (self.root.winfo_screenheight() - self.root.winfo_reqheight()) / 2 elif self.position == 5: x = (self.root.winfo_screenwidth() - self.root.winfo_reqwidth()) / 2 y = (self.root.winfo_screenheight() - self.root.winfo_reqheight()) / 2 elif self.position == 6: x = self.root.winfo_screenwidth() - self.root.winfo_reqwidth() y = (self.root.winfo_screenheight() - self.root.winfo_reqheight()) / 2 elif self.position == 7: x = 0 y = self.root.winfo_screenheight() - self.root.winfo_reqheight( ) elif self.position == 8: x = (self.root.winfo_screenwidth() - self.root.winfo_reqwidth()) / 2 y = self.root.winfo_screenheight() - self.root.winfo_reqheight( ) elif self.position == 9: x = self.root.winfo_screenwidth() - self.root.winfo_reqwidth() y = self.root.winfo_screenheight() - self.root.winfo_reqheight( ) elif self.position == 10: x = self.root.winfo_screenwidth( ) / 2 - self.root.winfo_reqwidth() y = (self.root.winfo_screenheight() - self.root.winfo_reqheight()) / 2 elif self.position == 11: x = self.root.winfo_screenwidth() / 2 y = (self.root.winfo_screenheight() - self.root.winfo_reqheight()) / 2 else: x = 0 y = 0 self.root.geometry('+%d+%d' % (int(x), int(y))) self.update_idletasks() self.finalised = True def show(self): self.finalise() self.root.wm_attributes("-topmost", 1) self.root.after_idle(self.root.attributes, '-topmost', 0) self.root.deiconify() self.update_idletasks() def loop(self, f_after=None): self.mainloop_on = True if f_after: self.root.after(500, f_after) self.root.mainloop() def hide(self): self.root.withdraw() def def_run(self): self.show() def run(self, f_after=None): print('\nStarting window: ', self.name) self.def_run() self.loop(f_after) def trigger_exit(self): self.exit = True def def_close(self): if self.mainloop_on: self.root.quit() for job in self.jobs: self.root.after_cancel(job) self.root.destroy() def close(self): self.def_close() # widgets def register(self, widget): self.widgets.append(widget) def disable(self): self.root.protocol('WM_DELETE_WINDOW', self.no_action) for widget in self.widgets: widget.disable() def activate(self): self.root.protocol('WM_DELETE_WINDOW', self.close) for widget in self.widgets: widget.activate() # help pop ups def askdirectory(self): return askdirectory() def askyesno(self, *args, **kwargs): return askyesno(*args, **kwargs) def askyesnocancel(self, *args, **kwargs): return askyesnocancel(*args, **kwargs) def showinfo(self, *args, **kwargs): return showinfo(*args, **kwargs) # tk variables initiated directly in - and linked with - the hops window def StringVar(self, value): return StringVar(self.root, value=value) def BooleanVar(self, value): return BooleanVar(self.root, value=value) def DoubleVar(self, value): return DoubleVar(self.root, value=value) def IntVar(self, value): return IntVar(self.root, value=value) # tk widgwts initiated directly in - and linked with - the hops window def Label(self, **kwargs): return HOPSLabel(self, **kwargs) def Entry(self, **kwargs): return HOPSEntry(self, **kwargs) def Button(self, **kwargs): return HOPSButton(self, **kwargs) def DropDown(self, **kwargs): return HOPSDropDown(self, **kwargs) def CheckButton(self, **kwargs): return HOPSCheckButton(self, **kwargs) def ListDisplay(self, **kwargs): return HOPSListDisplay(self, **kwargs) def Progressbar(self, **kwargs): return HOPSProgressbar(self, **kwargs) def FigureWindow(self, **kwargs): return HOPSFigureWindow(self, **kwargs) def FitsWindow(self, **kwargs): return HOPSFitsWindow(self, **kwargs) def Frame(self, *args, **kwargs): return Frame(self.main_frame, *args, **kwargs) def Radiobutton(self, *args, **kwargs): return Radiobutton(self.main_frame, *args, **kwargs) def setup_window(self, objects, title_font='times', main_font='times', button_font='times', entries_wd=20, entries_bd=3, buttons_bd=5): ff = min( 1, (self.root.winfo_screenheight() / self.root.winfo_fpixels('1i')) / 13) font_size = 15 * ff button_font = (button_font, int(1.1 * font_size), 'bold') main_font = (main_font, int(font_size)) title_font = (title_font, int(1.5 * font_size), 'bold') for row in range(len(objects)): if len(objects[row]) == 0: label_empty = Label(self.main_frame, text='') label_empty.grid(row=row, column=100) else: for obj in objects[row]: if obj[0].winfo_class() == 'Button': obj[0].config(borderwidth=buttons_bd, font=button_font, padx=1, pady=1) elif obj[0].winfo_class() == 'Entry': obj[0].configure(width=entries_wd, bd=entries_bd, font=main_font) elif obj[0].winfo_class() in [ 'Label', 'Radiobutton', 'Checkbutton' ]: if len(obj) == 5: if obj[4] == 'title': obj[0].configure(font=title_font, padx=0, pady=0) else: obj[0].configure(font=main_font, padx=0, pady=0) else: obj[0].configure(font=main_font, padx=0, pady=0) if len(obj) >= 4: obj[0].grid(row=row, column=obj[1], columnspan=obj[2], rowspan=obj[3]) elif len(obj) == 3: obj[0].grid(row=row, column=obj[1], columnspan=obj[2]) else: obj[0].grid(row=row, column=obj[1])
class Game(): def __init__(self): self.tk = Tk() self.tk.title('Connect 4') self.tk.resizable(False, False) self.tk.wm_attributes('-topmost', 1) self.update() self.canvas = Canvas(self.tk, width=600, height=600) self.canvas.pack() self.update() self.screenwidth = self.tk.winfo_screenwidth() self.screenheight = self.tk.winfo_screenheight() self.x_calc = int(self.screenwidth / 2 - 300) self.y_calc = int(self.screenheight / 2 - 300) self.tk.geometry('+{}+{}'.format(self.x_calc, self.y_calc)) self.update() self.column = Entry(self.tk, font=('Courier', -15), width=20) self.column.place(x=5, y=5) self.column.insert(0, 'Enter your move here') self.sprites = [] self.running = True self.tk.bind_all('<Return>', self.clear) self.update() def update(self): self.tk.update() self.tk.update_idletasks() def say(self, *args): self.otk = Tk() self.otk.title('Message') self.otk.resizable(False, False) self.otk.wm_attributes('-topmost', 1) time = -1 messages = [] for message in args: time += 1 messages.append(Label(self.otk, text=message, \ font=('Courier', 12))) done = Button(self.otk, text='OK', command=self.otk.quit) for label in messages: label.pack() done.pack() self.update() ww = self.otk.winfo_width() wh = self.otk.winfo_height() sw = self.otk.winfo_screenwidth() sh = self.otk.winfo_screenheight() x = int(sw / 2 - ww / 2) y = int(sh / 2 - wh / 2) self.otk.geometry('+{}+{}'.format(x, y)) self.otk.mainloop() self.otk.destroy() def clear(self, event): self.pl_mv = self.column.get() print(self.pl_mv) self.column.delete(0, 'end') def mainloop(self): self.timers = [] def forloop(): for sprite in self.sprites: if self.running: sprite.todo() self.update() else: for tmr in self.timers: self.tk.after_cancel(tmr) self.timers.append(self.tk.after(10, forloop)) self.timers.append(self.tk.after(10, forloop)) self.tk.mainloop() self.tk.destroy()
class App: canvas_animation_options = {'width': 720, 'height': 720, 'bg': '#2B2B2B'} canvas_data_options = {'width': 420, 'height': 500, 'bg': '#2B2B2B'} canvas_chart_options = {'width': 420, 'height': 220, 'bg': '#2B2B2B'} quarter_circle_options = { 'start': 0, 'extent': -90, 'width': 2, 'outline': 'gray', 'fill': '#3C3F41' } chart = None chart_factor = 1 _all_lists = { '_canvas_points_ids': [], '_canvas_data_ids': [], '_canvas_middle_line_ids': [], '_canvas_data_middle_line_ids': [], '_canvas_text_ids': [], '_caught_points': set(), '_canvas_text_math_error_id': [], '_all_values_pi': [] } _quarter_circle_descriptor = 0 canvas_size_error = 3 amount_points = 100 multiplicity = 1000 coord_y_for_table = 70 def __init__(self): self.root = Tk() self.root.resizable(width=False, height=False) # Полотно с математической моделью: self.animation_canvas = Canvas(self.root, **self.canvas_animation_options) self.animation_canvas.pack(side='left') # Полотно с исследуемыми данными: self.data_canvas = Canvas(self.root, **self.canvas_data_options) self.data_canvas.pack(side='top') # Полотно с диаграммой: self.chart_canvas = Canvas(self.root, **self.canvas_chart_options) self.chart_canvas.pack(side='bottom') self.draw_stationary_objects() self._physics_process() self._process() def draw_stationary_objects(self): """ Отрисовка всех неподвижных объектов """ self._quarter_circle_descriptor = self._draw_quarter_circle() self.print_data_about_pi_line() self.draw_pi_line() self.draw_table() def _draw_quarter_circle(self): """ Отрисовка четверти круга Returns: дескриптор четверти круга """ quarter_circle_descriptor = self.animation_canvas.create_arc( -self.canvas_animation_options['width'], -self.canvas_animation_options['height'], self.canvas_animation_options['width'], self.canvas_animation_options['height'], **self.quarter_circle_options) return quarter_circle_descriptor def print_data_about_pi_line(self): """ Печать точного значения числа pi. """ self.chart_canvas.create_text(100 + self.canvas_chart_options['width'] // 4, 30, text=f'Истинное pi = {pi}', font=('Comic Sans MS', 15), fill='#648658') def draw_pi_line(self): """ Отрисовка линии, которая соответствует точному значению числа pi. """ self.chart_canvas.create_line(0, (pi / 4) * 130, self.canvas_chart_options['width'], (pi / 4) * 130, fill='#499C54') def draw_table(self): for coord_x in range(self.canvas_data_options['width'] // 3, self.canvas_data_options['width'], self.canvas_data_options['width'] // 3): self.data_canvas.create_line(coord_x, 0, coord_x, self.canvas_data_options['height'], fill='#AFB9BA') for coord_y in range(self.canvas_data_options['height'] // 11, self.canvas_data_options['height'], self.canvas_data_options['height'] // 11): self.data_canvas.create_line(0, coord_y, self.canvas_data_options['width'], coord_y, fill='#AFB9BA') self.data_canvas.create_text(70, 25, text='Кол-во точек', font=('Comic Sans MS', 15), fill='#AFB9BA') self.data_canvas.create_text(210, 25, text='Число pi', font=('Comic Sans MS', 15), fill='#AFB9BA') self.data_canvas.create_text(350, 25, text='Погрешность', font=('Comic Sans MS', 15), fill='#AFB9BA') # for i in range(70, 500, 45): # self.data_canvas.create_text(350, i, text='Погрешность', font=('Comic Sans MS', 15), fill='#AFB9BA') def _physics_process(self): """ Создание экземпляра класса Point, т.е. создаётся конечное множество рандомно расположенных на полотне точек определённого цвета. """ self.points = RandomPoints(self.animation_canvas, self.amount_points) self.points.points_options['fill'] = '#ECBB06' self.points.points_options['outline'] = '#ECBB06' def _process(self): self.update_math_error() self.update_middle_line() self.update_data_middle_line() self.update_other_values_data_on_chart_canvas() process = self.root.after(100, self._process) self._draw_points() self._paint_points() self.print_math_error() self.draw_current_value_line() self.print_data_about_middle_line() self.print_other_data_on_chart_canvas() self.draw_chart( (self.points.drawed_points, self.count_probability * 130)) if self.points.drawed_points <= self.multiplicity * 10: self.fill_table() self.stop_process(process) def update_middle_line(self): """ Обновить среднюю линию. """ self.clean_canvas('_canvas_middle_line_ids') def update_data_middle_line(self): """ Обновление данных, стоящих рядом со средней линией """ self.clean_canvas('_canvas_data_middle_line_ids') def update_other_values_data_on_chart_canvas(self): self.clean_canvas('_canvas_text_ids') def update_math_error(self): self.clean_canvas('_canvas_text_math_error_id') def _draw_points(self): """ Рисовать точки """ self.points.draw() while len(self.points.canvas_points_ids) > 500: self.animation_canvas.delete(self.points.canvas_points_ids.pop(0)) def _paint_points(self): """ Перекрашывает точки, которые пересклись с кругом """ for point in self.points.canvas_points_ids: point_coord = self.animation_canvas.coords(point) if point_coord[0]**2 + point_coord[1]**2 <= ( self.canvas_animation_options['height'])**2: self.animation_canvas.itemconfig(point, fill='#4096C1', outline='#4096C1') self._all_lists['_caught_points'].add(point) def print_math_error(self): self._all_lists['_canvas_text_math_error_id'] = [ self.chart_canvas.create_text( 100 + self.canvas_chart_options['width'] // 4, 60, text=f'Поргрешность:' f' {abs(round(100 * (1 - pi / (4 * self.count_probability)), 5))} %', font=('Comic Sans MS', 15), fill='#648658') ] def draw_current_value_line(self): """ Отрисовка текущей линии, абсциса кординат которой соответствует значению вероятности попадания точек в круг """ self._all_lists['_canvas_middle_line_ids'] = [ self.chart_canvas.create_line(0, self.count_probability * 130, self.canvas_chart_options['width'], self.count_probability * 130, fill='#ECBB06') ] def print_data_about_middle_line(self): """ Печатать нужную информацию рядом со средней линией. В данном случае печатается текущее значение числа pi. """ self._all_lists['_canvas_data_middle_line_ids'] = [ self.chart_canvas.create_text( 3 * self.canvas_data_options['width'] // 4, 10 + self.count_probability * 130, text=f'{round(4 * self.count_probability, 5)}', font=('Comic Sans MS', 15), fill='#ECBB06') ] # print(f'Вероятность: {self.count_probability()}') # print(f'Число pi: {4 * self.count_probability()}') def print_other_data_on_chart_canvas(self): self._all_lists['_canvas_text_ids'] = [ self.chart_canvas.create_text( self.canvas_chart_options['width'] // 2, (4 * self.canvas_chart_options['height']) // 6 + 25, text=f'Общее число точек: {self.points.drawed_points}\n' f' Точки в круге: {len(self._all_lists["_caught_points"])}', font=('Comic Sans MS', 15), fill='#AFB9BA') ] def draw_chart(self, coordinates): """ Отрисовка диаграммы Args: coordinates: """ def scale_coordinates(coords): """Скалирует координаты по оси x""" for no, coord in enumerate(coords): yield coord * self.chart_factor if no % 2 == 0 else coord if self.chart is None: self.chart = self.chart_canvas.create_line(coordinates, coordinates[0] + 4, coordinates[1] + 4, fill='#4096C1', width=4) else: old_coords = self.chart_canvas.coords(self.chart) scaled_coordinates = scale_coordinates(coordinates) if self.chart_canvas.coords( self.chart)[-2] >= self.chart_canvas.winfo_width() - 100: self.chart_factor /= 2 old_coords = list(scale_coordinates(old_coords)) self.chart_canvas.coords(self.chart, *(old_coords + list(scaled_coordinates))) def fill_table(self): if self.points.drawed_points % self.multiplicity == 0: self.data_canvas.create_text(70, self.coord_y_for_table, text=f'{self.points.drawed_points}', font=('Comic Sans MS', 15), fill='#AFB9BA') self.data_canvas.create_text( 210, self.coord_y_for_table, text=f'{round(4 * self.count_probability, 7)}', font=('Comic Sans MS', 15), fill='#AFB9BA') self.data_canvas.create_text( 350, self.coord_y_for_table, text= f'{abs(round(100 * (1 - pi / (4 * self.count_probability)), 5))} %', font=('Comic Sans MS', 15), fill='#AFB9BA') self.coord_y_for_table += 45 def stop_process(self, process): """ Останавливает процесс при определённом условии (в данном случае при достижении общего кол-ва точек 3000) """ if len(self.points.canvas_points_ids) == 20000: self.root.after_cancel(process) def clean_canvas(self, group: str): """ Очистка элементов определённоой группы Args: group: список с дескрипторами удаляемой группы элементов """ for canvas_obj in self._all_lists[group]: self.chart_canvas.delete(canvas_obj) self._all_lists[group] = [] def run(self): self.root.mainloop() @property def count_probability(self): """ Вероятностт попадания точки в круг Returns: вероятность попадания точки в круг """ return len( self._all_lists['_caught_points']) / self.points.drawed_points
def welcome(o=""): """Function to open WELCOME SCREEN - Login screen that opens when you run.""" global clock global welcome_screen global home_screen global truth truth = True global home if o != "f": home.after_cancel(Home.AFTER) home.destroy() def show(): """Show button function""" global sh if not sh: e["show"] = "" sh = True shbtn.config(text="Hide") elif sh: e["show"] = "*" sh = False shbtn.config(text="Show") def try_again(): output_label["text"] = "Try again." confirmbtn["bg"] = "cyan" confirmbtn["state"] = "normal" def confirm(event=None): """Confirm button function""" global welcome_screen global password global tries global truth global AFTER global home_screen global clock p = e.get() if p == password: truth = False welcome_screen.after_cancel(AFTER) clock = None """Argument "y" tells that welcome screen exists and needs to be destroyed""" home_screen = Home("y") elif p != password: tries -= 1 output_label.place(x=0, y=320) output_label["text"] = ("Incorrect password, " + str(tries) + " tries remaining") if p != "": e.delete(0, END) e.focus_set() if tries == 0: output_label[ "text"] = "5 incorrect attempts, Try again after 30 seconds! " tries = 6 confirmbtn["bg"] = "black" confirmbtn["state"] = "disabled" t = threading.Timer(30.0, try_again) t.start() welcome_screen = Tk() # welcome screen truth = True welcome_screen.geometry(WINDOW_DIM) welcome_screen.resizable(False, False) welcome_screen.title("MarvellOS") welcome_screen.config(bg=background) title_label = Label( welcome_screen, text="MarvellOS", bg=background, fg="dark blue", font=("Curlz MT", 35), ) title_label.place(x=47, y=100) shbtn = Button( welcome_screen, text="Show", bg="light blue", fg="black", font=("Agency", 8), command=show, ) shbtn.place(x=265, y=237) confirmbtn = Button( welcome_screen, text="Confirm", bg="cyan", fg="black", font=("Arial black", 9), command=confirm, width=20, height=2, ) # confirm button confirmbtn.place(x=71, y=263) # Binding Enter key for login confirmation welcome_screen.bind("<Return>", confirm) welcome_frame = Frame(welcome_screen, relief=RIDGE, borderwidth=2) welcome_frame.pack() enterpass = Label( welcome_screen, text="Enter password:"******"Century"), bg=background, fg="black", ) enterpass.place(x=96, y=215) e = Entry(welcome_screen, show="*", width=27) e.focus_set() # password entry field e.place(x=45, y=240) output_label = Label(welcome_screen, bg="red", fg="black", width=41, font=("Calibri", 12)) # output label """----------------VERSION UPDATE LABEL----------------""" t = "MarvellOS v1.3" inf = Label( welcome_screen, text=t, bg=background, fg="red", font=("Century", 10, "bold"), ) inf.place(x=5, y=375) """Clock on the home screen""" global clock clock = Label( welcome_screen, font=("TIMES NEW ROMAN", 10), anchor="e", bg="black", fg="white", width=46, height=2, ) # clock label clock.place(x=0, y=0) if truth: try: tick() except Exception: welcome_screen.after_cancel(tick) welcome_screen.mainloop()
class Game: def __init__(self): self.root = Tk() self.area = Canvas(self.root, width=500, height=500, bg='lightgreen') self.area.pack() self.ballid = self.area.create_oval(30, 50, 50, 70, fill='red') self.paddleid = self.area.create_rectangle(210, 400, 310, 410, fill="blue") self.points_text = self.area.create_text(250, 450, text='0', font=('times', 20)) self.speed = 12.0 ##speed of ball self.xspeed = self.speed * sin( radians(40)) ##speed of ball horizontally self.yspeed = self.speed * sin(radians(40)) ##speed of ball vertically self.pspeed = 0 ##speed of paddle self.recall = '' ##for AFTER method self.points = 0 ##ponits counter self.gameon = True self.redraw_screen() ##controls for paddle self.area.bind_all('<KeyPress-Left>', self.turn_left) self.area.bind_all('<KeyPress-Right>', self.turn_right) #self.area.bind_all('<Control-n>', self.new_game) self.root.mainloop() def draw_points(self): self.points = self.points + self.speed / 50 self.area.itemconfig(self.points_text, text=round(self.points), fill="#991122") def redraw_screen(self): self.draw_ball() self.paddle_hit() self.draw_paddle() self.draw_points() if self.gameon: self.recall = self.root.after(17, self.redraw_screen) else: self.root.after_cancel(self.recall) def draw_ball(self): #left or right boundary if self.area.coords(self.ballid)[0] < 0 or self.area.coords( self.ballid)[2] > 500: self.xspeed = -self.xspeed ##top and bottom boundary if self.area.coords(self.ballid)[1] < 0: self.yspeed = -self.yspeed if self.area.coords(self.ballid)[3] > 500: self.xspeed = 0 self.yspeed = 0 self.game_over() self.area.move(self.ballid, self.xspeed, self.yspeed) #ball location #print(self.area.coords(self.ballid)) #canvas #print(self.area.winfo_height(), self.area.winfo_width()) def draw_paddle(self): self.area.move(self.paddleid, self.pspeed, 0) if self.area.coords(self.paddleid)[0] <= 0 or self.area.coords( self.paddleid)[2] >= 500: self.pspeed = 0 def paddle_hit(self): cb = self.area.coords(self.ballid)[0] + 10 cp = self.area.coords(self.paddleid)[0] + 50 ##to check if paddle is is paddle level if self.area.coords(self.ballid)[3] >= 400: ##if paddle is hit if cb > self.area.coords( self.paddleid)[0] and cb < self.area.coords( self.paddleid)[2] and self.area.coords( self.ballid)[3] < 405: dis = cp - cb self.xspeed = -self.speed * sin(radians(dis * 1.3)) self.yspeed = -self.speed * cos(radians(dis * 1.3)) def turn_left(self, arg): self.pspeed = -5 def turn_right(self, arg): self.pspeed = 5 def game_over(self): self.area.create_text(250, 250, text="Game over!", font=("times", 30)) #self.area.create_text(250,300, text="Press Ctrl+N for new game!", font=("times", 10)) self.gameon = False
class CollocationsView: _BACKGROUND_COLOUR = "#FFF" # white def __init__(self): self.queue = q.Queue() self.model = CollocationsModel(self.queue) self.top = Tk() self._init_top(self.top) self._init_menubar() self._init_widgets(self.top) self.load_corpus(self.model.DEFAULT_CORPUS) self.after = self.top.after(POLL_INTERVAL, self._poll) def _init_top(self, top): top.geometry("550x650+50+50") top.title("NLTK Collocations List") top.bind("<Control-q>", self.destroy) top.protocol("WM_DELETE_WINDOW", self.destroy) top.minsize(550, 650) def _init_widgets(self, parent): self.main_frame = Frame(parent, dict(background=self._BACKGROUND_COLOUR, padx=1, pady=1, border=1)) self._init_corpus_select(self.main_frame) self._init_results_box(self.main_frame) self._init_paging(self.main_frame) self._init_status(self.main_frame) self.main_frame.pack(fill="both", expand=True) def _init_corpus_select(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) self.var = StringVar(innerframe) self.var.set(self.model.DEFAULT_CORPUS) Label( innerframe, justify=LEFT, text=" Corpus: ", background=self._BACKGROUND_COLOUR, padx=2, pady=1, border=0 ).pack(side="left") other_corpora = list(self.model.CORPORA.keys()).remove(self.model.DEFAULT_CORPUS) om = OptionMenu( innerframe, self.var, self.model.DEFAULT_CORPUS, command=self.corpus_selected, *self.model.non_default_corpora() ) om["borderwidth"] = 0 om["highlightthickness"] = 1 om.pack(side="left") innerframe.pack(side="top", fill="x", anchor="n") def _init_status(self, parent): self.status = Label( parent, justify=LEFT, relief=SUNKEN, background=self._BACKGROUND_COLOUR, border=0, padx=1, pady=0 ) self.status.pack(side="top", anchor="sw") def _init_menubar(self): self._result_size = IntVar(self.top) menubar = Menu(self.top) filemenu = Menu(menubar, tearoff=0, borderwidth=0) filemenu.add_command(label="Exit", underline=1, command=self.destroy, accelerator="Ctrl-q") menubar.add_cascade(label="File", underline=0, menu=filemenu) editmenu = Menu(menubar, tearoff=0) rescntmenu = Menu(editmenu, tearoff=0) rescntmenu.add_radiobutton( label="20", variable=self._result_size, underline=0, value=20, command=self.set_result_size ) rescntmenu.add_radiobutton( label="50", variable=self._result_size, underline=0, value=50, command=self.set_result_size ) rescntmenu.add_radiobutton( label="100", variable=self._result_size, underline=0, value=100, command=self.set_result_size ) rescntmenu.invoke(1) editmenu.add_cascade(label="Result Count", underline=0, menu=rescntmenu) menubar.add_cascade(label="Edit", underline=0, menu=editmenu) self.top.config(menu=menubar) def set_result_size(self, **kwargs): self.model.result_count = self._result_size.get() def _init_results_box(self, parent): innerframe = Frame(parent) i1 = Frame(innerframe) i2 = Frame(innerframe) vscrollbar = Scrollbar(i1, borderwidth=1) hscrollbar = Scrollbar(i2, borderwidth=1, orient="horiz") self.results_box = Text( i1, font=tkinter.font.Font(family="courier", size="16"), state="disabled", borderwidth=1, yscrollcommand=vscrollbar.set, xscrollcommand=hscrollbar.set, wrap="none", width="40", height="20", exportselection=1, ) self.results_box.pack(side="left", fill="both", expand=True) vscrollbar.pack(side="left", fill="y", anchor="e") vscrollbar.config(command=self.results_box.yview) hscrollbar.pack(side="left", fill="x", expand=True, anchor="w") hscrollbar.config(command=self.results_box.xview) # there is no other way of avoiding the overlap of scrollbars while using pack layout manager!!! Label(i2, text=" ", background=self._BACKGROUND_COLOUR).pack(side="left", anchor="e") i1.pack(side="top", fill="both", expand=True, anchor="n") i2.pack(side="bottom", fill="x", anchor="s") innerframe.pack(side="top", fill="both", expand=True) def _init_paging(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) self.prev = prev = Button( innerframe, text="Previous", command=self.previous, width="10", borderwidth=1, highlightthickness=1, state="disabled", ) prev.pack(side="left", anchor="center") self.next = next = Button( innerframe, text="Next", command=self.__next__, width="10", borderwidth=1, highlightthickness=1, state="disabled", ) next.pack(side="right", anchor="center") innerframe.pack(side="top", fill="y") self.reset_current_page() def reset_current_page(self): self.current_page = -1 def _poll(self): try: event = self.queue.get(block=False) except q.Empty: pass else: if event == CORPUS_LOADED_EVENT: self.handle_corpus_loaded(event) elif event == ERROR_LOADING_CORPUS_EVENT: self.handle_error_loading_corpus(event) self.after = self.top.after(POLL_INTERVAL, self._poll) def handle_error_loading_corpus(self, event): self.status["text"] = "Error in loading " + self.var.get() self.unfreeze_editable() self.clear_results_box() self.freeze_editable() self.reset_current_page() def handle_corpus_loaded(self, event): self.status["text"] = self.var.get() + " is loaded" self.unfreeze_editable() self.clear_results_box() self.reset_current_page() # self.next() collocations = self.model.next(self.current_page + 1) self.write_results(collocations) self.current_page += 1 def corpus_selected(self, *args): new_selection = self.var.get() self.load_corpus(new_selection) def previous(self): self.freeze_editable() collocations = self.model.prev(self.current_page - 1) self.current_page = self.current_page - 1 self.clear_results_box() self.write_results(collocations) self.unfreeze_editable() def __next__(self): self.freeze_editable() collocations = self.model.next(self.current_page + 1) self.clear_results_box() self.write_results(collocations) self.current_page += 1 self.unfreeze_editable() def load_corpus(self, selection): if self.model.selected_corpus != selection: self.status["text"] = "Loading " + selection + "..." self.freeze_editable() self.model.load_corpus(selection) def freeze_editable(self): self.prev["state"] = "disabled" self.next["state"] = "disabled" def clear_results_box(self): self.results_box["state"] = "normal" self.results_box.delete("1.0", END) self.results_box["state"] = "disabled" def fire_event(self, event): # Firing an event so that rendering of widgets happen in the mainloop thread self.top.event_generate(event, when="tail") def destroy(self, *e): if self.top is None: return self.top.after_cancel(self.after) self.top.destroy() self.top = None def mainloop(self, *args, **kwargs): if in_idle(): return self.top.mainloop(*args, **kwargs) def unfreeze_editable(self): self.set_paging_button_states() def set_paging_button_states(self): if self.current_page == -1 or self.current_page == 0: self.prev["state"] = "disabled" else: self.prev["state"] = "normal" if self.model.is_last_page(self.current_page): self.next["state"] = "disabled" else: self.next["state"] = "normal" def write_results(self, results): self.results_box["state"] = "normal" row = 1 for each in results: self.results_box.insert(str(row) + ".0", each[0] + " " + each[1] + "\n") row += 1 self.results_box["state"] = "disabled"
class Client: """Base client class. """ def __init__(self, trace_path): """Initialising values and creating the window with game world """ self.trace_path = trace_path self.game_data = json.loads(open(trace_path, 'r').read()) self.rootpath = Path(__file__).parent self.root = Tk() self.root.title("RAINBOW hit") self.canvas = Canvas(self.root, width=WIDTH * 1.8, height=HEIGHT, bg="#f0f7f6") for i in range(GRID_SIZE): for j in range(GRID_SIZE): if i == 0 or j == 0 or i == GRID_SIZE - 1 or j == GRID_SIZE - 1: continue self.canvas.create_rectangle(i * CELL_SIZE, j * CELL_SIZE, i * CELL_SIZE + CELL_SIZE, j * CELL_SIZE + CELL_SIZE, fill='#c4badb', outline='#999090') self.canvas.create_rectangle(32, 32, HEIGHT - 32, HEIGHT - 32, outline='gray', width=2) self.canvas.grid(column=0, row=0) pilImage1 = Image.open(self.rootpath / "assets/ufo1.png") self.image1 = ImageTk.PhotoImage(pilImage1) self.canvas.create_image(592, 48, image=self.image1) self.health1 = self.canvas.create_rectangle(630, 36, 1010, 58, fill='#3ab03e', outline='gray') pilImage2 = Image.open(self.rootpath / "assets/ufo2.png") self.image2 = ImageTk.PhotoImage(pilImage2) self.canvas.create_image(592, 90, image=self.image2) self.health2 = self.canvas.create_rectangle(630, 78, 1010, 99, fill='#3ab03e', outline='gray') self.canvas.create_rectangle(630, 36, 1010, 58, width=2, outline='gray') self.canvas.create_rectangle(630, 78, 1010, 100, width=2, outline='gray') self.game_over = False self.step = 0 self.steps_text = self.canvas.create_text(632, 138, font="Times", text='Current step: 0') self.updater_job = None # message_entry = Text(width=56, height=22) # message_entry.place(x=566, y=168) # self.window = self.canvas.create_rectangle(566, 168, 1018, 524, width=5, outline='#f21d1d') self.start_button = Button(self.root, text="start", width=32, command=self.updater, bg="#3fbfbf") self.start_button.place(x=564, y=200) self.step_button = Button(self.root, text="step", width=32, command=self.step_once, bg="#3fbfbf") self.step_button.place(x=564, y=240) self.pause_btn = Button(self.root, text="pause", bg="#3fbfbf", activeforeground='#3fbfbf', activebackground='#3fbfbf', width=32, command=self.pause) self.pause_btn.place(x=564, y=280) message_entry = self.canvas.create_text(635, 335, font="Times", text='Step delay') self.scale = Scale(self.root, from_=25, to=400, length=200, orient=HORIZONTAL) self.scale.set(UPDATE_TIME) self.scale.place(x=564, y=350) def get_object(self, x, y): '''Get object by coordinates Parameters ---------- x : int y : int Returns ------- Optional[Gameobject] ''' for obj in self.objects: objx, objy = self.canvas.coords(obj.sprite) if objx == 48 + x * 32.0 and objy == 32.0 * 18 - (48 + y * 32): return obj return None def pop_object(self, x, y): '''Return object from objects list and remove it from list Parameters ---------- x : int y : int Returns ------- Optional[Gameobject] ''' for idx in range(len(self.objects)): objx, objy = self.canvas.coords(self.objects[idx].sprite) if objx == 48 + x * 32.0 and objy == 32.0 * 18 - (48 + y * 32): return self.objects.pop(idx) return None def get_ufo_by_name(self, name): '''Get UFO-object by name attribute Parameters ---------- name : str Returns ------- Optional[Gameobject] ''' for obj in self.objects: if isinstance(obj, Ufo): if obj.name == name: return obj return None def creating_game_objects(self): """Creating Ufos and clouds """ self.objects = [] for k, v in self.game_data[0]['init_world'].items(): x, y = eval(k) if v[0] == 'Bot' and v[1] == 'player1': self.objects.append( Ufo(48 + x * 32, 32 * 18 - (48 + y * 32), self.canvas, self.rootpath / "assets/ufo1.png", 'player1', v[2], v[3])) elif v[0] == 'Bot' and v[1] == 'player2': self.objects.append( Ufo(48 + x * 32, 32 * 18 - (48 + y * 32), self.canvas, self.rootpath / "assets/ufo2.png", 'player2', v[2], v[3])) else: self.objects.append( Cloud(48 + x * 32, 32 * 18 - (48 + y * 32), self.canvas, self.rootpath / "assets/Cloud.png")) self.game_data.pop(0) def actions(self): """Commands processing and visualising actions: move, fire """ self.root.bind('1' + '<Right>', self.ufo_1.move) self.root.bind('2' + '<Right>', self.ufo_2.move) self.root.bind('<Down>', self.ufo_2.laser) self.root.bind('<Up>', self.ufo_2.deleter) def pause(self, event=None): if self.updater_job != None: self.root.after_cancel(self.updater_job) def updater(self, event=None): self.step_once() self.updater_job = self.root.after(self.scale.get(), self.updater) def step_once(self, event=None): if self.game_over: return None cmd = self.game_data.pop(0) if list(cmd.keys())[0] == 'sleep': pass elif list(cmd.keys())[0] == 'step': player = cmd['step']['player'] newx = cmd['step']['new_x'] newy = cmd['step']['new_y'] player_ufo = self.get_ufo_by_name(player) player_ufo.move(newx, newy) elif list(cmd.keys())[0] == 'shoot': player = cmd['shoot']['player'] x_end = cmd['shoot']['x_end'] y_end = cmd['shoot']['y_end'] destroyed = cmd['shoot']['destroyed'] player_ufo = self.get_ufo_by_name(player) player_ufo.laser(x_end, y_end) shooted_obj = self.get_object(x_end, y_end) if isinstance(shooted_obj, Ufo): shooted_obj.health -= player_ufo.damage # if player == 'player1': # self.health_line(2, 100 - (shooted_obj.health / shooted_obj.max_health) * 100) # else: # self.health_line(1, 100 - (shooted_obj.health / shooted_obj.max_health) * 100) if destroyed: obj = self.pop_object(x_end, y_end) self.root.after(70, func=obj.deleter) elif list(cmd.keys())[0] == 'bot_state': name = cmd['bot_state']['name'] hp = cmd['bot_state']['hp'] if name == 'player1': self.health_line(1, 100 - hp * 10) # TODO no hardcode else: self.health_line(2, 100 - hp * 10) elif list(cmd.keys())[0] == 'game_over': self.game_over = True if cmd['game_over']['draw'] == True: messagebox.showinfo("GAME OVER", "Draw!") else: messagebox.showinfo("GAME OVER", f"Winner is: {cmd['game_over']['winner']}") if not self.game_over: self.step += 1 self.canvas.itemconfigure(self.steps_text, text=f'Current step: {self.step}') def health_line(self, ufo_number, percent): """Reduce life string Parameters ---------- ufo_number : int percent : int Returns ------- None """ if ufo_number == 1: ufo_health = self.health1 self.canvas.delete(ufo_health) ufo_health = self.canvas.create_rectangle(630, 36, 1010 - (1010 - 630) * percent * 0.01, 58, fill='green') self.health1 = ufo_health else: ufo_health = self.health2 self.canvas.delete(ufo_health) ufo_health = self.canvas.create_rectangle(630, 78, 1010 - (1010 - 630) * percent * 0.01, 99, fill='green') self.health2 = ufo_health def main_loop(self): self.root.mainloop()
class minesweeper: def __init__(self, difficulty): self.difficulty = difficulty self.root = Tk() self.root.title('MineSweeper') self.ms = Canvas(self.root, height=475, width=475) self.ms.pack() self.ids = {} #key= 'name of widgets', 'box_0' ##these are values ain list #0. rectangle id number - int #1. text id number #2. mines list - bool #3. number list - int #4. active list - bool #5. flag list - bool #6. unknown flags - bool self.time_min = 0 self.time_sec = 0 self.mines_rem = 40 self.game_on = False self.first_click = False self.timer = None self.creator() self.ui_creator() self.reset() self.root.mainloop() #removes all bombs and reset states def mine_setter(self): i = 0 while i < 40: ch = choice(range(225)) if self.ids['box_' + str(ch)][2] == False: self.ids['box_' + str(ch)][2] = True i += 1 self.number_setter() #number around the mines def number_setter(self): for i in range(225): name = 'box_' + str(i) if self.ids[name][2]: if i % 15 > 0: self.ids['box_' + str(i - 1)][3] += 1 #left if i > 14: self.ids['box_' + str(i - 16)][3] += 1 #topleft if i < 210: self.ids['box_' + str(i + 14)][3] += 1 #bottomleft if i % 15 < 14: self.ids['box_' + str(i + 1)][3] += 1 #right if i > 14: self.ids['box_' + str(i - 14)][3] += 1 #topright if i < 210: self.ids['box_' + str(i + 16)][3] += 1 #bottomright if i > 14: self.ids['box_' + str(i - 15)][3] += 1 #top if i < 210: self.ids['box_' + str(i + 15)][3] += 1 #bottom def reset(self): self.game_on = True self.first_click = False self.time_min = 0 self.time_sec = 0 self.mines_rem = 40 self.ms.itemconfig(self.ids['mine_box'][1], text=str(self.mines_rem)) self.ms.itemconfig(self.ids['timer_box'][1], text=str('00:00')) for i in range(225): name = 'box_' + str(i) ## resetting the variables self.ids[name][2] = False self.ids[name][3] = 0 self.ids[name][4] = False self.ids[name][5] = False self.ids[name][5] = False ##reset the grids to back self.ms.itemconfig(self.ids[name][0], fill='#dddddd', outline='#000000') self.ms.itemconfig(self.ids[name][1], text='') try: self.time_stopper() except: pass self.mine_setter() #self.time_starter() ##call this to loop with time def time_starter(self): if self.game_on: mins = str(self.time_min) secs = str(self.time_sec) if self.time_min <= 9: mins = '0' + mins if self.time_sec <= 9: secs = '0' + secs clock = mins + ':' + secs self.ms.itemconfig(self.ids['timer_box'][1], text=clock) self.timer = self.root.after(1000, self.time_starter) self.time_sec += 1 if self.time_sec >= 60: self.time_sec = 0 self.time_min += 1 else: self.time_stopper() def time_stopper(self): self.root.after_cancel(self.timer) self.timer = None def ui_creator(self): ##mines counter area num = self.ms.create_rectangle(50, 15, 150, 60, fill='#888888', tags='mine_box') text = self.ms.create_text(99, 37, text='030', font=('Fixedsys', 25), fill='#ff2222', tags='mine_text') self.ids['mine_box'] = [] self.ids['mine_box'].append(num) self.ids['mine_box'].append(text) ##reset button area num = self.ms.create_rectangle(170, 18, 270, 57, fill='#888888', tags='reset_box') text = self.ms.create_text(220, 37, text='New', font=('Fixedsys', 25), fill='#2222ff', tags='reset_text') self.ids['reset_box'] = [] self.ids['reset_box'].append(num) self.ids['reset_box'].append(text) ##thes for rectangle self.ms.tag_bind(self.ids['reset_box'][0], "<Enter>", lambda event: self.cursor_hover('reset_box')) self.ms.tag_bind(self.ids['reset_box'][0], "<Leave>", lambda event: self.cursor_leave('reset_box')) self.ms.tag_bind(self.ids['reset_box'][0], "<Button-1>", lambda event: self.reset()) ##these for the text self.ms.tag_bind(self.ids['reset_box'][1], "<Enter>", lambda event: self.cursor_hover('reset_box')) self.ms.tag_bind(self.ids['reset_box'][1], "<Leave>", lambda event: self.cursor_leave('reset_box')) self.ms.tag_bind(self.ids['reset_box'][1], "<Button-1>", lambda event: self.reset()) ##timer box area num = self.ms.create_rectangle(290, 15, 425, 60, fill='#888888', tags='time_box') text = self.ms.create_text(357, 37, text='00:00', font=('Fixedsys', 25), fill='#22ff22', tags='timer_text') self.ids['timer_box'] = [] self.ids['timer_box'].append(num) self.ids['timer_box'].append(text) def creator(self): ##easy is 9x9 grid #box size = 24x24 #distance b/w them = 1 for i in range(15): for j in range(15): num = i * 15 + j name = 'box_' + str(num) x1 = 50 + j * 25 x2 = x1 + 23 y1 = 75 + i * 25 y2 = y1 + 23 #print(name,x1,x2,y1,y2) self.ids[name] = [] num = self.ms.create_rectangle(x1, y1, x2, y2, fill='#dddddd', tags=name) text = self.ms.create_text((x1 + x2) / 2, (y1 + y2) / 2, text='', font=('', 15)) self.ids[name].append(num) #0 added the ID in 0 self.ids[name].append(text) #1 text id number self.ids[name].append(False) #2 mines self.ids[name].append(0) #3 numbers self.ids[name].append(False) #4 active self.ids[name].append(False) #5 flags self.ids[name].append(False) #6 unknown for i in self.ids: self.anims(i) #print(i) #for binding all the 255 boxes with events like hover and clicks def anims(self, x): ##these for the box self.ms.tag_bind(self.ids[x][0], "<Enter>", lambda event: self.cursor_hover(x)) self.ms.tag_bind(self.ids[x][0], "<Leave>", lambda event: self.cursor_leave(x)) self.ms.tag_bind(self.ids[x][0], "<Button-1>", lambda event: self.left_clicked(x)) self.ms.tag_bind(self.ids[x][0], "<Button-3>", lambda event: self.right_clicked(x)) ##these for the text self.ms.tag_bind(self.ids[x][1], "<Enter>", lambda event: self.cursor_hover(x)) self.ms.tag_bind(self.ids[x][1], "<Leave>", lambda event: self.cursor_leave(x)) self.ms.tag_bind(self.ids[x][1], "<Button-1>", lambda event: self.left_clicked(x)) self.ms.tag_bind(self.ids[x][1], "<Button-3>", lambda event: self.right_clicked(x)) def cursor_hover(self, name): if self.game_on: if name == 'reset_box': self.ms.itemconfig(self.ids[name][0], fill='#aaaaaa') elif not self.ids[name][4]: self.ms.itemconfig(self.ids[name][0], fill='#eeffee') def cursor_leave(self, name): if self.game_on: if name == 'reset_box': self.ms.itemconfig(self.ids[name][0], fill='#888888') elif not self.ids[name][4]: self.ms.itemconfig(self.ids[name][0], fill='#dddddd') def left_clicked(self, name): if self.game_on: if not self.ids[name][5] and not self.ids[name][ 6]: #to not accept click over flag or mark if self.ids[name][ 2] == True and not self.first_click: #iif clicked on mine self.reset() elif self.ids[name][2] and self.first_click: self.stepped_on_mine(name) return else: if not self.first_click: self.first_click = True self.time_starter() #print('left', name) if not self.ids[name][4]: self.ids[name][4] = True self.ms.itemconfig(self.ids[name][0], fill='#eeeeee', outline='#777777') if self.ids[name][3] != 0: self.ms.itemconfig(self.ids[name][1], text=str(self.ids[name][3])) num = int(name[name.index('_') + 1:]) if self.ids[name][3] == 0: #print(num//15, num%15) self.stepped_on_blank(num) def right_clicked(self, name): if not self.ids[name][4]: if self.ids[name][5] == False and self.ids[name][6] == False: self.ms.itemconfig(self.ids[name][1], text='⚐') self.ids[name][5] = True self.mines_rem -= 1 self.ms.itemconfig(self.ids['mine_box'][1], text=str(self.mines_rem)) self.win_check() elif self.ids[name][5] == True and self.ids[name][6] == False: self.ms.itemconfig(self.ids[name][1], text='?') self.ids[name][5] = False self.ids[name][6] = True self.mines_rem += 1 self.ms.itemconfig(self.ids['mine_box'][1], text=str(self.mines_rem)) elif self.ids[name][5] == False and self.ids[name][6] == True: self.ms.itemconfig(self.ids[name][1], text='') self.ids[name][5] = False self.ids[name][6] = False else: #if any other case arrises self.ms.itemconfig(self.ids[name][1], text='') self.ids[name][5] = False self.ids[name][6] = False #this function would find the nearby blank positions and activate them def stepped_on_blank(self, i): ##in 'i' position the [name][3] == 0 #print(i) if i % 15 < 14: ##rights boxes if not self.ids['box_' + str(i + 1)][4]: print(i // 15, i % 15) self.left_clicked('box_' + str(i + 1)) if i % 15 > 0: ##left boxes if not self.ids['box_' + str(i - 1)][4]: print(i // 15, i % 15) self.left_clicked('box_' + str(i - 1)) if i > 14: ##up boxes if not self.ids['box_' + str(i - 15)][4]: print(i // 15, i % 15) self.left_clicked('box_' + str(i - 15)) if i < 210: ##down boxes if not self.ids['box_' + str(i + 15)][4]: print(i // 15, i % 15) self.left_clicked('box_' + str(i + 15)) if i > 14 and i % 15 > 0: ##up left if not self.ids['box_' + str(i - 16)][4]: print(i // 15, i % 15) self.left_clicked('box_' + str(i - 16)) if i > 14 and i % 14 < 14: ##up right if not self.ids['box_' + str(i - 14)][4]: print(i // 15, i % 15) self.left_clicked('box_' + str(i - 14)) if i < 210 and i % 15 > 0: #down left if not self.ids['box_' + str(i + 14)][4]: print(i // 15, i % 15) self.left_clicked('box_' + str(i + 14)) if i < 210 and i % 15 < 14: #down left if not self.ids['box_' + str(i + 16)][4]: print(i // 15, i % 15) self.left_clicked('box_' + str(i + 16)) def win_check(self): check = 40 if self.mines_rem == 0: for i in range(225): if self.ids['box_' + str(i)][2] and self.ids['box_' + str(i)][5]: check -= 1 print(self.mines_rem, check) if check == 0: self.game_on = False self.win_game() def win_game(self): for i in range(225): if self.ids['box_' + str(i)][2]: self.ms.itemconfig(self.ids['box_' + str(i)][0], fill='#7777ff') self.ms.itemconfig(self.ids['mine_box'][0], fill='#33ff33') self.ms.itemconfig(self.ids['mine_box'][1], text='Won') def stepped_on_mine(self, name): self.game_on = False for i in range(225): if self.ids['box_' + str(i)][2]: self.ms.itemconfig(self.ids['box_' + str(i)][0], fill='#cc0000') self.ms.itemconfig(self.ids[name][0], fill='#ff0000')
class CollocationsView: _BACKGROUND_COLOUR='#FFF' #white def __init__(self): self.queue = q.Queue() self.model = CollocationsModel(self.queue) self.top = Tk() self._init_top(self.top) self._init_menubar() self._init_widgets(self.top) self.load_corpus(self.model.DEFAULT_CORPUS) self.after = self.top.after(POLL_INTERVAL, self._poll) def _init_top(self, top): top.geometry('550x650+50+50') top.title('NLTK Collocations List') top.bind('<Control-q>', self.destroy) top.protocol('WM_DELETE_WINDOW', self.destroy) top.minsize(550,650) def _init_widgets(self, parent): self.main_frame = Frame(parent, dict(background=self._BACKGROUND_COLOUR, padx=1, pady=1, border=1)) self._init_corpus_select(self.main_frame) self._init_results_box(self.main_frame) self._init_paging(self.main_frame) self._init_status(self.main_frame) self.main_frame.pack(fill='both', expand=True) def _init_corpus_select(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) self.var = StringVar(innerframe) self.var.set(self.model.DEFAULT_CORPUS) Label(innerframe, justify=LEFT, text=' Corpus: ', background=self._BACKGROUND_COLOUR, padx = 2, pady = 1, border = 0).pack(side='left') other_corpora = list(self.model.CORPORA.keys()).remove(self.model.DEFAULT_CORPUS) om = OptionMenu(innerframe, self.var, self.model.DEFAULT_CORPUS, command=self.corpus_selected, *self.model.non_default_corpora()) om['borderwidth'] = 0 om['highlightthickness'] = 1 om.pack(side='left') innerframe.pack(side='top', fill='x', anchor='n') def _init_status(self, parent): self.status = Label(parent, justify=LEFT, relief=SUNKEN, background=self._BACKGROUND_COLOUR, border=0, padx = 1, pady = 0) self.status.pack(side='top', anchor='sw') def _init_menubar(self): self._result_size = IntVar(self.top) menubar = Menu(self.top) filemenu = Menu(menubar, tearoff=0, borderwidth=0) filemenu.add_command(label='Exit', underline=1, command=self.destroy, accelerator='Ctrl-q') menubar.add_cascade(label='File', underline=0, menu=filemenu) editmenu = Menu(menubar, tearoff=0) rescntmenu = Menu(editmenu, tearoff=0) rescntmenu.add_radiobutton(label='20', variable=self._result_size, underline=0, value=20, command=self.set_result_size) rescntmenu.add_radiobutton(label='50', variable=self._result_size, underline=0, value=50, command=self.set_result_size) rescntmenu.add_radiobutton(label='100', variable=self._result_size, underline=0, value=100, command=self.set_result_size) rescntmenu.invoke(1) editmenu.add_cascade(label='Result Count', underline=0, menu=rescntmenu) menubar.add_cascade(label='Edit', underline=0, menu=editmenu) self.top.config(menu=menubar) def set_result_size(self, **kwargs): self.model.result_count = self._result_size.get() def _init_results_box(self, parent): innerframe = Frame(parent) i1 = Frame(innerframe) i2 = Frame(innerframe) vscrollbar = Scrollbar(i1, borderwidth=1) hscrollbar = Scrollbar(i2, borderwidth=1, orient='horiz') self.results_box = Text(i1, font=tkinter.font.Font(family='courier', size='16'), state='disabled', borderwidth=1, yscrollcommand=vscrollbar.set, xscrollcommand=hscrollbar.set, wrap='none', width='40', height = '20', exportselection=1) self.results_box.pack(side='left', fill='both', expand=True) vscrollbar.pack(side='left', fill='y', anchor='e') vscrollbar.config(command=self.results_box.yview) hscrollbar.pack(side='left', fill='x', expand=True, anchor='w') hscrollbar.config(command=self.results_box.xview) #there is no other way of avoiding the overlap of scrollbars while using pack layout manager!!! Label(i2, text=' ', background=self._BACKGROUND_COLOUR).pack(side='left', anchor='e') i1.pack(side='top', fill='both', expand=True, anchor='n') i2.pack(side='bottom', fill='x', anchor='s') innerframe.pack(side='top', fill='both', expand=True) def _init_paging(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) self.prev = prev = Button(innerframe, text='Previous', command=self.previous, width='10', borderwidth=1, highlightthickness=1, state='disabled') prev.pack(side='left', anchor='center') self.next = next = Button(innerframe, text='Next', command=self.__next__, width='10', borderwidth=1, highlightthickness=1, state='disabled') next.pack(side='right', anchor='center') innerframe.pack(side='top', fill='y') self.reset_current_page() def reset_current_page(self): self.current_page = -1 def _poll(self): try: event = self.queue.get(block=False) except q.Empty: pass else: if event == CORPUS_LOADED_EVENT: self.handle_corpus_loaded(event) elif event == ERROR_LOADING_CORPUS_EVENT: self.handle_error_loading_corpus(event) self.after = self.top.after(POLL_INTERVAL, self._poll) def handle_error_loading_corpus(self, event): self.status['text'] = 'Error in loading ' + self.var.get() self.unfreeze_editable() self.clear_results_box() self.freeze_editable() self.reset_current_page() def handle_corpus_loaded(self, event): self.status['text'] = self.var.get() + ' is loaded' self.unfreeze_editable() self.clear_results_box() self.reset_current_page() #self.next() collocations = self.model.next(self.current_page + 1) self.write_results(collocations) self.current_page += 1 def corpus_selected(self, *args): new_selection = self.var.get() self.load_corpus(new_selection) def previous(self): self.freeze_editable() collocations = self.model.prev(self.current_page - 1) self.current_page= self.current_page - 1 self.clear_results_box() self.write_results(collocations) self.unfreeze_editable() def __next__(self): self.freeze_editable() collocations = self.model.next(self.current_page + 1) self.clear_results_box() self.write_results(collocations) self.current_page += 1 self.unfreeze_editable() def load_corpus(self, selection): if self.model.selected_corpus != selection: self.status['text'] = 'Loading ' + selection + '...' self.freeze_editable() self.model.load_corpus(selection) def freeze_editable(self): self.prev['state'] = 'disabled' self.next['state'] = 'disabled' def clear_results_box(self): self.results_box['state'] = 'normal' self.results_box.delete("1.0", END) self.results_box['state'] = 'disabled' def fire_event(self, event): #Firing an event so that rendering of widgets happen in the mainloop thread self.top.event_generate(event, when='tail') def destroy(self, *e): if self.top is None: return self.top.after_cancel(self.after) self.top.destroy() self.top = None def mainloop(self, *args, **kwargs): if in_idle(): return self.top.mainloop(*args, **kwargs) def unfreeze_editable(self): self.set_paging_button_states() def set_paging_button_states(self): if self.current_page == -1 or self.current_page == 0: self.prev['state'] = 'disabled' else: self.prev['state'] = 'normal' if self.model.is_last_page(self.current_page): self.next['state'] = 'disabled' else: self.next['state'] = 'normal' def write_results(self, results): self.results_box['state'] = 'normal' row = 1 for each in results: self.results_box.insert(str(row) + '.0', each[0] + " " + each[1] + "\n") row += 1 self.results_box['state'] = 'disabled'
class ConcordanceSearchView(object): _BACKGROUND_COLOUR = "#FFF" # white # Colour of highlighted results _HIGHLIGHT_WORD_COLOUR = "#F00" # red _HIGHLIGHT_WORD_TAG = "HL_WRD_TAG" _HIGHLIGHT_LABEL_COLOUR = "#C0C0C0" # dark grey _HIGHLIGHT_LABEL_TAG = "HL_LBL_TAG" # Percentage of text left of the scrollbar position _FRACTION_LEFT_TEXT = 0.30 def __init__(self): self.queue = q.Queue() self.model = ConcordanceSearchModel(self.queue) self.top = Tk() self._init_top(self.top) self._init_menubar() self._init_widgets(self.top) self.load_corpus(self.model.DEFAULT_CORPUS) self.after = self.top.after(POLL_INTERVAL, self._poll) def _init_top(self, top): top.geometry("950x680+50+50") top.title("NLTK Concordance Search") top.bind("<Control-q>", self.destroy) top.protocol("WM_DELETE_WINDOW", self.destroy) top.minsize(950, 680) def _init_widgets(self, parent): self.main_frame = Frame(parent, dict(background=self._BACKGROUND_COLOUR, padx=1, pady=1, border=1)) self._init_corpus_select(self.main_frame) self._init_query_box(self.main_frame) self._init_results_box(self.main_frame) self._init_paging(self.main_frame) self._init_status(self.main_frame) self.main_frame.pack(fill="both", expand=True) def _init_menubar(self): self._result_size = IntVar(self.top) self._cntx_bf_len = IntVar(self.top) self._cntx_af_len = IntVar(self.top) menubar = Menu(self.top) filemenu = Menu(menubar, tearoff=0, borderwidth=0) filemenu.add_command(label="Exit", underline=1, command=self.destroy, accelerator="Ctrl-q") menubar.add_cascade(label="File", underline=0, menu=filemenu) editmenu = Menu(menubar, tearoff=0) rescntmenu = Menu(editmenu, tearoff=0) rescntmenu.add_radiobutton( label="20", variable=self._result_size, underline=0, value=20, command=self.set_result_size ) rescntmenu.add_radiobutton( label="50", variable=self._result_size, underline=0, value=50, command=self.set_result_size ) rescntmenu.add_radiobutton( label="100", variable=self._result_size, underline=0, value=100, command=self.set_result_size ) rescntmenu.invoke(1) editmenu.add_cascade(label="Result Count", underline=0, menu=rescntmenu) cntxmenu = Menu(editmenu, tearoff=0) cntxbfmenu = Menu(cntxmenu, tearoff=0) cntxbfmenu.add_radiobutton( label="60 characters", variable=self._cntx_bf_len, underline=0, value=60, command=self.set_cntx_bf_len ) cntxbfmenu.add_radiobutton( label="80 characters", variable=self._cntx_bf_len, underline=0, value=80, command=self.set_cntx_bf_len ) cntxbfmenu.add_radiobutton( label="100 characters", variable=self._cntx_bf_len, underline=0, value=100, command=self.set_cntx_bf_len ) cntxbfmenu.invoke(1) cntxmenu.add_cascade(label="Before", underline=0, menu=cntxbfmenu) cntxafmenu = Menu(cntxmenu, tearoff=0) cntxafmenu.add_radiobutton( label="70 characters", variable=self._cntx_af_len, underline=0, value=70, command=self.set_cntx_af_len ) cntxafmenu.add_radiobutton( label="90 characters", variable=self._cntx_af_len, underline=0, value=90, command=self.set_cntx_af_len ) cntxafmenu.add_radiobutton( label="110 characters", variable=self._cntx_af_len, underline=0, value=110, command=self.set_cntx_af_len ) cntxafmenu.invoke(1) cntxmenu.add_cascade(label="After", underline=0, menu=cntxafmenu) editmenu.add_cascade(label="Context", underline=0, menu=cntxmenu) menubar.add_cascade(label="Edit", underline=0, menu=editmenu) self.top.config(menu=menubar) def set_result_size(self, **kwargs): self.model.result_count = self._result_size.get() def set_cntx_af_len(self, **kwargs): self._char_after = self._cntx_af_len.get() def set_cntx_bf_len(self, **kwargs): self._char_before = self._cntx_bf_len.get() def _init_corpus_select(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) self.var = StringVar(innerframe) self.var.set(self.model.DEFAULT_CORPUS) Label( innerframe, justify=LEFT, text=" Corpus: ", background=self._BACKGROUND_COLOUR, padx=2, pady=1, border=0 ).pack(side="left") other_corpora = list(self.model.CORPORA.keys()).remove(self.model.DEFAULT_CORPUS) om = OptionMenu( innerframe, self.var, self.model.DEFAULT_CORPUS, command=self.corpus_selected, *self.model.non_default_corpora() ) om["borderwidth"] = 0 om["highlightthickness"] = 1 om.pack(side="left") innerframe.pack(side="top", fill="x", anchor="n") def _init_status(self, parent): self.status = Label( parent, justify=LEFT, relief=SUNKEN, background=self._BACKGROUND_COLOUR, border=0, padx=1, pady=0 ) self.status.pack(side="top", anchor="sw") def _init_query_box(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) another = Frame(innerframe, background=self._BACKGROUND_COLOUR) self.query_box = Entry(another, width=60) self.query_box.pack(side="left", fill="x", pady=25, anchor="center") self.search_button = Button(another, text="Search", command=self.search, borderwidth=1, highlightthickness=1) self.search_button.pack(side="left", fill="x", pady=25, anchor="center") self.query_box.bind("<KeyPress-Return>", self.search_enter_keypress_handler) another.pack() innerframe.pack(side="top", fill="x", anchor="n") def search_enter_keypress_handler(self, *event): self.search() def _init_results_box(self, parent): innerframe = Frame(parent) i1 = Frame(innerframe) i2 = Frame(innerframe) vscrollbar = Scrollbar(i1, borderwidth=1) hscrollbar = Scrollbar(i2, borderwidth=1, orient="horiz") self.results_box = Text( i1, font=tkinter.font.Font(family="courier", size="16"), state="disabled", borderwidth=1, yscrollcommand=vscrollbar.set, xscrollcommand=hscrollbar.set, wrap="none", width="40", height="20", exportselection=1, ) self.results_box.pack(side="left", fill="both", expand=True) self.results_box.tag_config(self._HIGHLIGHT_WORD_TAG, foreground=self._HIGHLIGHT_WORD_COLOUR) self.results_box.tag_config(self._HIGHLIGHT_LABEL_TAG, foreground=self._HIGHLIGHT_LABEL_COLOUR) vscrollbar.pack(side="left", fill="y", anchor="e") vscrollbar.config(command=self.results_box.yview) hscrollbar.pack(side="left", fill="x", expand=True, anchor="w") hscrollbar.config(command=self.results_box.xview) # there is no other way of avoiding the overlap of scrollbars while using pack layout manager!!! Label(i2, text=" ", background=self._BACKGROUND_COLOUR).pack(side="left", anchor="e") i1.pack(side="top", fill="both", expand=True, anchor="n") i2.pack(side="bottom", fill="x", anchor="s") innerframe.pack(side="top", fill="both", expand=True) def _init_paging(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) self.prev = prev = Button( innerframe, text="Previous", command=self.previous, width="10", borderwidth=1, highlightthickness=1, state="disabled", ) prev.pack(side="left", anchor="center") self.next = next = Button( innerframe, text="Next", command=self.__next__, width="10", borderwidth=1, highlightthickness=1, state="disabled", ) next.pack(side="right", anchor="center") innerframe.pack(side="top", fill="y") self.current_page = 0 def previous(self): self.clear_results_box() self.freeze_editable() self.model.prev(self.current_page - 1) def __next__(self): self.clear_results_box() self.freeze_editable() self.model.next(self.current_page + 1) def about(self, *e): ABOUT = "NLTK Concordance Search Demo\n" TITLE = "About: NLTK Concordance Search Demo" try: from tkinter.messagebox import Message Message(message=ABOUT, title=TITLE, parent=self.main_frame).show() except: ShowText(self.top, TITLE, ABOUT) def _bind_event_handlers(self): self.top.bind(CORPUS_LOADED_EVENT, self.handle_corpus_loaded) self.top.bind(SEARCH_TERMINATED_EVENT, self.handle_search_terminated) self.top.bind(SEARCH_ERROR_EVENT, self.handle_search_error) self.top.bind(ERROR_LOADING_CORPUS_EVENT, self.handle_error_loading_corpus) def _poll(self): try: event = self.queue.get(block=False) except q.Empty: pass else: if event == CORPUS_LOADED_EVENT: self.handle_corpus_loaded(event) elif event == SEARCH_TERMINATED_EVENT: self.handle_search_terminated(event) elif event == SEARCH_ERROR_EVENT: self.handle_search_error(event) elif event == ERROR_LOADING_CORPUS_EVENT: self.handle_error_loading_corpus(event) self.after = self.top.after(POLL_INTERVAL, self._poll) def handle_error_loading_corpus(self, event): self.status["text"] = "Error in loading " + self.var.get() self.unfreeze_editable() self.clear_all() self.freeze_editable() def handle_corpus_loaded(self, event): self.status["text"] = self.var.get() + " is loaded" self.unfreeze_editable() self.clear_all() self.query_box.focus_set() def handle_search_terminated(self, event): # todo: refactor the model such that it is less state sensitive results = self.model.get_results() self.write_results(results) self.status["text"] = "" if len(results) == 0: self.status["text"] = "No results found for " + self.model.query else: self.current_page = self.model.last_requested_page self.unfreeze_editable() self.results_box.xview_moveto(self._FRACTION_LEFT_TEXT) def handle_search_error(self, event): self.status["text"] = "Error in query " + self.model.query self.unfreeze_editable() def corpus_selected(self, *args): new_selection = self.var.get() self.load_corpus(new_selection) def load_corpus(self, selection): if self.model.selected_corpus != selection: self.status["text"] = "Loading " + selection + "..." self.freeze_editable() self.model.load_corpus(selection) def search(self): self.current_page = 0 self.clear_results_box() self.model.reset_results() query = self.query_box.get() if len(query.strip()) == 0: return self.status["text"] = "Searching for " + query self.freeze_editable() self.model.search(query, self.current_page + 1) def write_results(self, results): self.results_box["state"] = "normal" row = 1 for each in results: sent, pos1, pos2 = each[0].strip(), each[1], each[2] if len(sent) != 0: if pos1 < self._char_before: sent, pos1, pos2 = self.pad(sent, pos1, pos2) sentence = sent[pos1 - self._char_before : pos1 + self._char_after] if not row == len(results): sentence += "\n" self.results_box.insert(str(row) + ".0", sentence) word_markers, label_markers = self.words_and_labels(sent, pos1, pos2) for marker in word_markers: self.results_box.tag_add( self._HIGHLIGHT_WORD_TAG, str(row) + "." + str(marker[0]), str(row) + "." + str(marker[1]) ) for marker in label_markers: self.results_box.tag_add( self._HIGHLIGHT_LABEL_TAG, str(row) + "." + str(marker[0]), str(row) + "." + str(marker[1]) ) row += 1 self.results_box["state"] = "disabled" def words_and_labels(self, sentence, pos1, pos2): search_exp = sentence[pos1:pos2] words, labels = [], [] labeled_words = search_exp.split(" ") index = 0 for each in labeled_words: if each == "": index += 1 else: word, label = each.split("/") words.append((self._char_before + index, self._char_before + index + len(word))) index += len(word) + 1 labels.append((self._char_before + index, self._char_before + index + len(label))) index += len(label) index += 1 return words, labels def pad(self, sent, hstart, hend): if hstart >= self._char_before: return sent, hstart, hend d = self._char_before - hstart sent = "".join([" "] * d) + sent return sent, hstart + d, hend + d def destroy(self, *e): if self.top is None: return self.top.after_cancel(self.after) self.top.destroy() self.top = None def clear_all(self): self.query_box.delete(0, END) self.model.reset_query() self.clear_results_box() def clear_results_box(self): self.results_box["state"] = "normal" self.results_box.delete("1.0", END) self.results_box["state"] = "disabled" def freeze_editable(self): self.query_box["state"] = "disabled" self.search_button["state"] = "disabled" self.prev["state"] = "disabled" self.next["state"] = "disabled" def unfreeze_editable(self): self.query_box["state"] = "normal" self.search_button["state"] = "normal" self.set_paging_button_states() def set_paging_button_states(self): if self.current_page == 0 or self.current_page == 1: self.prev["state"] = "disabled" else: self.prev["state"] = "normal" if self.model.has_more_pages(self.current_page): self.next["state"] = "normal" else: self.next["state"] = "disabled" def fire_event(self, event): # Firing an event so that rendering of widgets happen in the mainloop thread self.top.event_generate(event, when="tail") def mainloop(self, *args, **kwargs): if in_idle(): return self.top.mainloop(*args, **kwargs)
class CollocationsView: _BACKGROUND_COLOUR = '#FFF' #white def __init__(self): self.queue = q.Queue() self.model = CollocationsModel(self.queue) self.top = Tk() self._init_top(self.top) self._init_menubar() self._init_widgets(self.top) self.load_corpus(self.model.DEFAULT_CORPUS) self.after = self.top.after(POLL_INTERVAL, self._poll) def _init_top(self, top): top.geometry('550x650+50+50') top.title('NLTK Collocations List') top.bind('<Control-q>', self.destroy) top.protocol('WM_DELETE_WINDOW', self.destroy) top.minsize(550, 650) def _init_widgets(self, parent): self.main_frame = Frame( parent, dict(background=self._BACKGROUND_COLOUR, padx=1, pady=1, border=1)) self._init_corpus_select(self.main_frame) self._init_results_box(self.main_frame) self._init_paging(self.main_frame) self._init_status(self.main_frame) self.main_frame.pack(fill='both', expand=True) def _init_corpus_select(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) self.var = StringVar(innerframe) self.var.set(self.model.DEFAULT_CORPUS) Label(innerframe, justify=LEFT, text=' Corpus: ', background=self._BACKGROUND_COLOUR, padx=2, pady=1, border=0).pack(side='left') other_corpora = list(self.model.CORPORA.keys()).remove( self.model.DEFAULT_CORPUS) om = OptionMenu(innerframe, self.var, self.model.DEFAULT_CORPUS, command=self.corpus_selected, *self.model.non_default_corpora()) om['borderwidth'] = 0 om['highlightthickness'] = 1 om.pack(side='left') innerframe.pack(side='top', fill='x', anchor='n') def _init_status(self, parent): self.status = Label(parent, justify=LEFT, relief=SUNKEN, background=self._BACKGROUND_COLOUR, border=0, padx=1, pady=0) self.status.pack(side='top', anchor='sw') def _init_menubar(self): self._result_size = IntVar(self.top) menubar = Menu(self.top) filemenu = Menu(menubar, tearoff=0, borderwidth=0) filemenu.add_command(label='Exit', underline=1, command=self.destroy, accelerator='Ctrl-q') menubar.add_cascade(label='File', underline=0, menu=filemenu) editmenu = Menu(menubar, tearoff=0) rescntmenu = Menu(editmenu, tearoff=0) rescntmenu.add_radiobutton(label='20', variable=self._result_size, underline=0, value=20, command=self.set_result_size) rescntmenu.add_radiobutton(label='50', variable=self._result_size, underline=0, value=50, command=self.set_result_size) rescntmenu.add_radiobutton(label='100', variable=self._result_size, underline=0, value=100, command=self.set_result_size) rescntmenu.invoke(1) editmenu.add_cascade(label='Result Count', underline=0, menu=rescntmenu) menubar.add_cascade(label='Edit', underline=0, menu=editmenu) self.top.config(menu=menubar) def set_result_size(self, **kwargs): self.model.result_count = self._result_size.get() def _init_results_box(self, parent): innerframe = Frame(parent) i1 = Frame(innerframe) i2 = Frame(innerframe) vscrollbar = Scrollbar(i1, borderwidth=1) hscrollbar = Scrollbar(i2, borderwidth=1, orient='horiz') self.results_box = Text(i1, font=tkinter.font.Font(family='courier', size='16'), state='disabled', borderwidth=1, yscrollcommand=vscrollbar.set, xscrollcommand=hscrollbar.set, wrap='none', width='40', height='20', exportselection=1) self.results_box.pack(side='left', fill='both', expand=True) vscrollbar.pack(side='left', fill='y', anchor='e') vscrollbar.config(command=self.results_box.yview) hscrollbar.pack(side='left', fill='x', expand=True, anchor='w') hscrollbar.config(command=self.results_box.xview) #there is no other way of avoiding the overlap of scrollbars while using pack layout manager!!! Label(i2, text=' ', background=self._BACKGROUND_COLOUR).pack(side='left', anchor='e') i1.pack(side='top', fill='both', expand=True, anchor='n') i2.pack(side='bottom', fill='x', anchor='s') innerframe.pack(side='top', fill='both', expand=True) def _init_paging(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) self.prev = prev = Button(innerframe, text='Previous', command=self.previous, width='10', borderwidth=1, highlightthickness=1, state='disabled') prev.pack(side='left', anchor='center') self.next = next = Button(innerframe, text='Next', command=self.__next__, width='10', borderwidth=1, highlightthickness=1, state='disabled') next.pack(side='right', anchor='center') innerframe.pack(side='top', fill='y') self.reset_current_page() def reset_current_page(self): self.current_page = -1 def _poll(self): try: event = self.queue.get(block=False) except q.Empty: pass else: if event == CORPUS_LOADED_EVENT: self.handle_corpus_loaded(event) elif event == ERROR_LOADING_CORPUS_EVENT: self.handle_error_loading_corpus(event) self.after = self.top.after(POLL_INTERVAL, self._poll) def handle_error_loading_corpus(self, event): self.status['text'] = 'Error in loading ' + self.var.get() self.unfreeze_editable() self.clear_results_box() self.freeze_editable() self.reset_current_page() def handle_corpus_loaded(self, event): self.status['text'] = self.var.get() + ' is loaded' self.unfreeze_editable() self.clear_results_box() self.reset_current_page() #self.next() collocations = self.model.next(self.current_page + 1) self.write_results(collocations) self.current_page += 1 def corpus_selected(self, *args): new_selection = self.var.get() self.load_corpus(new_selection) def previous(self): self.freeze_editable() collocations = self.model.prev(self.current_page - 1) self.current_page = self.current_page - 1 self.clear_results_box() self.write_results(collocations) self.unfreeze_editable() def __next__(self): self.freeze_editable() collocations = self.model.next(self.current_page + 1) self.clear_results_box() self.write_results(collocations) self.current_page += 1 self.unfreeze_editable() def load_corpus(self, selection): if self.model.selected_corpus != selection: self.status['text'] = 'Loading ' + selection + '...' self.freeze_editable() self.model.load_corpus(selection) def freeze_editable(self): self.prev['state'] = 'disabled' self.next['state'] = 'disabled' def clear_results_box(self): self.results_box['state'] = 'normal' self.results_box.delete("1.0", END) self.results_box['state'] = 'disabled' def fire_event(self, event): #Firing an event so that rendering of widgets happen in the mainloop thread self.top.event_generate(event, when='tail') def destroy(self, *e): if self.top is None: return self.top.after_cancel(self.after) self.top.destroy() self.top = None def mainloop(self, *args, **kwargs): if in_idle(): return self.top.mainloop(*args, **kwargs) def unfreeze_editable(self): self.set_paging_button_states() def set_paging_button_states(self): if self.current_page == -1 or self.current_page == 0: self.prev['state'] = 'disabled' else: self.prev['state'] = 'normal' if self.model.is_last_page(self.current_page): self.next['state'] = 'disabled' else: self.next['state'] = 'normal' def write_results(self, results): self.results_box['state'] = 'normal' row = 1 for each in results: self.results_box.insert( str(row) + '.0', each[0] + " " + each[1] + "\n") row += 1 self.results_box['state'] = 'disabled'
class GameApp: def __init__(self): self.model = Game() self.job = None self.speed = 1 self.root = Tk() self.canvas = Field(self.root, self.model) self.toolbar = Toolbar(self.root) self.toolbar.add_button(text='Load', command=self.load) self.toolbar.add_button(text='Save', command=self.save) self.toolbar.add_button(text='Origin', command=self.canvas.origin) self.toolbar.add_radio_button_group(buttons={ 1: 'Pause', 2: 'Play' }, command=self.on_state_changed) self.toolbar.add_radio_button_group(buttons={ 1: 'x1', 2: 'x2', 4: 'x4', 8: 'x8', 16: 'x16' }, command=self.on_speed_changed) self.toolbar.pack(side=TOP, fill=X) self.canvas.pack() self.root.resizable(False, False) def load(self): filename = filedialog.askopenfilename(initialdir="./data/", title="Select file", filetypes=(("Life files", "*.life"), ("All files", "*.*"))) try: with open(filename, 'r') as file: field = FieldSerializer.deserialize(file) self.model.field = field self.canvas.update() except IOError as error: print(error, file=sys.stderr) def save(self): filename = filedialog.asksaveasfilename( initialdir="./data/", title="Save file as", filetypes=(("Life files", "*.life"), ("All files", "*.*"))) try: with open(filename, 'w') as file: buffer = FieldSerializer.serialize(self.model.field) buffer.seek(0) shutil.copyfileobj(buffer, file) buffer.close() except IOError as error: print(error, file=sys.stderr) def play(self): self.model.next_state() self.job = self.root.after(int(self.speed * 1000), self.play) def pause(self): if self.job: self.root.after_cancel(self.job) def on_speed_changed(self, value): self.speed = 1 / value def on_state_changed(self, value): if value == 1: self.pause() elif value == 2: self.play()
class ConcordanceSearchView(object): _BACKGROUND_COLOUR = '#FFF' #white #Colour of highlighted results _HIGHLIGHT_WORD_COLOUR = '#F00' #red _HIGHLIGHT_WORD_TAG = 'HL_WRD_TAG' _HIGHLIGHT_LABEL_COLOUR = '#C0C0C0' # dark grey _HIGHLIGHT_LABEL_TAG = 'HL_LBL_TAG' #Percentage of text left of the scrollbar position _FRACTION_LEFT_TEXT = 0.30 def __init__(self): self.queue = q.Queue() self.model = ConcordanceSearchModel(self.queue) self.top = Tk() self._init_top(self.top) self._init_menubar() self._init_widgets(self.top) self.load_corpus(self.model.DEFAULT_CORPUS) self.after = self.top.after(POLL_INTERVAL, self._poll) def _init_top(self, top): top.geometry('950x680+50+50') top.title('NLTK Concordance Search') top.bind('<Control-q>', self.destroy) top.protocol('WM_DELETE_WINDOW', self.destroy) top.minsize(950, 680) def _init_widgets(self, parent): self.main_frame = Frame( parent, dict(background=self._BACKGROUND_COLOUR, padx=1, pady=1, border=1)) self._init_corpus_select(self.main_frame) self._init_query_box(self.main_frame) self._init_results_box(self.main_frame) self._init_paging(self.main_frame) self._init_status(self.main_frame) self.main_frame.pack(fill='both', expand=True) def _init_menubar(self): self._result_size = IntVar(self.top) self._cntx_bf_len = IntVar(self.top) self._cntx_af_len = IntVar(self.top) menubar = Menu(self.top) filemenu = Menu(menubar, tearoff=0, borderwidth=0) filemenu.add_command(label='Exit', underline=1, command=self.destroy, accelerator='Ctrl-q') menubar.add_cascade(label='File', underline=0, menu=filemenu) editmenu = Menu(menubar, tearoff=0) rescntmenu = Menu(editmenu, tearoff=0) rescntmenu.add_radiobutton(label='20', variable=self._result_size, underline=0, value=20, command=self.set_result_size) rescntmenu.add_radiobutton(label='50', variable=self._result_size, underline=0, value=50, command=self.set_result_size) rescntmenu.add_radiobutton(label='100', variable=self._result_size, underline=0, value=100, command=self.set_result_size) rescntmenu.invoke(1) editmenu.add_cascade(label='Result Count', underline=0, menu=rescntmenu) cntxmenu = Menu(editmenu, tearoff=0) cntxbfmenu = Menu(cntxmenu, tearoff=0) cntxbfmenu.add_radiobutton(label='60 characters', variable=self._cntx_bf_len, underline=0, value=60, command=self.set_cntx_bf_len) cntxbfmenu.add_radiobutton(label='80 characters', variable=self._cntx_bf_len, underline=0, value=80, command=self.set_cntx_bf_len) cntxbfmenu.add_radiobutton(label='100 characters', variable=self._cntx_bf_len, underline=0, value=100, command=self.set_cntx_bf_len) cntxbfmenu.invoke(1) cntxmenu.add_cascade(label='Before', underline=0, menu=cntxbfmenu) cntxafmenu = Menu(cntxmenu, tearoff=0) cntxafmenu.add_radiobutton(label='70 characters', variable=self._cntx_af_len, underline=0, value=70, command=self.set_cntx_af_len) cntxafmenu.add_radiobutton(label='90 characters', variable=self._cntx_af_len, underline=0, value=90, command=self.set_cntx_af_len) cntxafmenu.add_radiobutton(label='110 characters', variable=self._cntx_af_len, underline=0, value=110, command=self.set_cntx_af_len) cntxafmenu.invoke(1) cntxmenu.add_cascade(label='After', underline=0, menu=cntxafmenu) editmenu.add_cascade(label='Context', underline=0, menu=cntxmenu) menubar.add_cascade(label='Edit', underline=0, menu=editmenu) self.top.config(menu=menubar) def set_result_size(self, **kwargs): self.model.result_count = self._result_size.get() def set_cntx_af_len(self, **kwargs): self._char_after = self._cntx_af_len.get() def set_cntx_bf_len(self, **kwargs): self._char_before = self._cntx_bf_len.get() def _init_corpus_select(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) self.var = StringVar(innerframe) self.var.set(self.model.DEFAULT_CORPUS) Label(innerframe, justify=LEFT, text=' Corpus: ', background=self._BACKGROUND_COLOUR, padx=2, pady=1, border=0).pack(side='left') other_corpora = list(self.model.CORPORA.keys()).remove( self.model.DEFAULT_CORPUS) om = OptionMenu(innerframe, self.var, self.model.DEFAULT_CORPUS, command=self.corpus_selected, *self.model.non_default_corpora()) om['borderwidth'] = 0 om['highlightthickness'] = 1 om.pack(side='left') innerframe.pack(side='top', fill='x', anchor='n') def _init_status(self, parent): self.status = Label(parent, justify=LEFT, relief=SUNKEN, background=self._BACKGROUND_COLOUR, border=0, padx=1, pady=0) self.status.pack(side='top', anchor='sw') def _init_query_box(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) another = Frame(innerframe, background=self._BACKGROUND_COLOUR) self.query_box = Entry(another, width=60) self.query_box.pack(side='left', fill='x', pady=25, anchor='center') self.search_button = Button(another, text='Search', command=self.search, borderwidth=1, highlightthickness=1) self.search_button.pack(side='left', fill='x', pady=25, anchor='center') self.query_box.bind('<KeyPress-Return>', self.search_enter_keypress_handler) another.pack() innerframe.pack(side='top', fill='x', anchor='n') def search_enter_keypress_handler(self, *event): self.search() def _init_results_box(self, parent): innerframe = Frame(parent) i1 = Frame(innerframe) i2 = Frame(innerframe) vscrollbar = Scrollbar(i1, borderwidth=1) hscrollbar = Scrollbar(i2, borderwidth=1, orient='horiz') self.results_box = Text(i1, font=tkinter.font.Font(family='courier', size='16'), state='disabled', borderwidth=1, yscrollcommand=vscrollbar.set, xscrollcommand=hscrollbar.set, wrap='none', width='40', height='20', exportselection=1) self.results_box.pack(side='left', fill='both', expand=True) self.results_box.tag_config(self._HIGHLIGHT_WORD_TAG, foreground=self._HIGHLIGHT_WORD_COLOUR) self.results_box.tag_config(self._HIGHLIGHT_LABEL_TAG, foreground=self._HIGHLIGHT_LABEL_COLOUR) vscrollbar.pack(side='left', fill='y', anchor='e') vscrollbar.config(command=self.results_box.yview) hscrollbar.pack(side='left', fill='x', expand=True, anchor='w') hscrollbar.config(command=self.results_box.xview) #there is no other way of avoiding the overlap of scrollbars while using pack layout manager!!! Label(i2, text=' ', background=self._BACKGROUND_COLOUR).pack(side='left', anchor='e') i1.pack(side='top', fill='both', expand=True, anchor='n') i2.pack(side='bottom', fill='x', anchor='s') innerframe.pack(side='top', fill='both', expand=True) def _init_paging(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) self.prev = prev = Button(innerframe, text='Previous', command=self.previous, width='10', borderwidth=1, highlightthickness=1, state='disabled') prev.pack(side='left', anchor='center') self.next = next = Button(innerframe, text='Next', command=self.__next__, width='10', borderwidth=1, highlightthickness=1, state='disabled') next.pack(side='right', anchor='center') innerframe.pack(side='top', fill='y') self.current_page = 0 def previous(self): self.clear_results_box() self.freeze_editable() self.model.prev(self.current_page - 1) def __next__(self): self.clear_results_box() self.freeze_editable() self.model.next(self.current_page + 1) def about(self, *e): ABOUT = ("NLTK Concordance Search Demo\n") TITLE = 'About: NLTK Concordance Search Demo' try: from tkinter.messagebox import Message Message(message=ABOUT, title=TITLE, parent=self.main_frame).show() except: ShowText(self.top, TITLE, ABOUT) def _bind_event_handlers(self): self.top.bind(CORPUS_LOADED_EVENT, self.handle_corpus_loaded) self.top.bind(SEARCH_TERMINATED_EVENT, self.handle_search_terminated) self.top.bind(SEARCH_ERROR_EVENT, self.handle_search_error) self.top.bind(ERROR_LOADING_CORPUS_EVENT, self.handle_error_loading_corpus) def _poll(self): try: event = self.queue.get(block=False) except q.Empty: pass else: if event == CORPUS_LOADED_EVENT: self.handle_corpus_loaded(event) elif event == SEARCH_TERMINATED_EVENT: self.handle_search_terminated(event) elif event == SEARCH_ERROR_EVENT: self.handle_search_error(event) elif event == ERROR_LOADING_CORPUS_EVENT: self.handle_error_loading_corpus(event) self.after = self.top.after(POLL_INTERVAL, self._poll) def handle_error_loading_corpus(self, event): self.status['text'] = 'Error in loading ' + self.var.get() self.unfreeze_editable() self.clear_all() self.freeze_editable() def handle_corpus_loaded(self, event): self.status['text'] = self.var.get() + ' is loaded' self.unfreeze_editable() self.clear_all() self.query_box.focus_set() def handle_search_terminated(self, event): #todo: refactor the model such that it is less state sensitive results = self.model.get_results() self.write_results(results) self.status['text'] = '' if len(results) == 0: self.status['text'] = 'No results found for ' + self.model.query else: self.current_page = self.model.last_requested_page self.unfreeze_editable() self.results_box.xview_moveto(self._FRACTION_LEFT_TEXT) def handle_search_error(self, event): self.status['text'] = 'Error in query ' + self.model.query self.unfreeze_editable() def corpus_selected(self, *args): new_selection = self.var.get() self.load_corpus(new_selection) def load_corpus(self, selection): if self.model.selected_corpus != selection: self.status['text'] = 'Loading ' + selection + '...' self.freeze_editable() self.model.load_corpus(selection) def search(self): self.current_page = 0 self.clear_results_box() self.model.reset_results() query = self.query_box.get() if (len(query.strip()) == 0): return self.status['text'] = 'Searching for ' + query self.freeze_editable() self.model.search( query, self.current_page + 1, ) def write_results(self, results): self.results_box['state'] = 'normal' row = 1 for each in results: sent, pos1, pos2 = each[0].strip(), each[1], each[2] if len(sent) != 0: if (pos1 < self._char_before): sent, pos1, pos2 = self.pad(sent, pos1, pos2) sentence = sent[pos1 - self._char_before:pos1 + self._char_after] if not row == len(results): sentence += '\n' self.results_box.insert(str(row) + '.0', sentence) word_markers, label_markers = self.words_and_labels( sent, pos1, pos2) for marker in word_markers: self.results_box.tag_add(self._HIGHLIGHT_WORD_TAG, str(row) + '.' + str(marker[0]), str(row) + '.' + str(marker[1])) for marker in label_markers: self.results_box.tag_add(self._HIGHLIGHT_LABEL_TAG, str(row) + '.' + str(marker[0]), str(row) + '.' + str(marker[1])) row += 1 self.results_box['state'] = 'disabled' def words_and_labels(self, sentence, pos1, pos2): search_exp = sentence[pos1:pos2] words, labels = [], [] labeled_words = search_exp.split(' ') index = 0 for each in labeled_words: if each == '': index += 1 else: word, label = each.split('/') words.append((self._char_before + index, self._char_before + index + len(word))) index += len(word) + 1 labels.append((self._char_before + index, self._char_before + index + len(label))) index += len(label) index += 1 return words, labels def pad(self, sent, hstart, hend): if hstart >= self._char_before: return sent, hstart, hend d = self._char_before - hstart sent = ''.join([' '] * d) + sent return sent, hstart + d, hend + d def destroy(self, *e): if self.top is None: return self.top.after_cancel(self.after) self.top.destroy() self.top = None def clear_all(self): self.query_box.delete(0, END) self.model.reset_query() self.clear_results_box() def clear_results_box(self): self.results_box['state'] = 'normal' self.results_box.delete("1.0", END) self.results_box['state'] = 'disabled' def freeze_editable(self): self.query_box['state'] = 'disabled' self.search_button['state'] = 'disabled' self.prev['state'] = 'disabled' self.next['state'] = 'disabled' def unfreeze_editable(self): self.query_box['state'] = 'normal' self.search_button['state'] = 'normal' self.set_paging_button_states() def set_paging_button_states(self): if self.current_page == 0 or self.current_page == 1: self.prev['state'] = 'disabled' else: self.prev['state'] = 'normal' if self.model.has_more_pages(self.current_page): self.next['state'] = 'normal' else: self.next['state'] = 'disabled' def fire_event(self, event): #Firing an event so that rendering of widgets happen in the mainloop thread self.top.event_generate(event, when='tail') def mainloop(self, *args, **kwargs): if in_idle(): return self.top.mainloop(*args, **kwargs)
class PyCube: def __init__(self): self.root = Tk() self.root.title("PyCube") self.session = Session() # init UI self.initMenu() self.leftframe = Frame(self.root) self.leftframe.pack(side=LEFT, fill=BOTH, expand=1) self.rightframe = Frame(self.root) self.rightframe.pack(side=RIGHT, fill=BOTH, expand=1) self.cubesize = 3 scrambler.parse(self.cubesize, 30, False, False) scrambler.scramble() self.scramble = Label(self.leftframe, text=scrambler.scramblestring(0)) self.scramble.pack() self.time_label = Label(self.leftframe, text="0.000") self.time_label.pack() self.plus2_button = Button(self.leftframe, text="+2", command=self.plus2) self.plus2_button.pack() self.dnf_button = Button(self.leftframe, text="DNF", command=self.dnf) self.dnf_button.pack() self.delete_button = Button(self.leftframe, text="Delete", command=self.delete) self.delete_button.pack() self.scramble_img = Label(self.leftframe) self.scramble_img.pack() self.update_image() self.grid = Treeview(self.rightframe) self.grid["columns"] = ("times", "avg5", "avg12", "mean", "sd") self.grid.heading("#0", text='Time', anchor='w') self.grid.column("#0", stretch=NO, width=0, anchor="w") self.grid.heading("times", text="Times") self.grid.column('times', anchor='center', width=70) self.grid.heading("avg5", text="Avg. of 5") self.grid.column('avg5', anchor='center', width=70) self.grid.heading("avg12", text="Avg. of 12") self.grid.column('avg12', anchor='center', width=70) self.grid.heading("mean", text="Session mean") self.grid.column('mean', anchor='center', width=80) self.grid.heading("sd", text="SD") self.grid.column('sd', anchor='center', width=70) self.gridscroll = Scrollbar() self.gridscroll.configure(command=self.grid.yview) self.grid.configure(yscrollcommand=self.gridscroll.set) self.grid.pack(side=TOP) self.root.bind("<KeyRelease-space>", self.start_inspection) self._job = None self.running = False self.root.mainloop() def initMenu(self): menubar = Menu(self.root) self.root.config(menu=menubar) fileMenu = Menu(menubar) fileMenu.add_command(label="New", command=self.session_new) fileMenu.add_separator() fileMenu.add_command(label="Import", command=self.session_import) fileMenu.add_command(label="Export", command=self.session_export) menubar.add_cascade(label="File", menu=fileMenu) def start_timer(self, event=None): if not self.running: self.root.after_cancel(self._job) self._job = None self.t0 = float("%.3f" % time.time()) self.update_timer() self.running = True self.root.bind("<KeyPress-space>", self.reset_timer) def reset_timer(self, event=None): if self.running: self.root.after_cancel(self._job) self._job = None self.running = False self.root.bind("<KeyRelease-space>", self.rebind) t = self.time_label.cget("text") if ":" in str(t): mins, secs = t.split(":") t = (mins * 60) + secs self.session.addtime(t, 0, scrambler.scramblestring(0)) entry = self.session.data[-1][1:] self.grid.insert("", "end", values=(entry)) scrambler.parse(self.cubesize, 30, False, False) scrambler.scramble() scramblestr = scrambler.scramblestring(0) self.scramble.configure(text=scramblestr) self.update_image() def rebind(self, event=None): self.root.bind("<KeyRelease-space>", self.start_inspection) def update_timer(self): now = float("%.3f" % (time.time() - self.t0)) if now > 60: mins = math.floor(now / 60) secs = now - (mins * 60) now = f"{mins}:{secs}" self.time_label.configure(text=now, fg="black") self._job = self.root.after(10, self.update_timer) def start_inspection(self, event=None): self.inspect_time = 16 self.root.bind("<KeyRelease-space>", self.start_timer) self.update_inspection() def update_inspection(self): if self.inspect_time == 0: self.time_label.configure(text="DNF", fg="red") # Really ugly was to add a straight DNF, should fix later # NOTE: Known bug when first time is DNF. MUST FIX self.session.addtime(0, 2, scrambler.scramblestring(0)) entry = self.session.data[-1][1:] self.grid.insert("", "end", values=(entry)) self.dnf() else: self.inspect_time -= 1 self.time_label.configure(text=self.inspect_time, fg="red") self._job = self.root.after(1000, self.update_inspection) def update_image(self): img = ImageTk.PhotoImage(genimage(scrambler.imagestring(0), self.cubesize)) self.scramble_img.configure(image=img) self.scramble_img.image = img def delete(self, event=None): if len(self.session.data) > 0: last = self.session.getlastitemid() self.grid.delete(last) self.session.removetime(last) def plus2(self, event=None): if len(self.session.data) > 0: last = self.session.getlastitemid() index = len(self.session.data) - 1 entry = self.session.data[index] # Check if time isn't already +2 or DNF9 if entry[6] != 0: return entry[1] = float("%.3f" % (entry[1] + 2)) entry[6] = 1 entry = self.session.calcstats() vals = entry[1:] + [scrambler.scramblestring(0)] vals[0] = str(vals[0]) + "(+2)" self.grid.item(last, values=(vals)) def dnf(self, event=None): if len(self.session.data) > 0: last = self.session.getlastitemid() index = len(self.session.data) - 1 entry = self.session.data[index] entry[1] = "DNF" entry[6] = 2 entry = self.session.calcstats() vals = entry[1:] self.grid.item(last, values=(vals)) def session_new(self): self.session.clear() self.grid.delete(*self.grid.get_children()) def session_import(self): f = filedialog.askopenfilename(initialdir="../data/", title="Import session", filetypes=(("Text Documents", "*.txt"), ("All Files", "*.*"))) if f == '': return with open(f) as file: self.session_new() self.session = Session(file.read()) for entry in self.session.data: self.grid.insert("", "end", values=(entry[1:])) def session_export(self): if not os.path.isdir("../data/"): os.makedirs("../data/") name = str(datetime.now())[:-7].replace('-', '').replace(':', '').replace(' ', '') f = filedialog.asksaveasfilename(initialfile=name, initialdir="../data/", defaultextension="*.txt", title="Export session", filetypes=(("Text Documents","*.txt"), ("All Files","*.*"))) if f == '': return with open(f, 'w') as file: file.write(str(self.session))
class ConcordanceSearchView(object): _BACKGROUND_COLOUR = "#FFF" # white # Colour of highlighted results _HIGHLIGHT_WORD_COLOUR = "#F00" # red _HIGHLIGHT_WORD_TAG = "HL_WRD_TAG" _HIGHLIGHT_LABEL_COLOUR = "#C0C0C0" # dark grey _HIGHLIGHT_LABEL_TAG = "HL_LBL_TAG" # Percentage of text left of the scrollbar position _FRACTION_LEFT_TEXT = 0.30 def __init__(self): self.queue = q.Queue() self.model = ConcordanceSearchModel(self.queue) self.top = Tk() self._init_top(self.top) self._init_menubar() self._init_widgets(self.top) self.load_corpus(self.model.DEFAULT_CORPUS) self.after = self.top.after(POLL_INTERVAL, self._poll) def _init_top(self, top): top.geometry("950x680+50+50") top.title("NLTK Concordance Search") top.bind("<Control-q>", self.destroy) top.protocol("WM_DELETE_WINDOW", self.destroy) top.minsize(950, 680) def _init_widgets(self, parent): self.main_frame = Frame( parent, dict(background=self._BACKGROUND_COLOUR, padx=1, pady=1, border=1)) self._init_corpus_select(self.main_frame) self._init_query_box(self.main_frame) self._init_results_box(self.main_frame) self._init_paging(self.main_frame) self._init_status(self.main_frame) self.main_frame.pack(fill="both", expand=True) def _init_menubar(self): self._result_size = IntVar(self.top) self._cntx_bf_len = IntVar(self.top) self._cntx_af_len = IntVar(self.top) menubar = Menu(self.top) filemenu = Menu(menubar, tearoff=0, borderwidth=0) filemenu.add_command(label="Exit", underline=1, command=self.destroy, accelerator="Ctrl-q") menubar.add_cascade(label="File", underline=0, menu=filemenu) editmenu = Menu(menubar, tearoff=0) rescntmenu = Menu(editmenu, tearoff=0) rescntmenu.add_radiobutton( label="20", variable=self._result_size, underline=0, value=20, command=self.set_result_size, ) rescntmenu.add_radiobutton( label="50", variable=self._result_size, underline=0, value=50, command=self.set_result_size, ) rescntmenu.add_radiobutton( label="100", variable=self._result_size, underline=0, value=100, command=self.set_result_size, ) rescntmenu.invoke(1) editmenu.add_cascade(label="Result Count", underline=0, menu=rescntmenu) cntxmenu = Menu(editmenu, tearoff=0) cntxbfmenu = Menu(cntxmenu, tearoff=0) cntxbfmenu.add_radiobutton( label="60 characters", variable=self._cntx_bf_len, underline=0, value=60, command=self.set_cntx_bf_len, ) cntxbfmenu.add_radiobutton( label="80 characters", variable=self._cntx_bf_len, underline=0, value=80, command=self.set_cntx_bf_len, ) cntxbfmenu.add_radiobutton( label="100 characters", variable=self._cntx_bf_len, underline=0, value=100, command=self.set_cntx_bf_len, ) cntxbfmenu.invoke(1) cntxmenu.add_cascade(label="Before", underline=0, menu=cntxbfmenu) cntxafmenu = Menu(cntxmenu, tearoff=0) cntxafmenu.add_radiobutton( label="70 characters", variable=self._cntx_af_len, underline=0, value=70, command=self.set_cntx_af_len, ) cntxafmenu.add_radiobutton( label="90 characters", variable=self._cntx_af_len, underline=0, value=90, command=self.set_cntx_af_len, ) cntxafmenu.add_radiobutton( label="110 characters", variable=self._cntx_af_len, underline=0, value=110, command=self.set_cntx_af_len, ) cntxafmenu.invoke(1) cntxmenu.add_cascade(label="After", underline=0, menu=cntxafmenu) editmenu.add_cascade(label="Context", underline=0, menu=cntxmenu) menubar.add_cascade(label="Edit", underline=0, menu=editmenu) self.top.config(menu=menubar) def set_result_size(self, **kwargs): self.model.result_count = self._result_size.get() def set_cntx_af_len(self, **kwargs): self._char_after = self._cntx_af_len.get() def set_cntx_bf_len(self, **kwargs): self._char_before = self._cntx_bf_len.get() def _init_corpus_select(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) self.var = StringVar(innerframe) self.var.set(self.model.DEFAULT_CORPUS) Label( innerframe, justify=LEFT, text=" Corpus: ", background=self._BACKGROUND_COLOUR, padx=2, pady=1, border=0, ).pack(side="left") other_corpora = list(self.model.CORPORA.keys()).remove( self.model.DEFAULT_CORPUS) om = OptionMenu(innerframe, self.var, self.model.DEFAULT_CORPUS, command=self.corpus_selected, *self.model.non_default_corpora()) om["borderwidth"] = 0 om["highlightthickness"] = 1 om.pack(side="left") innerframe.pack(side="top", fill="x", anchor="n") def _init_status(self, parent): self.status = Label( parent, justify=LEFT, relief=SUNKEN, background=self._BACKGROUND_COLOUR, border=0, padx=1, pady=0, ) self.status.pack(side="top", anchor="sw") def _init_query_box(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) another = Frame(innerframe, background=self._BACKGROUND_COLOUR) self.query_box = Entry(another, width=60) self.query_box.pack(side="left", fill="x", pady=25, anchor="center") self.search_button = Button( another, text="Search", command=self.search, borderwidth=1, highlightthickness=1, ) self.search_button.pack(side="left", fill="x", pady=25, anchor="center") self.query_box.bind("<KeyPress-Return>", self.search_enter_keypress_handler) another.pack() innerframe.pack(side="top", fill="x", anchor="n") def search_enter_keypress_handler(self, *event): self.search() def _init_results_box(self, parent): innerframe = Frame(parent) i1 = Frame(innerframe) i2 = Frame(innerframe) vscrollbar = Scrollbar(i1, borderwidth=1) hscrollbar = Scrollbar(i2, borderwidth=1, orient="horiz") self.results_box = Text( i1, font=Font(family="courier", size="16"), state="disabled", borderwidth=1, yscrollcommand=vscrollbar.set, xscrollcommand=hscrollbar.set, wrap="none", width="40", height="20", exportselection=1, ) self.results_box.pack(side="left", fill="both", expand=True) self.results_box.tag_config(self._HIGHLIGHT_WORD_TAG, foreground=self._HIGHLIGHT_WORD_COLOUR) self.results_box.tag_config(self._HIGHLIGHT_LABEL_TAG, foreground=self._HIGHLIGHT_LABEL_COLOUR) vscrollbar.pack(side="left", fill="y", anchor="e") vscrollbar.config(command=self.results_box.yview) hscrollbar.pack(side="left", fill="x", expand=True, anchor="w") hscrollbar.config(command=self.results_box.xview) # there is no other way of avoiding the overlap of scrollbars while using pack layout manager!!! Label(i2, text=" ", background=self._BACKGROUND_COLOUR).pack(side="left", anchor="e") i1.pack(side="top", fill="both", expand=True, anchor="n") i2.pack(side="bottom", fill="x", anchor="s") innerframe.pack(side="top", fill="both", expand=True) def _init_paging(self, parent): innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) self.prev = prev = Button( innerframe, text="Previous", command=self.previous, width="10", borderwidth=1, highlightthickness=1, state="disabled", ) prev.pack(side="left", anchor="center") self.next = next = Button( innerframe, text="Next", command=self.__next__, width="10", borderwidth=1, highlightthickness=1, state="disabled", ) next.pack(side="right", anchor="center") innerframe.pack(side="top", fill="y") self.current_page = 0 def previous(self): self.clear_results_box() self.freeze_editable() self.model.prev(self.current_page - 1) def __next__(self): self.clear_results_box() self.freeze_editable() self.model.next(self.current_page + 1) def about(self, *e): ABOUT = "NLTK Concordance Search Demo\n" TITLE = "About: NLTK Concordance Search Demo" try: from tkinter.messagebox import Message Message(message=ABOUT, title=TITLE, parent=self.main_frame).show() except: ShowText(self.top, TITLE, ABOUT) def _bind_event_handlers(self): self.top.bind(CORPUS_LOADED_EVENT, self.handle_corpus_loaded) self.top.bind(SEARCH_TERMINATED_EVENT, self.handle_search_terminated) self.top.bind(SEARCH_ERROR_EVENT, self.handle_search_error) self.top.bind(ERROR_LOADING_CORPUS_EVENT, self.handle_error_loading_corpus) def _poll(self): try: event = self.queue.get(block=False) except q.Empty: pass else: if event == CORPUS_LOADED_EVENT: self.handle_corpus_loaded(event) elif event == SEARCH_TERMINATED_EVENT: self.handle_search_terminated(event) elif event == SEARCH_ERROR_EVENT: self.handle_search_error(event) elif event == ERROR_LOADING_CORPUS_EVENT: self.handle_error_loading_corpus(event) self.after = self.top.after(POLL_INTERVAL, self._poll) def handle_error_loading_corpus(self, event): self.status["text"] = "Error in loading " + self.var.get() self.unfreeze_editable() self.clear_all() self.freeze_editable() def handle_corpus_loaded(self, event): self.status["text"] = self.var.get() + " is loaded" self.unfreeze_editable() self.clear_all() self.query_box.focus_set() def handle_search_terminated(self, event): # todo: refactor the model such that it is less state sensitive results = self.model.get_results() self.write_results(results) self.status["text"] = "" if len(results) == 0: self.status["text"] = "No results found for " + self.model.query else: self.current_page = self.model.last_requested_page self.unfreeze_editable() self.results_box.xview_moveto(self._FRACTION_LEFT_TEXT) def handle_search_error(self, event): self.status["text"] = "Error in query " + self.model.query self.unfreeze_editable() def corpus_selected(self, *args): new_selection = self.var.get() self.load_corpus(new_selection) def load_corpus(self, selection): if self.model.selected_corpus != selection: self.status["text"] = "Loading " + selection + "..." self.freeze_editable() self.model.load_corpus(selection) def search(self): self.current_page = 0 self.clear_results_box() self.model.reset_results() query = self.query_box.get() if len(query.strip()) == 0: return self.status["text"] = "Searching for " + query self.freeze_editable() self.model.search(query, self.current_page + 1) def write_results(self, results): self.results_box["state"] = "normal" row = 1 for each in results: sent, pos1, pos2 = each[0].strip(), each[1], each[2] if len(sent) != 0: if pos1 < self._char_before: sent, pos1, pos2 = self.pad(sent, pos1, pos2) sentence = sent[pos1 - self._char_before:pos1 + self._char_after] if not row == len(results): sentence += "\n" self.results_box.insert(str(row) + ".0", sentence) word_markers, label_markers = self.words_and_labels( sent, pos1, pos2) for marker in word_markers: self.results_box.tag_add( self._HIGHLIGHT_WORD_TAG, str(row) + "." + str(marker[0]), str(row) + "." + str(marker[1]), ) for marker in label_markers: self.results_box.tag_add( self._HIGHLIGHT_LABEL_TAG, str(row) + "." + str(marker[0]), str(row) + "." + str(marker[1]), ) row += 1 self.results_box["state"] = "disabled" def words_and_labels(self, sentence, pos1, pos2): search_exp = sentence[pos1:pos2] words, labels = [], [] labeled_words = search_exp.split(" ") index = 0 for each in labeled_words: if each == "": index += 1 else: word, label = each.split("/") words.append((self._char_before + index, self._char_before + index + len(word))) index += len(word) + 1 labels.append((self._char_before + index, self._char_before + index + len(label))) index += len(label) index += 1 return words, labels def pad(self, sent, hstart, hend): if hstart >= self._char_before: return sent, hstart, hend d = self._char_before - hstart sent = "".join([" "] * d) + sent return sent, hstart + d, hend + d def destroy(self, *e): if self.top is None: return self.top.after_cancel(self.after) self.top.destroy() self.top = None def clear_all(self): self.query_box.delete(0, END) self.model.reset_query() self.clear_results_box() def clear_results_box(self): self.results_box["state"] = "normal" self.results_box.delete("1.0", END) self.results_box["state"] = "disabled" def freeze_editable(self): self.query_box["state"] = "disabled" self.search_button["state"] = "disabled" self.prev["state"] = "disabled" self.next["state"] = "disabled" def unfreeze_editable(self): self.query_box["state"] = "normal" self.search_button["state"] = "normal" self.set_paging_button_states() def set_paging_button_states(self): if self.current_page == 0 or self.current_page == 1: self.prev["state"] = "disabled" else: self.prev["state"] = "normal" if self.model.has_more_pages(self.current_page): self.next["state"] = "normal" else: self.next["state"] = "disabled" def fire_event(self, event): # Firing an event so that rendering of widgets happen in the mainloop thread self.top.event_generate(event, when="tail") def mainloop(self, *args, **kwargs): if in_idle(): return self.top.mainloop(*args, **kwargs)
class Game: def __init__(self, data, best_score, q_table, score, missed, ai_best_score, ai_trained_time): self.window = Tk() self.frame = Frame(self.window) self.canvas = Canvas(self.frame) self.state = "gameRunning" self.run_type = data self.ai_q_table = q_table self.ai_score = score self.ai_missed = missed self.ai_best = ai_best_score self.play_speed = 10 self.train_speed = 1000000000 self.ai_trained_time = ai_trained_time self.ai_timer = time() # AI Observer self.ai_observer_key = "" # AI Observer print("") print("------------------------------") if self.run_type == "playTheGame": print("Game Created for player to play.") elif self.run_type == "trainTheAI": print("Game Created for AI to train.") print("---------------") self.X, self.Y = (10, 10) ''' X, Y --> Count of blocks in window. ''' self.BLOCK_SIZE = 75 ''' BLOCK_SIZE --> Size of one block. ''' self.BACKGROUND_COLOR = '#303030' self.SNAKE_COLOR = '#0f0' self.SNAKE_HEAD_COLOR = '#003300' self.FOOD_COLOR = '#f00' self.INITIAL_SPEED = 10 self.BEST_SCORE = best_score self.PAUSED = 0 self.KEY_LEFT = 'Left' self.KEY_RIGHT = 'Right' self.KEY_UP = 'Up' self.KEY_DOWN = 'Down' self.KEY_QUIT = 'q' self.KEY_PAUSE = 'p' self.KEY_SPEED = 's' self.VALID_DIRECTIONS = { self.KEY_LEFT: {self.KEY_LEFT, self.KEY_UP, self.KEY_DOWN}, self.KEY_RIGHT: {self.KEY_RIGHT, self.KEY_UP, self.KEY_DOWN}, self.KEY_UP: {self.KEY_UP, self.KEY_LEFT, self.KEY_RIGHT}, self.KEY_DOWN: {self.KEY_DOWN, self.KEY_LEFT, self.KEY_RIGHT} } self.MOVEMENTS = { self.KEY_LEFT: lambda x, y: (x - 1, y), self.KEY_RIGHT: lambda x, y: (x + 1, y), self.KEY_UP: lambda x, y: (x, y - 1), self.KEY_DOWN: lambda x, y: (x, y + 1) } self.INITIAL_SPEED = self.play_speed self.game = self.create_game() self.snake_ai = None self.avg_score = 0 self.ai_current_score = 0 self.ai_gen = 1 self.reward = 0 self.tickID = None self.main() def on_closing(self): if self.ai_current_score > 0: self.snake_ai.score -= self.ai_current_score self.window.after_cancel(self.tickID) self.window.destroy() self.state = "gameClosed" self.snake_ai.save_informations() del self.snake_ai def __del__(self): if self.run_type == "playTheGame": print("---------------") print("Game Closed.") print("------------------------------") print("") def save_informations(self): pass def setup_game(self): self.window.focus_force() window_width, window_height = (self.X * self.BLOCK_SIZE, self.Y * self.BLOCK_SIZE) ''' window_width, window_height --> Width and height of window. ''' screen_width, screen_height = (self.window.winfo_screenwidth(), self.window.winfo_screenheight()) ''' screen_width, screen_height --> Width and height of screen. ''' x_coordinate, y_coordinate = (int((screen_width / 2) - (window_width / 2)), int((screen_height / 2) - (window_height / 2))) ''' x_coordinate, y_coordinate --> The starting points of window. ''' self.window.geometry("{}x{}+{}+{}".format(window_width, window_height, x_coordinate, y_coordinate)) self.window.resizable(False, False) self.frame.master.title( "Snake Game | SCORE : 0 | Best Score : 0 | Best AI Score : 0 " ) self.frame.pack(fill=BOTH, expand=1) self.canvas.pack(fill=BOTH, expand=1) def create_game(self): snake_randX = randint(0, self.X - 1) snake_randY = randint(0, self.Y - 1) direction_selector = randint(0, len(self.VALID_DIRECTIONS) - 1) food_randX = randint(0, self.X - 1) food_randY = randint(0, self.Y - 1) while (food_randX, food_randY) == (snake_randX, snake_randY): food_randX = randint(0, self.X - 1) food_randY = randint(0, self.Y - 1) # AI Observer self.ai_observer_key = list( self.VALID_DIRECTIONS.keys())[direction_selector] # AI Observer return { 'snake': deque(((snake_randX, snake_randY), (snake_randX, snake_randY))), 'food': (food_randX, food_randY), 'direction': list(self.VALID_DIRECTIONS.keys())[direction_selector], 'moves': deque(), 'points': 0, 'speed': self.INITIAL_SPEED } def reset(self): current_speed = self.game['speed'] self.game = self.create_game() self.game['speed'] = current_speed def message_box(self, subject, content): root = Tk() root.attributes("-topmost", True) root.withdraw() messagebox.showinfo(subject, content) try: root.destroy() except: pass def draw_rect(self, x, y, color=None): if color is None: color = self.SNAKE_COLOR x1 = x * self.BLOCK_SIZE y1 = y * self.BLOCK_SIZE x2 = x1 + self.BLOCK_SIZE y2 = y1 + self.BLOCK_SIZE return self.canvas.create_rectangle(x1, y1, x2, y2, outline='', fill=color) def render(self): self.canvas.delete('all') self.canvas.configure(background=self.BACKGROUND_COLOR) for i in range((len(self.game['snake']) - 1), 0, -1): if i == (len(self.game['snake']) - 1): self.draw_rect(self.game['snake'][i][0], self.game['snake'][i][1], self.SNAKE_HEAD_COLOR) else: self.draw_rect(self.game['snake'][i][0], self.game['snake'][i][1]) x, y = self.game['food'] self.draw_rect(x, y, color=self.FOOD_COLOR) def gen_food(self, snake): while True: food = randint(0, self.X - 1), randint(0, self.Y - 1) if food not in snake: return food def eat(self, snake): self.game['food'] = self.gen_food(snake) self.game['points'] += 1 if self.run_type == "trainTheAI": self.reward = 1 self.ai_current_score += 1 if self.ai_current_score > self.ai_best: self.ai_best = self.ai_current_score if self.run_type == "playTheGame": if self.game['points'] > self.BEST_SCORE: self.BEST_SCORE = self.game['points'] # AI Observer self.reward = 1 # AI Observer def move_snake(self, direction): snake = set(self.game['snake']) u, w = self.game['snake'][-1] next_point = self.MOVEMENTS[direction](u, w) x, y = next_point if x < 0: x = x + self.X if x >= self.X: x = x - self.X if y < 0: y = y + self.Y if y >= self.Y: y = y - self.Y next_point = x, y if next_point == self.game['food'] or self.game['snake'][ -1] == self.game['food']: self.eat(snake) else: self.game['snake'].popleft() self.reward = -0.1 self.frame.master.title("Snake Game | SCORE : " + str(self.game['points']) + " | Best Score : " + str(self.BEST_SCORE) + " | Best AI Score : " + str(self.ai_best)) in_snake = 0 if len(self.game['snake']) > 1: for i in range(1, len(self.game['snake'])): if next_point == self.game['snake'][i]: in_snake = 1 if in_snake == 1: if self.run_type == "playTheGame": self.message_box('You Lost!', 'Play again...') print("Player Score: " + str(self.game['points']) + " | Best Player Score: " + str(self.BEST_SCORE)) # AI Observer self.reward = -1 # AI Observer if self.run_type == "trainTheAI": if self.snake_ai.missed != 0: self.ai_gen = abs(self.snake_ai.missed) + 1 if self.ai_current_score != 0 and self.ai_gen != 0: self.avg_score = self.snake_ai.score / self.ai_gen ai_training_time = time() - self.ai_timer self.ai_trained_time = ai_training_time ai_training_time_seconds = int( (ai_training_time % 3600) % 60) ai_training_time_minutes = int(( (ai_training_time - ai_training_time_seconds) % 3600) / 60) ai_training_time_hours = int( (ai_training_time - 60 * ai_training_time_minutes - ai_training_time_seconds) / 3600) hours = str(ai_training_time_hours) if ai_training_time_hours < 10: hours = "0" + str(ai_training_time_hours) minutes = str(ai_training_time_minutes) if ai_training_time_minutes < 10: minutes = "0" + str(ai_training_time_minutes) seconds = str(ai_training_time_seconds) if ai_training_time_seconds < 10: seconds = "0" + str(ai_training_time_seconds) fixed_ai_gen = "" while len(fixed_ai_gen) < 7 - len(str(self.ai_gen)): fixed_ai_gen += "0" fixed_ai_gen += str(self.ai_gen) fixed_ai_current_score = "" while len(fixed_ai_current_score) < 7 - len( str(self.ai_current_score)): fixed_ai_current_score += "0" fixed_ai_current_score += str(self.ai_current_score) fixed_ai_best = "" while len(fixed_ai_best) < 7 - len(str(self.ai_best)): fixed_ai_best += "0" fixed_ai_best += str(self.ai_best) fixed_avg_score = str(self.avg_score) if len(fixed_avg_score) > 7: fixed_avg_score = str(self.avg_score)[:7] else: while len(fixed_avg_score) < 7: fixed_avg_score += "0" print("AI Training Time: " + hours + ":" + minutes + ":" + seconds + " | AI Generation: " + fixed_ai_gen + " | AI Current Score: " + fixed_ai_current_score + " | AI Best Score: " + fixed_ai_best + " | AI Average Score: " + fixed_avg_score) self.reward = -1 self.ai_current_score = 0 self.reset() else: self.game['snake'].append(next_point) else: self.game['snake'].append(next_point) def handle_next_movement(self): direction = self.game['moves'].popleft( ) if self.game['moves'] else self.game['direction'] self.game['direction'] = direction self.move_snake(direction) def on_press(self, event): key = event.keysym if self.PAUSED == 0 and self.run_type == "playTheGame": prev_direction = self.game['moves'][-1] if self.game[ 'moves'] else self.game['direction'] if key in self.VALID_DIRECTIONS[prev_direction]: self.game['moves'].append(key) # AI Observer self.ai_observer_key = key # AI Observer if self.run_type == "trainTheAI": if key.lower() == self.KEY_SPEED: if self.game['speed'] == self.play_speed: self.game['speed'] = self.train_speed else: self.game['speed'] = self.play_speed if key.lower() == self.KEY_QUIT: self.on_closing() elif key.lower() == self.KEY_PAUSE: if self.PAUSED == 0: self.PAUSED = 1 self.frame.master.title("Snake Game | PAUSED.") else: self.PAUSED = 0 self.frame.master.title("Snake Game | SCORE : " + str(self.game['points']) + " | Best Score : " + str(self.BEST_SCORE) + " | Best AI Score : " + str(self.ai_best)) def tick(self): if self.PAUSED == 0: if self.run_type == "playTheGame": # AI Observer currentState = self.snake_ai.whichStateNow() key = self.ai_observer_key # AI Observer self.handle_next_movement() self.render() # AI Observer instantReward = self.reward nextState = self.snake_ai.whichStateNow() if key == "Left" or key == "Right" or key == "Up" or key == "Down": self.snake_ai.updateQTable(currentState, nextState, instantReward, key) # AI Observer elif self.run_type == "trainTheAI": self.Algorithm() self.tickID = self.window.after(int(1000 / self.game['speed']), self.tick) def Algorithm(self): currentState = self.snake_ai.whichStateNow() key = self.snake_ai.bestAction(currentState) prev_direction = self.game['moves'][-1] if self.game[ 'moves'] else self.game['direction'] if key in self.VALID_DIRECTIONS[prev_direction]: self.game['moves'].append(key) self.handle_next_movement() self.render() instantReward = self.reward nextState = self.snake_ai.whichStateNow() self.snake_ai.updateQTable(currentState, nextState, instantReward, key) if instantReward > 0: self.snake_ai.score += trunc(instantReward) if instantReward < 0: self.snake_ai.missed += trunc(instantReward) def main(self): self.setup_game() if self.run_type == "trainTheAI": self.snake_ai = AI(self) self.snake_ai.qTable = self.ai_q_table self.snake_ai.score = self.ai_score self.snake_ai.missed = self.ai_missed self.ai_timer = time() - self.ai_trained_time else: self.snake_ai = AI(self) self.snake_ai.qTable = self.ai_q_table self.tick() self.window.bind('<Key>', self.on_press) self.window.protocol("WM_DELETE_WINDOW", self.on_closing) self.window.mainloop()