class Main(): """ Main entrypoint for the GUI. Class handles core logic and GUI elements. """ def __init__(self): self.label = 0 self.question_num = 1 self.filename = "temp.png" self.updateloop = False self.DEBUG = False self.DEBUG_LIST = [] self.queue = queue.Queue() self.player_manager = None self.gameType = None # string describing if game is spelling or not self.table_built = False def search(self, question, answers, question_num, data_obj=None): """ Takes question as string and answers as list of strings. performs forward search. This is a good place to put checks on question string """ if " not " in question.lower(): not_question = True else: not_question = False self.score_obj = Scorer(question, answers, not_question, question_num, self.mainlbl) if data_obj: self.score_obj.set_data_obj(data_obj) # signal all players that the question has started, we need a data object to initiate this properly if self.player_manager: self.player_manager.question_start(self.score_obj) if " not " in question.lower(): not_question = True birth_keywords = [ " born", " birth", "young" ] # "old" but don't want to interfere with date of death" if any(x in question.lower() for x in birth_keywords): self.dox_search("date of birth", "") death_keywords = ["die", "died", "death"] if any(x in question.lower() for x in death_keywords): self.dox_search("date of death", "") release_keywords = [ "released", "written", "sung", "sang", "published", "written" ] if any(x in question.lower() for x in release_keywords): self.dox_search("", "release date") self.forward_search(self.score_obj) def forward_search(self, score_obj): """ Search by googling question and looking for answers in results """ google_results = GoogleScrape().search( sanitise_text(score_obj.get_question())) score_obj.score_titles(google_results[1]) score_obj.score_descriptions(google_results[2]) score_obj.set_snippet(google_results[3]) self.updateloop = True threading.Thread( target=(lambda: self.scoreUpdateLoop(score_obj))).start() AsyncSearcher(google_results, score_obj) self.updateloop = False self.mainlbl["bg"] = "lightgreen" def scoreUpdateLoop(self, score_obj): while True: scores = score_obj.get_scores() google_scores = score_obj.get_google_scores() answers = score_obj.get_answers() self.answer1scorelbl[ "text"] = f"{round(scores[0],1)} ({google_scores[0]})" self.answer2scorelbl[ "text"] = f"{round(scores[1],1)} ({google_scores[1]})" self.answer3scorelbl[ "text"] = f"{round(scores[2],1)} ({google_scores[2]})" key_sentences = score_obj.get_key_sentences() s = "" for key in key_sentences: list_string = "" for el in key_sentences[key]: list_string += el s += (f"{key.title()}: {list_string}") + "\n\n" self.keySentenceslbl["text"] = s self.answer1whichlbl["text"] = "| " + html.unescape( score_obj.get_which_results()[0]) self.answer2whichlbl["text"] = "| " + html.unescape( score_obj.get_which_results()[1]) self.answer3whichlbl["text"] = "| " + html.unescape( score_obj.get_which_results()[2]) self.googleSentenceslbl["text"] = score_obj.get_snippet() table = score_obj.get_table() if table and not self.table_built: self.build_table(table) self.table_built = True if self.module1Active: s = "" for i in range(len(self.module1Results)): s += f"{answers[i]}: {self.module1Results[i]}\n" self.module1lbl["text"] = s else: self.module1lbl["text"] = "" if self.updateloop == False: break time.sleep(0.5) def build_table(self, table): max_col_width = 200 headers = table.get("headers")[0] # print(f"Headers: {headers}") rows = table.get("rows") # print(f"Rows: {rows}") self.tree["columns"] = headers # name the columns for col in headers: self.tree.heading(col, text=col.title(), command=lambda c=col: self.sort_table( self.tree, c, 0)) # add command for sort self.tree.column(col, width=tkFont.Font().measure(col.title())) for row in rows: self.tree.insert('', 'end', values=row) for i, val in enumerate(row): col_width = tkFont.Font().measure(val) if col_width < max_col_width: if self.tree.column(headers[i], width=None) < col_width: self.tree.column(headers[i], width=col_width) else: self.tree.column(headers[i], width=max_col_width) # ignored scaling here from example https://www.daniweb.com/programming/software-development/threads/350266/creating-table-in-python def sort_table(self, tree, col, descending): data = [(tree.set(child, col), child) for child in tree.get_children('')] data.sort(reverse=descending) for ix, item in enumerate(data): tree.move(item[1], '', ix) # switch the heading so it will sort in the opposite direction tree.heading(col, command=lambda col=col: self.sort_table( tree, col, int(not descending))) def show_image(self, filename): pil_img = Image.open(self.filename) [imageSizeWidth, imageSizeHeight] = pil_img.size scaling_factor = 0.35 newImageSizeWidth = int(imageSizeWidth * scaling_factor) newImageSizeHeight = int(imageSizeHeight * scaling_factor) pil_img = pil_img.resize((newImageSizeWidth, newImageSizeHeight), Image.ANTIALIAS) self.img = ImageTk.PhotoImage(pil_img) width = self.imglbl.winfo_width() height = self.imglbl.winfo_height() self.imglbl.create_image(width / 2, height / 2, anchor=CENTER, image=self.img) def run_vision_api_thread(self): self.adb_screencap(self.filename) self.show_image(self.filename) img_annotations = GoogleVisionAPI.image_to_tags(self.filename) self.imgtextlbl["text"] = img_annotations self.infolbl["text"] = "Ready." def trait_search(self, input_list): # input_list = [query, prestring, poststring, index_to_return] if len(input_list) == 4: if input_list[3] == -1: return GoogleScrape().search( f"{input_list[1]} {input_list[0]} {input_list[2]}", number_of_results=10) else: return GoogleScrape().search( f"{input_list[1]} {input_list[0]} {input_list[2]}", number_of_results=1)[input_list[3]] else: return GoogleScrape().search( f"{input_list[1]} {input_list[0]} {input_list[2]}", number_of_results=1)[4] def run_spellchecker(self, question=None, answers=None, data_obj=None): self.clear_all_text() if question and answers: print(f"gui: SpellChecker: We have question and answers") # all good, we have question and answers else: self.adb_screencap(self.filename) question, answers = ScreenProcessor.process_file(self.filename) self.score_obj = Scorer( question, answers, False, None, self.mainlbl) # untested 22/03/19 to allow answer input if data_obj: self.score_obj.set_data_obj(data_obj) # question = " .. ." # answers = ["a: massachusetts", "b: massachusettss", "0: mssachusetts"] self.answer1lbl["text"] = answers[0] + ": " self.answer2lbl["text"] = answers[1] + ": " self.answer3lbl["text"] = answers[2] + ": " in_list = [] for answer in answers: index = answer.find(":") in_list.append([answer[index + 1:].strip(), "define", "", -1]) pool = ThreadPool(3) pool_results = pool.map(self.trait_search, in_list) pool.close() pool.join() scores = [0, 0, 0] for result in pool_results: for i, answer in enumerate(answers): for title in result[1]: # titles if title.lower().find(answer[answer.find(':') + 1:].strip( ).lower() + " ") != -1 or title.lower().find( " " + answer[answer.find(':') + 1:].strip().lower()) != -1: scores[i] += 1 # print(f"True {answer}, {title}") for desc in result[2]: # descriptions if desc.lower().find(answer[answer.find(':') + 1:].strip( ).lower() + " ") != -1 or desc.lower().find( " " + answer[answer.find(':') + 1:].strip().lower()) != -1: scores[i] += 1 # print("added") else: pass try: self.answer1whichlbl["text"] = scores[0] self.answer2whichlbl["text"] = scores[1] self.answer3whichlbl["text"] = scores[2] except IndexError: self.infolbl[ "text"] = "Spell checking module: list out of bound. This shouldn't happen" print( "Spell checking module: list out of bound. This shouldn't happen" ) return self.infolbl["text"] = "Ready." def dox_search(self, pre, post): def internal_thread(pre, post, score_obj): in_list = [] prestring = pre poststring = post for answer in score_obj.get_answers(): in_list.append([answer, prestring, poststring]) # score google results pool = ThreadPool(3) self.module1Results = pool.map(self.trait_search, in_list) self.module1Active = True pool.close() pool.join() self.scoreUpdateLoop(self.score_obj) self.infolbl["text"] = f"Running {pre} {post} search..." threading.Thread(target=( lambda: internal_thread(pre, post, self.score_obj))).start() def keyEvent(self, event): def internal_thread(pool): pool.close() pool.join() self.scoreUpdateLoop(self.score_obj) print("Deactivated module 1") try: if event.char == 'o': # showImg threading.Thread(target=self.show_image, args=(self.filename, )).start() if event.char == "i": # visionAPI self.infolbl["text"] = "Running Vision API..." threading.Thread(target=self.run_vision_api_thread).start() if event.char == "c": # clear canvas self.imglbl.delete("all") self.imgtextlbl["text"] = " " if event.char == "b": # dob self.dox_search("date of birth", "") if event.char == "r": self.dox_search("", "release date") if event.char == "d": self.dox_search("date of death", "") if event.char == "s": # check spelling self.infolbl["text"] = "Running spell check module..." threading.Thread( target=(lambda: self.run_spellchecker())).start() if event.char == "l": url = "https://www.google.co.uk/maps/dir/" + "/".join( answer for answer in self.score_obj.get_answers()) print(url) webbrowser.open(url) if event.char == "1": # one, not 'l' self.player_manager.manually_send_answers(self.score_obj, 0) if event.char == "2": # one, not 'l' self.player_manager.manually_send_answers(self.score_obj, 1) if event.char == "3": # one, not 'l' self.player_manager.manually_send_answers(self.score_obj, 2) if event.char == "4": self.player_manager.manually_send_answers(self.score_obj, -1) except Exception as e: print(e) self.infolbl[ "text"] = f"Encountered a problem executing that key <{event.char}>" def enterEvent(self, event): self.infolbl["text"] = "Running..." self.clear_all_text() self.mainlbl["bg"] = self.defaultbg def enterThread(): try: if not self.DEBUG: self.infolbl["text"] = "Grabbing screenshot via ADB..." self.adb_screencap(self.filename) self.infolbl["text"] = "Processing image..." question, answers = ScreenProcessor.process_file( self.filename) else: self.infolbl["text"] = "Debug mode" question, answers = self.DEBUG_LIST[0], self.DEBUG_LIST[1] # make question one line question = question.replace("\n", " ") # fix dates p = re.compile(r"([0-9]{4})5") question = (re.sub(p, "\g<1>s", question)) # print(question) # for a in answers: # print(a) self.mainlbl["text"] = question self.mainlbl["bg"] = "orange" if len(answers) == 3: self.answer1lbl["text"] = answers[0] + ": " self.answer2lbl["text"] = answers[1] + ": " self.answer3lbl["text"] = answers[2] + ": " # time.sleep(1) self.infolbl["text"] = "Searching..." self.search(question, answers, self.question_num) self.infolbl["text"] = "Ready." self.question_num += 1 except SyntaxError as e: print(e.msg) time.sleep(0.25) except Exception as e: self.infolbl[ "text"] = "Error proccessing screenshot. Message: " + str( e) threading.Thread(target=enterThread).start() class WrappingLabel(Label): '''a type of Label that automatically adjusts the wrap to the size''' def __init__(self, master=None, **kwargs): Label.__init__(self, master, **kwargs) self.bind('<Configure>', lambda e: self.config(wraplength=self.winfo_width())) def clear_all_text(self): newText = "" self.answer1whichlbl["text"] = newText self.answer2whichlbl["text"] = newText self.answer3whichlbl["text"] = newText self.answer1lbl["text"] = newText self.answer2lbl["text"] = newText self.answer3lbl["text"] = newText self.answer1scorelbl["text"] = newText self.answer2scorelbl["text"] = newText self.answer3scorelbl["text"] = newText self.keySentenceslbl["text"] = "Key Sentences here" self.googleSentenceslbl["text"] = "Google Snippets here" self.mainlbl["text"] = newText self.module1lbl["text"] = "0" self.module1Active = False self.table_built = False for i in self.tree.get_children(): self.tree.delete(i) def run(self): window = Tk() window.title("QLive bot") window.geometry('700x500') # window.attributes('-fullscreen', True) window.state('zoomed') frame1 = Frame(relief=SOLID, bd=2) frame2 = Frame(relief=RAISED) frame3 = Frame() answerframe = Frame(frame3) # frame1.pack(fill=X) frame3.grid(column=0, row=0, sticky=(N, S, E, W)) frame1.grid(column=1, row=0, sticky=(N, S, E, W)) frame2.grid(column=0, row=1, sticky=(N, S, E, W), columnspan=2) window.grid_columnconfigure(0, weight=2, uniform="foo") window.grid_columnconfigure(1, weight=1, uniform="foo") window.grid_rowconfigure(0, weight=50) window.grid_rowconfigure(1, weight=1) self.imglbl = Canvas(frame1) self.imglbl.grid(column=0, row=0, sticky=(N, S, E, W)) self.imgtextlbl = Label(frame1, text=" ", anchor=NW, justify=LEFT, wraplength=600) self.imgtextlbl.grid(column=0, row=1, sticky=(N, S, E, W)) frame1.grid_rowconfigure(0, weight=5) frame1.grid_rowconfigure(1, weight=1) frame1.grid_columnconfigure(0, weight=1) answerfont = ("Helvetica", 12) scorefont = ("Helvetica", 16) self.mainlbl = Label( frame3, justify=LEFT, anchor=W, text= "Welcome to QLive bot. <Enter> to scan for question, <i> to run vision api. <s> to show previous image." ) self.mainlbl.pack(fill=X) answerframe.pack(fill=X) self.answer1lbl = Label(answerframe, justify=LEFT, text="answer 1:", font=answerfont) self.answer2lbl = Label(answerframe, justify=LEFT, text="answer 2:", font=answerfont) self.answer3lbl = Label(answerframe, justify=LEFT, text="answer 3:", font=answerfont) self.answer1lbl.grid(column=0, row=1, sticky="w") self.answer2lbl.grid(column=0, row=2, sticky="w") self.answer3lbl.grid(column=0, row=3, sticky="w") self.answer1scorelbl = Label(answerframe, justify=LEFT, text="10 (99)", width=10, font=scorefont) self.answer2scorelbl = Label(answerframe, justify=LEFT, text="10 (99)", width=10, font=scorefont) self.answer3scorelbl = Label(answerframe, justify=LEFT, text="10 (99)", width=10, font=scorefont) self.answer1scorelbl.grid(column=1, row=1, sticky="w") self.answer2scorelbl.grid(column=1, row=2, sticky="w") self.answer3scorelbl.grid(column=1, row=3, sticky="w") self.answer1whichlbl = Label(answerframe, justify=LEFT, text="| Interesting title 1") self.answer2whichlbl = Label(answerframe, justify=LEFT, text="| Interesting title 2") self.answer3whichlbl = Label(answerframe, justify=LEFT, text="| Interesting title 3") self.answer1whichlbl.grid(column=2, row=1, sticky="w") self.answer2whichlbl.grid(column=2, row=2, sticky="w") self.answer3whichlbl.grid(column=2, row=3, sticky="w") framepadx = 5 framepady = 5 googlesnippetframe = Frame(frame3, relief=SOLID, bd=2) googlesnippetframe.pack(fill=X, padx=framepadx, pady=(10, 0)) self.googleSentenceslbl = self.WrappingLabel( googlesnippetframe, anchor=NW, justify=LEFT, text="Google snippet box" * 20) self.googleSentenceslbl.pack(fill=X) keysentencesframe = Frame(frame3, relief=SOLID, bd=2, height=250) keysentencesframe.pack(fill=X, padx=framepadx, pady=framepady, expand=False) keysentencesframe.pack_propagate(0) # allows fixed height self.keySentenceslbl = self.WrappingLabel(keysentencesframe, justify=LEFT, anchor=NW, text="Key sentences here" * 10) self.keySentenceslbl.pack(fill=X) tableframe = Frame(frame3, relief=SOLID, bd=2, height=250) tableframe.pack(fill=X, padx=framepadx, pady=framepady, expand=False) tableframe.pack_propagate(0) # allows fixed height self.tree = ttk.Treeview(tableframe, columns=["Table to go here"], show="headings") self.tree.grid(column=0, row=0, sticky='nsew') vsb = ttk.Scrollbar(tableframe, orient="vertical", command=self.tree.yview) hsb = ttk.Scrollbar(tableframe, orient="horizontal", command=self.tree.xview) self.tree.configure(yscrollcommand=vsb.set, xscrollcommand=hsb.set) hsb.grid(column=0, row=1, sticky='ew') vsb.grid(column=1, row=0, sticky='ns') tableframe.grid_columnconfigure(0, weight=1) tableframe.grid_rowconfigure(0, weight=1) module_frame = Frame(frame3) module_frame.pack(fill=X, padx=framepadx, pady=framepady) self.module1lbl = Label(module_frame, justify=LEFT, anchor=NW) self.module1lbl.pack(fill=X) self.module1Active = False self.module1Results = [" ", " ", " "] self.infolbl = Label(frame2, text=" ", justify=LEFT, anchor=SW) self.infolbl.pack(side=LEFT, fill=BOTH) window.bind("<Key>", self.keyEvent) window.bind("<Return>", self.enterEvent) if not self.DEBUG: threading.Thread(target=self.setupThread).start() # start livefeed thread t = threading.Thread(target=self.live_feed_thread) t.start() t1 = threading.Thread(target=self.queue_thread) t1.start() self.clear_all_text() window.update( ) # needed to prevent problems with width/height measurements later self.defaultbg = window.cget('bg') window.mainloop() def adb_screencap(self, filename): start_time = time.time() cmd = "adb exec-out screencap -p > %s" % filename # print(cmd) if subprocess.call(cmd, shell=True) != 0: raise NameError("Couldn't initiate ADB. Retrying...") elapsed_time = time.time() - start_time print(elapsed_time) def setupThread(self): self.infolbl["text"] = "Starting ADB..." while True: try: self.adb_screencap(self.filename) break except NameError as e: # self.infolbl["text"] = str(e) time.sleep(0.5) self.infolbl["text"] = "Successfully initiated ADB. Ready." def run_without_screenshot_blocking(self, question, answers, data_obj): self.infolbl["text"] = "Running..." self.clear_all_text() self.mainlbl["bg"] = self.defaultbg try: self.infolbl["text"] = "Blind mode..." # make question one line question = question.replace("\n", " ") self.mainlbl["text"] = question self.mainlbl["bg"] = "orange" if len(answers) == 3: self.answer1lbl["text"] = answers[0] + ": " self.answer2lbl["text"] = answers[1] + ": " self.answer3lbl["text"] = answers[2] + ": " # time.sleep(1) self.infolbl["text"] = "Searching..." self.search(question, answers, self.question_num, data_obj=data_obj) self.infolbl["text"] = "Ready." self.question_num += 1 except SyntaxError as e: print(e.msg) time.sleep(0.25) except Exception as e: self.infolbl[ "text"] = "Error run_without_screenshot. Message: " + str(e) def live_feed_thread(self): livefeed_obj = LiveFeed.LiveFeed() self.infolbl["text"] = "Starting LiveFeed thread" # ids, times, types = livefeed_obj.get_next_game() #migrate return value to dictionary d = livefeed_obj.get_next_game() ids = d.get("ids") times = d.get("times") types = d.get("types") hosts = d.get("hosts") if hosts: if len(hosts) >= 1: host = hosts[0] print(f"gui: live_feed_thread: host: {host}") else: host = "api-prod--002.uk.theq.live" else: host = "api-prod--002.uk.theq.live" print( "gui: live_feed_thread: host: None. TODO handle this.\n-- RESTART PROGRAM!! --" ) # TODO Handle this, will need to restart getting next game # host = "api-prod--002.uk.theq.live" # create virtual players here self.player_manager = PlayerManager.PlayerManager(100, ids[0]) print(f"Types: {types}") start = datetime.time(18, 50, 0) end = datetime.time(19, 10, 0) time_now = datetime.datetime.now().time() if time_in_range(start, end, time_now): print("Time is spelling q") self.gameType = "The Spelling Q" else: self.gameType = types[0] print(f"gui: Gametype: {self.gameType}") # self.queue.put({"question":"hey?", "answers":["Hello", "Hellosajdk", "hopsad"]}) #to test spellcheck while True: if times[0] / 1000 < (time.time() + 3600): livefeed_obj.get_stream(ids[0], queue=self.queue, host=host) print(f"LiveFeed died, restarting... ID: {ids[0]}") else: print( f"Game starts in {round(times[0]/1000-time.time())+3600}s") time.sleep(1) def queue_thread(self): # polls queue.Queue object for questions and answers from live feed thread and executes in blocking way to avoid simultaneous exeuction while True: try: dic = self.queue.get() if dic.get("question"): # this is if a gamestart event has occured question = dic.get("question") answers = dic.get("answers") data_obj = dic.get("data_obj") if " Spelling " in self.gameType: print("gui: Running spell checker") self.run_spellchecker(question=question, answers=answers, data_obj=data_obj) else: self.run_without_screenshot_blocking( question, answers, data_obj) else: # gameresult event {"question_ID":question_ID, "choice_ID":choice_ID, "choice_human_string":choice_human_string} question_ID = dic.get("question_ID") choice_ID = dic.get("choice_ID") choice_human_string = dic.get("choice_human_string") if question_ID and choice_ID and choice_human_string: self.player_manager.append_correct_response( question_ID, choice_ID, choice_human_string) else: print( f"gui: queue_thread couldn't get all vars for correct response" ) except Exception as e: print(f"queue_thread: Exception {e}")