示例#1
0
    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
示例#2
0
 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
示例#3
0
 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
示例#4
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")
示例#5
0
 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()
示例#6
0
 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())
示例#7
0
    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)
示例#8
0
 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)
示例#9
0
 def __handle_about_request(context: LexContext):
     return LexResponses.close(
         context,
         'Fulfilled',
         {
             'contentType': 'PlainText',
             'content': Phrases.howto()
         }
     )
示例#10
0
    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
示例#11
0
 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)
示例#12
0
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
示例#13
0
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)
示例#14
0
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)
示例#15
0
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)
示例#16
0
 def _getPhrases(self):
     return Phrases.forLanguage(self.getLanguage())
示例#17
0
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()
示例#18
0
文件: lex.py 项目: s12v/weather-bot
 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())
示例#19
0
    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()
示例#20
0
    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()
示例#21
0
    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
示例#22
0
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()
示例#23
0
 def learn_default_responses(file, phrases):
     Phrases.add_phrases(file, phrases)
示例#24
0
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) + ")")
示例#25
0
    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()
示例#26
0
    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("", "")
示例#27
0
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
示例#28
0
 def setLanguage(self, language):
     language = Check.toString(language)
     if not (language in Phrases.getLanguages()):
         raise ValueError("language missing")
     self._language = language
示例#29
0
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
示例#30
0
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)