def __init__(self, data_dir): self.phrase_util = Phrases("resources/comm2.dev") self.plot_colors = ["#0000ff", "#4400ff", "#8800ff", "#cc00ff", "#ff00cc", "#ff0088", "#ff0044"] self.click_data_files = [] self.click_context_files = [] self.preconfig_file = None self.data_dir = data_dir for path, dir, files in os.walk(data_dir): for file in files: if "click_time_log" in file: self.click_data_files += [os.path.join(path, file)] if "params_data_use_num" in file: self.click_context_files += [os.path.join(path, file)] if "preconfig" in file: self.preconfig_file = os.path.join(path, file) self.rel_click_data = [] self.abs_click_data = [] self.selection_data = [] self.speed_changes = [] self.kde_list = [] self.clicks_by_speed = {} self.clicks_by_phrase = {} self.phrase_stats = {} self.corrected_clicks = None
def _testPhrase(self, language, phrase): assert Phrases.forLanguage(language).isPhrase( phrase), "phrase='%s' language=%s" % (phrase, language) number = Phrases.forLanguage(language).toNumber(phrase) detects = Phrases.detectLanguages(phrase) for lang2 in detects: number2 = Phrases.forLanguage(lang2).toNumber(phrase) phrase2 = Phrases.forLanguage(lang2).toPhrase(number) assert number == number2 assert phrase == phrase2
def __init__(self, lang): self.phrases = Phrases(lang) self.lang = lang self.words = WordsGameDictionary( self.lang, theme='', ) self.continue_game = True self.additional_info = '' self.current_word = ' ' self.quantity = 0
def number(self, phrase): phrase = Check.toString(phrase) phrases = self._getPhrases() if phrases.isPhrase(phrase): return phrases.toNumber(phrase) detects = Phrases.detectLanguages(phrase) if len(detects) > 0: return Phrases.forLanguage(detects.pop()).toNumber(phrase) raise ValueError("unknown phrase language")
def __init__(self, name=None, speech_input=False, facebook_input=False): self.phrases = Phrases() self.speech = Speech() self.knowledge = Knowledge(weather_api_token=weather_api_token) self.name = name self.facebook_input = facebook_input if self.facebook_input: self.facebook_response = list() self.speech_input = speech_input self.witai = Wit("S73IKQDWJ22OJMOSD6AOT4CSJOWXIPX6") self.fs = Fatsecret("90fe184a283449ed8a83e35790c04d65", "054e80b2be154337af191be2c9e11c28") self.translator = Translator()
def __geocode(self, context: LexContext): try: data = self.__geocoder.geocode(context) if len(data['results']) == 0: raise ValidationError(LexContext.SLOT_CITY, Phrases.provide_city()) if len(data['results']) > 1: raise ValidationError(LexContext.SLOT_AREA, Phrases.provide_area_details()) context.session['location'] = data['results'][0]['geometry']['location'] logger.debug("GEOCODE: session={}".format(json.dumps(context.session))) except KeyError: logger.exception("Unable to load location: {}".format(context.address)) raise ValidationError(LexContext.SLOT_CITY, Phrases.provide_city())
def _testPhrases(self, language): for number in range(100000): phrase = Phrases.forLanguage(language).toPhrase(number) self._testPhrase(language, phrase) for length in range(1, 20): phrase = self.mkPhrase(language, length) self._testPhrase(language, phrase)
def testPhrase(self): languages = Phrases.getLanguages() for language in [ "chinese_simplified", "chinese_traditional", "english", "french", "italian", "japanese", "korean", "spanish" ]: assert language in languages for language in languages: self._testPhrases(language)
def __handle_about_request(context: LexContext): return LexResponses.close( context, 'Fulfilled', { 'contentType': 'PlainText', 'content': Phrases.howto() } )
def testAmbiguity(self): languages = Phrases.getLanguages() allWords = dict() for language in languages: phrases = Phrases.forLanguage(language) for word in phrases.words: if not word in allWords: allWords[word] = dict() index = phrases.invWords[word] if not index in allWords[word]: allWords[word][index] = set() allWords[word][index].add(language) ambiguous = False for word in allWords: indexs = allWords[word] if len(indexs) <= 1: continue ambiguous = True for index in indexs: for lang in indexs[index]: print("word %s in %s index %d" % (word, index, lang)) assert not ambiguous
def mkPhrase(self, language, length): phrases = Phrases.forLanguage(language) words = phrases.words phrase = [words[self.rng.next(len(words))] for i in range(length)] return phrases.space().join(phrase)
parentdir = os.path.dirname(currentdir) sys.path.insert(0, parentdir) from phrases import Phrases languages = [ "ab", "chinese_simplified", "chinese_traditional", "english", "french", "italian", "japanese", "korean", "spanish" ] reserved = dict() for el1 in range(len(languages)): language = languages[el1] phrases = Phrases.forLanguage(language) words = phrases.words[:] sequence = [None for i in range(len(words))] reuses = False changed = False # put already reserved words at their index for i in range(len(words)): word = words[i] if word == None: continue if word in reserved: words[i] = None if phrases.invWords[word] != reserved[word]: changed = True sequence[reserved[word]] = word reuses = True
class Bot(object): def __init__(self, name=None, speech_input=False, facebook_input=False): self.phrases = Phrases() self.speech = Speech() self.knowledge = Knowledge(weather_api_token=weather_api_token) self.name = name self.facebook_input = facebook_input if self.facebook_input: self.facebook_response = list() self.speech_input = speech_input self.witai = Wit("S73IKQDWJ22OJMOSD6AOT4CSJOWXIPX6") self.fs = Fatsecret("90fe184a283449ed8a83e35790c04d65", "054e80b2be154337af191be2c9e11c28") self.translator = Translator() def gr_to_en(self, text): return self.translator.translate(text, 'en', 'el').text def en_to_gr(self, text): return self.translator.translate(text, 'el', 'en').text def start(self): if self.speech_input or self.facebook_input: self.decide_action() else: print("Γεία σου! Πως θα μπορούσα να σε βοηθήσω;") while 1: self.decide_action() def learn_action(self, filename, phraseslist): Knowledge.learn_default_responses(file=filename, phrases=phraseslist) def decide_action(self, facebook_input=None): if self.speech_input or self.facebook_input: if self.speech_input: recognizer, audio = self.speech.listen_for_audio() # received audio data, now we'll recognize it using Google Speech Recognition bot_input = self.speech.google_speech_recognition(recognizer, audio) if self.facebook_input: self.facebook_response[:] = [] bot_input = facebook_input else: bot_input = input() if bot_input is not None: try: resp = self.witai.message(bot_input) entities = None intent = None if 'entities' in resp and 'intent' in resp['entities']: entities = resp['entities'] intent = resp['entities']['intent'][0]["value"] # print('Intent: {intent}'.format(intent=intent)) if intent == 'greeting': self.__text_action(self.phrases.get_phrases('greetings_phrases')) elif intent == 'tutorial': self.__tutorial_action() elif intent == 'name': if self.name is not None: self.__text_action(self.phrases.get_phrases('name_phrases').replace('<name>', self.name)) else: self.__text_action('Οι δημιουργοί μου δεν μου έδωσαν κάποιο όνομα') elif intent == 'swear': self.__text_action(self.phrases.get_phrases('swear_phrases')) elif intent == 'funny': self.__text_action(self.phrases.get_phrases('funny_phrases')) elif intent == 'sex_type': self.__text_action('Λίγο απ\'όλα') elif intent == 'user_joke': self.__text_action('χαχαχα') elif intent == 'personal_status': self.__personal_status() elif intent == 'joke': self.__joke_action() elif intent == 'datetime': # print(resp) self.__datetime_action(entities) elif intent == 'weather': self.__weather_action() elif intent == 'search': self.__search_action(entities) elif intent == 'food_det': self.__food_action(entities) elif intent == 'recipe': self.__recipe_action(entities) elif intent == 'thanksgiving': self.__text_action(self.phrases.get_phrases('thanks_phrases')) elif intent == 'sticker': self.__text_action('Επίσης') else: # No recognized intent # print('Intent not recognized') self.__text_action(self.phrases.get_phrases('unrecognized_intent_phrases')) return except Exception as e: print("Exception occured") print(e) traceback.print_exc() self.__text_action("Έγινε κάποιο λάθος") return def __text_action(self, text=None): if text is not None: if self.speech_input: self.speech.synthesize_text(text) if self.facebook_input: self.facebook_response.append(text) if not (self.facebook_input or self.speech_input): print(text) def __tutorial_action(self): self.__text_action(self.phrases.get_phrases('tutorial_phrases')) def __personal_status(self): self.__text_action(self.phrases.get_phrases('personal_status_phrases')) def __joke_action(self): joke = self.phrases.get_phrases('joke_phrases') self.__text_action(joke) def __datetime_action(self, entities): dt = None if 'date' in entities: dt = entities['date'][0]['value'] print('Datetime: {dt}'.format(dt=dt)) if str(dt) == 'hour': self.__text_action('Η ώρα είναι {time}'.format(time=self.knowledge.get_time())) elif str(dt) == 'day': self.__text_action('Σήμερα είναι {weekday}'.format(weekday=self.knowledge.get_weekday())) elif str(dt) == 'date': self.__text_action('Σήμερα είναι {date}'.format(date=self.knowledge.get_date())) def __weather_action(self): weather_obj = self.knowledge.find_weather() self.__text_action('Η θερμοκρασία είναι ' + str(weather_obj['temperature']) + '° Κελσίου.') def __search_action(self, entities=None): self.__text_action(self.phrases.get_phrases('search_phrases')) if 'wikipedia_search_query' in entities: query = entities['wikipedia_search_query'][0]['value'] # print('wikipedia query: {query}'.format(query=query)) wikipedia.set_lang("el") try: self.__text_action(re.sub(r'\([^)]*\)', '', wikipedia.summary(query, sentences=1))) except wikipedia.PageError as e: print(e) self.__text_action('Δεν βρήκα κάποιο αποτέλεσμα') else: self.__text_action('Δεν μου είπες τί να ψάξω') def __food_action(self, entities): self.__text_action(self.phrases.get_phrases('search_phrases')) inp = self.gr_to_en(entities['wikipedia_search_query'][0]['value']) try: resp = self.fs.foods_search(inp) food = self.fs.food_get(resp[0]['food_id']) if 'nutrient_type' in entities.keys(): self.__text_action( self.en_to_gr( '{type} - 1 {serving}'.format(serving=food['servings']['serving'][0]['measurement_description'], type=resp[0]["food_name"]))) for nutrient in entities['nutrient_type']: self.__text_action(self.en_to_gr('{nutrient}: {value}'.format(nutrient=nutrient['value'], value= food['servings']['serving'][0][ nutrient['value']]))) else: self.__text_action(self.en_to_gr(resp[0]["food_name"] + "\n" + resp[0]["food_description"])) except Exception as e: self.__text_action( "Δεν υπάρχουν διαθέσιμες διατροφικές πληροφορίες για " + entities['wikipedia_search_query'][0]['value']) self.__search_action(entities) def __recipe_action(self, entities): self.__text_action(self.phrases.get_phrases('search_phrases')) inp = self.gr_to_en(entities['wikipedia_search_query'][0]['value']) try: resp = self.fs.recipes_search(inp) recipe = self.fs.recipe_get(resp[0]['recipe_id']) if self.facebook_input: self.__text_action("Μπορείς να δεις την συνταγή στο παρακάτω link:") self.__text_action(recipe['recipe_url']) else: self.__text_action(self.en_to_gr(recipe['recipe_name'] + "\n")) self.__text_action("Οδηγίες") for dir in recipe['directions']['direction']: self.__text_action(self.en_to_gr(dir['direction_description'])) self.__text_action("Συστατικά") for ing in recipe['ingredients']['ingredient']: self.__text_action(self.en_to_gr(ing['ingredient_description'])) except Exception as e: self.__text_action( "Δεν υπάρχει διαθέσιμη συνταγή για " + entities['wikipedia_search_query'][0]['value']) self.__search_action(entities)
class Keyboard(MainWindow): def __init__(self, screen_res, app): super(Keyboard, self).__init__(screen_res) self.app = app self.font_scale = 1 self.key_chars = kconfig.key_chars self.key_chars_sorted = kconfig.key_chars_sorted self.key_config = "sorted" # self.key_config = "default" self.num_words = 7 self.words_first = True # self.words_first = False self.sound_set = True self.pause_set = True self.lm_prefix = "" self.left_context = "" self.typed_versions = [""] self.cwd = os.getcwd() self.gen_data_handel() self.up_handel = PickleUtil( os.path.join(self.user_handel, 'user_preferences.p')) user_preferences = self.up_handel.safe_load() if user_preferences is None: user_preferences = [ 'sorted', config.default_rotate_ind, config.default_pause_ind, True ] self.up_handel.safe_save(user_preferences) self.key_config, self.speed, self.pause_index, self.is_write_data = user_preferences self.scanning_delay = config.period_li[self.speed] self.extra_delay = config.pause_li[self.pause_index] self.params_handle_dict = { 'speed': [], 'extra_delay': [], 'params': [], 'start': [], 'press': [], 'choice': [] } self.num_presses = 0 self.last_press_time = time.time() self.last_update_time = time.time() self.next_frame_time = time.time() self.params_handle_dict['params'].append( [config.period_li[config.default_rotate_ind], config.theta0]) self.params_handle_dict['start'].append(time.time()) self.click_time_list = [] lm_path = os.path.join(os.path.join(self.cwd, 'resources'), 'lm_word_medium.kenlm') vocab_path = os.path.join(os.path.join(self.cwd, 'resources'), 'vocab_100k') self.lm = LanguageModel(lm_path, vocab_path) self.phrase_prompts = False if self.phrase_prompts: self.phrases = Phrases("resources/comm2.dev") else: self.phrases = None # determine keyboard positions # set up file handle for printing useful stuff # set up "typed" text self.typed = "" self.context = "" self.old_context_li = [""] self.last_add_li = [0] # set up "talked" text # check for speech # talk_fid = open(self.talk_file, 'wb') # write words self.generate_layout() self.draw_words() self.wpm_data = [] self.decay_avg_wpm = 0 self.wpm_time = 0 self.error_data = [] self.decay_avg_error = 1 self.row_scan = True self.row_scan_num = -2 self.col_scan = False self.col_scan_num = -1 self.init_ui() # animate self.on_timer() def gen_data_handel(self): self.cwd = os.getcwd() self.data_path = user_data_dir('data', 'RowCol') if os.path.exists(self.data_path): user_files = list(os.walk(self.data_path)) users = user_files[0][1] else: pathlib.Path(self.data_path).mkdir(parents=True, exist_ok=True) # os.mkdir(data_path) user_files = None users = [] input_method = 'text' if user_files is not None and len(users) != 0: message = QtWidgets.QMessageBox( QtWidgets.QMessageBox.Information, "Load User Data", "You can either create a new user profile or " "load an existing user profile.") message.addButton(QtWidgets.QPushButton('Create New User'), QtWidgets.QMessageBox.YesRole) message.addButton(QtWidgets.QPushButton('Load Previous User'), QtWidgets.QMessageBox.NoRole) message.setDefaultButton(QtWidgets.QMessageBox.Yes) response = message.exec_() if response == 0: input_method = 'text' else: input_method = 'list' if input_method == 'text': valid_user_id = False input_text = "Please input a Number that will be used to save your user information" while not valid_user_id: num, ok = QtWidgets.QInputDialog.getInt( self, "User ID Number Input", input_text) if str(num) not in users: valid_user_id = True else: input_text = "The user ID you inputed already exists! \n please input a valid user ID or press " \ "\"cancel\" to choose an existing one from a list" if ok == 0: input_method = 'list' break if input_method == 'text': self.user_id = num user_id_path = os.path.join(self.data_path, str(self.user_id)) os.mkdir(user_id_path) if input_method == 'list': item, ok = QtWidgets.QInputDialog.getItem( self, "Select User ID", "List of save User IDs:", users, 0, False) self.user_id = item self.user_handel = os.path.join(self.data_path, str(self.user_id)) user_id_files = list(os.walk(self.user_handel)) user_id_calibrations = user_id_files[0][1] if len(user_id_calibrations) == 0: self.data_handel = os.path.join(self.user_handel, 'cal0') os.mkdir(self.data_handel) user_id_cal_files = None self.user_cal_num = 0 else: user_id_cal_files = user_id_files[-1][2] self.data_handel = user_id_files[-1][0] self.user_cal_num = len(user_id_calibrations) - 1 if user_id_cal_files is not None: self.use_num = sum([ 1 if 'params_data' in file_name else 0 for file_name in user_id_cal_files ]) else: self.use_num = 0 print(self.data_handel) def generate_layout(self): if self.key_config == "sorted": self.keys_list = np.array(self.key_chars_sorted) closest_square = int( np.ceil(np.sqrt(len(self.keys_list) + self.num_words))) self.key_freq_map = np.zeros((closest_square, closest_square)) for row_num, row in enumerate(self.key_freq_map): for col_num, _ in enumerate(row): self.key_freq_map[row_num][col_num] = row_num + col_num else: self.keys_list = np.array(self.key_chars) closest_square = int( np.ceil(np.sqrt(len(self.keys_list) + self.num_words))) self.key_freq_map = np.zeros((closest_square, closest_square)) for row_num, row in enumerate(self.key_freq_map): for col_num, _ in enumerate(row): self.key_freq_map[row_num][ col_num] = row_num * closest_square + col_num self.key_layout = np.empty((closest_square, closest_square), dtype=str) for word in range(self.num_words): # fill words first if self.words_first: word_index = np.unravel_index(word, (closest_square, closest_square)) else: word_index = np.unravel_index( closest_square**2 - self.num_words + word, (closest_square, closest_square)) self.key_layout[word_index] = kconfig.word_char self.key_freq_map[word_index] = float("inf") sorted_indicies = [] for i in range(len(self.keys_list)): arg_min_index = np.unravel_index(self.key_freq_map.argmin(), self.key_freq_map.shape) sorted_indicies += [arg_min_index] self.key_freq_map[arg_min_index] = float("inf") sorted_indicies.reverse() for key in self.keys_list: lowest_index = sorted_indicies.pop() self.key_layout[lowest_index] = key print(self.key_layout) empty_row_counts = np.sum(np.where(self.key_layout != '', 1, 0), axis=1).tolist() if 0 in empty_row_counts: empty_index = empty_row_counts.index(0) self.key_layout = np.delete(self.key_layout, (empty_index), axis=0) self.key_rows_num = len(self.key_layout) self.key_cols_nums = np.sum(np.where(self.key_layout != '', 1, 0), axis=1).tolist() # shift empty cells for row_num, row in enumerate(self.key_layout): if "" in row: row_list = row.tolist() row_list.sort(key=lambda x: 2 if x == "" else 1) self.key_layout[row_num] = np.array(row_list) def keyPressEvent(self, e): if e.key() == QtCore.Qt.Key_Space: self.last_gap_time = time.time() - self.last_update_time self.last_press_time = time.time() self.save_click_time(self.last_press_time, self.last_gap_time, (self.row_scan_num, self.col_scan_num)) self.on_press() def change_speed(self, value): old_rotate = config.period_li[self.speed] self.speed = value self.time_rotate = config.period_li[self.speed] self.params_handle_dict['speed'].append( [time.time(), old_rotate, self.scanning_delay]) def change_extra_delay(self, value): old_pause_length = config.pause_li[self.pause_index] self.pause_index = value self.extra_delay = config.pause_li[self.pause_index] self.params_handle_dict['extra_delay'].append( [time.time(), old_pause_length, self.extra_delay]) def toggle_sound_button(self, value): self.sound_set = value self.mainWidget.sldLabel.setFocus() def toggle_pause_button(self, value): self.pause_set = value self.mainWidget.sldLabel.setFocus() def draw_words(self): self.words_li = self.lm.get_words(self.left_context, self.lm_prefix, self.num_words) def on_timer(self): if self.focusWidget() == self.mainWidget.text_box: self.mainWidget.sldLabel.setFocus( ) # focus on not toggle-able widget to allow keypress event cur_time = time.time() if cur_time >= self.next_frame_time: self.update_frame() def update_frame(self): self.last_update_time = time.time() if self.row_scan: self.row_scan_num += 1 if self.row_scan_num >= self.key_rows_num: self.row_scan_num = 0 if self.row_scan_num == 0 and self.pause_set: self.next_frame_time += (config.period_li[self.speed] + self.extra_delay) else: self.next_frame_time += config.period_li[self.speed] elif self.col_scan: self.next_frame_time += config.period_li[self.speed] self.col_scan_num += 1 if self.col_scan_num >= self.key_cols_nums[self.row_scan_num]: self.col_scan_num = 0 self.mainWidget.highlight_grid() def on_press(self): if self.wpm_time == 0: self.wpm_time = time.time() if self.phrase_prompts: self.mainWidget.speed_slider.setEnabled(False) self.mainWidget.extra_delay_slider.setEnabled(False) self.mainWidget.speed_slider_label.setStyleSheet( 'QLabel { color: grey }') self.mainWidget.sldLabel.setStyleSheet('QLabel { color: grey }') self.mainWidget.extra_delay_label.setStyleSheet( 'QLabel { color: grey }') self.mainWidget.extra_sldLabel.setStyleSheet( 'QLabel { color: grey }') if self.sound_set: self.play() if self.row_scan: self.row_scan = False self.col_scan = True self.next_frame_time = time.time() self.on_timer() elif self.col_scan: self.make_selection() self.num_presses += 1 def save_click_time(self, last_press_time, last_gap_time, index): self.params_handle_dict['press'].append([last_press_time]) self.click_time_list.append((last_gap_time, index)) def make_selection(self): self.winner = self.key_layout[max(0, self.row_scan_num)][max( 0, self.col_scan_num)] self.draw_typed() self.draw_words() self.mainWidget.update_grid() print(self.winner) self.col_scan = False self.row_scan = True self.row_scan_num = -1 self.col_scan_num = -1 self.next_frame_time = time.time() self.on_timer() def draw_typed(self): if len(self.typed_versions) > 0: previous_text = self.typed_versions[-1] else: previous_text = "" if "_" in previous_text: previous_text = previous_text.replace("_", " ") if self.winner == kconfig.word_char: new_text = self.mainWidget.labels_by_row[self.row_scan_num][ self.col_scan_num].text()[1:] + ' ' new_text = new_text[len(self.lm_prefix):] else: new_text = self.winner if self.winner == kconfig.back_char: # self.prefix = self.prefix[:-1] if self.typed_versions[-1] != '': self.typed_versions += [previous_text[:-1]] input_text = "<span style='color:#000000;'>" + self.typed_versions[ -1] + "</span>" self.mainWidget.text_box.setText( "<span style='color:#000000;'>" + self.typed_versions[-1] + "</span>") else: input_text = "" elif self.winner == kconfig.mybad_char: if len(self.typed_versions) > 1: self.typed_versions = self.typed_versions[:-1] input_text = "<span style='color:#000000;'>" + self.typed_versions[ -1] + "</span>" self.mainWidget.text_box.setText( "<span style='color:#000000;'>" + self.typed_versions[-1] + "</span>") else: input_text = "" elif self.winner == kconfig.clear_char: if len(self.typed_versions) > 1: self.typed_versions += [" "] self.mainWidget.text_box.setText("") input_text = "" else: self.typed_versions += [previous_text + new_text] if new_text in kconfig.break_chars: new_text = new_text + ' ' if previous_text[-1] == " ": previous_text = previous_text[:-1] if len(new_text) > 0 and new_text[-1] == " ": new_text = new_text[:-1] + "_" new_text = new_text[:-1] + "_" input_text = "<span style='color:#000000;'>" + previous_text + "</span><span style='color:#0000dd;'>" \ + new_text + "</span>" self.mainWidget.text_box.setText( "<span style='color:#000000;'>" + previous_text + "</span><span style='color:#0000dd;'>" + new_text + "</span>") self.mainWidget.text_box.update() self.update_prefixes() # write output if self.is_write_data: choice_dict = { "time": time.time(), "undo": self.winner == kconfig.mybad_char, "backspace": self.winner == kconfig.back_char, "typed": self.typed_versions[-1] } if self.phrase_prompts: choice_dict["target"] = self.phrases.cur_phrase self.params_handle_dict['choice'].append(choice_dict) if self.phrase_prompts: self.update_phrases(self.typed_versions[-1], input_text) def text_stat_update(self, phrase, typed): _, cur_error = calc_MSD(phrase, typed) self.error_data = [cur_error] + self.error_data decaying_weights = np.power(0.8, np.arange(len(self.error_data))) decaying_weights /= np.sum(decaying_weights) decay_avg_error = sum(np.array(self.error_data) * decaying_weights) error_delta = decay_avg_error / (self.decay_avg_error + 0.000001) self.decay_avg_error = decay_avg_error self.wpm_data = [(len(typed.split(" ")) - 1) / (time.time() - self.wpm_time) * 60] + self.wpm_data self.wpm_time = 0 decaying_weights = np.power(0.8, np.arange(len(self.wpm_data))) decaying_weights /= np.sum(decaying_weights) decay_avg_wpm = sum(np.array(self.wpm_data) * decaying_weights) wpm_delta = decay_avg_wpm / (self.decay_avg_wpm + 0.000001) self.decay_avg_wpm = decay_avg_wpm if error_delta > 1: error_red = int(min(4, error_delta) * 63) error_green = 0 else: error_green = int(min(4, 1 / error_delta) * 63) error_red = 0 if wpm_delta < 1: wpm_red = int(min(4, wpm_delta) * 63) wpm_green = 0 else: wpm_green = int(min(4, 1 / wpm_delta) * 63) wpm_red = 0 self.mainWidget.error_label.setStyleSheet("color: rgb(" + str(error_red) + ", " + str(error_green) + ", 0);") self.mainWidget.wpm_label.setStyleSheet("color: rgb(" + str(wpm_red) + ", " + str(wpm_green) + ", 0);") self.mainWidget.error_label.setText("Error Rate: " + str(round(decay_avg_error, 2))) self.mainWidget.wpm_label.setText("Words/Min: " + str(round(decay_avg_wpm, 2))) def reset_context(self): self.left_context = "" self.context = "" self.typed = "" self.lm_prefix = "" def update_phrases(self, cur_text, input_text): cur_phrase_typed, next_phrase = self.phrases.compare(cur_text) cur_phrase_highlighted = self.phrases.highlight(cur_text) if next_phrase: self.text_stat_update(self.phrases.cur_phrase, self.typed_versions[-1]) self.typed_versions = [''] self.mainWidget.text_box.setText('') self.mainWidget.speed_slider.setEnabled(True) self.mainWidget.speed_slider_label.setStyleSheet( 'QLabel { color: blue }') self.mainWidget.sldLabel.setStyleSheet('QLabel { color: blue }') self.mainWidget.extra_delay_slider.setEnabled(True) self.mainWidget.extra_delay_label.setStyleSheet( 'QLabel { color: blue }') self.mainWidget.extra_sldLabel.setStyleSheet( 'QLabel { color: blue }') self.clear_text = False undo_text = 'Clear' self.phrases.sample() input_text = "" cur_phrase_highlighted = self.phrases.highlight("") self.reset_context() if self.is_write_data: choice_dict = { "time": time.time(), "undo": False, "backspace": False, "typed": "", "target": self.phrases.cur_phrase } self.params_handle_dict['choice'].append(choice_dict) self.mainWidget.text_box.setText("<p>" + cur_phrase_highlighted + "<\p><p>" + input_text + "</span><\p>") def update_prefixes(self): cur_text = self.typed_versions[-1] for bc in kconfig.break_chars: if bc in cur_text: cur_text = cur_text.split(bc)[-1] cur_text_words = cur_text.split(" ") print(cur_text_words) if cur_text_words[-1] == '' or cur_text_words[ -1] in kconfig.break_chars: self.lm_prefix = "" self.left_context = cur_text else: self.lm_prefix = cur_text_words[-1] self.left_context = cur_text[:(-len(self.lm_prefix) - 1)] def play(self): sound_file = "icons/bell.wav" QtMultimedia.QSound.play(sound_file) def data_auto_save(self): if len(self.click_time_list) > 0: print("auto saving data") self.save_data() def closeEvent(self, event): print("CLOSING THRU CLOSEEVENT") self.quit(event) # self.deleteLater() def quit(self, event=None): self.save_data() self.close() def save_data(self): user_preferences = [ self.key_config, self.speed, self.pause_index, self.is_write_data ] self.up_handel.safe_save(user_preferences) self.click_data_path = os.path.join( self.data_handel, 'click_time_log_' + str(self.use_num) + '.p') self.params_data_path = os.path.join( self.data_handel, 'params_data_use_num' + str(self.use_num) + '.p') print(self.params_data_path) PickleUtil(self.click_data_path).safe_save({ 'user id': self.user_id, 'use_num': self.use_num, 'click time list': self.click_time_list, 'rotate index': self.speed }) PickleUtil(self.params_data_path).safe_save(self.params_handle_dict)
class DataUtil: def __init__(self, data_dir): self.phrase_util = Phrases("resources/comm2.dev") self.plot_colors = ["#0000ff", "#4400ff", "#8800ff", "#cc00ff", "#ff00cc", "#ff0088", "#ff0044"] self.click_data_files = [] self.click_context_files = [] self.preconfig_file = None self.data_dir = data_dir for path, dir, files in os.walk(data_dir): for file in files: if "click_time_log" in file: self.click_data_files += [os.path.join(path, file)] if "params_data_use_num" in file: self.click_context_files += [os.path.join(path, file)] if "preconfig" in file: self.preconfig_file = os.path.join(path, file) self.rel_click_data = [] self.abs_click_data = [] self.selection_data = [] self.speed_changes = [] self.kde_list = [] self.clicks_by_speed = {} self.clicks_by_phrase = {} self.phrase_stats = {} self.corrected_clicks = None def load_data(self): # size_list = [] for data_file in self.click_data_files: data_handel = PickleUtil(data_file) click_dict = data_handel.safe_load() # size_list += [len(click_dict["click time list"])] self.rel_click_data += [click[0] for click in click_dict["click time list"]] self.rel_click_data = np.array(self.rel_click_data) # size_list_2 = [] for data_file in self.click_context_files: data_handel = PickleUtil(data_file) context_dict = data_handel.safe_load() self.abs_click_data += context_dict["press"] # size_list_2 += [len(flatten(context_dict["press"]))] self.speed_changes += context_dict["speed"] self.selection_data += context_dict["choice"] self.abs_click_data = np.array(flatten(self.abs_click_data)) self.speed_changes.sort(key=lambda x: x[0]) if self.preconfig_file is not None: preconfig_handel = PickleUtil(self.preconfig_file) preconfig = preconfig_handel.safe_load() self.kde_list = np.array(preconfig["li"])/np.sum(preconfig["li"]) if len(self.abs_click_data) != len(self.rel_click_data): raise ValueError("Click data length does not match context data length!") print("Loaded " + str(len(self.abs_click_data)) + " clicks") def split_data_phrase(self): self.phrases = [] self.phrase_times = {} phrase_start = 0 uncorrected_error = 0 corrected_error = 0 session_num = 0 start_time_prev = 0 for selection_num, selection in enumerate(self.selection_data): # scan through data to find phrases & time ints if "target" in selection: phrase = selection["target"] typed = selection["typed"] is_backspace = selection["backspace"] is_undo = selection["undo"] if phrase not in self.phrases: self.phrases.append(phrase) phrase_start = selection["time"] if is_backspace: # accumulate corrected errors corrected_error += 1 if is_undo: if selection_num > 0: # prev_typed = self.selection_data[selection_num-1]["typed"] # typed_difference = prev_typed[len(typed):] corrected_error += 1 _, completed_phrase = self.phrase_util.compare(typed, phrase) if completed_phrase and not is_undo: self.phrase_times[phrase] = (phrase_start, selection["time"]) uncorrected_error = calc_MSD(typed, phrase)[0] total_error = (uncorrected_error + corrected_error) / len(max(typed, phrase)) self.phrase_stats[phrase] = {"error_unc": uncorrected_error, "error_cor": corrected_error, "error_tot": total_error} uncorrected_error = 0 corrected_error = 0 self.phrases = list(self.phrase_times.keys()) self.phrases.sort(key=lambda x: self.phrase_times[x][0]) print("Data partitioned into " + str(len(self.phrases)) + " sets by phrase") for phrase in self.phrases: # split data according to phrase times calculated above phrase_start, phrase_end = self.phrase_times[phrase] phrase_click_indices = np.where((self.abs_click_data >= phrase_start) & (self.abs_click_data <= phrase_end)) phrase_abs_clicks = self.abs_click_data[phrase_click_indices] phrase_rel_clicks = self.rel_click_data[phrase_click_indices] if len(phrase_abs_clicks) > 0: first_click_time = min(phrase_abs_clicks) self.clicks_by_phrase[phrase] = {"abs": phrase_abs_clicks, "rel": phrase_rel_clicks} num_clicks = len(phrase_abs_clicks) time_int = phrase_end - first_click_time num_characters = len(phrase) num_words = len(phrase.split(" ")) clicks_per_char = num_clicks / num_characters clicks_per_word = num_clicks / num_words chars_per_min = num_characters / time_int * 60 words_per_min = num_words / time_int * 60 stat_dict = self.phrase_stats[phrase] stat_dict["clicks_char"] = clicks_per_char stat_dict["clicks_word"] = clicks_per_word stat_dict["chars_min"] = chars_per_min stat_dict["words_min"] = words_per_min if phrase_start - start_time_prev > 20 * 60: session_num += 1 start_time_prev = phrase_start stat_dict["session"] = session_num stat_dict["start_time"] = phrase_start else: del self.phrase_stats[phrase] self.phrases = list(self.phrase_stats.keys()) # print(self.phrase_stats) def split_data_speed(self): num_speed_changes = len(self.speed_changes) for change_index in range(num_speed_changes): time_min = self.speed_changes[change_index][0] if change_index == num_speed_changes-1: time_max = float("inf") else: time_max = self.speed_changes[change_index+1][0] clicks_int = np.array(np.where((self.abs_click_data > time_min) & (self.abs_click_data < time_max))[0], dtype='int64') matrix_index = np.vectorize(lambda m_index: self.rel_click_data[m_index]) if clicks_int.size > 0: clock_speed = round(self.speed_changes[change_index][2], 2) clicks_rel_int = matrix_index(clicks_int) if clock_speed in self.clicks_by_speed.keys(): prev_clicks = self.clicks_by_speed[clock_speed] self.clicks_by_speed[clock_speed] = np.concatenate((prev_clicks, clicks_rel_int)) else: self.clicks_by_speed[clock_speed] = clicks_rel_int print("Data partitioned into " + str(len(self.clicks_by_speed.keys())) + " sets by clock rotation speed") def correct_data_speed(self): print(self.clicks_by_speed.keys()) if 0.6 not in self.clicks_by_speed.keys(): raise ValueError("Base rotation speed not in data!") base_clicks = self.clicks_by_speed[0.6] base_clicks_mean = np.mean(base_clicks) base_clicks = base_clicks - base_clicks_mean base_clicks_std = np.std(base_clicks) clock_speeds = list(self.clicks_by_speed.keys()) self.corrected_clicks = [] for clock_speed in clock_speeds: clicks = self.clicks_by_speed[clock_speed] clicks_mean = np.mean(clicks) clicks = clicks - clicks_mean clicks_std = np.std(clicks) clicks *= base_clicks_std/clicks_std self.corrected_clicks += clicks.tolist() self.corrected_clicks = np.array(self.corrected_clicks) def make_data_frame(self): for phrase_num, phrase in enumerate(self.phrases): if phrase_num == 0: DF = pd.DataFrame(pd.DataFrame([self.phrase_stats[phrase]])) DF["phrase"] = phrase else: df = pd.DataFrame([self.phrase_stats[phrase]]) df["phrase"] = phrase DF = DF.append(df, ignore_index=True) # DF = DF.sort_values(by=['start_time']) self.DF = DF def plot_data(self): fig = plt.figure() ax = plt.subplot(111) plot_num = 0 for clock_speed in self.clicks_by_speed.keys(): plot_color = self.plot_colors[plot_num] clicks = self.clicks_by_speed[clock_speed]/clock_speed*80 clicks_mean = np.mean(clicks) clicks_std = np.std(clicks) plot_label = "speed: "+str(clock_speed)+" ("+str(len(clicks))+" points)" ax.hist(clicks, 20, range=[0, 80], density=True, color=plot_color, alpha=0.3, label=plot_label) kernel = stats.gaussian_kde(clicks) res = 10 plt.plot(np.arange(80*res)/res, kernel(np.arange(80*res)/res), color=plot_color, linewidth=2) plot_num += 1 plt.axvline(clicks_mean, color=plot_color, linestyle="--", alpha=0.8) for i in [-1,1]: plt.axvline(clicks_mean + i*clicks_std, color=plot_color, linestyle=":", alpha=0.6) if self.corrected_clicks is not None: kernel = stats.gaussian_kde(self.corrected_clicks) res = 10 plot_color = self.plot_colors[plot_num] plot_label = "speed_adj (" + str(len(self.corrected_clicks)) + " points)" plt.plot(np.arange(80 * res) / res, kernel(np.arange(80 * res) / res - 40), linestyle="--", color="0000", linewidth=2, label=plot_label) # ax.bar(np.arange(self.kde_list.size), self.kde_list, fill=False, edgecolor='black', label="KDE") ax.legend() ax.set_xlim(0,80) plt.show() def plot_phrase_stats(self): ind_var_name = "session" for data_label in ['error_tot', 'error_tot', 'clicks_char', 'clicks_word', 'words_min', 'chars_min']: dep_var_name = data_label # if data_label == 'words_min': # dep_var_name = "Words per Minute" # elif data_label == 'chars_min': # dep_var_name = "Characters per Minute" # elif data_label == 'clicks_char': # dep_var_name = "Presses per Character" # elif data_label == 'clicks_word': # dep_var_name = "Presses per Word" # elif data_label == 'error_tot': # dep_var_name = "Error Rate (Errors/Selection)" # else: # raise ValueError("Data Attribute Unknown: " + data_label) DF = self.DF pd.set_option('display.max_columns', 500) fig, ax = plt.subplots() fig.set_size_inches(10, 8) sns.set(font_scale=1.5, rc={"lines.linewidth": 3}) sns.set_style({'font.serif': 'Helvetica'}) sns.lineplot(x=ind_var_name, y=dep_var_name, data=DF, ci="sd", ax=ax) # sns.lineplot(x=ind_var_name, y=dep_var_name, # data=DF, ax=ax) # sns.lineplot(x=ind_var_name, y=dep_var_name, hue="Adjusted Scanning Delay", # palette=sns.cubehelix_palette(2, start=3, rot=0.2, dark=.2, light=.7, reverse=True), # data=DF, ci="sd", ax=ax) plt.title("Row Column Scanner: " + dep_var_name + " vs. " + ind_var_name) sns.axes_style("darkgrid") plt.show() def print_stat_avg(self): errors = [] clicks_char = [] clicks_word = [] words_min = [] chars_min = [] for phrase in self.phrases: data_dict = self.phrase_stats[phrase] errors.append(data_dict["error_tot"]) clicks_char.append(data_dict["clicks_char"]) clicks_word.append(data_dict["clicks_word"]) words_min.append(data_dict["words_min"]) chars_min.append(data_dict["chars_min"]) print("\n") print("Characters/Min: ", np.average(chars_min), "+/-", np.std(chars_min)) print("Words/Min: ", np.average(words_min), "+/-", np.std(words_min)) print("Clicks/Character: ", np.average(clicks_char), "+/-", np.std(clicks_char)) print("Clicks/Word: ", np.average(clicks_word), "+/-", np.std(clicks_word)) print("Error Rate (%): ", np.average(errors) * 100, "+/-", np.std(errors) * 100) def gen_kernel(self): kernel = stats.gaussian_kde(self.rel_click_data) kernel_handle = PickleUtil("resources\\kde_kernel.p") kernel_handle.safe_save(kernel)
def _getPhrases(self): return Phrases.forLanguage(self.getLanguage())
class Keyboard(MainWindow): def __init__(self, screen_res, app): super(Keyboard, self).__init__(screen_res) self.app = app self.is_simulation = False self.pretrain_window = False # 2 is turn fully on, 1 is turn on but reduce, 0 is turn off self.word_pred_on = 2 # Number of word clocks to display in case word prediction == 1 (reduced) self.reduce_display = 5 # get user data before initialization self.gen_data_handel() self.up_handel = PickleUtil( os.path.join(self.user_handel, 'user_preferences.p')) user_preferences = self.up_handel.safe_load() if user_preferences is None: first_load = True user_preferences = [ 'default', 1, False, 'alpha', 'off', config.default_rotate_ind, True ] self.up_handel.safe_save(user_preferences) else: first_load = False self.clock_type, self.font_scale, self.high_contrast, self.layout_preference, self.pf_preference, \ self.start_speed, self.is_write_data = user_preferences self.phrase_prompts = False # set to true for data collection mode if self.phrase_prompts: self.phrases = Phrases( "resources/twitter-phrases/watch-combined.txt") else: self.phrases = None if self.layout_preference == 'alpha': self.target_layout = kconfig.alpha_target_layout self.key_chars = kconfig.key_chars elif self.layout_preference == 'qwerty': self.target_layout = kconfig.qwerty_target_layout self.key_chars = kconfig.key_chars elif self.layout_preference == 'emoji': self.target_layout = kconfig.emoji_target_layout self.key_chars = kconfig.emoji_keys self.word_pred_on = 0 # set up dictionary tree # splash = StartWindow(screen_res, True) self.pause_animation = False self.output_manager = outputManager() self.is_output_text = False self.lm_prefix = "" self.left_context = "" self.cwd = os.getcwd() word_lm_path = os.path.join( os.path.join(self.cwd, 'resources'), 'mix4_opt_min_lower_100k_4gram_2.5e-9_prob8_bo4_compress255.kenlm') char_lm_path = os.path.join( os.path.join(self.cwd, 'resources'), 'mix4_opt_min_lower_12gram_6e-9_prob9_bo4_compress255.kenlm') vocab_path = os.path.join(os.path.join(self.cwd, 'resources'), 'vocab_lower_100k.txt') char_path = os.path.join(os.path.join(self.cwd, 'resources'), 'char_set.txt') self.lm = LanguageModel(word_lm_path, char_lm_path, vocab_path, char_path) # initialize pygame and joystick if kconfig.target_evt is kconfig.joy_evt: pygame.init() if pygame.joystick.get_count() < 1: # no joysticks found print("Please connect a joystick.\n") self.quit(None) else: # create a new joystick object from # ---the first joystick in the list of joysticks Joy0 = pygame.joystick.Joystick(0) # tell pygame to record joystick events Joy0.init() # start looking for events self.parent.after(0, self.find_events) # not in selection pause self.in_pause = False # determine keyboard positions self.init_locs() # get old data if there is such # Just for default. Loaded again when bc initializes self.rotate_index = config.default_rotate_ind # set up file handle for printing useful stuff self.undefined = False self.params_handle_dict = { 'speed': [], 'params': [], 'start': [], 'press': [], 'choice': [] } self.num_presses = 0 self.params_handle_dict['params'].append( [config.period_li[config.default_rotate_ind], config.theta0]) self.params_handle_dict['start'].append(time.time()) self.gen_scale() self.pause_set = True # set up "typed" text self.typed = "" self.btyped = "" self.context = "" self.old_context_li = [""] self.last_add_li = [0] # set up "talked" text # self.talk_file = "talk.txt" self.sound_set = True self.press_lock = False self.press_lock_status = False # check for speech # talk_fid = open(self.talk_file, 'wb') # write words self.init_words() self.bars = kconfig.bars self.bc_init = False self.previous_undo_text = '' self.previous_winner = 0 self.wpm_data = [] self.decay_avg_wpm = 0 self.wpm_time = 0 self.error_data = [] self.decay_avg_error = 1 self.clear_text = False self.pretrain = False self.emoji_box_highlight = [-1, -1] self.init_ui() self.mainWidget.clockgrid_widget.update_word_clocks(self.words_li) sound_file = "icons/bell.wav" self.sound_player = QtMultimedia.QSound(sound_file) self.time_rotate = config.period_li[self.start_speed] # get language model results self.gen_word_prior(False) self.clock_spaces = np.zeros((len(self.clock_centers), 2)) self.bc = BroderClocks(self) self.mainWidget.change_value(self.start_speed) self.bc.init_follow_up(self.word_score_prior) # draw histogram self.init_histogram() self.save_environment() self.consent = False if first_load: self.pretrain = True self.welcome = Pretraining(self, screen_res) # animate # record to prevent double tap self.last_key_press_time = time.time() self.last_release_time = time.time() self.update_radii = False self.on_timer() def gen_data_handel(self): self.cwd = os.getcwd() self.data_path = user_data_dir('data', 'Nomon') if os.path.exists(self.data_path): user_files = list(os.walk(self.data_path)) users = user_files[0][1] else: pathlib.Path(self.data_path).mkdir(parents=True, exist_ok=True) user_files = None users = [] input_method = 'text' if user_files is not None and len(users) != 0: message = QtWidgets.QMessageBox() message.setText("You can either create a new user profile or " "load an existing user profile.") message.setWindowTitle("Load User") message.addButton('Create New User', QtWidgets.QMessageBox.YesRole) message.addButton('Load Previous User', QtWidgets.QMessageBox.NoRole) message.setDefaultButton(QtWidgets.QMessageBox.Yes) response = message.exec_() if response == 0: input_method = 'text' else: input_method = 'list' if input_method == 'text': valid_user_id = False input_text = "Please input a Number that will be used to save your user information" while not valid_user_id: num, ok = QtWidgets.QInputDialog.getInt( self, "User ID Number Input", input_text) if str(num) not in users: valid_user_id = True else: input_text = "The user ID you inputed already exists! \n please input a valid user ID or press " \ "\"cancel\" to choose an existing one from a list" if ok == 0: input_method = 'list' break if input_method == 'text': self.user_id = num user_id_path = os.path.join(self.data_path, str(self.user_id)) os.mkdir(user_id_path) if input_method == 'list': item, ok = QtWidgets.QInputDialog.getItem( self, "Select User ID", "List of save User IDs:", users, 0, False) self.user_id = item self.user_handel = os.path.join(self.data_path, str(self.user_id)) user_id_files = list(os.walk(self.user_handel)) user_id_calibrations = user_id_files[0][1] if len(user_id_calibrations) == 0: self.data_handel = os.path.join(self.user_handel, 'cal0') os.mkdir(self.data_handel) user_id_cal_files = None self.user_cal_num = 0 else: user_id_cal_files = user_id_files[-1][2] self.data_handel = user_id_files[-1][0] self.user_cal_num = len(user_id_calibrations) - 1 if user_id_cal_files is not None: self.use_num = sum([ 1 if 'params_data' in file_name else 0 for file_name in user_id_cal_files ]) else: self.use_num = 0 print(self.data_handel) def save_environment(self): self.rotate_index_past = self.rotate_index self.window_size_past = self.frameGeometry() self.clock_type_past = self.clock_type self.layout_preference_past = self.layout_preference self.high_contrast_past = self.high_contrast def data_auto_save(self): if len(self.bc.click_time_list) > 0: print("auto saving data") self.bc.save_when_quit() def find_events(self): # check everything in the queue of pygame events events = pygame.event.get() for event in events: # event type for pressing any of the joystick buttons down if event.type == pygame.JOYBUTTONDOWN: # generate the event I've defined self.canvas.event_generate(kconfig.joy_evt) # return to check for more events in a moment self.parent.after(20, self.find_events) def keyPressEvent(self, e): # if e.key() == QtCore.Qt.Key_Space: # if self.press_lock: # if not self.press_lock_status: # self.on_press() # self.press_lock_status = True # else: # self.on_press() if e.key() == QtCore.Qt.Key_Control: if self.press_lock: if self.press_lock_status: self.press_lock_status = False if e.key() == QtCore.Qt.Key_Return: if self.phrase_prompts: if len(self.typed_versions) > 0: self.update_phrases(self.typed_versions[-1], "", next_phrase=True) else: self.update_phrases("", "", next_phrase=True) def init_locs(self): # size of keyboard key_chars = kconfig.key_chars self.N_rows = len(key_chars) self.N_keys_row = [] self.N_keys = 0 self.N_alpha_keys = 0 for row in range(self.N_rows): n_keys = len(key_chars[row]) for col in range(n_keys): if not isinstance(key_chars[row], list): if (key_chars[row][col] in kconfig.main_chars and (len(key_chars[row]) == 1)): self.N_alpha_keys = self.N_alpha_keys + 1 elif ((key_chars[row] == kconfig.space_char) and (len(key_chars[row]) == 1)): self.N_alpha_keys = self.N_alpha_keys + 1 elif (key_chars[row] == kconfig.break_chars[1] and (len(key_chars[row]) == 1)): self.N_alpha_keys = self.N_alpha_keys + 1 self.N_keys_row.append(n_keys) self.N_keys += n_keys # print "NKEYS is " + str(self.N_keys) # print "And N_alpha_keys is " + str(self.N_alpha_keys) # width difference when include letter word_clock_offset = 7 * kconfig.clock_rad rect_offset = word_clock_offset - kconfig.clock_rad word_offset = 8.5 * kconfig.clock_rad rect_end = rect_offset + kconfig.word_w # clock, key, word locations self.clock_centers = [] self.win_diffs = [] self.word_locs = [] self.char_locs = [] self.rect_locs = [] self.keys_li = [] self.keys_ref = [] index = 0 # how far into the clock_centers matrix word = 0 # word index key = 0 # key index # kconfig.N_pred = 2 # number of words per key self.key_height = 6.5 * kconfig.clock_rad self.w_canvas = 0 self.index_to_wk = [] # overall index to word or key index for row in range(0, self.N_rows): y = row * self.key_height self.w_canvas = max( self.w_canvas, self.N_keys_row[row] * (6 * kconfig.clock_rad + kconfig.word_w)) for col in range(0, self.N_keys_row[row]): x = col * (6 * kconfig.clock_rad + kconfig.word_w) # predictive words self.clock_centers.append( [x + word_clock_offset, y + 1 * kconfig.clock_rad]) self.clock_centers.append( [x + word_clock_offset, y + 3 * kconfig.clock_rad]) self.clock_centers.append( [x + word_clock_offset, y + 5 * kconfig.clock_rad]) # win diffs self.win_diffs.extend([ config.win_diff_base, config.win_diff_base, config.win_diff_base ]) # word position self.word_locs.append( [x + word_offset, y + 1 * kconfig.clock_rad]) self.word_locs.append( [x + word_offset, y + 3 * kconfig.clock_rad]) self.word_locs.append( [x + word_offset, y + 5 * kconfig.clock_rad]) # rectangles self.rect_locs.append([ x + rect_offset, y, x + rect_end, y + 2 * kconfig.clock_rad ]) self.rect_locs.append([ x + rect_offset, y + 2 * kconfig.clock_rad, x + rect_end, y + 4 * kconfig.clock_rad ]) self.rect_locs.append([ x + rect_offset, y + 4 * kconfig.clock_rad, x + rect_end, y + 6 * kconfig.clock_rad ]) # indices self.index_to_wk.append(word) self.index_to_wk.append(word + 1) self.index_to_wk.append(word + 2) index += 3 word += 3 ## key character # reference to index of key character key_char = key_chars[row][col] self.keys_li.append(key_chars[row][col]) self.keys_ref.append(index) self.index_to_wk.append(key) # key character position self.char_locs.append( [x + 2 * kconfig.clock_rad, y + 3 * kconfig.clock_rad]) # clock position for key character self.clock_centers.append( [x + 1 * kconfig.clock_rad, y + 3 * kconfig.clock_rad]) # rectangles self.rect_locs.append( [x, y, x + rect_offset, y + 6 * kconfig.clock_rad]) # win diffs if (key_char == kconfig.mybad_char) or ( key_char == kconfig.yourbad_char) or ( key_char == kconfig.back_char ): # or (key_char == kconfig.break_char) self.win_diffs.append(config.win_diff_high) else: self.win_diffs.append(config.win_diff_base) index += 1 key += 1 def gen_scale(self): scale_length = self.w_canvas / 2 # (len(kconfig.key_chars[0])-1)*kconfig.word_w tick_int = int((len(config.period_li) - 1) * kconfig.word_pt * 3 / (1.0 * scale_length)) + 1 self.time_rotate = config.period_li[self.rotate_index] def toggle_pause_button(self, value): self.pause_set = value self.mainWidget.sldLabel.setFocus() def toggle_sound_button(self, value): self.sound_set = value self.mainWidget.sldLabel.setFocus() def toggle_talk_button(self, value): self.talk_set = value self.mainWidget.sldLabel.setFocus( ) # focus on not toggle-able widget to allow keypress event def toggle_learn_button(self, value): config.is_learning = value self.mainWidget.sldLabel.setFocus( ) # focus on not toggle-able widget to allow keypress event def change_speed(self, index): # speed (as shown on scale) speed_index = int(index) # period (as stored in config.py) self.rotate_index = speed_index old_rotate = self.time_rotate self.time_rotate = config.period_li[self.rotate_index] self.bc.clock_inf.clock_util.change_period(self.time_rotate) # note period change in log file self.params_handle_dict['speed'].append( [time.time(), old_rotate, self.time_rotate]) # update the histogram self.draw_histogram() def init_histogram(self): # histogram bars = self.bc.get_histogram() self.bars = bars # undo_text self.undo_loc = [ (self.N_keys_row[self.N_rows - 1] - 1) * (6 * kconfig.clock_rad + kconfig.word_w) - self.w_canvas / 2, 2 * kconfig.clock_rad ] def draw_histogram(self, bars=None): if bars == None: bars = self.bc.get_histogram() # bars = [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] self.bars = bars self.mainWidget.histogram.update() def init_words(self): if self.word_pred_on == 1: num_words_total = 5 elif self.word_pred_on == 0: num_words_total = 0 else: num_words_total = kconfig.num_words_total (self.words_li, self.word_freq_li, self.key_freq_li) = self.lm.get_words(self.left_context, self.context, self.keys_li, num_words_total=num_words_total) if self.layout_preference == "emoji": self.key_freq_li = np.array([ np.log(1 / len(self.key_chars)) for i in range(len(self.key_chars)) ]) self.word_id = [] self.word_pair = [] word = 0 index = 0 windex = 0 self.words_on = [] self.words_off = [] self.word_list = [] # self.flag_args = [] temp_word_list = [ word_item for sublist in self.words_li for word_item in sublist ] for word_item in temp_word_list: if word_item != '': self.word_list.append(word_item) # print "TURNED ON AND WORD LIST IS" + str(self.word_list) len_con = len(self.context) for key in range(0, self.N_alpha_keys): for pred in range(0, kconfig.N_pred): word_str = self.words_li[key][pred] len_word = len(word_str) if (len_con > 1) and (len_word > kconfig.max_chars_display): word_str = "+" + word_str[len_con:len_word] self.word_pair.append((key, pred)) if word_str == '': self.words_off.append(index) else: self.words_on.append(index) windex += 1 word += 1 index += 1 self.words_on.append(index) self.word_pair.append((key, )) index += 1 for key in range(self.N_alpha_keys, self.N_keys): for pred in range(0, kconfig.N_pred): word_str = self.words_li[key][pred] self.word_pair.append((key, pred)) self.words_off.append(index) index += 1 self.words_on.append(index) self.word_pair.append((key, )) index += 1 self.typed_versions = [''] def draw_words(self): if self.word_pred_on == 1: num_words_total = 5 elif self.word_pred_on == 0: num_words_total = 0 else: num_words_total = kconfig.num_words_total (self.words_li, self.word_freq_li, self.key_freq_li) = self.lm.get_words(self.left_context, self.context, self.keys_li, num_words_total=num_words_total) self.mainWidget.clockgrid_widget.update_word_clocks(self.words_li) if self.layout_preference == "emoji": self.key_freq_li = np.array([ np.log(1 / len(self.key_chars)) for i in range(len(self.key_chars)) ]) word = 0 index = 0 self.words_on = [] self.words_off = [] self.word_list = [] temp_word_list = [ word_item for sublist in self.words_li for word_item in sublist ] for word_item in temp_word_list: if word_item != '' and all(c in "abcdefghijklmnopqrstuvwxyz\' " for c in word_item): self.word_list.append(word_item) len_con = len(self.context) windex = 0 for key in range(0, self.N_alpha_keys): for pred in range(0, kconfig.N_pred): word_str = self.words_li[key][pred] len_word = len(word_str) if len_con > 1 and len_word > kconfig.max_chars_display: word_str = "+" + word_str[len_con:len_word] if word_str == '': self.words_off.append(index) else: self.words_on.append(index) windex += 1 word += 1 index += 1 self.words_on.append(index) self.word_pair.append((key, )) index += 1 for key in range(self.N_alpha_keys, self.N_keys): for pred in range(0, kconfig.N_pred): self.word_pair.append((key, pred)) self.words_off.append(index) index += 1 self.words_on.append(index) self.word_pair.append((key, )) index += 1 def gen_word_prior(self, is_undo): self.word_score_prior = [] N_on = len(self.words_on) if not is_undo: for index in self.words_on: pair = self.word_pair[index] # word case if len(pair) == 2: (key, pred) = pair prob = self.word_freq_li[key][pred] + np.log( kconfig.rem_prob) self.word_score_prior.append(prob) else: key = pair[0] prob = self.key_freq_li[key] prob = prob + np.log(kconfig.rem_prob) if self.keys_li[key] == kconfig.mybad_char or self.keys_li[ key] == kconfig.yourbad_char: prob = np.log(kconfig.undo_prob) # if self.keys_li[key] in kconfig.break_chars: # prob = np.log(kconfig.break_prob) if self.keys_li[key] == kconfig.back_char: prob = np.log(kconfig.back_prob) if self.keys_li[key] == kconfig.clear_char: prob = np.log(kconfig.undo_prob) if self.keys_li[key] == kconfig.speak_char: prob = np.log(kconfig.undo_prob) self.word_score_prior.append(prob) else: for index in self.words_on: pair = self.word_pair[index] if len(pair) == 1: key = pair[0] if (self.keys_li[key] == kconfig.mybad_char) or ( self.keys_li[key] == kconfig.yourbad_char): prob = kconfig.undo_prob self.word_score_prior.append(np.log(prob)) else: self.word_score_prior.append(-float("inf")) else: self.word_score_prior.append(-float("inf")) def reset_context(self): self.left_context = "" self.context = "" self.typed = "" self.lm_prefix = "" self.draw_words() def update_phrases(self, cur_text, input_text, next_phrase=False): cur_phrase_typed, _ = self.phrases.compare(cur_text) cur_phrase_highlighted = self.phrases.highlight(cur_text) if next_phrase: if len(cur_text + input_text) > 0: self.text_stat_update(self.phrases.cur_phrase, self.typed_versions[-1]) self.typed_versions = [''] self.mainWidget.text_box.setText('') self.mainWidget.speed_slider.setEnabled(True) self.mainWidget.speed_slider_label.setStyleSheet( 'QLabel { color: blue }') self.mainWidget.sldLabel.setStyleSheet('QLabel { color: blue }') self.clear_text = False undo_text = 'Clear' self.phrases.sample() input_text = "" cur_phrase_highlighted = self.phrases.highlight("") self.reset_context() if self.is_write_data: choice_dict = { "time": time.time(), "undo": False, "backspace": False, "typed": "", "target": self.phrases.cur_phrase } self.params_handle_dict['choice'].append(choice_dict) self.mainWidget.text_box.setText("<p>" + cur_phrase_highlighted + "<\p><p>" + input_text + "</span><\p>") if self.layout_preference == 'emoji': if len(self.phrases.cur_phrase) > len(cur_text): target_emoji = self.phrases.cur_phrase[len(cur_text)] self.emoji_box_highlight = np.where( np.array(self.target_layout[:-1]) == target_emoji) else: self.emoji_box_highlight = [-1, -1] self.mainWidget.update() # next_emoji_clock = self.mainWidget.clockgrid_widget.clocks[4*key_index+3] def draw_typed(self): new_text = '' redraw_words = False # phrases delete = False undo = False undo_text = "" if self.phrase_prompts: previous_text = self.mainWidget.text_box.toPlainText().split( "\n")[-1] else: previous_text = self.mainWidget.text_box.toPlainText() if len(previous_text) > 0 and previous_text[-1] == "_": previous_text = previous_text[:-1] + " " if len(self.last_add_li) > 1: last_add = self.last_add_li[-1] if last_add > 0: # typed something new_text = self.typed[-last_add:len(self.typed)] undo_text = new_text elif last_add == -1: # backspace new_text = '' delete = True undo_text = kconfig.back_char else: new_text = '' undo_text = new_text if new_text in [". ", ", ", "? ", "! "]: previous_text = previous_text[:-1] redraw_words = True index = self.previous_winner if self.mainWidget.clockgrid_widget.clocks[index] != '': if self.mainWidget.clockgrid_widget.clocks[index].text in [ kconfig.mybad_char, 'Undo' ]: undo = True delete = False if self.typed_versions[-1] == '' and len(self.typed_versions) > 1: undo_text = 'Clear' if self.clear_text: if self.is_output_text: self.output_manager.remove_text(len(self.typed_versions[-1])) self.typed_versions += [''] input_text = "" self.lm_prefix = "" self.mainWidget.text_box.setText('') self.clear_text = False undo_text = 'Clear' elif delete: # self.prefix = self.prefix[:-1] if self.typed_versions[-1] != '': self.typed_versions += [previous_text[:-1]] new_text = self.typed_versions[-1] if len(new_text) > 0 and new_text[-1] == " ": new_text = new_text[:-1] + "_" input_text = "<span style='color:#000000;'>" + new_text + "</span>" self.mainWidget.text_box.setText( "<span style='color:#000000;'>" + new_text + "</span>") if self.is_output_text: self.output_manager.type_text(kconfig.back_char) else: input_text = "" elif undo: if len(self.typed_versions) > 1: if self.is_output_text: cur_length = len(self.typed_versions[-1]) prev_length = len(self.typed_versions[-2]) if prev_length > cur_length: text_diff_length = prev_length - cur_length text_diff = self.typed_versions[-2][-text_diff_length:] self.output_manager.type_text(text_diff) else: if cur_length >= 2 and self.typed_versions[-1][ -2:] in ["? ", "! ", ", ", ". "]: self.output_manager.type_text(kconfig.back_char + kconfig.back_char + " ") else: self.output_manager.remove_text(cur_length - prev_length) self.typed_versions = self.typed_versions[:-1] new_text = self.typed_versions[-1] if len(new_text) > 0 and new_text[-1] == " ": new_text = new_text[:-1] + "_" input_text = "<span style='color:#000000;'>" + new_text + "</span>" self.mainWidget.text_box.setText( "<span style='color:#000000;'>" + new_text + "</span>") else: input_text = "" else: self.typed_versions += [previous_text + new_text] if self.is_output_text: text_to_output = new_text for break_char in kconfig.break_chars: text_to_output = text_to_output.replace( break_char + " ", kconfig.back_char + break_char + " ") self.output_manager.type_text(text_to_output) if len(new_text) > 0 and new_text[-1] == " ": new_text = new_text[:-1] + "_" input_text = "<span style='color:#000000;'>" + previous_text + "</span><span style='color:#0000dd;'>"\ + new_text + "</span>" self.mainWidget.text_box.setText( "<span style='color:#000000;'>" + previous_text + "</span><span style='color:#0000dd;'>" + new_text + "</span>") if undo_text == kconfig.mybad_char: undo_text = "Undo" elif undo_text == kconfig.back_char: undo_text = "Backspace" elif undo_text == kconfig.clear_char: undo_text = "Clear" self.previous_undo_text = undo_text self.mainWidget.undo_label.setText("<font color='green'>" + undo_text + "</font>") if self.phrase_prompts: self.update_phrases(self.typed_versions[-1], input_text) def text_stat_update(self, phrase, typed): _, cur_error = calc_MSD(phrase, typed) self.error_data = [cur_error] + self.error_data decaying_weights = np.power(0.8, np.arange(len(self.error_data))) decaying_weights /= np.sum(decaying_weights) decay_avg_error = sum(np.array(self.error_data) * decaying_weights) error_delta = decay_avg_error / (self.decay_avg_error + 0.000001) self.decay_avg_error = decay_avg_error self.wpm_data = [(len(typed.split(" ")) - 1) / (time.time() - self.wpm_time) * 60] + self.wpm_data self.wpm_time = 0 decaying_weights = np.power(0.8, np.arange(len(self.wpm_data))) decaying_weights /= np.sum(decaying_weights) decay_avg_wpm = sum(np.array(self.wpm_data) * decaying_weights) wpm_delta = decay_avg_wpm / (self.decay_avg_wpm + 0.000001) self.decay_avg_wpm = decay_avg_wpm if error_delta > 1: error_red = int(min(4, error_delta) * 63) error_green = 0 else: error_green = int(min(4, 1 / error_delta) * 63) error_red = 0 if wpm_delta < 1: wpm_red = int(min(4, wpm_delta) * 63) wpm_green = 0 else: wpm_green = int(min(4, 1 / wpm_delta) * 63) wpm_red = 0 self.mainWidget.error_label.setStyleSheet("color: rgb(" + str(error_red) + ", " + str(error_green) + ", 0);") self.mainWidget.wpm_label.setStyleSheet("color: rgb(" + str(wpm_red) + ", " + str(wpm_green) + ", 0);") self.mainWidget.error_label.setText("Error Rate: " + str(round(decay_avg_error, 2))) self.mainWidget.wpm_label.setText("Words/Min: " + str(round(decay_avg_wpm, 2))) def on_pause(self): self.mainWidget.pause_timer.start(kconfig.pause_length) self.in_pause = True self.mainWidget.keygrid_widget.highlight = True self.mainWidget.keygrid_widget.update() def end_pause(self): self.mainWidget.pause_timer.stop() self.in_pause = False self.mainWidget.keygrid_widget.highlight = False self.mainWidget.keygrid_widget.update() self.on_timer() def on_timer(self): if self.focusWidget() == self.mainWidget.text_box: self.mainWidget.sldLabel.setFocus( ) # focus on not toggle-able widget to allow keypress event if self.bc_init: self.bc.clock_inf.clock_util.increment(self.words_on) is_input = win32api.GetAsyncKeyState(win32con.VK_SPACE) if is_input: if not self.output_manager.ignore_keyevent: win32api.keybd_event(0x20, 0, win32con.KEYEVENTF_KEYUP, 0) if self.is_output_text: win32api.keybd_event(0x08, 0, 0, 0) win32api.keybd_event(0x08, 0, win32con.KEYEVENTF_KEYUP, 0) if not self.pretrain: self.on_press() else: self.output_manager.ignore_keyevent = False def on_press(self): if time.time() - self.last_key_press_time > 1: self.last_key_press_time = time.time() # self.canvas.focus_set() if self.wpm_time == 0: self.wpm_time = time.time() if self.sound_set: self.play() if self.phrase_prompts: self.mainWidget.speed_slider.setEnabled(False) self.mainWidget.speed_slider_label.setStyleSheet( 'QLabel { color: grey }') self.mainWidget.sldLabel.setStyleSheet( 'QLabel { color: grey }') if self.is_write_data: self.num_presses += 1 self.bc.select() def play(self): self.sound_player.play() def highlight_winner(self, index): if self.mainWidget.clockgrid_widget.clocks[index] != '': self.mainWidget.clockgrid_widget.clocks[index].selected = True self.mainWidget.highlight_timer.start(kconfig.pause_length) def end_highlight(self): index = self.previous_winner if self.mainWidget.clockgrid_widget.clocks[index] != '': self.mainWidget.clockgrid_widget.clocks[index].selected = False self.mainWidget.highlight_timer.stop() def talk_winner(self, talk_string): pass def make_choice(self, index): is_undo = False is_equalize = False is_backspace = False # now pause (if desired) if self.pause_set == 1: self.on_pause() self.mainWidget.pause_timer.start(kconfig.pause_length) # highlight winner self.previous_winner = index self.highlight_winner(index) # initialize talk string talk_string = "" # if selected a key if (index - kconfig.N_pred) % (kconfig.N_pred + 1) == 0: new_char = self.keys_li[self.index_to_wk[index]] # special characters if new_char == kconfig.speak_char: new_char = "" if len(self.typed_versions) > 0: text_to_speak = self.typed_versions[-1] text_to_speak = text_to_speak.replace("_", " ") else: text_to_speak = "" self.output_manager.speak_text(text_to_speak) self.old_context_li.append(self.context) self.context = "" self.last_add_li.append(0) elif new_char == kconfig.space_char: if len(self.context) > 1: talk_string = self.context else: talk_string = "space" new_char = ' ' self.old_context_li.append(self.context) self.context = "" self.last_add_li.append(1) elif new_char == kconfig.mybad_char or new_char == kconfig.yourbad_char: talk_string = new_char # if added characters that turn if len(self.last_add_li) > 1: last_add = self.last_add_li.pop() self.context = self.old_context_li.pop() if last_add > 0: # if added text that turn self.typed = self.typed[0:-last_add] elif last_add == -1: # if backspaced that turn letter = self.btyped[-1] self.btyped = self.btyped[0:-1] self.typed += letter if new_char == kconfig.yourbad_char: is_equalize = True new_char = '' is_undo = True elif new_char == kconfig.back_char: talk_string = new_char is_backspace = True # if delete the last character that turn self.old_context_li.append(self.context) print(self.context) lt = len(self.typed) if lt > 0: # typed anything yet? self.btyped += self.typed[-1] self.last_add_li.append(-1) self.typed = self.typed[0:-1] lt -= 1 if lt == 0: self.context = "" elif len(self.context) > 0: self.context = self.context[0:-1] elif not (self.typed[-1]).isalpha(): self.context = "" else: i = -1 while (i >= -lt) and (self.typed[i].isalpha()): i -= 1 self.context = self.typed[i + 1:lt] new_char = '' elif new_char == kconfig.clear_char: talk_string = 'clear' new_char = '_' self.old_context_li.append(self.context) self.context = "" self.last_add_li.append(1) self.reset_context() self.clear_text = True elif new_char.isalpha( ) or new_char == "'" or new_char in kconfig.emoji_keys: talk_string = new_char self.old_context_li.append(self.context) self.context += new_char self.last_add_li.append(1) if new_char in [".", ",", "?", "!"]: talk_string = "Full stop" self.old_context_li.append(self.context) self.context = "" self.typed += new_char if " " + new_char in self.typed: self.last_add_li.append(2) self.typed = self.typed.replace(" " + new_char, new_char + " ") else: self.typed += new_char # if selected a word else: key = self.index_to_wk[index] // kconfig.N_pred pred = self.index_to_wk[index] % kconfig.N_pred new_word = self.words_li[key][pred] new_selection = new_word length = len(self.context) talk_string = new_word.rstrip(kconfig.space_char) # talk string if length > 0: self.typed = self.typed[0:-length] self.typed += new_word self.last_add_li.append(len(new_word) - len(self.context)) self.old_context_li.append(self.context) self.context = "" # update the screen if self.context != "": self.left_context = self.typed[:-len(self.context)] else: self.left_context = self.typed # write output if self.is_write_data: choice_dict = { "time": time.time(), "undo": is_undo, "backspace": is_backspace, "typed": self.typed } if self.phrase_prompts: choice_dict["target"] = self.phrases.cur_phrase self.params_handle_dict['choice'].append(choice_dict) self.draw_words() self.draw_typed() # update the word prior self.gen_word_prior(is_undo) # # talk the string # if self.talk_set.get() == 1: # self.talk_winner(talk_string) return self.words_on, self.words_off, self.word_score_prior, is_undo, is_equalize def present_choice(self): self.draw_histogram() # self.canvas.update_idletasks() def closeEvent(self, event): print("CLOSING THRU CLOSEEVENT") self.quit(event) # self.deleteLater() def quit(self, event=None): self.bc.quit_bc() self.close() def launch_help(self): help_window = Pretraining(self, self.mainWidget.screen_res, help_screen=True) help_window.show() self.pretrain = True self.mainWidget.repaint() def launch_retrain(self): retrain_window = Pretraining(self, self.mainWidget.screen_res) retrain_window.screen_num = 3 retrain_window.next_screen() retrain_window.show() # retrain_window.setCentralWidget(retrain_window.mainWidget) retrain_window.retrain = True self.pretrain = True self.mainWidget.update()
def validate(self, context: LexContext): if not context.city: raise ValidationError(LexContext.SLOT_CITY, Phrases.provide_city()) if context.date and not self.__is_valid_date(context.date): raise ValidationError(LexContext.SLOT_DATE, Phrases.provide_date())
def __init__(self, screen_res, app): super(Keyboard, self).__init__(screen_res) self.app = app self.font_scale = 1 self.key_chars = kconfig.key_chars self.key_chars_sorted = kconfig.key_chars_sorted self.key_config = "sorted" # self.key_config = "default" self.num_words = 7 self.words_first = True # self.words_first = False self.sound_set = True self.pause_set = True self.lm_prefix = "" self.left_context = "" self.typed_versions = [""] self.cwd = os.getcwd() self.gen_data_handel() self.up_handel = PickleUtil( os.path.join(self.user_handel, 'user_preferences.p')) user_preferences = self.up_handel.safe_load() if user_preferences is None: user_preferences = [ 'sorted', config.default_rotate_ind, config.default_pause_ind, True ] self.up_handel.safe_save(user_preferences) self.key_config, self.speed, self.pause_index, self.is_write_data = user_preferences self.scanning_delay = config.period_li[self.speed] self.extra_delay = config.pause_li[self.pause_index] self.params_handle_dict = { 'speed': [], 'extra_delay': [], 'params': [], 'start': [], 'press': [], 'choice': [] } self.num_presses = 0 self.last_press_time = time.time() self.last_update_time = time.time() self.next_frame_time = time.time() self.params_handle_dict['params'].append( [config.period_li[config.default_rotate_ind], config.theta0]) self.params_handle_dict['start'].append(time.time()) self.click_time_list = [] lm_path = os.path.join(os.path.join(self.cwd, 'resources'), 'lm_word_medium.kenlm') vocab_path = os.path.join(os.path.join(self.cwd, 'resources'), 'vocab_100k') self.lm = LanguageModel(lm_path, vocab_path) self.phrase_prompts = False if self.phrase_prompts: self.phrases = Phrases("resources/comm2.dev") else: self.phrases = None # determine keyboard positions # set up file handle for printing useful stuff # set up "typed" text self.typed = "" self.context = "" self.old_context_li = [""] self.last_add_li = [0] # set up "talked" text # check for speech # talk_fid = open(self.talk_file, 'wb') # write words self.generate_layout() self.draw_words() self.wpm_data = [] self.decay_avg_wpm = 0 self.wpm_time = 0 self.error_data = [] self.decay_avg_error = 1 self.row_scan = True self.row_scan_num = -2 self.col_scan = False self.col_scan_num = -1 self.init_ui() # animate self.on_timer()
def phrase_prompts_event(self): if self.phrase_prompts: phrase_status = False else: phrase_status = True # if self.phrases is None: if self.layout_preference == 'emoji': self.phrases = Phrases("resources/emojis.txt") else: self.phrases = Phrases( "resources/twitter-phrases/watch-combined.txt") self.phrase_prompts = phrase_status if phrase_status == True: self.phrases.sample() self.update_phrases(self.typed_versions[-1], "") choice_dict = { "time": time.time(), "undo": False, "backspace": False, "typed": "", "target": self.phrases.cur_phrase } self.params_handle_dict['choice'].append(choice_dict) self.is_write_data = True self.mainWidget.cb_learn.setChecked(True) self.mainWidget.cb_pause.setChecked(True) self.default_clock_action.trigger() self.med_font_action.trigger() if self.layout_preference != 'emoji': self.high_word_action.trigger() self.default_layout_action.trigger() self.mainWidget.cb_learn.setEnabled(False) self.mainWidget.cb_pause.setEnabled(False) self.mainWidget.speed_slider_label.setStyleSheet( 'QLabel { color: grey }') self.mainWidget.sldLabel.setStyleSheet('QLabel { color: grey }') self.default_clock_action.setEnabled(False) self.radar_clock_action.setEnabled(False) self.ball_clock_action.setEnabled(False) self.pacman_clock_action.setEnabled(False) self.bar_clock_action.setEnabled(False) # self.default_layout_action.setEnabled(False) self.qwerty_layout_action.setEnabled(False) self.high_contrast_action.setEnabled(False) self.low_word_action.setEnabled(False) self.high_word_action.setEnabled(False) self.off_word_action.setEnabled(False) self.small_font_action.setEnabled(False) self.med_font_action.setEnabled(False) self.large_font_action.setEnabled(False) self.log_data_action.setEnabled(False) else: self.typed_versions.append("") self.left_context = "" self.context = "" self.typed = "" self.lm_prefix = "" self.mainWidget.text_box.setText("") self.mainWidget.cb_learn.setEnabled(True) self.mainWidget.cb_pause.setEnabled(True) self.mainWidget.speed_slider.setEnabled(True) self.default_clock_action.setEnabled(True) self.radar_clock_action.setEnabled(True) self.ball_clock_action.setEnabled(True) self.pacman_clock_action.setEnabled(True) self.bar_clock_action.setEnabled(True) # self.default_layout_action.setEnabled(True) self.qwerty_layout_action.setEnabled(True) self.high_contrast_action.setEnabled(True) self.low_word_action.setEnabled(True) self.high_word_action.setEnabled(True) self.off_word_action.setEnabled(True) self.small_font_action.setEnabled(True) self.med_font_action.setEnabled(True) self.large_font_action.setEnabled(True) self.log_data_action.setEnabled(True) self.mainWidget.speed_slider_label.setStyleSheet( 'QLabel { color: black }') self.mainWidget.sldLabel.setStyleSheet('QLabel { color: black }') self.mainWidget.error_label.setStyleSheet("color: rgb(0, 0, 0);") self.mainWidget.wpm_label.setStyleSheet("color: rgb(0, 0, 0);") self.mainWidget.wpm_label.setText("Words/Min: " + "----") self.mainWidget.error_label.setText("Error Rate: " + "----") self.check_filemenu()
def __init__(self, cwd=os.getcwd(), job_num=None, sub_call=False): self.is_simulation = True if not sub_call: click_dist = np.zeros(80) click_dist[40] = 1 self.click_dist = list(click_dist) self.job_num = job_num self.plot = None self.working_dir=cwd if not sub_call: self.cwd = os.getcwd() self.N_pred = kconfig.N_pred self.prob_thres = kconfig.prob_thres self.num_words_total = 26*self.N_pred self.lm_left_context = True self.win_diff_base = config.win_diff_base self.rotate_index = config.default_rotate_ind self.time_rotate = config.period_li[self.rotate_index] self.phrases = Phrases(os.path.join(os.path.join( os.path.join(self.cwd, 'resources'), 'twitter-phrases'), 'watch-combined.txt')) self.easy_phrase = 1 self.false_positive_rate = 0.0 self.num_fp = 0 self.init_sim_data() lm_path = os.path.join(os.path.join(self.cwd, 'resources'), 'mix4_opt_min_lower_100k_4gram_2.5e-9_prob8_bo4_compress255.kenlm') vocab_path = os.path.join(os.path.join(self.cwd, 'resources'), 'vocab_lower_100k.txt') word_lm_path = os.path.join(os.path.join(self.cwd, 'resources'), 'mix4_opt_min_lower_100k_4gram_2.5e-9_prob8_bo4_compress255.kenlm') char_lm_path = os.path.join(os.path.join(self.cwd, 'resources'), 'mix4_opt_min_lower_12gram_6e-9_prob9_bo4_compress255.kenlm') vocab_path = os.path.join(os.path.join(self.cwd, 'resources'), 'vocab_lower_100k.txt') char_path = os.path.join(os.path.join(self.cwd, 'resources'), 'char_set.txt') self.lm = LanguageModel(word_lm_path, char_lm_path, vocab_path, char_path) self.time = Time(self) self.prev_time = 0 # 2 is turn fully on, 1 is turn on but reduce, 0 is turn off self.word_pred_on = 2 # Number of word clocks to display in case word prediction == 1 (reduced) self.reduce_display = 5 # # get user data before initialization # self.gen_data_handel() user_preferences = ['default', 1, False, 'alpha', 'off', 12, False] self.clock_type, self.font_scale, self.high_contrast,self.layout_preference, self.pf_preference, \ self.start_speed, self.is_write_data = user_preferences self.phrase_prompts = False # set to true for data collection mod self.key_chars = kconfig.key_chars # set up dictionary tree # splash = StartWindow(screen_res, True) self.pause_animation = False self.lm_prefix = "" self.left_context = "" self.in_pause = False # determine keyboard positions self.init_locs() # get old data if there is such # Just for default. Loaded again when bc initializes # set up file handle for printing useful stuff self.undefined = False self.gen_scale() self.pause_set = True # set up "typed" text self.typed = "" self.btyped = "" self.context = "" self.old_context_li = [""] self.last_add_li = [0] self.sound_set = True self.init_words() self.bars = kconfig.bars self.bc_init = False self.previous_undo_text = '' self.previous_winner = 0 # self.wpm_data = config.Stack(config.wpm_history_length) self.last_press_time = 0 self.wpm_time = 0 self.clear_text = False self.pretrain = False # get language model results self.gen_word_prior(False) self.clock_spaces = np.zeros((len(self.clock_centers), 2)) self.bc = BroderClocks(self) self.bc.init_follow_up(self.word_score_prior) self.clock_params = np.zeros((len(self.clock_centers), 8)) self.consent = False
class SimulatedUser: def __init__(self, cwd=os.getcwd(), job_num=None, sub_call=False): self.is_simulation = True if not sub_call: click_dist = np.zeros(80) click_dist[40] = 1 self.click_dist = list(click_dist) self.job_num = job_num self.plot = None self.working_dir=cwd if not sub_call: self.cwd = os.getcwd() self.N_pred = kconfig.N_pred self.prob_thres = kconfig.prob_thres self.num_words_total = 26*self.N_pred self.lm_left_context = True self.win_diff_base = config.win_diff_base self.rotate_index = config.default_rotate_ind self.time_rotate = config.period_li[self.rotate_index] self.phrases = Phrases(os.path.join(os.path.join( os.path.join(self.cwd, 'resources'), 'twitter-phrases'), 'watch-combined.txt')) self.easy_phrase = 1 self.false_positive_rate = 0.0 self.num_fp = 0 self.init_sim_data() lm_path = os.path.join(os.path.join(self.cwd, 'resources'), 'mix4_opt_min_lower_100k_4gram_2.5e-9_prob8_bo4_compress255.kenlm') vocab_path = os.path.join(os.path.join(self.cwd, 'resources'), 'vocab_lower_100k.txt') word_lm_path = os.path.join(os.path.join(self.cwd, 'resources'), 'mix4_opt_min_lower_100k_4gram_2.5e-9_prob8_bo4_compress255.kenlm') char_lm_path = os.path.join(os.path.join(self.cwd, 'resources'), 'mix4_opt_min_lower_12gram_6e-9_prob9_bo4_compress255.kenlm') vocab_path = os.path.join(os.path.join(self.cwd, 'resources'), 'vocab_lower_100k.txt') char_path = os.path.join(os.path.join(self.cwd, 'resources'), 'char_set.txt') self.lm = LanguageModel(word_lm_path, char_lm_path, vocab_path, char_path) self.time = Time(self) self.prev_time = 0 # 2 is turn fully on, 1 is turn on but reduce, 0 is turn off self.word_pred_on = 2 # Number of word clocks to display in case word prediction == 1 (reduced) self.reduce_display = 5 # # get user data before initialization # self.gen_data_handel() user_preferences = ['default', 1, False, 'alpha', 'off', 12, False] self.clock_type, self.font_scale, self.high_contrast,self.layout_preference, self.pf_preference, \ self.start_speed, self.is_write_data = user_preferences self.phrase_prompts = False # set to true for data collection mod self.key_chars = kconfig.key_chars # set up dictionary tree # splash = StartWindow(screen_res, True) self.pause_animation = False self.lm_prefix = "" self.left_context = "" self.in_pause = False # determine keyboard positions self.init_locs() # get old data if there is such # Just for default. Loaded again when bc initializes # set up file handle for printing useful stuff self.undefined = False self.gen_scale() self.pause_set = True # set up "typed" text self.typed = "" self.btyped = "" self.context = "" self.old_context_li = [""] self.last_add_li = [0] self.sound_set = True self.init_words() self.bars = kconfig.bars self.bc_init = False self.previous_undo_text = '' self.previous_winner = 0 # self.wpm_data = config.Stack(config.wpm_history_length) self.last_press_time = 0 self.wpm_time = 0 self.clear_text = False self.pretrain = False # get language model results self.gen_word_prior(False) self.clock_spaces = np.zeros((len(self.clock_centers), 2)) self.bc = BroderClocks(self) self.bc.init_follow_up(self.word_score_prior) self.clock_params = np.zeros((len(self.clock_centers), 8)) self.consent = False def init_sim_data(self): # self.init_clocks() self.num_selections = 0 self.sel_per_min = [] self.num_chars = 0 self.char_per_min = [] self.num_words = 0 self.num_presses = 0 self.press_per_sel = [] self.press_per_char = [] self.press_per_word = [] self.num_errors = 0 self.error_rate_avg = [] self.kde_errors = [] self.kde_errors_avg = None self.update_radii = False # self.on_timer() self.winner = False self.winner_text = "" def parameter_metrics(self, parameters, num_clicks=500, trials=1, attribute=None): self.init_sim_data() # Load parameters or use defaults if "click_dist" in parameters: self.click_dist_xrange, self.click_dist = parameters["click_dist"] else: click_dist = np.zeros(80) click_dist[40] = 1 self.click_dist = list(click_dist) self.click_dist_xrange = (np.arange(0, 80)-40)/80 if "dp_dist" in parameters: self.dp_kernel = parameters["dp_dist"] else: self.dp_kernel = None if "N_pred" in parameters: self.N_pred = parameters["N_pred"] else: self.N_pred = kconfig.N_pred if "prob_thresh" in parameters: self.prob_thres = parameters["prob_thresh"] else: self.prob_thres = kconfig.prob_thres if "num_words" in parameters: self.num_words_total = parameters["num_words"] else: self.num_words_total = 26*self.N_pred if "left_context" in parameters: self.lm_left_context = parameters["left_context"] else: self.lm_left_context = True if "win_diff" in parameters: self.win_diff_base = parameters["win_diff"] else: self.win_diff_base = config.win_diff_base if "time_rotate" in parameters: self.rotate_index = parameters["time_rotate"] self.time_rotate = config.period_li[self.rotate_index] self.change_speed() else: self.rotate_index = config.default_rotate_ind self.time_rotate = config.period_li[self.rotate_index] # if "corpus" in parameters: # self.phrases = Phrases(parameters["corpus"]) # self.easy_phrase = parameters["corpus"] == "resources/comm2.dev" # else: # self.phrases = Phrases("resources/comm2.dev") # self.easy_phrase = 1 if "false_positive" in parameters: self.false_positive_rate = parameters["false_positive"] else: self.false_positive_rate = 0.00 self.gen_data_dir() for trial in range(trials): self.__init__(sub_call=True) self.update_progress_bar(trial, trials, num_clicks) while self.num_presses < num_clicks: text = self.phrases.sample() # print("New Phrase: \"" + text + "\"") self.type_text(text, verbose=False) # print(round(self.num_presses/num_clicks*100), " %") self.num_chars += int(len(self.typed)) self.num_words += int(len(self.typed.split(" "))) self.num_errors += calc_MSD(self.typed, text)[0] # print(calc_MSD(self.typed, text)) # # print(self.typed) self.typed = "" # reset tracking and context for lm -- new sentence self.update_progress_bar(trial, trials, num_clicks) sys.stdout.write('\r') sys.stdout.write(" \n") sys.stdout.flush() print("selections per minute: ", self.num_selections / (self.time.time() / 60)) print("characters per minute: ", self.num_chars / (self.time.time() / 60)) print("presses per selection: ", self.num_presses / (self.num_selections + 1)) print("presses per character: ", self.num_presses / (self.num_chars + 1)) print("presses per word: ", self.num_presses / (self.num_words + 1)) print("error rate: ", self.num_errors / (self.num_selections + 1) * 100) print("fp rate: ", self.num_fp / self.time.time()) self.update_sim_averages(trials) self.num_selections = 0 self.num_chars = 0 self.num_words = 0 self.num_presses = 0 self.num_errors = 0 self.kde_errors = [] self.save_simulation_data(attribute=attribute) def update_progress_bar(self, trial, trials, num_clicks): progress = int((trial + self.num_presses / num_clicks) / trials * 50) sys.stdout.write('\r') # the exact output you're looking for: sys.stdout.write("[%-50s] %d%%" % ('=' * progress, 2 * progress)) sys.stdout.flush() def update_sim_averages(self, num_trials): time_int = self.time.time() - self.prev_time self.prev_time = float(self.time.time()) self.sel_per_min += [self.num_selections / (time_int / 60)] self.char_per_min += [self.num_chars / (time_int / 60)] if self.num_selections > 0: self.press_per_sel += [self.num_presses / self.num_selections] self.error_rate_avg += [self.num_errors / self.num_selections] else: self.press_per_sel += [float("inf")] self.error_rate_avg += [float("inf")] if self.num_chars > 0: self.press_per_char += [self.num_presses / self.num_chars] else: self.press_per_char += [float("inf")] if self.num_words > 0: self.press_per_word += [self.num_presses / self.num_words] else: self.press_per_word += [float("inf")] if self.kde_errors_avg is None: self.kde_errors_avg = np.array(self.kde_errors) / num_trials else: length = min(len(self.kde_errors_avg), len(self.kde_errors)) if len(self.kde_errors_avg) < len(self.kde_errors): self.kde_errors_avg += np.array(self.kde_errors[:length]) / num_trials else: self.kde_errors_avg = self.kde_errors_avg[:length] + np.array(self.kde_errors) / num_trials def type_text(self, text, verbose=False): self.target_text = text prev_target_text = self.target_text success = True while len(self.target_text) > 0 or not success: if success: prev_target_text = self.target_text target_clock, self.target_text = self.next_target(self.target_text) else: target_clock, self.target_text = self.next_target(prev_target_text) if verbose: print("Target: ", self.clock_to_text(target_clock), target_clock) # recovery_time = 0.9 # self.time.set_time(self.time.time() + recovery_time) # self.on_timer() success = self.select_clock(target_clock, verbose=verbose) if success is not None: if not success: undo_depth = 1 while undo_depth > 0: undo_clock = self.keys_li.index(kconfig.mybad_char) * (self.N_pred + 1) + self.N_pred undo_success = self.select_clock(undo_clock, verbose=verbose, undo_depth=undo_depth) if undo_success is not None: if undo_success: undo_depth -= 1 else: undo_depth += 1 # recovery_time = 0.9 # self.time.set_time(self.time.time() + recovery_time) # self.on_timer() def select_clock(self, target_clock, verbose=False, undo_depth=0): ndt = self.bc.clock_inf.clock_util.num_divs_time num_press = 0 time_elapsed = 0 for i in range(15): self.winner = False if self.bc.clock_inf.clock_util.cur_hours[target_clock] > ndt / 2: time_delta = (ndt * 3 / 2 - self.bc.clock_inf.clock_util.cur_hours[ target_clock]) / ndt * self.time_rotate else: time_delta = (ndt / 2 - self.bc.clock_inf.clock_util.cur_hours[target_clock]) / ndt * self.time_rotate click_offset = np.random.choice(self.click_dist_xrange, p=self.click_dist) dp_sample = self.dp_kernel.resample(1)[0][0] time_delta += click_offset while time_delta < dp_sample: time_delta += self.time_rotate time_elapsed += time_delta self.time.set_time(self.time.time() + time_delta) self.on_timer() self.on_press() num_press += 1 # recovery_time = 0.4 # self.time.set_time(self.time.time() + recovery_time) # self.on_timer() if self.winner: if len(self.bc.clock_inf.win_history) > 1: selected_clock = self.bc.clock_inf.win_history[1] else: selected_clock = self.bc.clock_inf.win_history[0] if verbose: if undo_depth > 0: tab = " " else: tab = "" print(tab + ">>> Clock " + self.winner_text + " selected in " + str(num_press) + " presses, " + str(round(time_elapsed, 2)) + " seconds") print(tab + " Typed \"" + self.typed + "\"") self.winner = False if selected_clock != target_clock: undo_clock = self.keys_li.index(kconfig.mybad_char) * (self.N_pred + 1) + self.N_pred self.kde_errors += [self.kde_mse()] * num_press if target_clock == undo_clock and self.winner_text == kconfig.mybad_char: # if successful undo return True else: self.num_errors += 1 return False else: if undo_depth == 0: self.num_selections += 1 return True self.kde_errors += [self.kde_mse()] * num_press return None def next_target(self, text): words = text.split(" ") if "" in words: words.remove("") if len(words) > 1: remaining_words = "" for word in words[1:]: if remaining_words != "": remaining_words += " " remaining_words += word first_word = words[0] else: remaining_words = "" first_word = words[0] target_word = self.context + first_word + " " if target_word in self.word_list: words_list_flattened = [word for sublist in self.words_li for word in sublist+[""]] return words_list_flattened.index(target_word), remaining_words target_letter = text[0] if target_letter == " ": target_letter = "_" return self.keys_li.index(target_letter)*(self.N_pred+1) + self.N_pred, text[1:] def clock_to_text(self, index): if (index - self.N_pred) % (self.N_pred + 1) == 0: typed = self.keys_li[self.index_to_wk[index]] else: key = self.index_to_wk[index] // self.N_pred pred = self.index_to_wk[index] % self.N_pred typed = self.words_li[key][pred] return typed def gen_false_positive(self, return_time): self.num_fp += 1 self.on_timer() self.on_press() self.time.set_time(return_time) self.on_timer() def plot_hist(self): bars = self.bc.get_histogram() bars = np.array(bars)/np.sum(bars) self.plot.plot_hist(bars) def kde_mse(self): bars = self.bc.get_histogram() bars = np.array(bars) / np.sum(bars) # plt.plot(bars) # plt.show() return np.sum(np.square(bars - self.click_dist)) def init_locs(self): # size of keyboard self.N_rows = len(self.key_chars) self.N_keys_row = [] self.N_keys = 0 self.N_alpha_keys = 0 for row in range(0, self.N_rows): n_keys = len(self.key_chars[row]) for col in range(0, n_keys): if not isinstance(self.key_chars[row][col], list): if self.key_chars[row][col].isalpha() and (len(self.key_chars[row][col]) == 1): self.N_alpha_keys = self.N_alpha_keys + 1 elif self.key_chars[row][col] == kconfig.space_char and (len(self.key_chars[row][col]) == 1): self.N_alpha_keys = self.N_alpha_keys + 1 elif self.key_chars[row][col] == kconfig.break_chars[1] and ( len(self.key_chars[row][col]) == 1): self.N_alpha_keys = self.N_alpha_keys + 1 self.N_keys_row.append(n_keys) self.N_keys += n_keys # print "NKEYS is " + str(self.N_keys) # print "And N_alpha_keys is " + str(self.N_alpha_keys) # width difference when include letter word_clock_offset = 7 * kconfig.clock_rad rect_offset = word_clock_offset - kconfig.clock_rad word_offset = 8.5 * kconfig.clock_rad rect_end = rect_offset + kconfig.word_w # clock, key, word locations self.clock_centers = [] self.win_diffs = [] self.word_locs = [] self.char_locs = [] self.rect_locs = [] self.keys_li = [] self.keys_ref = [] index = 0 # how far into the clock_centers matrix word = 0 # word index key = 0 # key index # self.N_pred = 2 # number of words per key self.key_height = 6.5 * kconfig.clock_rad self.w_canvas = 0 self.index_to_wk = [] # overall index to word or key index for row in range(0, self.N_rows): y = row * self.key_height self.w_canvas = max(self.w_canvas, self.N_keys_row[row] * (6 * kconfig.clock_rad + kconfig.word_w)) for col in range(0, self.N_keys_row[row]): x = col * (6 * kconfig.clock_rad + kconfig.word_w) # predictive words for word_index in range(self.N_pred): self.clock_centers.append([x + word_clock_offset, y + (1 + word_index * 2) * kconfig.clock_rad]) self.word_locs.append([x + word_offset, y + (1 + word_index * 2) * kconfig.clock_rad]) # self.clock_centers.append([x + word_clock_offset, y + 3 * kconfig.clock_rad]) # self.clock_centers.append([x + word_clock_offset, y + 5 * kconfig.clock_rad]) self.index_to_wk.append(word + word_index) # win diffs self.win_diffs.extend([self.win_diff_base for i in range(self.N_pred)]) # word position # self.word_locs.append([x + word_offset, y + 1 * kconfig.clock_rad]) # self.word_locs.append([x + word_offset, y + 3 * kconfig.clock_rad]) # self.word_locs.append([x + word_offset, y + 5 * kconfig.clock_rad]) # rectangles self.rect_locs.append([x + rect_offset, y, x + rect_end, y + 2 * kconfig.clock_rad]) self.rect_locs.append( [x + rect_offset, y + 2 * kconfig.clock_rad, x + rect_end, y + 4 * kconfig.clock_rad]) self.rect_locs.append( [x + rect_offset, y + 4 * kconfig.clock_rad, x + rect_end, y + 6 * kconfig.clock_rad]) # indices # self.index_to_wk.append(word) # self.index_to_wk.append(word + 1) # self.index_to_wk.append(word + 2) index += self.N_pred word += self.N_pred ## key character # reference to index of key character key_char = self.key_chars[row][col] self.keys_li.append(self.key_chars[row][col]) self.keys_ref.append(index) self.index_to_wk.append(key) # key character position self.char_locs.append([x + 2 * kconfig.clock_rad, y + 3 * kconfig.clock_rad]) # clock position for key character self.clock_centers.append([x + 1 * kconfig.clock_rad, y + 3 * kconfig.clock_rad]) # rectangles self.rect_locs.append([x, y, x + rect_offset, y + 6 * kconfig.clock_rad]) # win diffs if (key_char == kconfig.mybad_char) or (key_char == kconfig.yourbad_char) or ( key_char == kconfig.back_char): # or (key_char == kconfig.break_char) self.win_diffs.append(config.win_diff_high) else: self.win_diffs.append(self.win_diff_base) index += 1 key += 1 def gen_scale(self): scale_length = self.w_canvas / 2 # (len(kconfig.key_chars[0])-1)*kconfig.word_w tick_int = int((len(config.period_li) - 1) * kconfig.word_pt * 3 / (1.0 * scale_length)) + 1 self.time_rotate = config.period_li[self.rotate_index] def change_speed(self): self.bc.clock_inf.clock_util.change_period(self.time_rotate) def init_histogram(self): # histogram bars = self.bc.get_histogram() self.bars = bars # undo_text self.undo_loc = [ (self.N_keys_row[self.N_rows - 1] - 1) * (6 * kconfig.clock_rad + kconfig.word_w) - self.w_canvas / 2, 2 * kconfig.clock_rad] def draw_histogram(self, bars=None): if bars == None: bars = self.bc.get_histogram() # bars = [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] self.bars = bars # self.mainWidget.histogram.update() def init_words(self): if self.lm_left_context: lm_left_context = self.left_context else: lm_left_context = "" (self.words_li, self.word_freq_li, self.key_freq_li) = self.lm.get_words(lm_left_context, self.context, self.keys_li, num_words_total=self.num_words_total) self.word_id = [] self.word_pair = [] word = 0 index = 0 windex = 0 self.words_on = [] self.words_off = [] self.word_list = [] # self.flag_args = [] # if word prediction on but reduced if self.word_pred_on == 1: flat_freq_list = np.array([freq for sublist in self.word_freq_li for freq in sublist]) if len(flat_freq_list) >= self.reduce_display: # replacement_count = 0 for arg in flat_freq_list.argsort()[-self.reduce_display:]: word_to_add = self.words_li[(arg // self.N_pred)][arg % self.N_pred] # ============================================================================= # while len(word_to_add) == 1: # replacement_count +=1 # new_arg = flat_freq_list.argsort()[-self.reduce_display-replacement_count] # word_to_add = self.words_li[(new_arg /3)][new_arg%3] # ============================================================================= if word_to_add != '': if word_to_add not in self.word_list: self.word_list.append(word_to_add) else: temp_word_list = [word_item for sublist in self.words_li for word_item in sublist] for word_item in temp_word_list: if word_item != '': self.word_list.append(word_item) # if word prediction completely on elif self.word_pred_on == 2: temp_word_list = [word_item for sublist in self.words_li for word_item in sublist] for word_item in temp_word_list: if word_item != '': self.word_list.append(word_item) # print "TURNED ON AND WORD LIST IS" + str(self.word_list) len_con = len(self.context) for key in range(0, self.N_alpha_keys): for pred in range(0, self.N_pred): word_str = self.words_li[key][pred] len_word = len(word_str) if (len_con > 1) and (len_word > kconfig.max_chars_display): word_str = "+" + word_str[len_con:len_word] self.word_pair.append((key, pred)) if word_str == '': self.words_off.append(index) else: # turn word prediciton off if self.word_pred_on == 0: self.words_off.append(index) # word prediction completely on elif self.word_pred_on == 2: self.words_on.append(index) # word prediction turned on but reduced # rank words frequency and display only three else: if windex in flat_freq_list.argsort()[-self.reduce_display:]: self.words_on.append(index) else: self.words_off.append(index) windex += 1 word += 1 index += 1 self.words_on.append(index) self.word_pair.append((key,)) index += 1 for key in range(self.N_alpha_keys, self.N_keys): for pred in range(0, self.N_pred): word_str = self.words_li[key][pred] self.word_pair.append((key, pred)) self.words_off.append(index) index += 1 self.words_on.append(index) self.word_pair.append((key,)) index += 1 self.typed_versions = [''] def draw_words(self): if self.lm_left_context: lm_left_context = self.left_context else: lm_left_context = "" (self.words_li, self.word_freq_li, self.key_freq_li) = self.lm.get_words(lm_left_context, self.context, self.keys_li, num_words_total=self.num_words_total) word = 0 index = 0 self.words_on = [] self.words_off = [] self.word_list = [] # if word prediction on but reduced if self.word_pred_on == 1: flat_freq_list = np.array([np.exp(freq) for sublist in self.word_freq_li for freq in sublist]) if len(flat_freq_list) >= self.reduce_display: for arg in flat_freq_list.argsort()[-self.reduce_display:]: word_to_add = self.words_li[(arg // 3)][arg % 3] if word_to_add != '': self.word_list.append(word_to_add) else: temp_word_list = [word_item for sublist in self.words_li for word_item in sublist] for word_item in temp_word_list: if word_item != '': self.word_list.append(word_item) self.word_list.reverse() # if word prediction completely on elif self.word_pred_on == 2: temp_word_list = [word_item for sublist in self.words_li for word_item in sublist] for word_item in temp_word_list: if word_item != '': self.word_list.append(word_item) len_con = len(self.context) windex = 0 for key in range(0, self.N_alpha_keys): for pred in range(0, self.N_pred): word_str = self.words_li[key][pred] len_word = len(word_str) if len_con > 1 and len_word > kconfig.max_chars_display: word_str = "+" + word_str[len_con:len_word] if word_str == '': self.words_off.append(index) else: # turn word prediciton off if self.word_pred_on == 0: self.words_off.append(index) # word prediction completely on elif self.word_pred_on == 2: self.words_on.append(index) # word prediction turned on but reduced # rank words frequency and display only three else: if windex in flat_freq_list.argsort()[-self.reduce_display:]: self.words_on.append(index) else: self.words_off.append(index) windex += 1 word += 1 index += 1 self.words_on.append(index) self.word_pair.append((key,)) index += 1 for key in range(self.N_alpha_keys, self.N_keys): for pred in range(0, self.N_pred): self.word_pair.append((key, pred)) self.words_off.append(index) index += 1 self.words_on.append(index) self.word_pair.append((key,)) index += 1 # self.mainWidget.update_clocks() # self.init_clocks() def gen_word_prior(self, is_undo): self.word_score_prior = [] N_on = len(self.words_on) if not is_undo: for index in self.words_on: pair = self.word_pair[index] # word case if len(pair) == 2: (key, pred) = pair prob = self.word_freq_li[key][pred] + np.log(kconfig.rem_prob) self.word_score_prior.append(prob) else: key = pair[0] prob = self.key_freq_li[key] prob = prob + np.log(kconfig.rem_prob) if self.keys_li[key] == kconfig.mybad_char or self.keys_li[key] == kconfig.yourbad_char: prob = np.log(kconfig.undo_prob) # if self.keys_li[key] in kconfig.break_chars: # prob = np.log(kconfig.break_prob) if self.keys_li[key] == kconfig.back_char: prob = np.log(kconfig.back_prob) if self.keys_li[key] == kconfig.clear_char: prob = np.log(kconfig.undo_prob) self.word_score_prior.append(prob) else: for index in self.words_on: pair = self.word_pair[index] if len(pair) == 1: key = pair[0] if (self.keys_li[key] == kconfig.mybad_char) or (self.keys_li[key] == kconfig.yourbad_char): prob = kconfig.undo_prob self.word_score_prior.append(np.log(prob)) else: self.word_score_prior.append(0) else: self.word_score_prior.append(0) def reset_context(self): self.left_context = "" self.context = "" self.typed = "" self.lm_prefix = "" self.draw_words() def draw_typed(self): self.wpm_update() redraw_words = False # phrases delete = False undo = False if self.phrase_prompts: previous_text = self.mainWidget.text_box.toPlainText().split("\n")[-1] else: previous_text = self.mainWidget.text_box.toPlainText() if len(self.last_add_li) > 1: last_add = self.last_add_li[-1] if last_add > 0: # typed something new_text = self.typed[-last_add:len(self.typed)] undo_text = new_text elif last_add == -1: # backspace new_text = '' delete = True undo_text = kconfig.back_char else: new_text = '' undo_text = new_text self.winner_text = new_text if new_text in [". ",", ","? ", "! "]: previous_text = previous_text[:-1] redraw_words = True index = self.previous_winner if self.mainWidget.clocks[index] != '': if self.mainWidget.clocks[index].text in [kconfig.mybad_char, 'Undo']: undo = True delete = False if self.typed_versions[-1] == '' and len(self.typed_versions) > 1: undo_text = 'Clear' if self.clear_text: self.typed_versions += [''] self.mainWidget.text_box.setText('') self.clear_text = False undo_text = 'Clear' elif delete: # self.prefix = self.prefix[:-1] if self.typed_versions[-1] != '': self.typed_versions += [previous_text[:-1]] self.mainWidget.text_box.setText("<span style='color:#000000;'>" + self.typed_versions[-1] + "</span>") elif undo: if len(self.typed_versions) > 1: self.typed_versions = self.typed_versions[:-1] self.mainWidget.text_box.setText("<span style='color:#000000;'>" + self.typed_versions[-1] + "</span>") else: self.typed_versions += [previous_text + new_text] self.mainWidget.text_box.setText( "<span style='color:#000000;'>" + previous_text + "</span><span style='color:#00dd00;'>" + new_text + "</span>") if undo_text == kconfig.mybad_char: undo_text = "Undo" elif undo_text == kconfig.back_char: undo_text = "Backspace" elif undo_text == kconfig.clear_char: undo_text = "Clear" self.previous_undo_text = undo_text self.mainWidget.undo_label.setText("<font color='green'>" + undo_text + "</font>") if redraw_words: self.reset_context() if self.phrase_prompts: self.update_phrases(self.typed_versions[-1]) def on_timer(self): self.bc.clock_inf.clock_util.increment(self.words_on) def on_press(self): self.num_presses += 1 self.last_press_time = self.time.time() self.bc.select() def make_choice(self, index): is_undo = False is_equalize = False # highlight winner self.previous_winner = index # self.highlight_winner(index) # initialize talk string talk_string = "" # if selected a key if (index - self.N_pred) % (self.N_pred + 1) == 0: new_char = self.keys_li[self.index_to_wk[index]] # special characters if new_char == kconfig.space_char: if len(self.context) > 1: talk_string = self.context else: talk_string = "space" new_char = ' ' self.old_context_li.append(self.context) self.context = "" self.last_add_li.append(1) elif new_char == kconfig.mybad_char or new_char == kconfig.yourbad_char: talk_string = new_char # if added characters that turn if len(self.last_add_li) > 1: last_add = self.last_add_li.pop() self.context = self.old_context_li.pop() if last_add > 0: # if added text that turn self.typed = self.typed[0:-last_add] elif last_add == -1: # if backspaced that turn letter = self.btyped[-1] self.btyped = self.btyped[0:-1] self.typed += letter if new_char == kconfig.yourbad_char: is_equalize = True new_char = '' is_undo = True elif new_char == kconfig.back_char: talk_string = new_char # if delete the last character that turn self.old_context_li.append(self.context) # print(self.context) lt = len(self.typed) if lt > 0: # typed anything yet? self.btyped += self.typed[-1] self.last_add_li.append(-1) self.typed = self.typed[0:-1] lt -= 1 if lt == 0: self.context = "" elif len(self.context) > 0: self.context = self.context[0:-1] elif not (self.typed[-1]).isalpha(): self.context = "" else: i = -1 while (i >= -lt) and (self.typed[i].isalpha()): i -= 1 self.context = self.typed[i + 1:lt] new_char = '' elif new_char == kconfig.clear_char: talk_string = 'clear' new_char = '_' self.old_context_li.append(self.context) self.context = "" self.last_add_li.append(1) self.clear_text = True elif new_char.isalpha() or new_char == "'": talk_string = new_char self.old_context_li.append(self.context) self.context += new_char self.last_add_li.append(1) if new_char in [".", ",", "?", "!"]: talk_string = "Full stop" self.old_context_li.append(self.context) self.context = "" self.typed += new_char if " "+new_char in self.typed: self.last_add_li.append(1) else: self.last_add_li.append(1) self.typed = self.typed.replace(" "+new_char, new_char+" ") else: self.typed += new_char # if selected a word else: key = self.index_to_wk[index] // self.N_pred pred = self.index_to_wk[index] % self.N_pred new_word = self.words_li[key][pred] new_selection = new_word length = len(self.context) talk_string = new_word.rstrip(kconfig.space_char) # talk string if length > 0: self.typed = self.typed[0:-length] self.typed += new_word self.last_add_li.append(len(new_word) - len(self.context)) self.old_context_li.append(self.context) self.context = "" # update the screen if self.context != "": self.left_context = self.typed[:-len(self.context)] else: self.left_context = self.typed self.draw_words() # self.draw_typed() # update the word prior self.gen_word_prior(is_undo) # # talk the string # if self.talk_set.get() == 1: # self.talk_winner(talk_string) # write output if self.is_write_data: self.params_handle_dict['choice'].append([time.time(), is_undo, is_equalize, self.typed]) return self.words_on, self.words_off, self.word_score_prior, is_undo, is_equalize def save_simulation_data(self, attribute=None): data_file = os.path.join(self.data_loc, "npred_"+str(self.N_pred)+"_nwords_"+str(self.num_words_total)+"_lcon_" +str(int(self.lm_left_context))+"_wdiff_"+str(round(np.exp(self.win_diff_base))) +"_rot_"+str(self.rotate_index)+"_cor_"+str(self.easy_phrase)+"_fp_" +str(self.false_positive_rate)+".p") data_handel = PickleUtil(data_file) dist_id_file = os.path.join(self.data_loc, "dist_id.p") dist_id_handel = PickleUtil(dist_id_file) dist_id_handel.safe_save(self.click_dist) data_dict = dict() data_dict["N_pred"] = self.N_pred data_dict["prob_thresh"] = self.prob_thres data_dict["win_diff"] = self.win_diff_base data_dict["num_words"] = self.num_words_total data_dict["time_rotate"] = self.time_rotate data_dict["false_positive"] = self.false_positive_rate data_dict["errors"] = self.error_rate_avg data_dict["selections"] = self.sel_per_min data_dict["characters"] = self.char_per_min data_dict["presses_sel"] = self.press_per_sel data_dict["presses_char"] = self.press_per_char data_dict["presses_word"] = self.press_per_word data_dict["kde_mses"] = self.kde_errors_avg data_dict["kde"] = self.bc.get_histogram() data_dict["kde"] = self.bc.get_histogram() if attribute is not None: data_dict["attribute"] = attribute data_handel.safe_save(data_dict) def gen_data_dir(self): if self.job_num is not None: if not os.path.exists(os.path.join(self.working_dir, "sim_data")): try: os.mkdir(os.path.join(self.working_dir, "sim_data")) except FileExistsError: pass if not os.path.exists(os.path.join(os.path.join(self.working_dir, "sim_data"), str(self.job_num))): try: os.mkdir(os.path.join(os.path.join(self.working_dir, "sim_data"), str(self.job_num))) except FileExistsError: pass self.data_loc = os.path.join(os.path.join(self.working_dir, "sim_data"), str(self.job_num)) else: dist_found = False highest_user_num = 0 if not os.path.exists(os.path.join(self.working_dir, "sim_data")): try: os.mkdir(os.path.join(self.working_dir, "sim_data")) except FileExistsError: pass for path, dir, files in os.walk(os.path.join(self.working_dir, "sim_data")): highest_user_num = max(max([0]+[int(d) for d in dir]), highest_user_num) for file in files: if "dist_id" in file: file_handel = PickleUtil(os.path.join(path, file)) dist_id = file_handel.safe_load() if np.sum(np.array(dist_id) - np.array(self.click_dist)) == 0: dist_found = True self.data_loc = path if not dist_found: try: os.mkdir(os.path.join(os.path.join(self.working_dir, "sim_data"), str(highest_user_num+1))) except FileExistsError: pass self.data_loc = os.path.join(os.path.join(self.working_dir, "sim_data"), str(highest_user_num+1)) def closeEvent(self, event): print("CLOSING THRU CLOSEEVENT") self.quit(event) # self.deleteLater() def quit(self, event=None): self.bc.quit_bc() self.close()
def learn_default_responses(file, phrases): Phrases.add_phrases(file, phrases)
class DataUtil: def __init__(self, data_directory): self.data_dir = data_directory self.phrase_util = Phrases( "resources/twitter-phrases/watch-combined.txt") self.plot_colors = [ "#0000ff", "#4400ff", "#8800ff", "#cc00ff", "#ff00cc", "#ff0088", "#ff0044" ] self.click_data_files = [] self.click_context_files = [] self.preconfig_file = None for path, dir, files in os.walk(self.data_dir): for file in files: if "click_time_log" in file: self.click_data_files += [os.path.join(path, file)] if "params_data_use_num" in file: self.click_context_files += [os.path.join(path, file)] if "preconfig" in file: self.preconfig_file = os.path.join(path, file) self.rel_click_data = [] self.abs_click_data = [] self.selection_data = [] self.speed_changes = [] self.kde_list = [] self.clicks_by_speed = {} self.clicks_by_phrase = {} self.phrase_stats = {} self.corrected_clicks = None def load_data(self): # size_list = [] self.full_click_data = [] for data_file in self.click_data_files: data_handel = PickleUtil(data_file) click_dict = data_handel.safe_load() # size_list += [len(click_dict["click time list"])] self.full_click_data += click_dict["click time list"] self.rel_click_data += [ click[0] for click in click_dict["click time list"] ] self.rel_click_data = np.array(self.rel_click_data) self.bin_click_data = np.array( [click[1] for click in self.full_click_data]) # size_list_2 = [] for data_file in self.click_context_files: data_handel = PickleUtil(data_file) context_dict = data_handel.safe_load() self.abs_click_data += context_dict["press"] # size_list_2 += [len(flatten(context_dict["press"]))] self.speed_changes += context_dict["speed"] self.selection_data += context_dict["choice"] self.abs_click_data = np.array(flatten(self.abs_click_data)) self.speed_changes.sort(key=lambda x: x[0]) if self.preconfig_file is not None: preconfig_handel = PickleUtil(self.preconfig_file) preconfig = preconfig_handel.safe_load() self.kde_list = np.array(preconfig["li"]) / np.sum(preconfig["li"]) if len(self.abs_click_data) != len(self.rel_click_data): raise ValueError( "Click data length does not match context data length!") print("Loaded " + str(len(self.abs_click_data)) + " clicks") def plot_click_recovery(self): click_locations = [click[1] for click in self.full_click_data] # print(click_locations) click_pairs = [] abs_click_pairs = [] index = 0 while index < len(click_locations): cur_click = click_locations[index] abs_cur_click = self.abs_click_data[index] if cur_click[1] == -1: # row scan if len(click_pairs) == 0: click_pairs.append([cur_click]) abs_click_pairs.append([abs_cur_click]) elif len(click_pairs[-1]) == 1: click_pairs = click_pairs[:-1] abs_click_pairs = abs_click_pairs[:-1] else: click_pairs.append([cur_click]) abs_click_pairs.append([abs_cur_click]) else: if len(click_pairs[-1]) == 1: click_pairs[-1].append(cur_click) abs_click_pairs[-1].append(abs_cur_click) index += 1 click_diffs = np.array( [pair[1] - pair[0] for pair in abs_click_pairs if len(pair) == 2]) pair_orders = np.array( [pair[1][1] for pair in click_pairs if len(pair) == 2]) recovery_times = click_diffs[np.where(pair_orders == 0)] recovery_times = recovery_times[np.where(recovery_times <= 3)] plt.hist(recovery_times, bins=20) PickleUtil("recovery_time_951.p").safe_save(recovery_times) # plt.show() # plt.hist(pair_orders, bins=8) plt.xlabel("time (s)") plt.ylabel("count") plt.title("Recovery Time") plt.show() def split_data_phrase(self): self.phrases = [] self.phrase_times = {} phrase_start = 0 uncorrected_error = 0 corrected_error = 0 session_num = 0 start_time_prev = 0 phrase_selections = 0 for selection_num, selection in enumerate( self.selection_data ): # scan through data to find phrases & time ints if "target" in selection: phrase = selection["target"] typed = selection["typed"] is_backspace = selection["backspace"] is_undo = selection["undo"] if phrase not in self.phrases: self.phrases.append(phrase) phrase_start = selection["time"] phrase_selections += 1 # if is_backspace: # accumulate corrected errors # corrected_error += 1 if is_undo: if selection_num > 0: # prev_typed = self.selection_data[selection_num-1]["typed"] # typed_difference = prev_typed[len(typed):] corrected_error += 1 _, completed_phrase = self.phrase_util.compare(typed, phrase) if completed_phrase and not is_undo: self.phrase_times[phrase] = (phrase_start, selection["time"]) uncorrected_error = calc_MSD(typed, phrase)[0] / len( max(typed, phrase)) * 100 self.phrase_stats[phrase] = { "error_unc": uncorrected_error, "error_cor": corrected_error / phrase_selections * 100, "selections": phrase_selections } corrected_error = 0 phrase_selections = 0 self.phrases = list(self.phrase_times.keys()) self.phrases.sort(key=lambda x: self.phrase_times[x][0]) print("Data partitioned into " + str(len(self.phrases)) + " sets by phrase") for phrase in self.phrases: # split data according to phrase times calculated above phrase_start, phrase_end = self.phrase_times[phrase] phrase_click_indices = np.where( (self.abs_click_data >= phrase_start) & (self.abs_click_data <= phrase_end)) phrase_abs_clicks = self.abs_click_data[phrase_click_indices] phrase_rel_clicks = self.rel_click_data[phrase_click_indices] phrase_bin_clicks = self.bin_click_data[phrase_click_indices] if len(phrase_abs_clicks) > 0: first_click_time = min(phrase_abs_clicks) self.clicks_by_phrase[phrase] = { "abs": phrase_abs_clicks, "rel": phrase_rel_clicks, "bin": phrase_bin_clicks } stat_dict = self.phrase_stats[phrase] num_clicks = len(phrase_abs_clicks) time_int = phrase_end - first_click_time num_characters = len(phrase) num_words = len(phrase.split(" ")) num_sel = stat_dict["selections"] clicks_per_char = num_clicks / num_characters clicks_per_word = num_clicks / num_words clicks_per_sel = num_sel / num_words chars_per_min = num_characters / time_int * 60 words_per_min = num_words / time_int * 60 sel_per_min = num_sel / time_int * 60 click_dist_mean = np.mean(phrase_bin_clicks) click_dist_var = np.var(phrase_bin_clicks) stat_dict["clicks_char"] = clicks_per_char stat_dict["clicks_word"] = clicks_per_word stat_dict["clicks_sel"] = clicks_per_sel stat_dict["chars_min"] = chars_per_min stat_dict["words_min"] = words_per_min stat_dict["sel_min"] = sel_per_min stat_dict["click_mean"] = click_dist_mean stat_dict["click_var"] = click_dist_var if phrase_start - start_time_prev > 19 * 60: session_num += 1 start_time_prev = phrase_start stat_dict["session"] = session_num stat_dict["start_time"] = phrase_start else: del self.phrase_stats[phrase] self.phrases = list(self.phrase_stats.keys()) # print(self.phrase_stats) def split_data_speed(self): num_speed_changes = len(self.speed_changes) for change_index in range(num_speed_changes): time_min = self.speed_changes[change_index][0] if change_index == num_speed_changes - 1: time_max = float("inf") else: time_max = self.speed_changes[change_index + 1][0] clicks_int = np.array( np.where((self.abs_click_data > time_min) & (self.abs_click_data < time_max))[0], dtype='int64') matrix_index = np.vectorize( lambda m_index: self.rel_click_data[m_index]) if clicks_int.size > 0: clock_speed = round(self.speed_changes[change_index][2], 2) clicks_rel_int = matrix_index(clicks_int) if clock_speed in self.clicks_by_speed.keys(): prev_clicks = self.clicks_by_speed[clock_speed] self.clicks_by_speed[clock_speed] = np.concatenate( (prev_clicks, clicks_rel_int)) else: self.clicks_by_speed[clock_speed] = clicks_rel_int print("Data partitioned into " + str(len(self.clicks_by_speed.keys())) + " sets by clock rotation speed") def correct_data_speed(self): if 1.44 in self.clicks_by_speed.keys(): base_index = 1.44 elif round(period_li[default_rotate_ind], 2) in self.clicks_by_speed.keys(): base_index = round(period_li[default_rotate_ind], 2) else: base_index = max(self.clicks_by_speed.keys()) base_clicks = self.clicks_by_speed[base_index] base_clicks_mean = np.mean(base_clicks) # base_clicks = base_clicks - base_clicks_mean base_clicks_std = np.std(base_clicks) clock_speeds = list(self.clicks_by_speed.keys()) self.corrected_clicks = [] for clock_speed in clock_speeds: clicks = self.clicks_by_speed[clock_speed] clicks_mean = np.mean(clicks) clicks = clicks - clicks_mean clicks_std = np.std(clicks) clicks *= base_clicks_std / clicks_std clicks += base_clicks_mean self.corrected_clicks += clicks.tolist() self.corrected_clicks = np.array(self.corrected_clicks) def make_data_frame(self): for phrase_num, phrase in enumerate(self.phrases): if phrase_num == 0: DF = pd.DataFrame(pd.DataFrame([self.phrase_stats[phrase]])) DF["phrase"] = phrase else: df = pd.DataFrame([self.phrase_stats[phrase]]) df["phrase"] = phrase DF = DF.append(df, ignore_index=True) # DF = DF.sor _values(by=['start_time']) self.DF = DF def save_hist(self): # kernel = stats.gaussian_kde(self.rel_click_data) # rel_click_data hist = self.kde_list time_rotate = self.speed_changes[-1][-1] x_range = (np.arange(80) - 40) / 80 * time_rotate print(os.path.join(self.data_dir, "user_histogram.p")) PickleUtil(os.path.join(self.data_dir, "user_histogram.p")).safe_save( (x_range, hist)) def plot_data(self): fig = plt.figure() ax = plt.subplot(111) plot_num = 0 for clock_speed in self.clicks_by_speed.keys(): plot_color = self.plot_colors[plot_num] clicks = self.clicks_by_speed[clock_speed] clicks_mean = np.mean(clicks) clicks_std = np.std(clicks) # clicks = clicks - clicks_mean plot_label = "rotation: " + str(clock_speed) + " (" + str( len(clicks)) + " points)" # ax.hist(clicks, 30, range=[0, 80], density=True, color=plot_color, alpha=0.3, label=plot_label) kernel = stats.gaussian_kde(clicks) res = 80 plt.plot(np.arange(2.5 * res) / res, kernel(np.arange(2.5 * res) / res), color=plot_color, linewidth=2, label=plot_label) plot_num += 1 # plt.axvline(clicks_mean, color=plot_color, linestyle="--", alpha=0.8) # for i in [-1,1]: # plt.axvline(clicks_mean + i*clicks_std, color=plot_color, linestyle=":", alpha=0.6) if self.corrected_clicks is not None: kernel = stats.gaussian_kde(self.corrected_clicks) res = 80 plot_label = "rotation_adj (" + str(len( self.corrected_clicks)) + " points)" plt.plot(np.arange(2.5 * res) / res, kernel(np.arange(2.5 * res) / res), linestyle="--", color="0000", linewidth=2, label=plot_label) # ax.bar(np.arange(self.kde_list.size), self.kde_list, fill=False, edgecolor='black', label="KDE") ax.legend() # ax.set_xlim(20,60) plt.show() def plot_phrase_stats(self, DF2=None): ind_var_name = "session" for data_label in [ 'error_unc', 'error_unc', 'error_cor', 'clicks_char', 'chars_min' ]: var_name_dict = { 'sel_min': "Selections/Min", 'words_min': "Words/Min", 'chars_min': "Characters/Min", 'clicks_char': "Clicks/Character", 'clicks_word': "Clicks/Word", 'clicks_sel': "Clicks/Selection", 'error_unc': "Uncorrected Error Rate (%)", 'error_cor': "Corrections/Selections (%)" } dep_var_name = var_name_dict[data_label] DF = self.DF pd.set_option('display.max_columns', 500) fig, ax = plt.subplots() fig.set_size_inches(10, 8) sns.set(font_scale=1.4, rc={"lines.linewidth": 3}) sns.set_style({'font.serif': 'Helvetica'}) if DF2 is not None: sns.lineplot(x=ind_var_name, y=data_label, color="rosybrown", data=DF2, ci="sd", ax=ax) sns.lineplot(x=ind_var_name, y=data_label, color="brown", data=DF2, ax=ax) sns.lineplot(x=ind_var_name, y=data_label, color="cadetblue", data=DF, ci="sd", ax=ax) sns.lineplot(x=ind_var_name, y=data_label, color="darkslategrey", data=DF, ax=ax) ax.set(xlabel=ind_var_name, ylabel=dep_var_name) plt.legend(title='Key:', labels=[ 'Row Col (SD)', ' (95% CI)', 'Nomon (SD)', ' (95% CI)' ]) plt.title("Webcam Double Switch: " + dep_var_name + " vs. " + ind_var_name) sns.axes_style("darkgrid") plt.show() def plot_click_dist_phrase(self): DF = self.DF fig, ax = plt.subplots() fig.set_size_inches(10, 8) sns.set(font_scale=1.4, rc={"lines.linewidth": 3}) sns.set_style({'font.serif': 'Helvetica'}) sns.lineplot(x="session", y="error_unc", color="darkslategrey", data=DF, ax=ax) plt.show() fig, ax = plt.subplots() fig.set_size_inches(10, 8) sns.set(font_scale=1.4, rc={"lines.linewidth": 3}) sns.set_style({'font.serif': 'Helvetica'}) ind_var_name = "session" sns.lineplot(x=ind_var_name, y="click_var", color="darkslateblue", data=DF, ci="sd", ax=ax) sns.lineplot(x=ind_var_name, y="click_var", color="slateblue", data=DF, ax=ax) sns.lineplot(x=ind_var_name, y="click_mean", color="cadetblue", data=DF, ci="sd", ax=ax) sns.lineplot(x=ind_var_name, y="click_mean", color="darkslategrey", data=DF, ax=ax) ax.set(xlabel=ind_var_name, ylabel="Pressing Distribution Variance and Mean (Bins)") plt.legend(title='Key:', labels=[ 'Click Dist Var (SD)', ' (95% CI)', 'Click Dist Mean (SD)', ' (95% CI)' ]) plt.title("Participant 951: Pressing Distribution vs. " + ind_var_name) sns.axes_style("darkgrid") plt.show() def print_stat_avg(self): errors = [] clicks_char = [] clicks_word = [] words_min = [] chars_min = [] for phrase in self.phrases: data_dict = self.phrase_stats[phrase] errors.append(data_dict["error_cor"]) clicks_char.append(data_dict["clicks_char"]) clicks_word.append(data_dict["clicks_word"]) words_min.append(data_dict["words_min"]) chars_min.append(data_dict["chars_min"]) print("\n") print("Characters/Min: ", np.average(chars_min), "+/-", np.std(chars_min)) print("Words/Min: ", np.average(words_min), "+/-", np.std(words_min)) print("Clicks/Character: ", np.average(clicks_char), "+/-", np.std(clicks_char)) print("Clicks/Word: ", np.average(clicks_word), "+/-", np.std(clicks_word)) print("Correction Rate (%): ", np.average(errors), "+/-", np.std(errors)) def test_significance(self, DF2=None): var_name_dict = { 'words_min': "Words/Min: ", 'chars_min': "Characters/Min: ", 'clicks_char': "Clicks/Character: ", 'clicks_word': "Clicks/Word: ", 'error_unc': "Error Rate: ", 'sel_min': "Selections/Min", 'error_cor': "Correction Rate: " } if DF2 is None: df_start = self.DF[self.DF["session"] == 1] df_end = self.DF[self.DF["session"] == max(self.DF["session"])] print("\n") print("Difference in means from first and last session: \n") for data_label in ['error_unc', 'clicks_char', 'chars_min']: sub_data_start = df_start[data_label] sub_data_end = df_end[data_label] t_val, p_val = stats.ttest_ind(sub_data_start.values, sub_data_end.values, equal_var=False) if p_val > 0.05: print(var_name_dict[data_label] + ": Not statistically significant (p-value: " + str(p_val) + ")") else: print(var_name_dict[data_label] + ": Statistically significant (p-value: " + str(p_val) + ")") else: # df_self = self.DF[self.DF["session"].isin([max(self.DF["session"]), max(self.DF["session"])-1])] # df_other = DF2[DF2["session"].isin([max(DF2["session"]), max(DF2["session"])-1])] df_self = self.DF df_other = DF2 print("\n") print( "Difference in means from Nomon and Row Col in last session: \n" ) for data_label in ['error_cor', 'clicks_char', 'chars_min']: sub_data_self = df_self[data_label] sub_data_other = df_other[data_label] t_val, p_val = stats.ttest_ind(sub_data_self.values, sub_data_other.values, equal_var=False) # print(stats.f_oneway(sub_data_self.values, sub_data_other.values)) if p_val > 0.05: print(var_name_dict[data_label] + ": Not statistically significant (p-value: " + str(p_val) + ")") else: print(var_name_dict[data_label] + ": Statistically significant (p-value: " + str(p_val) + ")")
def __init__(self, screen_res, app): super(Keyboard, self).__init__(screen_res) self.app = app self.is_simulation = False self.pretrain_window = False # 2 is turn fully on, 1 is turn on but reduce, 0 is turn off self.word_pred_on = 2 # Number of word clocks to display in case word prediction == 1 (reduced) self.reduce_display = 5 # get user data before initialization self.gen_data_handel() self.up_handel = PickleUtil( os.path.join(self.user_handel, 'user_preferences.p')) user_preferences = self.up_handel.safe_load() if user_preferences is None: first_load = True user_preferences = [ 'default', 1, False, 'alpha', 'off', config.default_rotate_ind, True ] self.up_handel.safe_save(user_preferences) else: first_load = False self.clock_type, self.font_scale, self.high_contrast, self.layout_preference, self.pf_preference, \ self.start_speed, self.is_write_data = user_preferences self.phrase_prompts = False # set to true for data collection mode if self.phrase_prompts: self.phrases = Phrases( "resources/twitter-phrases/watch-combined.txt") else: self.phrases = None if self.layout_preference == 'alpha': self.target_layout = kconfig.alpha_target_layout self.key_chars = kconfig.key_chars elif self.layout_preference == 'qwerty': self.target_layout = kconfig.qwerty_target_layout self.key_chars = kconfig.key_chars elif self.layout_preference == 'emoji': self.target_layout = kconfig.emoji_target_layout self.key_chars = kconfig.emoji_keys self.word_pred_on = 0 # set up dictionary tree # splash = StartWindow(screen_res, True) self.pause_animation = False self.output_manager = outputManager() self.is_output_text = False self.lm_prefix = "" self.left_context = "" self.cwd = os.getcwd() word_lm_path = os.path.join( os.path.join(self.cwd, 'resources'), 'mix4_opt_min_lower_100k_4gram_2.5e-9_prob8_bo4_compress255.kenlm') char_lm_path = os.path.join( os.path.join(self.cwd, 'resources'), 'mix4_opt_min_lower_12gram_6e-9_prob9_bo4_compress255.kenlm') vocab_path = os.path.join(os.path.join(self.cwd, 'resources'), 'vocab_lower_100k.txt') char_path = os.path.join(os.path.join(self.cwd, 'resources'), 'char_set.txt') self.lm = LanguageModel(word_lm_path, char_lm_path, vocab_path, char_path) # initialize pygame and joystick if kconfig.target_evt is kconfig.joy_evt: pygame.init() if pygame.joystick.get_count() < 1: # no joysticks found print("Please connect a joystick.\n") self.quit(None) else: # create a new joystick object from # ---the first joystick in the list of joysticks Joy0 = pygame.joystick.Joystick(0) # tell pygame to record joystick events Joy0.init() # start looking for events self.parent.after(0, self.find_events) # not in selection pause self.in_pause = False # determine keyboard positions self.init_locs() # get old data if there is such # Just for default. Loaded again when bc initializes self.rotate_index = config.default_rotate_ind # set up file handle for printing useful stuff self.undefined = False self.params_handle_dict = { 'speed': [], 'params': [], 'start': [], 'press': [], 'choice': [] } self.num_presses = 0 self.params_handle_dict['params'].append( [config.period_li[config.default_rotate_ind], config.theta0]) self.params_handle_dict['start'].append(time.time()) self.gen_scale() self.pause_set = True # set up "typed" text self.typed = "" self.btyped = "" self.context = "" self.old_context_li = [""] self.last_add_li = [0] # set up "talked" text # self.talk_file = "talk.txt" self.sound_set = True self.press_lock = False self.press_lock_status = False # check for speech # talk_fid = open(self.talk_file, 'wb') # write words self.init_words() self.bars = kconfig.bars self.bc_init = False self.previous_undo_text = '' self.previous_winner = 0 self.wpm_data = [] self.decay_avg_wpm = 0 self.wpm_time = 0 self.error_data = [] self.decay_avg_error = 1 self.clear_text = False self.pretrain = False self.emoji_box_highlight = [-1, -1] self.init_ui() self.mainWidget.clockgrid_widget.update_word_clocks(self.words_li) sound_file = "icons/bell.wav" self.sound_player = QtMultimedia.QSound(sound_file) self.time_rotate = config.period_li[self.start_speed] # get language model results self.gen_word_prior(False) self.clock_spaces = np.zeros((len(self.clock_centers), 2)) self.bc = BroderClocks(self) self.mainWidget.change_value(self.start_speed) self.bc.init_follow_up(self.word_score_prior) # draw histogram self.init_histogram() self.save_environment() self.consent = False if first_load: self.pretrain = True self.welcome = Pretraining(self, screen_res) # animate # record to prevent double tap self.last_key_press_time = time.time() self.last_release_time = time.time() self.update_radii = False self.on_timer()
def phrase_prompts_event(self): if self.phrase_prompts: phrase_status = False else: phrase_status = True if self.phrases is None: self.phrases = Phrases("resources/comm2.dev") self.phrase_prompts = phrase_status if phrase_status == True: self.phrases.sample() self.update_phrases(self.typed_versions[-1], "") self.is_write_data = True choice_dict = { "time": time.time(), "undo": False, "backspace": False, "typed": "", "target": self.phrases.cur_phrase } self.params_handle_dict['choice'].append(choice_dict) self.mainWidget.cb_pause.setChecked(True) self.top_word_action.trigger() self.sorted_layout_action.trigger() self.mainWidget.cb_pause.setEnabled(False) self.mainWidget.speed_slider_label.setStyleSheet( 'QLabel { color: grey }') self.mainWidget.sldLabel.setStyleSheet('QLabel { color: grey }') self.mainWidget.extra_delay_label.setStyleSheet( 'QLabel { color: grey }') self.mainWidget.extra_sldLabel.setStyleSheet( 'QLabel { color: grey }') self.default_layout_action.setEnabled(False) self.sorted_layout_action.setEnabled(False) self.bottom_word_action.setEnabled(False) self.top_word_action.setEnabled(False) self.log_data_action.setEnabled(False) else: self.typed_versions.append("") self.left_context = "" self.context = "" self.typed = "" self.lm_prefix = "" self.mainWidget.text_box.setText("") self.mainWidget.cb_pause.setEnabled(True) self.mainWidget.speed_slider.setEnabled(True) self.mainWidget.extra_delay_slider.setEnabled(True) self.default_layout_action.setEnabled(True) self.sorted_layout_action.setEnabled(True) self.top_word_action.setEnabled(True) self.bottom_word_action.setEnabled(True) self.log_data_action.setEnabled(True) self.mainWidget.speed_slider_label.setStyleSheet( 'QLabel { color: black }') self.mainWidget.sldLabel.setStyleSheet('QLabel { color: black }') self.mainWidget.extra_delay_label.setStyleSheet( 'QLabel { color: black }') self.mainWidget.extra_sldLabel.setStyleSheet( 'QLabel { color: black }') self.mainWidget.error_label.setStyleSheet("color: rgb(0, 0, 0);") self.mainWidget.wpm_label.setStyleSheet("color: rgb(0, 0, 0);") self.check_filemenu() self.update_phrases("", "")
class MainWindow(QtWidgets.QMainWindow): def __init__(self, screen_res): super(MainWindow, self).__init__() self.screen_res = screen_res # Load User Preferences def init_ui(self): self.mainWidget = MainKeyboardWidget(self, self.key_chars, self.screen_res) self.mainWidget.init_ui() self.setCentralWidget(self.mainWidget) # SimulatedUser Layout Menu Actions self.default_layout_action = QtWidgets.QAction('&Alphabetical', self, checkable=True) self.default_layout_action.triggered.connect( lambda: self.layout_change_event('default')) self.sorted_layout_action = QtWidgets.QAction('&Frequency Sorted', self, checkable=True) self.sorted_layout_action.triggered.connect( lambda: self.layout_change_event('sorted')) # Word Location Action self.top_word_action = QtWidgets.QAction('&Top (Default)', self, checkable=True) self.top_word_action.triggered.connect( lambda: self.word_change_event('top')) self.bottom_word_action = QtWidgets.QAction('&Bottom', self, checkable=True) self.bottom_word_action.triggered.connect( lambda: self.word_change_event('bottom')) # Phrase Prompts Action self.phrase_prompts_action = QtWidgets.QAction('&Study Mode', self, checkable=True) self.phrase_prompts_action.triggered.connect(self.phrase_prompts_event) exit_action = QtWidgets.QAction('&Exit', self) exit_action.setShortcut('Ctrl+Q') exit_action.setStatusTip('Exit application') # exit_action.triggered.connect(QtWidgets.qApp.quit) exit_action.triggered.connect(self.closeEvent) menubar = self.menuBar() file_menu = menubar.addMenu('&File') file_menu.addAction(exit_action) # View Menu Actions view_menu = menubar.addMenu('&View') keyboard_menu = view_menu.addMenu('&Keybaord Layout') keyboard_menu.addAction(self.default_layout_action) keyboard_menu.addAction(self.sorted_layout_action) word_menu = view_menu.addMenu('&Word Prediction Location') word_menu.addAction(self.top_word_action) word_menu.addAction(self.bottom_word_action) # Tools Menu Actions self.log_data_action = QtWidgets.QAction('&Data Logging', self, checkable=True) self.log_data_action.triggered.connect(self.log_data_event) self.compress_data_action = QtWidgets.QAction('&Compress Data', self, checkable=False) self.compress_data_action.triggered.connect(self.compress_data_event) # Help Menu Actions help_action = QtWidgets.QAction('&Help', self) help_action.setStatusTip('Nomon help') help_action.triggered.connect(self.help_event) about_action = QtWidgets.QAction('&About', self) about_action.setStatusTip('Application information') about_action.triggered.connect(self.about_event) tools_menu = menubar.addMenu('&Tools') tools_menu.addAction(self.phrase_prompts_action) tools_menu.addAction(self.log_data_action) tools_menu.addAction(self.compress_data_action) self.setWindowTitle('Row Column Scanner') self.icon = QtGui.QIcon(os.path.join("icons/", 'rowcol.png')) self.setWindowIcon(self.icon) self.setGeometry(self.screen_res[0] * 0.05, self.screen_res[1] * 0.0675, self.screen_res[0] * 0.9, self.screen_res[1] * 0.85) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHeightForWidth(True) self.setSizePolicy(sizePolicy) self.show() self.window_size = (self.size().width(), self.size().height()) self.check_filemenu() def check_filemenu(self): def switch(unit, mode): if mode == unit.isChecked(): pass else: unit.toggle() # check layout switch(self.default_layout_action, self.key_config == "default") switch(self.sorted_layout_action, self.key_config == "sorted") # check word count switch(self.top_word_action, self.words_first) switch(self.bottom_word_action, not self.words_first) # check log data switch(self.log_data_action, self.is_write_data) switch(self.phrase_prompts_action, self.phrase_prompts) def phrase_prompts_event(self): if self.phrase_prompts: phrase_status = False else: phrase_status = True if self.phrases is None: self.phrases = Phrases("resources/comm2.dev") self.phrase_prompts = phrase_status if phrase_status == True: self.phrases.sample() self.update_phrases(self.typed_versions[-1], "") self.is_write_data = True choice_dict = { "time": time.time(), "undo": False, "backspace": False, "typed": "", "target": self.phrases.cur_phrase } self.params_handle_dict['choice'].append(choice_dict) self.mainWidget.cb_pause.setChecked(True) self.top_word_action.trigger() self.sorted_layout_action.trigger() self.mainWidget.cb_pause.setEnabled(False) self.mainWidget.speed_slider_label.setStyleSheet( 'QLabel { color: grey }') self.mainWidget.sldLabel.setStyleSheet('QLabel { color: grey }') self.mainWidget.extra_delay_label.setStyleSheet( 'QLabel { color: grey }') self.mainWidget.extra_sldLabel.setStyleSheet( 'QLabel { color: grey }') self.default_layout_action.setEnabled(False) self.sorted_layout_action.setEnabled(False) self.bottom_word_action.setEnabled(False) self.top_word_action.setEnabled(False) self.log_data_action.setEnabled(False) else: self.typed_versions.append("") self.left_context = "" self.context = "" self.typed = "" self.lm_prefix = "" self.mainWidget.text_box.setText("") self.mainWidget.cb_pause.setEnabled(True) self.mainWidget.speed_slider.setEnabled(True) self.mainWidget.extra_delay_slider.setEnabled(True) self.default_layout_action.setEnabled(True) self.sorted_layout_action.setEnabled(True) self.top_word_action.setEnabled(True) self.bottom_word_action.setEnabled(True) self.log_data_action.setEnabled(True) self.mainWidget.speed_slider_label.setStyleSheet( 'QLabel { color: black }') self.mainWidget.sldLabel.setStyleSheet('QLabel { color: black }') self.mainWidget.extra_delay_label.setStyleSheet( 'QLabel { color: black }') self.mainWidget.extra_sldLabel.setStyleSheet( 'QLabel { color: black }') self.mainWidget.error_label.setStyleSheet("color: rgb(0, 0, 0);") self.mainWidget.wpm_label.setStyleSheet("color: rgb(0, 0, 0);") self.check_filemenu() self.update_phrases("", "") def word_change_event(self, location): if location == 'top': self.words_first = True elif location == 'bottom': self.words_first = False self.check_filemenu() self.generate_layout() self.mainWidget = MainKeyboardWidget(self, self.key_chars, self.screen_res) self.mainWidget.init_ui() self.setCentralWidget(self.mainWidget) def layout_change_event(self, layout): message_box = QtWidgets.QMessageBox( QtWidgets.QMessageBox.Warning, "Change SimulatedUser Layout", "This will change the clock " "layout to <b>" + layout + "</b" "> order. <b>NOTICE:</b> You " "will have to restart Nomon for" " these changes to take effect", QtWidgets.QMessageBox.Cancel | QtWidgets.QMessageBox.Ok) message_box.setDefaultButton(QtWidgets.QMessageBox.Cancel) message_box.setWindowIcon(self.icon) self.key_config = layout self.check_filemenu() self.generate_layout() self.mainWidget = MainKeyboardWidget(self, self.key_chars, self.screen_res) self.mainWidget.init_ui() self.setCentralWidget(self.mainWidget) def log_data_event(self): message_box = QtWidgets.QMessageBox( QtWidgets.QMessageBox.Warning, "Data Logging Consent", "We would like to save " "some data regarding your clicking time relative to Noon to help us improve Nomon. " "All data collected is anonymous and only your click times will be saved. <b> Do you" " consent to allowing us to log click timing data locally?</b> (Note: you can change" " your preference anytime in the Tools menu).") message_box.addButton(QtWidgets.QMessageBox.Yes) message_box.addButton(QtWidgets.QMessageBox.No) message_box.setDefaultButton(QtWidgets.QMessageBox.Yes) message_box.setWindowIcon(self.icon) reply = message_box.exec_() if reply == QtWidgets.QMessageBox.No: self.is_write_data = False elif reply == QtWidgets.QMessageBox.Yes: self.is_write_data = True self.check_filemenu() def compress_data_event(self): self.save_data() data_save_path, _ = os.path.split(self.data_path) data_zip_path = os.path.join(data_save_path, "row_col_data.zip") zf = zipfile.ZipFile(data_zip_path, "w") for dirname, subdirs, files in os.walk(self.data_path): sub_dirname = dirname[len(self.data_path):] zf.write(dirname, sub_dirname) for filename in files: file_path = os.path.join(dirname, filename) sub_file_path = file_path[len(self.data_path):] zf.write(file_path, sub_file_path) zf.close() message_box = QtWidgets.QMessageBox( QtWidgets.QMessageBox.Warning, "Data Compression", "We have compressed your data into a ZIP" " archive accessible in the location" "listed under \"Details\". Please press \"" "Show Details\", and email the " "ZIP archive to the listed email address. We" " greatly appreciate your willingness to " "help us make Row Column Scanner better!") message_box.setDetailedText("File Path: \n" + data_save_path + "\n\n Email: \[email protected]") message_box.addButton(QtWidgets.QMessageBox.Ok) message_box.setWindowIcon(self.icon) reply = message_box.exec_() def about_event(self): # noinspection PyTypeChecker QtWidgets.QMessageBox.question( self, 'About Nomon', " Copyright 2019 Nicholas Bonaker, Keith Vertanen," " Emli-Mari Nel, Tamara Broderick. This file is part of " "the Nomon software. Nomon is free software: you can " "redistribute it and/or modify it under the terms of the " "MIT License reproduced below.\n\n " "Permission is hereby granted, free of charge, to any " "person obtaining a copy of this software and associated" " documentation files (the \"Software\"), to deal in the" " Software without restriction, including without " "limitation the rights to use, copy, modify, merge, " "publish, distribute, sublicense, and/or sell copies of the" " Software,and to permit persons to whom the Software is" " furnished to do so, subject to the following conditions: " "The above copyright notice and this permission notice" " shall be included in all copies or substantial portions" " of the Software. \n\n " "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY" "OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT" " LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS" " FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO" " EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE" " LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY," " WHETHER IN AN ACTION OF CONTRACT, TORT OR" " OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION" " WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS" " IN THE SOFTWARE.\n\n" " <https://opensource.org/licenses/mit-license.html>", QtWidgets.QMessageBox.Ok) def help_event(self): self.launch_help() def retrain_event(self): self.launch_retrain() def resizeEvent(self, event): self.environment_change = True self.in_pause = True # for clock in self.mainWidget.clocks: # clock.redraw_text = True # clock.calculate_clock_size() # QtCore.QTimer.singleShot(100, self.init_clocks) QtWidgets.QMainWindow.resizeEvent(self, event) self.window_size = (self.size().width(), self.size().height()) self.in_pause = False
def setLanguage(self, language): language = Check.toString(language) if not (language in Phrases.getLanguages()): raise ValueError("language missing") self._language = language
class MainWindow(QtWidgets.QMainWindow): def __init__(self, screen_res): super(MainWindow, self).__init__() self.screen_res = screen_res # Load User Preferences def init_ui(self): self.setStyleSheet(""" QMenuBar { background-color: rgb(240,240,240); color: rgb(0,0,0); border: 1px solid #fff; } QMenuBar::item { background-color: rgb(240,240,240); color: rgb(0,0,0); } QMenuBar::item::selected { background-color: rgb(255,255,255); color: rgb(0,0,0); } QMenu { background-color: rgb(240,240,240); color: rgb(0,0,0); } QMenu::item::selected { background-color: rgb(255,255,255); color: rgb(0,0,0); } QMainWindow { background-color: white; } """) self.mainWidget = MainKeyboardWidget(self, self.key_chars, self.screen_res) self.mainWidget.init_ui() self.mainWidget.clockgrid_widget.setParent(self.mainWidget) self.mainWidget.clockhands_widget.setParent(self.mainWidget) self.setCentralWidget(self.mainWidget) self.clock_text_align('auto', message=False) # File Menu Actions restart_action = QtWidgets.QAction('&Restart', self) restart_action.setShortcut('Ctrl+R') restart_action.setStatusTip('Restart application') # restart_action.triggered.connect(self.restartEvent) exit_action = QtWidgets.QAction('&Exit', self) exit_action.setShortcut('Ctrl+Q') exit_action.setStatusTip('Exit application') # exit_action.triggered.connect(QtWidgets.qApp.quit) exit_action.triggered.connect(self.closeEvent) # Clock Menu Actions self.high_contrast_action = QtWidgets.QAction('&High Contrast Mode', self, checkable=True) self.high_contrast_action.triggered.connect( lambda: self.high_contrast_event()) self.default_clock_action = QtWidgets.QAction('&Default (Clock)', self, checkable=True) self.default_clock_action.setStatusTip( 'Regular Nomon clock with sweeping minute hand') self.default_clock_action.triggered.connect( lambda: self.clock_change_event('default')) self.default_clock_action.setIcon( QtGui.QIcon(os.path.join("icons/", 'default.png'))) self.radar_clock_action = QtWidgets.QAction('&Radar (Clock)', self, checkable=True) self.radar_clock_action.setStatusTip( 'Nomon clock with sweeping minute hand and radar trails') self.radar_clock_action.triggered.connect( lambda: self.clock_change_event('radar')) self.radar_clock_action.setIcon( QtGui.QIcon(os.path.join("icons/", 'radar.png'))) self.ball_clock_action = QtWidgets.QAction('&Ball (Filling)', self, checkable=True) self.ball_clock_action.triggered.connect( lambda: self.clock_change_event('ball')) self.ball_clock_action.setIcon( QtGui.QIcon(os.path.join("icons/", 'ball.png'))) self.pacman_clock_action = QtWidgets.QAction( '&Pac Man (Filling Pac Man)', self, checkable=True) self.pacman_clock_action.triggered.connect( lambda: self.clock_change_event('pac man')) self.pacman_clock_action.setIcon( QtGui.QIcon(os.path.join("icons/", 'pac_man.png'))) self.bar_clock_action = QtWidgets.QAction('&Progress Bar', self, checkable=True) self.bar_clock_action.triggered.connect( lambda: self.clock_change_event('bar')) self.bar_clock_action.setIcon( QtGui.QIcon(os.path.join("icons/", 'bar.png'))) # Font Menu Actions self.small_font_action = QtWidgets.QAction('&Small', self, checkable=True) self.small_font_action.triggered.connect( lambda: self.change_font_size('small')) self.med_font_action = QtWidgets.QAction('&Medium (Default)', self, checkable=True) self.med_font_action.triggered.connect( lambda: self.change_font_size('med')) self.large_font_action = QtWidgets.QAction('&Large', self, checkable=True) self.large_font_action.triggered.connect( lambda: self.change_font_size('large')) # Text Menu Actions self.auto_text_align_action = QtWidgets.QAction('&Auto (Recommended)', self, checkable=True) self.auto_text_align_action.triggered.connect( lambda: self.clock_text_align('auto')) self.tc_text_align_action = QtWidgets.QAction('&Top Center', self, checkable=True) self.tc_text_align_action.triggered.connect( lambda: self.clock_text_align('tc')) self.cl_text_align_action = QtWidgets.QAction('&Center Left', self, checkable=True) self.cl_text_align_action.triggered.connect( lambda: self.clock_text_align('cl')) self.cc_text_align_action = QtWidgets.QAction('&Center', self, checkable=True) self.cc_text_align_action.triggered.connect( lambda: self.clock_text_align('cc')) self.cr_text_align_action = QtWidgets.QAction('&Center Right', self, checkable=True) self.cr_text_align_action.triggered.connect( lambda: self.clock_text_align('cr')) self.bc_text_align_action = QtWidgets.QAction('&Bottom Center', self, checkable=True) self.bc_text_align_action.triggered.connect( lambda: self.clock_text_align('bc')) # Keyboard Layout Menu Actions self.default_layout_action = QtWidgets.QAction( '&Alphabetical (Default)', self, checkable=True) self.default_layout_action.triggered.connect( lambda: self.layout_change_event('alpha')) self.qwerty_layout_action = QtWidgets.QAction('&QWERTY', self, checkable=True) self.qwerty_layout_action.triggered.connect( lambda: self.layout_change_event('qwerty')) self.emoji_layout_action = QtWidgets.QAction('&Emoji', self, checkable=True) self.emoji_layout_action.triggered.connect( lambda: self.layout_change_event('emoji')) # Word Count Action self.high_word_action = QtWidgets.QAction('&High (Default)', self, checkable=True) self.high_word_action.triggered.connect( lambda: self.word_change_event('high')) self.low_word_action = QtWidgets.QAction('&Low (5 Words)', self, checkable=True) self.low_word_action.triggered.connect( lambda: self.word_change_event('low')) self.off_word_action = QtWidgets.QAction('&Off', self, checkable=True) self.off_word_action.triggered.connect( lambda: self.word_change_event('off')) # Tools Menu Actions # self.profanity_filter_action = QtWidgets.QAction('&Profanity Filter', self, checkable=True) # self.profanity_filter_action.triggered.connect(self.profanity_filter_event) self.retrain_action = QtWidgets.QAction('&Retrain', self) self.retrain_action.triggered.connect(self.retrain_event) self.phrase_prompts_action = QtWidgets.QAction('&Study Mode', self, checkable=True) self.phrase_prompts_action.triggered.connect(self.phrase_prompts_event) self.press_lock_action = QtWidgets.QAction('&Press Lock', self, checkable=True) self.press_lock_action.triggered.connect(self.press_lock_event) self.log_data_action = QtWidgets.QAction('&Data Logging', self, checkable=True) self.log_data_action.triggered.connect(self.log_data_event) self.compress_data_action = QtWidgets.QAction('&Compress Data', self, checkable=False) self.compress_data_action.triggered.connect(self.compress_data_event) self.output_text_action = QtWidgets.QAction('&Output Text', self, checkable=True) self.output_text_action.triggered.connect(self.output_text_event) # Help Menu Actions help_action = QtWidgets.QAction('&Help', self) help_action.setStatusTip('Nomon help') help_action.triggered.connect(self.help_event) about_action = QtWidgets.QAction('&About', self) about_action.setStatusTip('Application information') about_action.triggered.connect(self.about_event) menubar = self.menuBar() file_menu = menubar.addMenu('&File') file_menu.addAction(exit_action) view_menu = menubar.addMenu('&View') # view_menu.addAction(self.high_contrast_action) # clock_menu = view_menu.addMenu('&Clocks') # clock_menu.addAction(self.default_clock_action) # clock_menu.addAction(self.radar_clock_action) # clock_menu.addAction(self.ball_clock_action) # clock_menu.addAction(self.pacman_clock_action) # clock_menu.addAction(self.bar_clock_action) # text_menu = view_menu.addMenu('&Text Alignment') # text_menu.addAction(self.auto_text_align_action) # center_text_menu = text_menu.addMenu('&Center') # center_text_menu.addAction(self.cl_text_align_action) # center_text_menu.addAction(self.cc_text_align_action) # center_text_menu.addAction(self.cr_text_align_action) font_menu = view_menu.addMenu('&Font Size') font_menu.addAction(self.small_font_action) font_menu.addAction(self.med_font_action) font_menu.addAction(self.large_font_action) keyboard_menu = view_menu.addMenu('&Keyboard Layout') keyboard_menu.addAction(self.default_layout_action) keyboard_menu.addAction(self.qwerty_layout_action) # keyboard_menu.addAction(self.emoji_layout_action) # word prediction word_menu = view_menu.addMenu('&Word Prediction Frequency') word_menu.addAction(self.high_word_action) word_menu.addAction(self.low_word_action) word_menu.addAction(self.off_word_action) tools_menu = menubar.addMenu('&Tools') # tools_menu.addAction(self.profanity_filter_action) # tools_menu.addAction(self.press_lock_action) tools_menu.addAction(self.log_data_action) # tools_menu.addAction(self.phrase_prompts_action) tools_menu.addAction(self.retrain_action) tools_menu.addAction(self.compress_data_action) tools_menu.addAction(self.output_text_action) help_menu = menubar.addMenu('&Help') help_menu.addAction(help_action) help_menu.addSeparator() help_menu.addAction(about_action) self.setWindowTitle('Nomon Keyboard') self.icon = QtGui.QIcon(os.path.join("icons/", 'nomon.png')) self.setWindowIcon(self.icon) self.setGeometry(self.screen_res[0] * 0.05, self.screen_res[1] * 0.0675, self.screen_res[0] * 0.9, self.screen_res[1] * 0.85) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHeightForWidth(True) self.setSizePolicy(sizePolicy) self.show() self.window_size = (self.size().width(), self.size().height()) self.check_filemenu() def check_filemenu(self): def switch(unit, mode): if mode == unit.isChecked(): pass else: unit.toggle() # check clocks switch(self.default_clock_action, self.clock_type == 'default') switch(self.radar_clock_action, self.clock_type == 'radar') switch(self.ball_clock_action, self.clock_type == 'ball') switch(self.pacman_clock_action, self.clock_type == 'pac_man') switch(self.bar_clock_action, self.clock_type == 'bar') # check text alignment switch(self.cc_text_align_action, self.alignment == 'cc') switch(self.cl_text_align_action, self.alignment == 'cl') switch(self.cr_text_align_action, self.alignment == 'cr') switch(self.auto_text_align_action, self.mainWidget.text_alignment == 'auto') # check profanity # switch(self.profanity_filter_action, self.pf_preference == 'on') # check log data switch(self.log_data_action, self.is_write_data) switch(self.press_lock_action, self.press_lock) switch(self.phrase_prompts_action, self.phrase_prompts) switch(self.output_text_action, self.is_output_text) # check font menu switch(self.small_font_action, self.font_scale == 0) switch(self.med_font_action, self.font_scale == 1) switch(self.large_font_action, self.font_scale == 2) # check high contrast switch(self.high_contrast_action, self.high_contrast) # check layout switch(self.default_layout_action, self.target_layout == kconfig.alpha_target_layout) switch(self.qwerty_layout_action, self.target_layout == kconfig.qwerty_target_layout) switch(self.qwerty_layout_action, self.target_layout == kconfig.emoji_target_layout) # check word count switch(self.high_word_action, self.word_pred_on == 2) switch(self.low_word_action, self.word_pred_on == 1) switch(self.off_word_action, self.word_pred_on == 0) def word_change_event(self, frequency): if frequency == 'high': self.word_pred_on = 2 elif frequency == 'low': self.word_pred_on = 1 elif frequency == 'off': self.word_pred_on = 0 self.check_filemenu() self.draw_words() self.gen_word_prior(False) self.in_pause = True self.mainWidget.update_widget_positions() self.in_pause = False self.environment_change = True def change_font_size(self, size): if size == 'small': size = 0 elif size == 'med': size = 1 elif size == 'large': size = 2 self.font_scale = size self.up_handel.safe_save([ self.clock_type, size, self.high_contrast, self.layout_preference, self.pf_preference, self.start_speed, self.is_write_data ]) self.mainWidget.sldLabel.setFont(config.top_bar_font[size]) self.mainWidget.speed_slider_label.setFont(config.top_bar_font[size]) self.mainWidget.wpm_label.setFont(config.top_bar_font[size]) self.mainWidget.error_label.setFont(config.top_bar_font[size]) self.mainWidget.cb_learn.setFont(config.top_bar_font[size]) self.mainWidget.cb_pause.setFont(config.top_bar_font[size]) self.mainWidget.cb_sound.setFont(config.top_bar_font[size]) self.mainWidget.text_box.setFont(config.text_box_font[size]) self.mainWidget.wpm_label.update() self.mainWidget.error_label.update() # self.mainWidget.cb_talk.update() self.mainWidget.cb_learn.update() self.mainWidget.cb_pause.update() self.mainWidget.sldLabel.update() self.mainWidget.speed_slider_label.update() self.mainWidget.text_box.update() self.check_filemenu() def high_contrast_event(self): if self.high_contrast: hc_status = False else: hc_status = True self.up_handel.safe_save([ self.clock_type, self.font_scale, hc_status, self.layout_preference, self.pf_preference, self.start_speed, self.is_write_data ]) self.high_contrast = hc_status self.mainWidget.color_index = hc_status self.environment_change = True def phrase_prompts_event(self): if self.phrase_prompts: phrase_status = False else: phrase_status = True # if self.phrases is None: if self.layout_preference == 'emoji': self.phrases = Phrases("resources/emojis.txt") else: self.phrases = Phrases( "resources/twitter-phrases/watch-combined.txt") self.phrase_prompts = phrase_status if phrase_status == True: self.phrases.sample() self.update_phrases(self.typed_versions[-1], "") choice_dict = { "time": time.time(), "undo": False, "backspace": False, "typed": "", "target": self.phrases.cur_phrase } self.params_handle_dict['choice'].append(choice_dict) self.is_write_data = True self.mainWidget.cb_learn.setChecked(True) self.mainWidget.cb_pause.setChecked(True) self.default_clock_action.trigger() self.med_font_action.trigger() if self.layout_preference != 'emoji': self.high_word_action.trigger() self.default_layout_action.trigger() self.mainWidget.cb_learn.setEnabled(False) self.mainWidget.cb_pause.setEnabled(False) self.mainWidget.speed_slider_label.setStyleSheet( 'QLabel { color: grey }') self.mainWidget.sldLabel.setStyleSheet('QLabel { color: grey }') self.default_clock_action.setEnabled(False) self.radar_clock_action.setEnabled(False) self.ball_clock_action.setEnabled(False) self.pacman_clock_action.setEnabled(False) self.bar_clock_action.setEnabled(False) # self.default_layout_action.setEnabled(False) self.qwerty_layout_action.setEnabled(False) self.high_contrast_action.setEnabled(False) self.low_word_action.setEnabled(False) self.high_word_action.setEnabled(False) self.off_word_action.setEnabled(False) self.small_font_action.setEnabled(False) self.med_font_action.setEnabled(False) self.large_font_action.setEnabled(False) self.log_data_action.setEnabled(False) else: self.typed_versions.append("") self.left_context = "" self.context = "" self.typed = "" self.lm_prefix = "" self.mainWidget.text_box.setText("") self.mainWidget.cb_learn.setEnabled(True) self.mainWidget.cb_pause.setEnabled(True) self.mainWidget.speed_slider.setEnabled(True) self.default_clock_action.setEnabled(True) self.radar_clock_action.setEnabled(True) self.ball_clock_action.setEnabled(True) self.pacman_clock_action.setEnabled(True) self.bar_clock_action.setEnabled(True) # self.default_layout_action.setEnabled(True) self.qwerty_layout_action.setEnabled(True) self.high_contrast_action.setEnabled(True) self.low_word_action.setEnabled(True) self.high_word_action.setEnabled(True) self.off_word_action.setEnabled(True) self.small_font_action.setEnabled(True) self.med_font_action.setEnabled(True) self.large_font_action.setEnabled(True) self.log_data_action.setEnabled(True) self.mainWidget.speed_slider_label.setStyleSheet( 'QLabel { color: black }') self.mainWidget.sldLabel.setStyleSheet('QLabel { color: black }') self.mainWidget.error_label.setStyleSheet("color: rgb(0, 0, 0);") self.mainWidget.wpm_label.setStyleSheet("color: rgb(0, 0, 0);") self.mainWidget.wpm_label.setText("Words/Min: " + "----") self.mainWidget.error_label.setText("Error Rate: " + "----") self.check_filemenu() def clock_change_event(self, design): message_box = QtWidgets.QMessageBox( QtWidgets.QMessageBox.Warning, "Change Clock Design", "This will change the clocks " "to the <b>" + design + "</b" "> design", QtWidgets.QMessageBox.Cancel | QtWidgets.QMessageBox.Ok) design = design.replace(' ', '_') message_box.setDefaultButton(QtWidgets.QMessageBox.Cancel) message_box.setIconPixmap( QtGui.QPixmap(os.path.join("icons/", design + '.png'))) message_box.setWindowIcon(self.icon) self.clock_type = design self.up_handel.safe_save([ design, self.font_scale, self.high_contrast, self.layout_preference, self.pf_preference, self.start_speed, self.is_write_data ]) self.check_filemenu() if self.mainWidget.text_alignment == 'auto': self.clock_text_align('auto', message=False) self.check_filemenu() self.environment_change = True def layout_change_event(self, layout): message_box = QtWidgets.QMessageBox( QtWidgets.QMessageBox.Warning, "Change Keyboard Layout", "This will change the clock " "layout to <b>" + layout + "</b" "> order. <b>NOTICE:</b> You " "will have to restart Nomon for" " these changes to take effect", QtWidgets.QMessageBox.Cancel | QtWidgets.QMessageBox.Ok) message_box.setDefaultButton(QtWidgets.QMessageBox.Cancel) message_box.setWindowIcon(self.icon) if layout == 'alpha': self.up_handel.safe_save([ self.clock_type, self.font_scale, self.high_contrast, 'alpha', self.pf_preference, self.start_speed, self.is_write_data ]) self.target_layout = kconfig.alpha_target_layout self.key_chars = kconfig.key_chars # self.word_pred_on = 2 elif layout == 'qwerty': self.up_handel.safe_save([ self.clock_type, self.font_scale, self.high_contrast, 'qwerty', self.pf_preference, self.start_speed, self.is_write_data ]) self.target_layout = kconfig.qwerty_target_layout self.key_chars = kconfig.key_chars # self.word_pred_on = 2 elif layout == 'emoji': self.up_handel.safe_save([ self.clock_type, self.font_scale, self.high_contrast, 'emoji', self.pf_preference, self.start_speed, self.is_write_data ]) self.target_layout = kconfig.emoji_target_layout self.key_chars = kconfig.emoji_keys self.word_pred_on = 0 self.layout_preference = layout # self.init_locs() self.init_words() # self.clock_spaces = np.zeros((len(self.clock_centers), 2)) # self.bc = BroderClocks(self) self.gen_word_prior(False) self.in_pause = True self.mainWidget.update_widget_positions() self.in_pause = False self.environment_change = True self.check_filemenu() def clock_text_align(self, alignment, message=True): if alignment == "auto": self.mainWidget.text_alignment = 'auto' message_box = QtWidgets.QMessageBox( QtWidgets.QMessageBox.Warning, "Change Text Alignment", "The text will be <b>" "Auto-Aligned</b> to " "best suit the keyboard" " layout. (recommended)", QtWidgets.QMessageBox.Cancel | QtWidgets.QMessageBox.Ok) if self.clock_type == "bar": alignment = "cc" else: alignment = "cr" else: self.mainWidget.text_alignment = alignment alignment_name = "" if alignment == "cr": alignment_name = 'Center Right' elif alignment == "cc": alignment_name = 'Center' elif alignment == "cl": alignment_name = 'Center Left' elif alignment == "bc": alignment_name = 'Bottom Center' elif alignment == "tc": alignment_name = 'Top Center' message_box = QtWidgets.QMessageBox( QtWidgets.QMessageBox.Warning, "Change Text Alignment", "This will change the " "text to be aligned on the <b>" + alignment_name + "</b> of the clocks", QtWidgets.QMessageBox.Cancel | QtWidgets.QMessageBox.Ok) self.alignment = alignment self.mainWidget.alignment = alignment # self.resize_clocks() if message: self.check_filemenu() # def resize_clocks(self): # if self.alignment[0] == 'b' or self.mainWidget.alignment[0] == 't': # for clock in self.mainWidget.clocks: # clock.setMaximumHeight(clock.maxSize*2) # # clock.setMinimumSize(clock.minSize*2.1, clock.minSize*2.1) # else: # for clock in self.mainWidget.clocks: # clock.setMaximumHeight(clock.maxSize) # # clock.setMinimumSize(clock.minSize, clock.minSize) def press_lock_event(self): message_box = QtWidgets.QMessageBox( QtWidgets.QMessageBox.Warning, "Press Lock", "Press Lock is feature that requires the _ key to be triggered before an" " additional click is registered. Would you like to turn this feature on?" ) message_box.addButton(QtWidgets.QMessageBox.Yes) message_box.addButton(QtWidgets.QMessageBox.No) message_box.setDefaultButton(QtWidgets.QMessageBox.Yes) message_box.setWindowIcon(self.icon) reply = message_box.exec_() if reply == QtWidgets.QMessageBox.No: self.press_lock = False elif reply == QtWidgets.QMessageBox.Yes: self.press_lock = True self.check_filemenu() def log_data_event(self): message_box = QtWidgets.QMessageBox( QtWidgets.QMessageBox.Warning, "Data Logging Consent", "We would like to save " "some data regarding your clicking time relative to Noon to help us improve Nomon. " "All data collected is anonymous and only your click times will be saved. <b> Do you" " consent to allowing us to log click timing data locally?</b> (Note: you can change" " your preference anytime in the Tools menu).") message_box.addButton(QtWidgets.QMessageBox.Yes) message_box.addButton(QtWidgets.QMessageBox.No) message_box.setDefaultButton(QtWidgets.QMessageBox.Yes) message_box.setWindowIcon(self.icon) reply = message_box.exec_() if reply == QtWidgets.QMessageBox.No: self.up_handel.safe_save([ self.clock_type, self.font_scale, self.high_contrast, self.layout_preference, self.pf_preference, self.start_speed, False ]) self.is_write_data = False elif reply == QtWidgets.QMessageBox.Yes: self.up_handel.safe_save([ self.clock_type, self.font_scale, self.high_contrast, self.layout_preference, self.pf_preference, self.start_speed, True ]) self.is_write_data = True self.check_filemenu() def compress_data_event(self): self.bc.save_when_quit() data_save_path, _ = os.path.split(self.data_path) data_zip_path = os.path.join(data_save_path, "nomon_data.zip") zf = zipfile.ZipFile(data_zip_path, "w") for dirname, subdirs, files in os.walk(self.data_path): sub_dirname = dirname[len(self.data_path):] zf.write(dirname, sub_dirname) for filename in files: file_path = os.path.join(dirname, filename) sub_file_path = file_path[len(self.data_path):] zf.write(file_path, sub_file_path) zf.close() message_box = QtWidgets.QMessageBox( QtWidgets.QMessageBox.Warning, "Data Compression", "We have compressed your data into a ZIP" " archive accessible in the location" "listed under \"Details\". Please press \"" "Show Details\", and email the " "ZIP archive to the listed email address. We" " greatly appreciate your willingness to " "help us make Nomon better!") message_box.setDetailedText("File Path: \n" + data_save_path + "\n\n Email: \[email protected]") message_box.addButton(QtWidgets.QMessageBox.Ok) message_box.setWindowIcon(self.icon) reply = message_box.exec_() def output_text_event(self): message_box = QtWidgets.QMessageBox( QtWidgets.QMessageBox.Warning, "Text Output", "This will use simulated keystrokes to output the text you type to the " "input field currently in focus. It will work on any application that " "supports text input. Make sure you are running Nomon as Administrator." " Do you want to turn this feature on?") message_box.addButton(QtWidgets.QMessageBox.Yes) message_box.addButton(QtWidgets.QMessageBox.No) message_box.setDefaultButton(QtWidgets.QMessageBox.Yes) message_box.setWindowIcon(self.icon) reply = message_box.exec_() if reply == QtWidgets.QMessageBox.No: self.is_output_text = False elif reply == QtWidgets.QMessageBox.Yes: self.is_output_text = True self.check_filemenu() def about_event(self): # noinspection PyTypeChecker QtWidgets.QMessageBox.question( self, 'About Nomon', " Copyright 2019 Nicholas Bonaker, Keith Vertanen," " Emli-Mari Nel, Tamara Broderick. This file is part of " "the Nomon software. Nomon is free software: you can " "redistribute it and/or modify it under the terms of the " "MIT License reproduced below.\n\n " "Permission is hereby granted, free of charge, to any " "person obtaining a copy of this software and associated" " documentation files (the \"Software\"), to deal in the" " Software without restriction, including without " "limitation the rights to use, copy, modify, merge, " "publish, distribute, sublicense, and/or sell copies of the" " Software,and to permit persons to whom the Software is" " furnished to do so, subject to the following conditions: " "The above copyright notice and this permission notice" " shall be included in all copies or substantial portions" " of the Software. \n\n " "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY" "OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT" " LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS" " FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO" " EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE" " LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY," " WHETHER IN AN ACTION OF CONTRACT, TORT OR" " OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION" " WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS" " IN THE SOFTWARE.\n\n" " <https://opensource.org/licenses/mit-license.html>", QtWidgets.QMessageBox.Ok) def help_event(self): self.launch_help() def retrain_event(self): self.launch_retrain() def resizeEvent(self, event): self.environment_change = True self.in_pause = True QtWidgets.QMainWindow.resizeEvent(self, event) self.mainWidget.update_widget_positions() self.window_size = (self.size().width(), self.size().height()) self.in_pause = False
class WordsGameBot(): def __init__(self, lang): self.phrases = Phrases(lang) self.lang = lang self.words = WordsGameDictionary( self.lang, theme='', ) self.continue_game = True self.additional_info = '' self.current_word = ' ' self.quantity = 0 def init_dictionary(self, theme): self.words = WordsGameDictionary(self.lang, theme) # game is over when the bot doesn't know any words starting from the last two letters of the previous word def is_game_over(self): if self.current_word[-1] in self.words.used_letters and \ self.current_word[-2] in self.words.used_letters: self.continue_game = False return True else: return False # check if the first letter of user's word is correct - it should be equal to # the last letter of bot's word def is_correct_first_letter(self, person_word, indent): if person_word[0].lower( ) != self.current_word[indent] and self.quantity > 0: return False return True # check if user's word is in current dictionary def is_bot_knows_person_word(self, person_word): index_of_letter = self.return_index_of_letter(person_word[0].lower()) if person_word.lower() in self.words.dictionary[index_of_letter]: return True else: return False # check if user's word was used def is_person_word_used(self, person_word): if person_word.lower() in self.words.used_words: return True else: return False def return_index_of_letter(self, letter): index_of_letter = self.words.alphabet.find(letter.lower()) return index_of_letter # Add used word, delete it from the main dictionary def add_used_word_del_from_dic(self, word): self.words.used_words.append(word.lower()) index_of_letter = self.return_index_of_letter(word[0]) self.words.dictionary[index_of_letter].remove(word.lower()) # check if bot knows words starting from the first letter # and check if the bot knows any words starting from the last two letters of the word - if no - game is over def check_word_and_letters(self, word): if not self.words.is_know_words_on_letter(word[0]): self.additional_info += self.phrases.used_letter_was_added( word[0].lower()) + '\n' if self.is_game_over(): self.additional_info = self.phrases.give_game_over_phrases() + '\n' # reinitialize all attributes to reset the bot def restart_bot(self, lang='RU'): self.__init__(lang) # define bot's answers to the input def bot_answer_is(self, person_word): # if user wants to stop the game if person_word in self.phrases.user_exit_phrases: self.continue_game = False return [self.phrases.give_exit_phrases(self.quantity)] # if user just pushed enter without typing # might be redundant in telegram bot implementation if not person_word: return [self.phrases.give_enter_phrases()] # Define an indent for the future checking if self.current_word[-1] in self.words.used_letters: indent = -2 else: indent = -1 # Check if last two letters was used (no more words started from them are exist in our dictionary) # we have to stop the whole game if self.is_game_over(): return [self.phrases.give_game_over_phrases()] # Check if user's word started from correct letter, according to intend if not self.is_correct_first_letter(person_word, indent): return [ self.phrases.give_false_letter_phrases( self.current_word[indent]) ] # Check if user's word already used if self.is_person_word_used(person_word): return [self.phrases.give_used_phrases()] # Check if bot knows user's word then return an answer if self.is_bot_knows_person_word(person_word): self.additional_info = '' self.add_used_word_del_from_dic(person_word) self.check_word_and_letters(person_word) self.quantity += 1 self.current_word = self.bot_word(person_word) if self.current_word == "GiveUp": self.continue_game = False return [self.phrases.give_game_over_phrases()] self.check_word_and_letters(self.current_word) if self.continue_game: return [self.additional_info, self.current_word] else: return [self.current_word, self.additional_info] else: return [self.phrases.give_false_phrases()] # give bot answer def bot_word(self, person_word): if person_word[-1] in self.words.used_letters: indent = -2 else: indent = -1 index_of_letter = self.return_index_of_letter(person_word[indent]) try: bot_word = self.words.dictionary[index_of_letter][random.randint( 0, len(self.words.dictionary[index_of_letter]) - 1)] self.add_used_word_del_from_dic(bot_word) return bot_word.title() # Might be redundant except ValueError: return "GiveUp" # Test class and whole game # # my_bot = WordsGameBot('data/citiesForTest.txt') # # while my_bot.continue_game: # person_word = input('Введи слово\n') # bot_answer = my_bot.bot_answer_is(person_word.strip()) # print(bot_answer)