def update_typer_html(typer,errors): '''Organizational function. Given a Typer, updates its html based on settings (not including invisible mode)''' #dict : str -> str ; original and displacement strs in error region (for easier display) v = unicode(typer.toPlainText()) v_err_replacements = {} if Settings.get('text_area_replace_spaces'): #if want to make replacements change spaces in text area as well (risky!) v_err_replacements.update(space_replacement_dict_from_setting('text_area_mistakes_space_char')) if Settings.get('text_area_replace_return'): #want to make replacements change returns in text area as well (a little less risky since there's usually fewer) v_err_replacements["\n"] = Settings.get('text_area_return_replacement') error_colors = {} #dict : int -> str, mapping errors to color v_replaced_list = list(v) #list of strs, initially one char each, to operate on v_replaced_list = html_list_process_spaces(v_replaced_list) if Settings.get("show_text_area_mistakes"): error_colors = dict(map(lambda i : (i,Settings.get('text_area_mistakes_color')),errors)) v_replaced_list = replace_at_locs(v_replaced_list,v_err_replacements,errors) v_colored_list = html_color_strs(v_replaced_list,error_colors) htmlized = "".join(v_colored_list).replace("\n","<BR>") set_typer_html(typer,htmlized)
def setPalettes(self): self.palettes = { "wrong": QPalette( Qt.black, Qt.lightGray, Qt.lightGray, Qt.darkGray, Qt.gray, Settings.getColor("quiz_wrong_fg"), Qt.white, Settings.getColor("quiz_wrong_bg"), Qt.yellow, ), "right": QPalette( Qt.black, Qt.lightGray, Qt.lightGray, Qt.darkGray, Qt.gray, Settings.getColor("quiz_right_fg"), Qt.yellow, Settings.getColor("quiz_right_bg"), Qt.yellow, ), "inactive": QPalette( Qt.black, Qt.lightGray, Qt.lightGray, Qt.darkGray, Qt.gray, Qt.black, Qt.lightGray, ), } self.setPalette(self.palettes["inactive"])
def to_lessons(sentences): backlog = [] backlen = 0 min_chars = Settings.get('min_chars') max_chars = Settings.get('max_chars') sweet_size = 3*(min_chars + max_chars) // 4 for s in sentences: ssplit = [] while len(s) > sweet_size: idx = s.find(' ', sweet_size) if idx == -1: break if idx != -1: ssplid.append(s[:idx]) s = s[idx+1:] ssplit.append(s) for xs in ssplit: backlog.append(xs) backlen += len(xs) if backlen >= min_chars: yield u' '.join(backlog) backlog = [] backlen = 0 if backlen > 0: yield u' '.join(backlog)
def __init__(self, fname): super(LessonMiner, self).__init__() with codecs.open(fname, "r", "utf_8_sig") as f: self.text = f.read() self.lessons = None self.min_chars = Settings.get('min_chars') self.split_regex = Settings.get('sentence_regex') self.split = re.compile(self.split_regex).split(self.text)
def setPalettes(self): self.palettes = { 'wrong': QPalette(Qt.black, Qt.lightGray, Qt.lightGray, Qt.darkGray, Qt.gray, Settings.getColor("quiz_wrong_fg"), Qt.white, Settings.getColor("quiz_wrong_bg"), Qt.yellow), 'right': QPalette(Qt.black, Qt.lightGray, Qt.lightGray, Qt.darkGray, Qt.gray, Settings.getColor("quiz_right_fg"), Qt.yellow, Settings.getColor("quiz_right_bg"), Qt.yellow), 'inactive': QPalette(Qt.black, Qt.lightGray, Qt.lightGray, Qt.darkGray, Qt.gray, Qt.black, Qt.lightGray)} self.setPalette(self.palettes['inactive'])
def color_position(settings_color_var, use_space_var, space_var): '''Colors position with the color stored in settings_color_var. strs use_space_var and space_var are settings variables to look up. If [setting] use_space_var, space at position is replaced with [setting] space_var Returns the new text_strs list (for assignment).''' colors[position] = Settings.get(settings_color_var) if Settings.get(use_space_var): return replace_at_locs(text_strs,space_replacement_dict_from_setting(space_var),[position]) else: return text_strs
def generate_automatic_insertion_regex(): '''From settings info, returns the regex for which to re.match the automatically inserted chars Or None if no chars are to be automatically inserted.''' #the str of characters (in regex-escaped form, but not regex) to automatically insert automatically_inserted_chars = "" if Settings.get('use_automatic_other_insertion'): automatically_inserted_chars += re.escape(Settings.get('automatic_other_insertion')) for s,c in ('automatic_space_insertion',u" "),('automatic_return_insertion',u"\n"): if Settings.get(s): automatically_inserted_chars += re.escape(c) return "[{0}]+".format(automatically_inserted_chars) if automatically_inserted_chars else None
def paras(self, f): p = [] ps = [] previous_line_empty = True for l in f: #designated replacements for unicode text if Settings.get('transliteration_manual_unicode'): for orig, repl in unicode_replacements: l = l.replace(orig, repl) ascii_line = l if unidecode_imported and Settings.get('transliteration_method') == INDEX_TRANSLITERATION_UNIDECODE: #tries to use unidecode if it exists ascii_line = unidecode.unidecode(ascii_line) elif Settings.get('transliteration_method') == INDEX_TRANSLITERATION_DELETE: #deletes all remaining non-ascii chars try: ascii_line = ascii_line.decode('ascii') except UnicodeEncodeError: ascii_line = '' for c in l: if ord(c) < 128: ascii_line += c else: ascii_line += "" #replaces any 1+ adjacent whitespace chars (spaces, tabs, newlines, etc) with one ascii space if Settings.get('single_space_only'): ascii_line = re.sub("\s+"," ",ascii_line) #designated replacements for ascii text if Settings.get('transliteration_manual_ascii'): for orig, repl in ascii_replacements: ascii_line = ascii_line.replace(orig, repl) l = ascii_line.strip() current_line_empty = not l #the current line is empty: insert empty line #or the current line and previous line both nonempty: need to insert empty line between them if (current_line_empty or not previous_line_empty) and len(p) > 0: ps.append(SentenceSplitter(u" ".join(p))) p = [] if not current_line_empty: p.append(l) previous_line_empty = current_line_empty if len(p) > 0: ps.append(SentenceSplitter(u" ".join(p))) return ps
def __init__(self, *args): super(Quizzer, self).__init__(*args) self.result = QLabel() self.typer = Typer() self.label = WWLabel() self.result.setVisible(Settings.get("show_last")) #self.label.setFrameStyle(QFrame.Raised | QFrame.StyledPanel) #self.typer.setBuddy(self.label) #self.info = QLabel() self.connect(self.typer, SIGNAL("done"), self.done) self.connect(self.typer, SIGNAL("textChanged"), self.checkText) self.connect(self.typer, SIGNAL("cancel"), SIGNAL("wantText")) self.connect(Settings, SIGNAL("change_typer_font"), self.readjust) self.connect(Settings, SIGNAL("change_show_last"), self.result.setVisible) self.text = ('','', 0, None) layout = QVBoxLayout() #layout.addWidget(self.info) #layout.addSpacing(20) layout.addWidget(self.result, 0, Qt.AlignRight) layout.addWidget(self.label, 1, Qt.AlignBottom) layout.addWidget(self.typer, 1) self.setLayout(layout) self.readjust()
def readjust(self): f = Settings.getFont("typer_font") f.setKerning(False) #TODO: get rid of "vertical kerning" # f.setFixedPitch(True) didn't work self.label.setFont(f) self.typer.setFont(f)
def __init__(self, fname): super(LessonMiner, self).__init__() #print time.clock() with codecs.open(fname, "r", "utf_8_sig") as f: self.paras = self.paras(f) self.lessons = None self.min_chars = Settings.get('min_chars')
def __init__(self, x, y, *args): super(Plot, self).__init__(*args) # self.connect(self, SIGNAL("sceneRectChanged(QRectF)"), self.setSceneRect) if len(x) < 2: return min_x, max_x = min(x), max(x) min_y, max_y = min(y), max(y) p = QPen(Qt.blue) p.setCosmetic(True) p.setWidthF(2.0) p.setCapStyle(Qt.RoundCap) for i in range(0, len(x) - 1): self.addLine(x[i], -y[i], x[i + 1], -y[i + 1], p) # Add axes if Settings.get("show_xaxis"): if min_y > 0: min_y = 0 elif max_y < 0: max_y = 0 if min_y <= 0 <= min_y: self.addLine(min_x, 0, max_x, 0) if min_x <= 0 <= max_x: self.addLine(0, -min_y, 0, -max_y) w, h = max_x - min_x, max_y - min_y if h <= 0 or w <= 0: return # Add background lines spc = math.pow(10.0, math.ceil(math.log10(h) - 1)) while h / spc < 5: spc /= 2 ns = int(min_y / spc) * spc start = ns qp = QPen(QColor(Qt.lightGray)) qp.setStyle(Qt.DotLine) while start < max_y + spc: lin = self.addLine(min_x, -start, max_x, -start, qp) lin.setZValue(-1.0) lbl = QGraphicsSimpleTextItem("%g" % start) th, tw = lbl.boundingRect().height(), lbl.boundingRect().width() lbl.scale(0.026 * w / tw, spc / th) lbl.setPos(QPointF(min_x - 0.03 * w, -start - spc / 2)) self.addItem(lbl) start += spc qr = QRectF(min_x - 0.03 * w, -start + spc / 2, 1.03 * w, start - ns) self.setSceneRect(qr)
def getWaitText(self): if Settings.get("req_space"): return ( "Press SPACE and then immediately start typing the text\n" + "Press ESCAPE to restart with a new text at any time" ) else: return "Press ESCAPE to restart with a new text at any time"
def paras(self, f): p = [] ps = [] previous_line_empty = True for l in f: #designated replacements for unicode text if Settings.get('transliteration_manual_unicode'): for orig, repl in unicode_replacements: l = l.replace(orig, repl) ascii_line = l if unidecode_imported and Settings.get('transliteration_method') == INDEX_TRANSLITERATION_UNIDECODE: #tries to use unidecode if it exists ascii_line = unidecode.unidecode(ascii_line) elif Settings.get('transliteration_method') == INDEX_TRANSLITERATION_DELETE: #deletes all remaining non-ascii chars try: ascii_line = ascii_line.decode('ascii') except UnicodeEncodeError: ascii_line = filter(lambda c : ord(c) < 128, ascii_line) #replaces any 1+ adjacent whitespace chars (spaces, tabs, newlines, etc) with one ascii space if Settings.get('single_space_only'): ascii_line = re.sub("\s+"," ",ascii_line) #TODO: newlines doesn't work since all this is done line-by-line #replaces multiple adjacent instances (possibly including spaces, newlines) of those characters #in list multiple_replacements (e.g. "start ! !!!!! ! ! !!\n !\nend" might get replaced with #"start !\nend") if Settings.get('multiple_replacement_enabled'): additional_chars = (" " if Settings.get('multiple_replacement_allow_spaces') else "") + ("\n" if Settings.get('multiple_replacement_allow_newlines') else "") for m in Settings.get('multiple_replacement_chars'): ascii_line = re.sub("{0}[{0}{1}]*{0}".format(re.escape(m),additional_chars), m, ascii_line) #designated replacements for ascii text if Settings.get('transliteration_manual_ascii'): for orig, repl in ascii_replacements: ascii_line = ascii_line.replace(orig, repl) l = ascii_line.strip() current_line_empty = not l #the current line is empty: insert empty line #or the current line and previous line both nonempty: need to insert empty line between them if (current_line_empty or not previous_line_empty) and len(p) > 0: ps.append(SentenceSplitter(u" ".join(p))) p = [] if not current_line_empty: p.append(l) previous_line_empty = current_line_empty if len(p) > 0: ps.append(SentenceSplitter(u" ".join(p))) return ps
def updateLabel(self,position,errors): '''Populates the label with colors depending on current position and errors.''' #dict : str -> str ; original and displacement strs in error region (for easier display) err_replacements = {"\n":u"{0}<BR>".format(Settings.get('label_return_symbol'))} colors = {} #dict : int -> str, mapping errors to color if Settings.get('show_label_mistakes'): #showing mistakes; need to populate color colors = dict([(i,Settings.get('label_mistakes_color')) for i in errors]) if Settings.get('label_replace_spaces_in_mistakes'): err_replacements.update(space_replacement_dict_from_setting('label_mistakes_space_char')) text_strs = list(self.text[2]) #list of strs, initially one char each, to operate on text_strs = html_list_process_spaces(text_strs) text_strs = replace_at_locs(text_strs,err_replacements,errors) def color_position(settings_color_var, use_space_var, space_var): '''Colors position with the color stored in settings_color_var. strs use_space_var and space_var are settings variables to look up. If [setting] use_space_var, space at position is replaced with [setting] space_var Returns the new text_strs list (for assignment).''' colors[position] = Settings.get(settings_color_var) if Settings.get(use_space_var): return replace_at_locs(text_strs,space_replacement_dict_from_setting(space_var),[position]) else: return text_strs #designates colors and replacements of position if Settings.get('show_label_position_with_prior_mistake') and position - 1 in errors: text_strs = color_position('label_position_with_prior_mistake_color', 'label_replace_spaces_in_position_with_prior_mistake', 'label_position_with_prior_mistake_space_char') elif Settings.get('show_label_position_with_mistakes') and errors: text_strs = color_position('label_position_with_mistakes_color', 'label_replace_spaces_in_position_with_mistakes', 'label_position_with_mistakes_space_char') elif Settings.get('show_label_position'): text_strs = color_position('label_position_color', 'label_replace_spaces_in_position', 'label_position_space_char') htmlized = "".join(html_color_strs(text_strs,colors)) htmlized = htmlized.replace(u"\n", u"{0}<BR>".format(Settings.get('label_return_symbol'))) self.label.setText(htmlized)
def checkText(self): if self.target is None or self.editflag: return v = unicode(self.toPlainText()) if self.when[0] == 0: space = len(v) > 0 and v[-1] == u" " req = Settings.get('req_space') self.editflag = True if space: self.when[0] = timer() self.clear() self.setPalette(self.palettes['right']) elif req: self.setText(self.getWaitText()) self.selectAll() self.editflag = False if req or space: return else: self.when[0] = -1 y = 0 for y in xrange(min(len(v), len(self.target)), -1, -1): if v[0:y] == self.target[0:y]: break lcd = v[0:y] self.where = y if self.when[y] == 0 and y == len(v): self.when[y] = timer() if y > 0: self.times[y-1] = self.when[y] - self.when[y-1] if lcd == self.target: self.emit(SIGNAL("done")) return if y < len(v) and y < len(self.target): self.mistake[y] = True self.mistakes[y] = self.target[y] + v[y] if v == lcd: self.setPalette(self.palettes['right']) else: self.setPalette(self.palettes['wrong'])
def __init__(self): GtkUtil.AmphBoxLayout.__init__(self) self.model = WordModel() treeview = GtkUtil.AmphTreeView(self.model) self.update() which = SettingsCombo('ana_which', [ ('wpm asc', 'slowest'), ('wpm desc', 'fastest'), ('viscosity desc', 'least fluid'), ('viscosity asc', 'most fluid'), ('accuracy asc', 'least accurate'), ('misses desc', 'most mistyped'), ('total desc', 'most common'), ('damage desc', 'most damaging'), ]) what = SettingsCombo('ana_what', ['keys', 'trigrams', 'words']) lim = SettingsEdit('ana_many') mincount = SettingsEdit('ana_count') # XXX why are sometimes no args provided, sometimes Config.Settings? Settings.connect("change_ana_which", lambda *_: self.update()) Settings.connect("change_ana_what", lambda *_: self.update()) Settings.connect("change_ana_many", lambda *_: self.update()) Settings.connect("change_ana_count", lambda *_: self.update()) # TODO send lessons to generator send_to_generator = lambda: None self.append_layout([ [ "Display statistics about the", which, what, None, GtkUtil.new_button("Update list", self.update), GtkUtil.new_button("Send list to Lesson Generator", send_to_generator) ], [ "Limit list to", lim, "items and don't show items with a count less than", mincount ], (treeview, ), ])
def loadpath(s="show"): # old mess global settings, fdir pffmpeg = glob.glob(f"{spath}/ffmpeg*") pffprobe = glob.glob(f"{spath}/ffprobe*") if (not pffmpeg and not pffprobe): fdir = False else: fdir = True # new stuff if (os.path.exists(settingsPath)): settings = Settings.fromJson(settingsPath) else: firstrun() if (s == "show"): print(Style.BRIGHT + "audio is saved to: " + Style.RESET_ALL, end="") print(settings.Youtubedl.audioDir) print(Style.BRIGHT + "video is saved to: " + Style.RESET_ALL, end="") print(settings.Youtubedl.videoDir + "\n")
def updatePalette(self): self.setPalette( QPalette( Settings.getColor("main_text_color"), #color of typing text Settings.getColor( "widgets_background_color" ), #label tab gets part of it color from here Settings.getColor("main_borders_color"), #borders Settings.getColor( "widgets_text_color"), #rectangles in sources Qt.gray, #tab arrow Settings.getColor( "widgets_text_color"), #color of widgets text Qt.white, Settings.getColor( "main_text_area_color"), #most text areas' backgrounds Settings.getColor( "main_background_color" ) #most backgrounds. label tab gets part of its color from here. ))
def __init__(self, *args): super(Quizzer, self).__init__(*args) self.result = QLabel() self.typer = Typer() self.label = WWLabel() self.tryout_label = QLabel() self.tryout_label.setText("Tryout layout: ") self.tryout_layout = QComboBox() self.result.setVisible(Settings.get("show_last")) #self.label.setFrameStyle(QFrame.Raised | QFrame.StyledPanel) #self.typer.setBuddy(self.label) #self.info = QLabel() self.connect(self.typer, SIGNAL("done"), self.done) self.connect(self.typer, SIGNAL("cancel"), SIGNAL("wantText")) self.connect(Settings, SIGNAL("change_typer_font"), self.readjust) self.connect(Settings, SIGNAL("change_show_last"), self.result.setVisible) self.connect(self.tryout_layout, SIGNAL("currentIndexChanged(int)"), self.changeTryoutLayout) self.text = ('','', '', None) self.originalText = ('','', '', None) self.keyboardLayouts = [] layout = QVBoxLayout() #layout.addWidget(self.info) #layout.addSpacing(20) hlayout = QHBoxLayout() hlayout.addWidget(self.tryout_label, 0, Qt.AlignLeft) hlayout.addWidget(self.tryout_layout, 0, Qt.AlignLeft) hlayout.addStretch() hlayout.addWidget(self.result, 0, Qt.AlignRight) layout.addLayout(hlayout) layout.addWidget(self.label, 1, Qt.AlignBottom) layout.addWidget(self.typer, 1) self.setLayout(layout) self.readjust() self.addLayouts()
def __init__(self): self.strings = StringListWidget() self.sample = Gtk.TextView( wrap_mode=Gtk.WrapMode.WORD, editable=False ) self.lesson_name_field = Gtk.Entry() scroll_sample = Gtk.ScrolledWindow() scroll_sample.add(self.sample) combo_what = SettingsCombo('str_what', [('e', 'encompassing'), ('s', 'similar'), ('r', 'random')]) layout = [ "Welcome to Amphetype's automatic lesson generator!", "You can retrieve a list of words/keys/trigrams to practice from the Analysis tab," " import from an external file, or even type in your own (separated by space).\n", 10, ["In generating lessons, I will make", SettingsEdit("gen_copies"), "copies of the list below and divide them into sublists of size", SettingsEdit("gen_take"), "(0 for all)"], ["I will then", SettingsCombo("gen_mix", [('c', "concatenate"), ('m', "commingle")]), "corresponding sublists into atomic building blocks which are fashioned into lessons" " according to your lesson size preferences."], [["Input words", self.strings, [ SettingsCombo("str_clear", [('s', "Supplement"), ('r', "Replace")]), "list with", SettingsEdit("str_extra"), combo_what, "words from", GtkUtil.new_button("a file", self.strings.add_from_file), "or", GtkUtil.new_button("analysis database", self.strings.add_from_typed)]], ["Lessons", scroll_sample, [ GtkUtil.new_button("Add to sources", self.accept_lessons), "with name", self.lesson_name_field]] ], ] GtkUtil.AmphBoxLayout.__init__(self, layout) Settings.connect("change_gen_take", lambda: self.generate_preview()) Settings.connect("change_gen_copies", lambda: self.generate_preview()) Settings.connect("change_gen_mix", lambda: self.generate_preview()) self.strings.connect("updated", lambda _: self.generate_preview())
def space_replacement_dict_from_setting(replacement_var): '''Returns a dict that assigns to html spaces the value in the setting replacement_var''' return space_replacement_dict(Settings.get(replacement_var))
def setText(self, text): self.text = text self.label.setText(self.text[2].replace( u"\n", u"{0}\n".format(Settings.get('label_return_symbol')))) self.typer.setTarget(self.text[2]) self.typer.setFocus()
def fetchall(self, *args): return sqlite3.Connection.execute(self, *args).fetchall() def fetchone(self, sql, default, *args): x = sqlite3.Connection.execute(self, sql, *args) g = x.fetchone() if g is None: return default return g def getSource(self, source, lesson=None): v = self.fetchall('select rowid from source where name = ? limit 1', (source, )) if len(v) > 0: sqlite3.Connection.execute( self, 'update source set disabled = NULL where rowid = ?', v[0]) sqlite3.Connection.commit(self) return v[0][0] sqlite3.Connection.execute( self, 'insert into source (name, discount) values (?, ?)', (source, lesson)) return self.getSource(source) dbname = Settings.get("db_name") # GLOBAL DB = sqlite3.connect(dbname, 5, 0, "DEFERRED", False, AmphDatabase)
def readjust(self): f = Settings.getFont("typer_font") self.label.setFont(f) self.typer.setFont(f)
def done(self): print("DONE") # TODO split into smaller bits now = timer() elapsed, chars, times, mis, mistakes = self.typer.get_stats() text = self.text[2] assert chars == len(text) accuracy = 1.0 - sum(1 for f in mis if f) / chars spc = elapsed / chars viscosity = sum((t / spc - 1)**2 for t in times) / chars DB.execute( """insert into result (w, text_id, source, wpm, accuracy, viscosity) values (?,?,?,?,?,?)""", (now, self.text[0], self.text[1], 12.0 / spc, accuracy, viscosity)) wpm_median, acc_median = DB.fetchone( f"""select agg_median(wpm),agg_median(acc) from (select wpm,100.0*accuracy as acc from result order by w desc limit {Settings.get("def_group_by")})""", (0.0, 100.0)) self.result.set_text( "Last: {:.1f}wpm ({:.1f}%), last 10 average: {:.1f}wpm ({:.1f}%)". format(12.0 / spc, 100.0 * accuracy, wpm_median, acc_median)) self.emit("stats-changed") stats = collections.defaultdict(Statistic) viscs = collections.defaultdict(Statistic) for char, time, mistake in zip(text, times, mis): stats[char].append(time, mistake) viscs[char].append((time / spc - 1)**2) def gen_tup(start, end): span = end - start char_avg = sum(times[start:end]) / span visc = sum((t / char_avg - 1)**2 for t in times[start:end]) / span return (text[start:end], char_avg, sum(1 for f in mis[start:end] if f), visc) for trigraph, time, mist, visc in [ gen_tup(i, i + 3) for i in range(0, chars - 2) ]: stats[trigraph].append(time, mist > 0) viscs[trigraph].append(visc) regex = re.compile(r"(\w|'(?![A-Z]))+(-\w(\w|')*)*") for word, time, mist, visc in [ gen_tup(*m.span()) for m in regex.finditer(text) if m.end() - m.start() > 3 ]: stats[word].append(time, mist > 0) viscs[word].append(visc) def kind(key): if len(key) == 1: return 0 if len(key) == 3: return 1 return 2 vals = [] for key, stat in stats.items(): visc = viscs[key].median() vals.append((stat.median(), visc * 100.0, now, len(stat), stat.flawed(), kind(key), key)) is_lesson = DB.fetchone("select discount from source where rowid=?", (None, ), (self.text[1], ))[0] if Settings.get("use_lesson_stats") or not is_lesson: DB.executemany( """insert into statistic (time,viscosity,w,count,mistakes,type,data) values (?,?,?,?,?,?,?)""", vals) DB.executemany( "insert into mistake (w,target,mistake,count) values (?,?,?,?)", [(now, k[0], k[1], v) for k, v in mistakes.items()]) if is_lesson: mins = (Settings.get("min_lesson_wpm"), Settings.get("min_lesson_acc")) else: mins = (Settings.get("min_wpm"), Settings.get("min_acc")) if 12.0 / spc < mins[0] or accuracy < mins[1] / 100.0: self.set_target(self.text) elif not is_lesson and Settings.get('auto_review'): words = [x for x in vals if x[5] == 2] if not words: self.emit("want-text") return words.sort(key=lambda x: (x[4], x[0]), reverse=True) i = 0 while words[i][4] != 0: i += 1 i += (len(words) - i) // 4 # TODO support want-review # self.emit("want-review", [x[6] for x in words[0:i]]) else: self.emit("want-text")
def checkText(self): if self.typer.target is None or self.typer.editflag: return v = unicode(self.typer.toPlainText()) if Settings.get('allow_mistakes') and len(v) >= len(self.typer.target): v = self.typer.target if not self.typer.started: if Settings.get('req_space'): #space is required before beginning the passage proper if v == u" ": #the first char typed was space #the first space only starts the session; clear the typer set_typer_text(self.typer, "", cursor_position=0) self.typer.when[0] = timer() self.typer.started = True return else: #reset the wait text set_typer_text(self.typer, self.typer.getWaitText()) self.typer.selectAll() return else: self.typer.when[0] = -1 self.typer.when[1] = timer( ) #have to set starting time regardless of correctness self.typer.started = True old_cursor = self.typer.textCursor() old_position = old_cursor.position() old_str_position = old_position - 1 #the position that has (presumably) just been typed #colors text in typer depending on errors errors = disagreements(v, self.typer.target, case_sensitive=Settings.get('case_sensitive')) first_error = errors[0] if errors else None #records time that any char was correctly hit #except that if we've already been correct farther, we don't record if self.typer.farthest_correct < old_str_position < len( self.typer.target) and not old_str_position in errors: self.typer.when[old_str_position + 1] = timer() #zeroth position is original space self.typer.farthest_correct = old_str_position automatic_insertion_regex = generate_automatic_insertion_regex() #TODO: refactor so this doesn't rely on text setting then re-calling gimmick #TODO: change statistics to account for this #Automatically insert characters into the text area if automatic_insertion_regex and old_position == len( v): #only works if cursor is at the end automatic_insertion = re.match(automatic_insertion_regex, self.typer.target[old_position:]) if automatic_insertion: new_end = old_position + automatic_insertion.end() set_typer_text(self.typer, v[:old_position] + self.typer.target[old_position:new_end], cursor_position=new_end) self.checkText() #recovers the formatting return #TODO: refactor so this doesn't rely on text setting then re-calling gimmick #Prevent advancement until user correctly types space if Settings.get('ignore_until_correct_space') and self.typer.target[ old_str_position] == u" " and old_str_position in errors: #gets rid of new character (sets as plaintext) set_typer_text(self.typer, v[:old_str_position] + v[old_str_position + 1:], cursor_position=old_position - 1) self.checkText() #recovers the formatting return if len(v) >= len(self.typer.target) and ( not first_error or first_error >= len(self.typer.target)): self.done() return if new_error(old_str_position, errors): self.typer.mistake[old_str_position] = True self.typer.mistakes[old_str_position] = self.typer.target[ old_str_position] + v[old_str_position] if Settings.get('quiz_invisible'): self.typer.activate_invisibility() else: if Settings.get("quiz_use_wrong_palette") and errors: self.typer.setPalette(self.typer.palettes['wrong']) else: self.typer.setPalette(self.typer.palettes['right']) update_typer_html(self.typer, errors) #updates the label depending on errors self.updateLabel(old_position, errors)
def checkText(self, automatically_inserted = False): if self.typer.target is None or self.typer.editflag: return v = unicode(self.typer.toPlainText()) if Settings.get('allow_mistakes') and len(v) >= len(self.typer.target): v = self.typer.target if self.typer.start_time == None and Settings.get('req_space'): #space is required before beginning the passage proper if v == u" ": #the first char typed was space #the first space only starts the session; clear the typer set_typer_text(self.typer,"",cursor_position=0) self.typer.start_time = 0 return else: #reset the wait text set_typer_text(self.typer,self.typer.getWaitText()) self.typer.selectAll() return if not self.typer.start_time: self.typer.start_time = timer() if 1 < len(v) < len(self.typer.target): #checks whether wait text was accidentally left in diff = list(difflib.Differ().compare(list(self.typer.getWaitText()),list(v))) #the vast majority of the wait text is still there wait_text_present = len(filter(lambda line : line[0] == "-", diff)) <= 1 if wait_text_present: #leave only the additional typed text new_str = "".join(line[2:] for line in diff if line[0] == "+") set_typer_text(self.typer,new_str,cursor_position=len(new_str)) v = new_str old_cursor = self.typer.textCursor() old_position = old_cursor.position() old_str_position = old_position - 1 #the position that has (presumably, unless delete was used) just been typed #colors text in typer depending on errors errors = disagreements(v,self.typer.target,case_sensitive=Settings.get('case_sensitive')) first_error = errors[0] if errors else None #records time that any char was hit #except that if we've already been correct farther, we don't record if self.typer.farthest_correct < old_str_position < len(self.typer.target): if old_str_position not in errors: self.typer.farthest_correct = old_str_position #invalidates all farther-out times that might have previously been written if old_str_position < self.typer.farthest_data: for i in xrange(old_str_position+1, self.typer.farthest_data + 1): self.typer.data[i].when = None self.typer.farthest_data = old_str_position self.typer.data[old_str_position] = Letter(char=self.typer.target[old_str_position], when=timer(), automatically_inserted=automatically_inserted) automatic_insertion_regex = generate_automatic_insertion_regex() #TODO: refactor so this doesn't rely on text setting then re-calling gimmick #TODO: change statistics to account for this #Automatically insert characters into the text area if automatic_insertion_regex and old_position == len(v): #only works if cursor is at the end automatic_insertion = re.match(automatic_insertion_regex,self.typer.target[old_position:]) if automatic_insertion: new_end = old_position + automatic_insertion.end() set_typer_text(self.typer, v[:old_position] + self.typer.target[old_position:new_end], cursor_position = new_end) self.checkText(automatically_inserted=True) #recovers the formatting return #TODO: refactor so this doesn't rely on text setting then re-calling gimmick #Prevent advancement until user correctly types space if Settings.get('ignore_until_correct_space') and self.typer.target[old_str_position] == u" " and old_str_position in errors: #gets rid of new character (sets as plaintext) set_typer_text(self.typer,v[:old_str_position] + v[old_str_position+1:],cursor_position = old_position - 1) self.checkText() #recovers the formatting return if len(v) >= len(self.typer.target) and (not first_error or first_error >= len(self.typer.target)): self.done() return if new_error(old_str_position,errors): self.typer.mistake[old_str_position] = True self.typer.mistakes[old_str_position] = self.typer.target[old_str_position] + v[old_str_position] if Settings.get('quiz_invisible'): self.typer.activate_invisibility() else: if Settings.get("quiz_use_wrong_palette") and errors: self.typer.setPalette(self.typer.palettes['wrong']) else: self.typer.setPalette(self.typer.palettes['right']) update_typer_html(self.typer,errors) #updates the label depending on errors self.updateLabel(old_position,errors)
def executemany_(self, *args): sqlite3.Connection.executemany(self, *args) def executemany(self, *args): sqlite3.Connection.executemany(self, *args) #self.commit() def fetchall(self, *args): return sqlite3.Connection.execute(self, *args).fetchall() def fetchone(self, sql, default, *args): x = sqlite3.Connection.execute(self, sql, *args) g = x.fetchone() if g is None: return default return g def getSource(self, source, lesson=None): v = self.fetchall('select rowid from source where name = ? limit 1', (source, )) if len(v) > 0: sqlite3.Connection.execute(self, 'update source set disabled = NULL where rowid = ?', v[0]) sqlite3.Connection.commit(self) return v[0][0] sqlite3.Connection.execute(self, 'insert into source (name, discount) values (?, ?)', (source, lesson)) return self.getSource(source) dbname = Settings.get("db_name") # GLOBAL DB = sqlite3.connect(dbname, 5, 0, "DEFERRED", False, AmphDatabase)
import os import sys # Get command-line --database argument before importing # modules which count on database support from Config import Settings import optparse opts = optparse.OptionParser() opts.add_option("-d", "--database", metavar="FILE", help="use database FILE") v = opts.parse_args()[0] if v.database is not None: Settings.set('db_name', v.database) from Data import DB from Quizzer import Quizzer from StatWidgets import StringStats from TextManager import TextManager from Performance import PerformanceHistory from Config import PreferenceWidget from Lesson import LessonGenerator from Widgets.Database import DatabaseWidget from PyQt4.QtCore import * from PyQt4.QtGui import * QApplication.setStyle('cleanlooks')
def done(self): now = time.time() elapsed, chars, times, mis, mistakes = self.typer.getStats() assert chars == len(self.text[2]) accuracy = 1.0 - len(filter(None, mis)) / chars spc = elapsed / chars viscosity = sum(map(lambda x: ((x - spc) / spc)**2, times)) / chars DB.execute( 'insert into result (w,text_id,source,wpm,accuracy,viscosity) values (?,?,?,?,?,?)', (now, self.text[0], self.text[1], 12.0 / spc, accuracy, viscosity)) v2 = DB.fetchone( """select agg_median(wpm),agg_median(acc) from (select wpm,100.0*accuracy as acc from result order by w desc limit %d)""" % Settings.get('def_group_by'), (0.0, 100.0)) self.result.setText( "Last: %.1fwpm (%.1f%%), last 10 average: %.1fwpm (%.1f%%)" % ((12.0 / spc, 100.0 * accuracy) + v2)) self.emit(SIGNAL("statsChanged")) stats = collections.defaultdict(Statistic) visc = collections.defaultdict(Statistic) text = self.text[2] for c, t, m in zip(text, times, mis): stats[c].append(t, m) visc[c].append(((t - spc) / spc)**2) def gen_tup(s, e): perch = sum(times[s:e]) / (e - s) visc = sum(map(lambda x: ((x - perch) / perch)**2, times[s:e])) / (e - s) return (text[s:e], perch, len(filter(None, mis[s:e])), visc) for tri, t, m, v in [gen_tup(i, i + 3) for i in xrange(0, chars - 2)]: stats[tri].append(t, m > 0) visc[tri].append(v) regex = re.compile(r"(\w|'(?![A-Z]))+(-\w(\w|')*)*") for w, t, m, v in [ gen_tup(*x.span()) for x in regex.finditer(text) if x.end() - x.start() > 3 ]: stats[w].append(t, m > 0) visc[w].append(v) def type(k): if len(k) == 1: return 0 elif len(k) == 3: return 1 return 2 vals = [] for k, s in stats.iteritems(): v = visc[k].median() vals.append( (s.median(), v * 100.0, now, len(s), s.flawed(), type(k), k)) is_lesson = DB.fetchone("select discount from source where rowid=?", (None, ), (self.text[1], ))[0] if Settings.get('use_lesson_stats') or not is_lesson: DB.executemany_( '''insert into statistic (time,viscosity,w,count,mistakes,type,data) values (?,?,?,?,?,?,?)''', vals) DB.executemany_( 'insert into mistake (w,target,mistake,count) values (?,?,?,?)', [(now, k[0], k[1], v) for k, v in mistakes.iteritems()]) if is_lesson: mins = (Settings.get("min_lesson_wpm"), Settings.get("min_lesson_acc")) else: mins = (Settings.get("min_wpm"), Settings.get("min_acc")) if 12.0 / spc < mins[0] or accuracy < mins[1] / 100.0: self.setText(self.text) elif not is_lesson and Settings.get('auto_review'): ws = filter(lambda x: x[5] == 2, vals) if len(ws) == 0: self.emit(SIGNAL("wantText")) return ws.sort(key=lambda x: (x[4], x[0]), reverse=True) i = 0 while ws[i][4] != 0: i += 1 i += (len(ws) - i) // 4 self.emit(SIGNAL("wantReview"), map(lambda x: x[6], ws[0:i])) else: self.emit(SIGNAL("wantText"))
import os import sys # Get command-line --database argument before importing # modules which count on database support from Config import Settings import optparse opts = optparse.OptionParser() opts.add_option("-d", "--database", metavar="FILE", help="use database FILE") v = opts.parse_args()[0] if v.database is not None: Settings.set("db_name", v.database) from Data import DB from Quizzer import Quizzer from StatWidgets import StringStats from TextManager import TextManager from Performance import PerformanceHistory from Config import PreferenceWidget from Lesson import LessonGenerator from Widgets.Database import DatabaseWidget from PyQt4.QtCore import * from PyQt4.QtGui import * QApplication.setStyle("cleanlooks")
def setPalettes(self): inactive_palette = QPalette(Qt.black, Settings.getColor("quiz_inactive_bd"), Qt.lightGray, Qt.darkGray, Qt.gray, Settings.getColor("quiz_inactive_fg"), Qt.yellow, Settings.getColor("quiz_inactive_bg"), Qt.yellow) inactive_palette.setColor(QPalette.Highlight, Settings.getColor("quiz_inactive_hl")) inactive_palette.setColor(QPalette.HighlightedText, Settings.getColor("quiz_inactive_hl_text")) self.palettes = { 'wrong': QPalette(Qt.black, Settings.getColor("quiz_wrong_bd"), Qt.lightGray, Qt.darkGray, Qt.gray, Settings.getColor("quiz_wrong_fg"), Qt.white, Settings.getColor("quiz_wrong_bg"), Qt.yellow), 'right': QPalette(Qt.black, Settings.getColor("quiz_right_bd"), Qt.lightGray, Qt.darkGray, Qt.gray, Settings.getColor("quiz_right_fg"), Qt.yellow, Settings.getColor("quiz_right_bg"), Qt.yellow), 'invisible': QPalette(Qt.black, Settings.getColor("quiz_invisible_bd"), Qt.lightGray, Qt.darkGray, Qt.gray, Settings.getColor("quiz_invisible_color"), Qt.yellow, Settings.getColor("quiz_invisible_color"), Qt.yellow), 'inactive':inactive_palette } self.setPalette(self.palettes['inactive'])
def new_error(position,errors): '''Given list of error positions and current position, returns whether or there's a new error at position''' #considers adjacent errors to be part of the same error if the setting is toggled return position in errors and not (Settings.get('adjacent_errors_not_counted') and position - 1 in errors)
def activate_invisibility(self): '''Turns on invisible mode''' self.setPalette(self.palettes['invisible']) set_colored_typer_text(self,Settings.get('quiz_invisible_color')) #flushes out html with plaintext
def done(self): now = time.time() elapsed, chars, times, mis, mistakes = self.typer.getStats() assert chars == len(self.text[2]) accuracy = 1.0 - len(filter(None, mis)) / chars spc = elapsed / chars viscosity = sum(map(lambda x: ((x-spc)/spc)**2, times)) / chars DB.execute('insert into result (w,text_id,source,wpm,accuracy,viscosity) values (?,?,?,?,?,?)', (now, self.text[0], self.text[1], 12.0/spc, accuracy, viscosity)) v2 = DB.fetchone("""select agg_median(wpm),agg_median(acc) from (select wpm,100.0*accuracy as acc from result order by w desc limit %d)""" % Settings.get('def_group_by'), (0.0, 100.0)) self.result.setText("Last: %.1fwpm (%.1f%%), last 10 average: %.1fwpm (%.1f%%)" % ((12.0/spc, 100.0*accuracy) + v2)) self.emit(SIGNAL("statsChanged")) stats = collections.defaultdict(Statistic) visc = collections.defaultdict(Statistic) text = self.text[2] for c, t, m in zip(text, times, mis): stats[c].append(t, m) visc[c].append(((t-spc)/spc)**2) def gen_tup(s, e): perch = sum(times[s:e])/(e-s) visc = sum(map(lambda x: ((x-perch)/perch)**2, times[s:e]))/(e-s) return (text[s:e], perch, len(filter(None, mis[s:e])), visc) for tri, t, m, v in [gen_tup(i, i+3) for i in xrange(0, chars-2)]: stats[tri].append(t, m > 0) visc[tri].append(v) regex = re.compile(r"(\w|'(?![A-Z]))+(-\w(\w|')*)*") for w, t, m, v in [gen_tup(*x.span()) for x in regex.finditer(text) if x.end()-x.start() > 3]: stats[w].append(t, m > 0) visc[w].append(v) def type(k): if len(k) == 1: return 0 elif len(k) == 3: return 1 return 2 vals = [] for k, s in stats.iteritems(): v = visc[k].median() vals.append( (s.median(), v*100.0, now, len(s), s.flawed(), type(k), k) ) is_lesson = DB.fetchone("select discount from source where rowid=?", (None,), (self.text[1], ))[0] if Settings.get('use_lesson_stats') or not is_lesson: DB.executemany_('''insert into statistic (time,viscosity,w,count,mistakes,type,data) values (?,?,?,?,?,?,?)''', vals) DB.executemany_('insert into mistake (w,target,mistake,count) values (?,?,?,?)', [(now, k[0], k[1], v) for k, v in mistakes.iteritems()]) if is_lesson: mins = (Settings.get("min_lesson_wpm"), Settings.get("min_lesson_acc")) else: mins = (Settings.get("min_wpm"), Settings.get("min_acc")) if 12.0/spc < mins[0] or accuracy < mins[1]/100.0: self.setText(self.text) elif not is_lesson and Settings.get('auto_review'): ws = filter(lambda x: x[5] == 2, vals) if len(ws) == 0: self.emit(SIGNAL("wantText")) return ws.sort(key=lambda x: (x[4],x[0]), reverse=True) i = 0 while ws[i][4] != 0: i += 1 i += (len(ws) - i) // 4 self.emit(SIGNAL("wantReview"), map(lambda x:x[6], ws[0:i])) else: self.emit(SIGNAL("wantText"))
def activate_invisibility(self): '''Turns on invisible mode''' self.setPalette(self.palettes['invisible']) set_colored_typer_text(self, Settings.get( 'quiz_invisible_color')) #flushes out html with plaintext
def get_wait_text(): if Settings.get("req_space"): return ("Press SPACE and then immediately start typing the text\n" "Press ESCAPE to restart with a new text at any time") return "Press ESCAPE to restart with a new text at any time"
def checkText(self): if self.typer.target is None or self.typer.editflag: return v = unicode(self.typer.toPlainText()) if Settings.get('allow_mistakes') and len(v) >= len(self.typer.target): v = self.typer.target if not self.typer.started: if Settings.get('req_space'): #space is required before beginning the passage proper if v == u" ": #the first char typed was space #the first space only starts the session; clear the typer set_typer_text(self.typer,"",cursor_position=0) self.typer.when[0] = timer() self.typer.started = True return else: #reset the wait text set_typer_text(self.typer,self.typer.getWaitText()) self.typer.selectAll() return else: self.typer.when[0] = -1 self.typer.when[1] = timer() #have to set starting time regardless of correctness self.typer.started = True old_cursor = self.typer.textCursor() old_position = old_cursor.position() old_str_position = old_position - 1 #the position that has (presumably) just been typed #colors text in typer depending on errors errors = disagreements(v,self.typer.target,case_sensitive=Settings.get('case_sensitive')) first_error = errors[0] if errors else None #records time that any char was correctly hit #except that if we've already been correct farther, we don't record if self.typer.farthest_correct < old_str_position < len(self.typer.target) and not old_str_position in errors: self.typer.when[old_str_position+1] = timer() #zeroth position is original space self.typer.farthest_correct = old_str_position automatic_insertion_regex = generate_automatic_insertion_regex() #TODO: refactor so this doesn't rely on text setting then re-calling gimmick #TODO: change statistics to account for this #Automatically insert characters into the text area if automatic_insertion_regex and old_position == len(v): #only works if cursor is at the end automatic_insertion = re.match(automatic_insertion_regex,self.typer.target[old_position:]) if automatic_insertion: new_end = old_position + automatic_insertion.end() set_typer_text(self.typer, v[:old_position] + self.typer.target[old_position:new_end], cursor_position = new_end) self.checkText() #recovers the formatting return #TODO: refactor so this doesn't rely on text setting then re-calling gimmick #Prevent advancement until user correctly types space if Settings.get('ignore_until_correct_space') and self.typer.target[old_str_position] == u" " and old_str_position in errors: #gets rid of new character (sets as plaintext) set_typer_text(self.typer,v[:old_str_position] + v[old_str_position+1:],cursor_position = old_position - 1) self.checkText() #recovers the formatting return if len(v) >= len(self.typer.target) and (not first_error or first_error >= len(self.typer.target)): self.done() return if new_error(old_str_position,errors): self.typer.mistake[old_str_position] = True self.typer.mistakes[old_str_position] = self.typer.target[old_str_position] + v[old_str_position] if Settings.get('quiz_invisible'): self.typer.activate_invisibility() else: if Settings.get("quiz_use_wrong_palette") and errors: self.typer.setPalette(self.typer.palettes['wrong']) else: self.typer.setPalette(self.typer.palettes['right']) update_typer_html(self.typer,errors) #updates the label depending on errors self.updateLabel(old_position,errors)
def setText(self, text): self.text = text self.label.setText(self.text[2].replace(u"\n", u"{0}\n".format(Settings.get('label_return_symbol')))) self.typer.setTarget(self.text[2]) self.typer.setFocus()
def getWaitText(self): if Settings.get('req_space'): return "Press SPACE and then immediately start typing the text\n" + \ "Press ESCAPE to restart with a new text at any time" else: return "Press ESCAPE to restart with a new text at any time"
def __init__(self, fname): GObject.Object.__init__(self) with codecs.open(fname, "r", "utf_8_sig") as file: self.paras = self.get_paras(file) self.lessons = None self.min_chars = Settings.get("min_chars")
def updateLabel(self, position, errors): '''Populates the label with colors depending on current position and errors.''' #dict : str -> str ; original and displacement strs in error region (for easier display) err_replacements = { "\n": u"{0}<BR>".format(Settings.get('label_return_symbol')) } colors = {} #dict : int -> str, mapping errors to color if Settings.get('show_label_mistakes'): #showing mistakes; need to populate color colors = dict([(i, Settings.get('label_mistakes_color')) for i in errors]) if Settings.get('label_replace_spaces_in_mistakes'): err_replacements.update( space_replacement_dict_from_setting( 'label_mistakes_space_char')) text_strs = list( self.text[2] ) #list of strs, initially one char each, to operate on text_strs = html_list_process_spaces(text_strs) text_strs = replace_at_locs(text_strs, err_replacements, errors) def color_position(settings_color_var, use_space_var, space_var): '''Colors position with the color stored in settings_color_var. strs use_space_var and space_var are settings variables to look up. If [setting] use_space_var, space at position is replaced with [setting] space_var Returns the new text_strs list (for assignment).''' colors[position] = Settings.get(settings_color_var) if Settings.get(use_space_var): return replace_at_locs( text_strs, space_replacement_dict_from_setting(space_var), [position]) else: return text_strs #designates colors and replacements of position if Settings.get('show_label_position_with_prior_mistake' ) and position - 1 in errors: text_strs = color_position( 'label_position_with_prior_mistake_color', 'label_replace_spaces_in_position_with_prior_mistake', 'label_position_with_prior_mistake_space_char') elif Settings.get('show_label_position_with_mistakes') and errors: text_strs = color_position( 'label_position_with_mistakes_color', 'label_replace_spaces_in_position_with_mistakes', 'label_position_with_mistakes_space_char') elif Settings.get('show_label_position'): text_strs = color_position('label_position_color', 'label_replace_spaces_in_position', 'label_position_space_char') htmlized = "".join(html_color_strs(text_strs, colors)) htmlized = htmlized.replace( u"\n", u"{0}<BR>".format(Settings.get('label_return_symbol'))) self.label.setText(htmlized)
def main(players_list, player, username, config_level: Settings): # Auxiliary variables difficulty_level = config_level.getLevel() difficulty_level_spanish = '' if difficulty_level == 'easy': difficulty_level_spanish = 'Fácil' elif difficulty_level == 'medium': difficulty_level_spanish = 'Medio' elif difficulty_level == 'hard': difficulty_level_spanish = 'Difícil' letter_changes_available = LETTER_CHANGES_AVAILABLE # Setting savefile name savefile_name = 'user_' + username + '-level_{}-saved_game'.format(difficulty_level) + '.pkl' # Data structures for the interface to be updated with all_letters = [] for k, v in config_level.getLetterPool().items(): for i in range(v): all_letters.append(k) user_letter_array = [] ai_letter_array = [] # Assign letters for i in range(ARRAY_LENGTH): # For the user letter = random.choice(all_letters).upper() user_letter_array.append(letter) all_letters.remove(letter) # For the AI letter = random.choice(all_letters).upper() ai_letter_array.append(letter) all_letters.remove(letter) # Association between letter positions and colors for scoring in a matrix letter_matrix = [[[annotate_button((i, j), difficulty_level), colorize_button((i, j), difficulty_level)] for i in range(BOARD_WIDTH)] for j in range(BOARD_HEIGHT)] # Positions for the AI to choose from board_positions = [(i, j) for i in range(BOARD_WIDTH) for j in range(BOARD_HEIGHT)] # Random initial turn user_turn = random.choice([True, False]) # Initializing scores user_total_score = 0 ai_total_score = 0 #### Layouts #### # Board column layout ColumnBoard= [ [sg.Button(key=(i, j), button_text='{}'.format(letter_matrix[i][j][0]), button_color=(None, letter_matrix[i][j][1]), size=BUTTON_SIZE, pad=(BUTTON_PADDING, BUTTON_PADDING), enable_events=True) for i in range(BOARD_WIDTH)] for j in range(BOARD_HEIGHT) ] Column1= [ [sg.Button(key=i + ARRAY_LENGTH, button_text='?', size=(5,2), pad=((BUTTON_PADDING, BUTTON_PADDING), (8, BUTTON_PADDING)), enable_events=True, disabled=True) for i in range(ARRAY_LENGTH)], [sg.Text('Puntaje del ordenador:', key='-AI_TOTAL_SCORE_TEXT-', size=(19, 1), font=('Verdana', 10)),sg.Text(key='-AI_TOTAL_SCORE-', size=(8, 1), font=('Verdana', 11))], [sg.Text('_' * ((BUTTON_WIDTH + BUTTON_PADDING) * BOARD_WIDTH + 4), key='-BOTTOM_H_SEPARATOR1-')], [sg.Column(ColumnBoard, key='-C1-')], [sg.Text('_' * ((BUTTON_WIDTH + BUTTON_PADDING) * BOARD_WIDTH + 4), key='-BOTTOM_H_SEPARATOR2-')], [sg.Text('Puntaje del usuario:', key='-USER_TOTAL_SCORE_TEXT-', size=(19, 1), font=('Verdana', 10)), sg.Text(key='-USER_TOTAL_SCORE-', size=(8, 1), font=('Verdana', 11))], [sg.Button(key=i, button_text=user_letter_array[i], size=(5,2), pad=((BUTTON_PADDING, BUTTON_PADDING), (8, BUTTON_PADDING)), enable_events=True) for i in range(ARRAY_LENGTH)] ] Column2=[ [sg.T(' ' * 10, key='-t1-')], [sg.T(' ' * 10, key='-t2-')], [sg.Text('Nivel:',font=("Verdana", "9"),text_color='black', key='-t3-')], [sg.Text(difficulty_level_spanish, size=(15, 1), justification='center', font=("Verdana", "30", "bold"), text_color='#d7191c', key='-t4-')], [sg.T(' ' * 10, key='-t5-')], [sg.T(' ' * 10, key='-t6-')], [sg.Text('Tiempo restante de juego:', justification='center', key='-CLOCK_TEXT-')], [sg.Text(key='-CLOCK-', size=(8, 2), font=('Verdana', 10), justification='center')], [sg.T(' ' * 10, key='-t7-')], [sg.T(' ' * 10, key='-t8-')], [sg.T(' ' * 5),sg.Button('Regresar letras al atril', key='-RETURN_LETTERS-', pad=((28, BUTTON_PADDING), (4, BUTTON_PADDING)))], [sg.T(' ' * 10, key='-t9-')], [sg.T(' ' * 20),sg.Button('Puntuar', key='-SCORE-', pad=((0, BUTTON_PADDING), (8, BUTTON_PADDING)))], [sg.T(' ' * 10, key='-t10_save-')], [sg.Button('Guardar', key='-SAVE-', pad=((50, BUTTON_PADDING), (8, BUTTON_PADDING))), sg.Button('Cargar', key='-LOAD-', pad=((50, BUTTON_PADDING), (8, BUTTON_PADDING)))], [sg.T(' ' * 10, key='-t10-')], [sg.T(' ' * 8),sg.Button('Terminar', key='-FINISH-', pad=((50, BUTTON_PADDING), (8, BUTTON_PADDING)))], [sg.T(' ' * 10, key='-t11-')], [sg.T(' ' * 8),sg.Button(f'Cambiar letras ({letter_changes_available})', key='-CHANGE_LETTERS-', pad=((28, BUTTON_PADDING), (8, BUTTON_PADDING)))] ] layout= [ [sg.Column(Column1, key='-C3-'),sg.VerticalSeparator(),sg.Column(Column2, key='-C4-')]] window = sg.Window('ScrabbleAR', layout, grab_anywhere=True, no_titlebar=True) # Auxiliary variables letter_grabbed = '' user_word = '' user_word_score = 0 ai_word_score = 0 first_letter_placement_ever = True first_letter_placement = True moves_horizontally = True moves_vertically = True pos = () changing_letters = False user_chose_letter_to_change = False # These three variables are for keeping track of letters and positions in play letter_matrix_positions_updated = [] user_letter_array_positions_updated = [] letters_in_use = [] # Time variables game_duration = config_level.getGameDuration() start_time = time.time() finish_time = start_time + game_duration endgame = False # Loop while True: event, values = window.read(timeout=10) if event == None: break if endgame: sg.PopupOK(f'Juego finalizado. Tus puntos: {user_total_score}. Puntos del ordenador: {ai_total_score}', no_titlebar=True, grab_anywhere=True) # Saves max points for the user if user_total_score > player.get_puntos(): player.set_score(user_total_score) players_list.agregar_jugador(player) Database.guardo_base(players_list) sg.PopupOK(f'Has quedado en nuestro ranking!', no_titlebar=True, grab_anywhere=True) window.close() break # Updating time current_time = time.time() remaining_time = finish_time - current_time hours, rem = divmod(remaining_time, 3600) minutes, seconds = divmod(rem, 60) window['-CLOCK-'].Update('{:0>2}:{:0>2}:{:02d}'.format(int(hours),int(minutes),int(seconds))) # Checks for endtime if remaining_time <= 0: sg.PopupOK('Se acabó el tiempo', no_titlebar=True, grab_anywhere=True) endgame = True # Checks if save/load feature can be enabled if not changing_letters and user_turn and '' not in user_letter_array: window['-SAVE-'].Update(disabled=False) window['-LOAD-'].Update(disabled=False) else: window['-SAVE-'].Update(disabled=True) window['-LOAD-'].Update(disabled=True) # Checks for save button if event == '-SAVE-': if path.exists(savefile_name): sg.PopupOK(f'Se sobreescribirá la partida anterior.', no_titlebar=True, grab_anywhere=True) game_state = [letter_matrix, all_letters, user_letter_array, ai_letter_array, user_total_score, ai_total_score, remaining_time, letter_changes_available, config_level] with open(savefile_name, 'wb') as f: pickle.dump(game_state, f) sg.PopupOK(f'Juego guardado.', no_titlebar=True, grab_anywhere=True) # Checks for load button if event == '-LOAD-': # game_state = [letter_matrix, all_letters, user_letter_array, ai_letter_array, user_total_score, ai_total_score, remaining_time, letter_changes_available, config_level] try: with open(savefile_name, 'rb') as f: game_state = pickle.load(f) # loads game state variables letter_matrix = game_state[0] all_letters = game_state[1] user_letter_array = game_state[2] ai_letter_array = game_state[3] user_total_score = game_state[4] ai_total_score = game_state[5] remaining_time = game_state[6] letter_changes_available = game_state[7] config_level = game_state[8] # Resetting the time game_duration = remaining_time start_time = time.time() finish_time = start_time + game_duration # Updates board for pos in board_positions: window[pos].Update('{}'.format(letter_matrix[pos[0]][pos[1]][0])) # Updates user array for i in range(ARRAY_LENGTH): window[i].Update('{}'.format(user_letter_array[i])) # Updates letter changes available button window['-CHANGE_LETTERS-'].Update(f'Cambiar letras ({letter_changes_available})') # Popup confirmation sg.PopupOK(f'Juego cargado.', no_titlebar=True, grab_anywhere=True) except FileNotFoundError: sg.PopupOK(f'No se encontró partida guardada.', no_titlebar=True, grab_anywhere=True) # Checks for exit button if event == '-FINISH-': endgame = True # Updates score in the display window['-USER_TOTAL_SCORE-'].Update(user_total_score) window['-AI_TOTAL_SCORE-'].Update(ai_total_score) # AI play if not user_turn: # Lists valid words ai_word = '' ai_words = [] for i in range(len(ai_letter_array)): ai_words += [ai_word.join(ai_letter) for ai_letter in permutations(ai_letter_array, i + 1)] ai_unique_words = set(ai_words) ai_valid_words = [word for word in filter(valid, ai_unique_words)] # Tries to make a word try: # Chooses a valid word # Sorts them by length ai_valid_words.sort(key=lambda x: len(x)) if difficulty_level == 'easy': # Picks the shortest ai_word_choice = ai_valid_words.pop(0) elif difficulty_level == 'medium': # Picks the word in the middle ai_word_choice = ai_valid_words.pop(len(ai_valid_words) // 2) elif difficulty_level == 'hard': # Picks the longest ai_word_choice = ai_valid_words.pop() # Finds place to put the word positions_needed = len(ai_word_choice) while positions_needed: if first_letter_placement_ever: first_letter_placement_ever = False first_letter_placement = False # First letter ever to the center of the board pos = (BOARD_WIDTH // 2, BOARD_HEIGHT // 2) direction = random.choice(['downwards', 'rightwards']) positions_needed -= 1 letter_matrix_positions_updated.append(pos) elif first_letter_placement: first_letter_placement = False # First letter of a word is random pos = random.choice(board_positions) direction = random.choice(['downwards', 'rightwards']) positions_needed -= 1 letter_matrix_positions_updated.append(pos) else: if direction == 'downwards': for i in range(positions_needed): pos = list(pos) pos[1] += 1 pos = tuple(pos) letter_matrix_positions_updated.append(pos) positions_needed -= 1 else: # direction is rightwards for i in range(positions_needed): pos = list(pos) pos[0] += 1 pos = tuple(pos) letter_matrix_positions_updated.append(pos) positions_needed -= 1 if positions_needed == 0: # Checks if it's all available spots for p in letter_matrix_positions_updated: if p[0] >= BOARD_WIDTH or p[1] >= BOARD_HEIGHT or not board_is_empty(letter_matrix[p[0]][p[1]][0]): # Preparing for the next search positions_needed = len(ai_word_choice) first_letter_placement = True letter_matrix_positions_updated = [] break # Places the letters in the board for i in range(len(ai_word_choice)): # Grabs a letter ai_letter = ai_word_choice[i] # Erases it from the ai letter array ai_letter_array[ai_letter_array.index(ai_letter)] = '' # Gets the corresponding position pos = letter_matrix_positions_updated[i] # Updates association matrix letter_matrix[pos[0]][pos[1]][0] = ai_letter # Reflects it on the display window[pos].Update('{}'.format(letter_matrix[pos[0]][pos[1]][0])) # Scores the AI word ai_word_score = score(ai_word_choice, letter_matrix, letter_matrix_positions_updated, config_level) ai_total_score += ai_word_score # Checks for endgame if there aren't enough letters in the pool to replace the last used if len(ai_word_choice) > len(all_letters): sg.PopupOK('No hay más letras en la bolsa', no_titlebar=True, grab_anywhere=True) endgame = True else: # Assign more letters for the ai for i in range(len(ai_word_choice)): letter = random.choice(all_letters).upper() ai_letter_array[ai_letter_array.index('')] = letter all_letters.remove(letter) # The turn goes back to the user user_turn = not user_turn # Resets for the user first_letter_placement = True letter_matrix_positions_updated = [] # Handles .pop() at line 208 if no valid words were found except IndexError: sg.PopupOK('El ordenador no ha encontrado palabra', no_titlebar=True, grab_anywhere=True) endgame = True # Interaction with user's set of letters if type(event) == int and not changing_letters and user_turn: # Grabs the letter from the array if user_letter_array[event] != '' and letter_grabbed == '': letter_grabbed = user_letter_array[event] letters_in_use.append(letter_grabbed) user_letter_array[event] = '' window[event].Update('') user_letter_array_positions_updated.append(event) # Puts the letter back in the array elif user_letter_array[event] == '' and letter_grabbed != '': user_letter_array[event] = letter_grabbed letters_in_use.remove(letter_grabbed) letter_grabbed = '' window[event].Update('{}'.format(user_letter_array[event])) user_letter_array_positions_updated.remove(event) # Interaction with game board if type(event) == tuple and board_is_empty(letter_matrix[event[0]][event[1]][0]) and letter_grabbed != '' and not changing_letters and user_turn: # The first ever letter placement goes in the center if first_letter_placement_ever: pos = (BOARD_WIDTH // 2, BOARD_HEIGHT // 2) # Checks whether the first letter is positioned correctly if event[0] == pos[0] and event[1] == pos[1]: first_letter_placement_ever = False first_letter_placement = False letter_matrix[event[0]][event[1]][0] = letter_grabbed window[event].Update('{}'.format(letter_matrix[event[0]][event[1]][0])) user_word += letter_grabbed letter_grabbed = '' pos = event[0], event[1] letter_matrix_positions_updated.append(event) # Gives a hint to where should the letter go else: window[(pos[0], pos[1])].Update('{}'.format(letter_grabbed)) window.Refresh() time.sleep(0.1) window[(pos[0], pos[1])].Update(annotate_button((pos[0], pos[1]), difficulty_level)) else: # The subsequent first letter placement are free if first_letter_placement: # Updates board letter_matrix[event[0]][event[1]][0] = letter_grabbed window[event].Update('{}'.format(letter_matrix[event[0]][event[1]][0])) user_word += letter_grabbed letter_grabbed = '' pos = event[0], event[1] letter_matrix_positions_updated.append(event) first_letter_placement = False # If it's not the first letter placement, restrains the placements to exclusively rightwards or downwards elif (event[0] - 1 == pos[0] and event[1] == pos[1]) or (event[0] == pos[0] and event[1] - 1 == pos[1]): if moves_horizontally and event[0] - 1 == pos[0] and event[1] == pos[1]: moves_vertically = False # Updates board letter_matrix[event[0]][event[1]][0] = letter_grabbed window[event].Update('{}'.format(letter_matrix[event[0]][event[1]][0])) user_word += letter_grabbed letter_grabbed = '' pos = event[0], event[1] letter_matrix_positions_updated.append(event) # Gives a hint to where should the letter go (the exception could raise if the movement would be illegal) elif moves_horizontally and not moves_vertically: try: window[(pos[0] + 1, pos[1])].Update('{}'.format(letter_grabbed)) window.Refresh() time.sleep(0.1) window[(pos[0] + 1, pos[1])].Update(annotate_button((pos[0] + 1, pos[1]), difficulty_level)) except AttributeError: pass elif moves_vertically and event[0] == pos[0] and event[1] - 1 == pos[1]: moves_horizontally = False # Updates board letter_matrix[event[0]][event[1]][0] = letter_grabbed window[event].Update('{}'.format(letter_matrix[event[0]][event[1]][0])) user_word += letter_grabbed letter_grabbed = '' pos = event[0], event[1] letter_matrix_positions_updated.append(event) # Gives a hint to where should the letter go (the exception could raise if the movement would be illegal) elif moves_vertically and not moves_horizontally: try: window[(pos[0], pos[1] + 1)].Update('{}'.format(letter_grabbed)) window.Refresh() time.sleep(0.1) window[(pos[0], pos[1] + 1)].Update(annotate_button((pos[0], pos[1] + 1), difficulty_level)) except AttributeError: pass # Gives a hint to where should the letter go (the exception could raise if the movement would be illegal) elif moves_horizontally and not moves_vertically: try: window[(pos[0] + 1, pos[1])].Update('{}'.format(letter_grabbed)) window.Refresh() time.sleep(0.1) window[(pos[0] + 1, pos[1])].Update(annotate_button((pos[0] + 1, pos[1]), difficulty_level)) except AttributeError: pass # Gives a hint to where should the letter go (the exception could raise if the movement would be illegal) elif moves_vertically and not moves_horizontally: try: window[(pos[0], pos[1] + 1)].Update('{}'.format(letter_grabbed)) window.Refresh() time.sleep(0.1) window[(pos[0], pos[1] + 1)].Update(annotate_button((pos[0], pos[1] + 1), difficulty_level)) except AttributeError: pass # Resetting current word if event == '-RETURN_LETTERS-' and user_turn: letter_grabbed = '' # Reverses current play # Clears movement flags first_letter_placement = True moves_horizontally = True moves_vertically = True pos = () for t in letter_matrix_positions_updated: letter_matrix[t[0]][t[1]][0] = annotate_button(t, difficulty_level) window[t].Update('{}'.format(letter_matrix[t[0]][t[1]][0])) while user_letter_array_positions_updated: i = user_letter_array_positions_updated.pop() user_letter_array[i] = letters_in_use.pop() window[i].Update('{}'.format(user_letter_array[i])) # Checks if the first word has been placed if user_total_score == 0 and ai_total_score == 0: first_letter_placement_ever = True # User starts changing letters if event == '-CHANGE_LETTERS-' and not changing_letters and user_turn: changing_letters = True window['-CHANGE_LETTERS-'].Update('Selecciónalas') # User chooses letters to change if type(event) == int and changing_letters: user_chose_letter_to_change = True window['-CHANGE_LETTERS-'].Update('Hacer el cambio') # Grabs the letter from the array if user_letter_array[event] != '': letter_grabbed = user_letter_array[event] letters_in_use.append(letter_grabbed) user_letter_array[event] = '' window[event].Update('') user_letter_array_positions_updated.append(event) # Puts the letter back in the array elif user_letter_array[event] == '' and letter_grabbed != '': user_letter_array[event] = letter_grabbed letters_in_use.remove(letter_grabbed) letter_grabbed = '' window[event].Update('{}'.format(user_letter_array[event])) user_letter_array_positions_updated.remove(event) # User finishes changing letters if event == '-CHANGE_LETTERS-' and changing_letters and user_chose_letter_to_change: letter_changes_available -= 1 letter_grabbed = '' # Resetting the flags user_chose_letter_to_change = False changing_letters = False # Putting back the chosen letters quantity_chosen = len(letters_in_use) for i in range(quantity_chosen): all_letters.append(letters_in_use.pop()) # Assign new letters new_letters = [] for i in range(quantity_chosen): letter = random.choice(all_letters).upper() new_letters.append(letter) all_letters.remove(letter) # Update the user letter array for i in user_letter_array_positions_updated: user_letter_array[i] = new_letters.pop() # Update the display of the user letter array while user_letter_array_positions_updated: i = user_letter_array_positions_updated.pop() window[i].Update('{}'.format(user_letter_array[i])) window['-CHANGE_LETTERS-'].Update(f'Cambiar letras ({letter_changes_available})') if letter_changes_available == 0: window['-CHANGE_LETTERS-'].Update(disabled=True) # When the user changes their letters, their turn finishes user_turn = not user_turn # Score the created word if event == '-SCORE-' and user_turn: if valid(user_word): user_word_score = score(user_word, letter_matrix, letter_matrix_positions_updated, config_level) user_total_score += user_word_score user_word_score = 0 # Check if there are enough letters in the pool to replace the last used if len(user_word) > len(all_letters): sg.PopupOK('No hay más letras en la bolsa', no_titlebar=True, grab_anywhere=True) endgame = True # Assign more letters for the user for i in range(len(user_word)): letter = random.choice(all_letters).upper() user_letter_array[user_letter_array.index('')] = letter all_letters.remove(letter) # Updates user letter array display with the new and the remaining letters for i in range(len(user_letter_array)): window[i].Update('{}'.format(user_letter_array[i])) # Resets variables for next invalid word letter_matrix_positions_updated = [] user_letter_array_positions_updated = [] letters_in_use = [] # When the user scores a valid word, their turn finishes user_turn = not user_turn else: # Reverses current play for t in letter_matrix_positions_updated: letter_matrix[t[0]][t[1]][0] = annotate_button(t, difficulty_level) window[t].Update('{}'.format(letter_matrix[t[0]][t[1]][0])) while user_letter_array_positions_updated: i = user_letter_array_positions_updated.pop() user_letter_array[i] = letters_in_use.pop() window[i].Update('{}'.format(user_letter_array[i])) sg.PopupOK('Tu palabra era inválida', no_titlebar=True, grab_anywhere=True) # Checks if the first word has been placed if user_total_score == 0 and ai_total_score == 0: first_letter_placement_ever = True # Clears movement flags first_letter_placement = True moves_horizontally = True moves_vertically = True pos = () user_word = ''
def setPalettes(self): inactive_palette = QPalette(Qt.black, Settings.getColor("quiz_inactive_bd"), Qt.lightGray, Qt.darkGray, Qt.gray, Settings.getColor("quiz_inactive_fg"), Qt.yellow, Settings.getColor("quiz_inactive_bg"), Qt.yellow) inactive_palette.setColor(QPalette.Highlight, Settings.getColor("quiz_inactive_hl")) inactive_palette.setColor(QPalette.HighlightedText, Settings.getColor("quiz_inactive_hl_text")) self.palettes = { 'wrong': QPalette(Qt.black, Settings.getColor("quiz_wrong_bd"), Qt.lightGray, Qt.darkGray, Qt.gray, Settings.getColor("quiz_wrong_fg"), Qt.white, Settings.getColor("quiz_wrong_bg"), Qt.yellow), 'right': QPalette(Qt.black, Settings.getColor("quiz_right_bd"), Qt.lightGray, Qt.darkGray, Qt.gray, Settings.getColor("quiz_right_fg"), Qt.yellow, Settings.getColor("quiz_right_bg"), Qt.yellow), 'invisible': QPalette(Qt.black, Settings.getColor("quiz_invisible_bd"), Qt.lightGray, Qt.darkGray, Qt.gray, Settings.getColor("quiz_invisible_color"), Qt.yellow, Settings.getColor("quiz_invisible_color"), Qt.yellow), 'inactive': inactive_palette } self.setPalette(self.palettes['inactive'])
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) uic.loadUi("gui5.ui", self) self.show() self.setAcceptDrops(True) def messagePopup(title, icon, text, callf=None): msg = QMessageBox( ) # Pylance is being stupid, I had to disable Type checking. msg.setWindowTitle(title) msg.setIcon(icon) msg.setText(text) if callf is not None: msg.setStandardButtons( QMessageBox.Ok | QMessageBox.Cancel ) # F**k pylance it says that this line is wrong anf that int can't bd a button, there is not int. msg.buttonClicked.connect(callf) msg.exec_() def SaveDefaultConfig(i): text: str = i.text().lower() if "ok" in text: nonlocal settings settings = Settings.loadDefault() settings.toJson(settingsPath) else: exit() def status( s="" ): # shows status message and changes color of the status bar. self.statusBar().showMessage(s) if s == "Ready.": self.statusBar().setStyleSheet("background-color: #00BB00") elif s == "Busy.": self.statusBar().setStyleSheet("background-color: #FF6600") else: self.statusBar().setStyleSheet("background-color: #A9A9A9") def process_start(cmd: List[str], output_console): if (sys.platform.startswith("win")): # (os.name == "nt"): process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, creationflags=0x08000000, universal_newlines=True, encoding="utf8" ) # this one does not check if another process is running else: # (sys.platform.startswith(("linux", "darwin", "freebsd"))): #(os.name == "posix"): #other oeses should be fine with this process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, errors="ignore") while True: test = process.stdout.readline() if not test: break test = str(test) if "\\n" in test: test = test.replace("\\n", "\n") output_console.insertPlainText(test) self.scrollbar = output_console.verticalScrollBar() self.scrollbar.setValue(self.scrollbar.maximum()) QtWidgets.QApplication.processEvents() print("\a") output_console.insertPlainText("#yt-dl# Process has finished.\n\n") QtWidgets.QApplication.processEvents() self.scrollbar = output_console.verticalScrollBar() self.scrollbar.setValue(self.scrollbar.maximum()) def openFolder(loc=""): if (sys.platform.startswith("win")): os.system(f"start {loc}") elif (sys.platform.startswith( ("darwin", "haiku"))): # haiku support :3 os.system(f"open {loc}") else: # (sys.platform.startswith(("linux", "freebsd"))): #hoping that other OSes use xdg-open os.system(f"xdg-open {loc}") # region ===== startup ===== pffmpeg = glob.glob(f"{spath}/ffmpeg*") pffprobe = glob.glob(f"{spath}/ffprobe*") if (not pffmpeg and not pffprobe): fdir = False else: fdir = True if (os.path.exists(settingsPath)): settings = Settings.fromJson(settingsPath) else: messagePopup( "Settings error", QMessageBox.Critical, "You are missing a config file,\nPress OK to load default config.", SaveDefaultConfig) self.setWindowTitle(f"yt-dl {ver}") self.tabWidget.setCurrentIndex( settings.defaultTab ) # the code will not get here if settings is undefined. global running running = False status("Ready.") # endregion # region ==========🎶AUDIO🎶========== def Audio(): global running if not running: running = True status("Busy.") self.tabWidget.setTabText(0, "*Audio") self.aud_output_console.setHtml( "") # clearing the output_console url = self.aud_url_bar.text() if self.aud_playlist_checkbox.isChecked(): numb = self.aud_playlist_bar.text() else: numb = None if (numb is None): cmd = [[ "youtube-dl", "-o", f"{settings.Youtubedl.audioDir}%(title)s.%(ext)s", "--no-playlist", "-x", "--prefer-ffmpeg" ], ["--audio-format", "mp3", f"{url}"]] elif (numb == ""): cmd = [[ "youtube-dl", "-o", f"{settings.Youtubedl.audioDir}%(playlist_index)s. %(title)s.%(ext)s", "--yes-playlist", "-i", "-x", "--prefer-ffmpeg" ], ["--audio-format", "mp3", f"{url}"]] else: cmd = [[ "youtube-dl", "-o", f"{settings.Youtubedl.audioDir}%(playlist_index)s. %(title)s.%(ext)s", "--yes-playlist", "-i", "--playlist-items", f"{numb}", "-x", "--prefer-ffmpeg" ], ["--audio-format", "mp3", f"{url}"]] floc = [f"--ffmpeg-location", f"{spath}"] if (fdir is True): cmd = cmd[0] + floc + cmd[1] else: cmd = cmd[0] + cmd[1] self.aud_output_console.insertPlainText( "#yt-dl# starting youtube-dl please wait...\n") process_start(cmd, self.aud_output_console) running = False status("Ready.") self.tabWidget.setTabText(0, "Audio") else: messagePopup("Process warning", QMessageBox.Warning, "One process already running!") def aud_playlist_bar_toggle(): self.aud_playlist_bar.setEnabled( self.aud_playlist_checkbox.isChecked()) if (self.aud_playlist_checkbox.isChecked()): self.aud_playlist_bar.setStyleSheet( "background-color: #909090;") else: self.aud_playlist_bar.setStyleSheet( "background-color: #707070;") def aud_open(): openFolder(settings.Youtubedl.audioDir) # =====aud_controls=====# self.aud_folder_button.clicked.connect(aud_open) self.aud_download_button.clicked.connect(Audio) self.aud_playlist_checkbox.clicked.connect(aud_playlist_bar_toggle) self.aud_output_console.setHtml( "#yt-dl# Welcome to yt-dl-gui (Audio) paste a link and hit download." ) # endregion # region ==========📼VIDEO📼========== def Video(): global running if running is False: running = True status("Busy.") self.tabWidget.setTabText(1, "*Video") self.vid_output_console.setHtml( "") # clearing the output_console. url = self.vid_url_bar.text() if self.vid_playlist_checkbox.isChecked(): numb = self.vid_playlist_bar.text() else: numb = None if (numb is None): cmd = [[ "youtube-dl", "-o", f"{settings.Youtubedl.videoDir}%(title)s.%(ext)s", "-f" ], ["--no-playlist", f"{url}"]] elif (numb == ""): cmd = [[ "youtube-dl", "-o", f"{settings.Youtubedl.videoDir}%(playlist_index)s. %(title)s.%(ext)s", "-f" ], ["--yes-playlist", f"{url}"]] else: cmd = [[ "youtube-dl", "-o", f"{settings.Youtubedl.videoDir}%(playlist_index)s. %(title)s.%(ext)s", "-f" ], [ "--yes-playlist", "--playlist-items", f"{numb}", f"{url}" ]] if self.vid_best_radio.isChecked(): qual = ["bestvideo+bestaudio"] elif self.vid_custom_radio.isChecked(): qual = [self.vid_quality_bar.text()] else: qual = ["best"] floc = [f"--ffmpeg-location", f"{spath}"] if (fdir is True): cmd = cmd[0] + qual + floc + cmd[1] else: cmd = cmd[0] + qual + cmd[1] self.vid_output_console.insertPlainText( "#yt-dl# starting youtube-dl please wait...\n") process_start(cmd, self.vid_output_console) running = False status("Ready.") self.tabWidget.setTabText(1, "Video") else: messagePopup("Process warning", QMessageBox.Warning, "One process already running!") def vid_quality(): global running if running is False: running = True status("Busy.") self.vid_output_console.setHtml( "") # clearing the output_console url = self.vid_url_bar.text() cmd = ["youtube-dl", "-F", "--no-playlist", f"{url}"] self.vid_output_console.insertPlainText( "#yt-dl# starting yt-dl please wait...\n") process_start(cmd, self.vid_output_console) running = False status("Ready.") else: messagePopup("Process warning", QMessageBox.Warning, "One process already running!") def vid_playlist_bar_toggle(): self.vid_playlist_bar.setEnabled( self.vid_playlist_checkbox.isChecked()) if (self.vid_playlist_checkbox.isChecked()): self.vid_playlist_bar.setStyleSheet( "background-color: #909090;") else: self.vid_playlist_bar.setStyleSheet( "background-color: #707070;") def vid_quality_bar_toggle(): self.vid_quality_bar.setEnabled(self.vid_custom_radio.isChecked()) if (self.vid_custom_radio.isChecked()): self.vid_quality_bar.setStyleSheet( "background-color: #909090;") else: self.vid_quality_bar.setStyleSheet( "background-color: #707070;") def vid_open(): openFolder(settings.Youtubedl.videoDir) # =====vid_controls=====# self.vid_folder_button.clicked.connect(vid_open) self.vid_download_button.clicked.connect(Video) self.vid_quality_button.clicked.connect(vid_quality) self.vid_playlist_checkbox.clicked.connect(vid_playlist_bar_toggle) self.vid_custom_radio.toggled.connect(vid_quality_bar_toggle) self.vid_output_console.setHtml( "#yt-dl# Welcome to yt-dl-gui (Video) paste a link and hit download." ) # endregion # region ==========📑SUBS📑========== def Subs(): global running if running is False: running = True status("Busy.") self.tabWidget.setTabText(2, "*Subs") temp = tempfile.mkdtemp() + os.path.sep self.sub_output_console.setHtml( "") # clearing the output_console. url = self.sub_url_bar.text() cmd = [[], []] if self.sub_playlist_checkbox.isChecked(): if self.sub_playlist_bar.text() == "": cmd[0] = [ "youtube-dl", "-o", f"{temp}%(title)s.%(ext)s", "--yes-playlist", "--write-sub", "--write-auto-sub" ] else: numb = self.sub_playlist_bar.text() cmd[0] = [ "youtube-dl", "-o", f"{temp}%(title)s.%(ext)s", "--yes-playlist", "--playlist-items", f"{numb}", "--write-sub", "--write-auto-sub" ] else: cmd[0] = [ "youtube-dl", "-o", f"{temp}%(title)s.%(ext)s", "--no-playlist", "--write-sub", "--write-auto-sub" ] cmd[1] = ["--sub-format", "vtt", "--skip-download", f"{url}"] lang = None if self.sub_lang_checkbox.isChecked(): lang = self.sub_lang_bar.text() floc = [f"--ffmpeg-location", f"{spath}"] if (fdir is True): if lang is not None: cmd = cmd[0] + lang + floc + cmd[1] else: cmd = cmd[0] + floc + cmd[1] else: if lang is not None: cmd = cmd[0] + lang + cmd[1] else: cmd = cmd[0] + cmd[1] self.sub_output_console.insertPlainText( "#yt-dl# starting yt-dl please wait...\n") process_start(cmd, self.sub_output_console) subpath = glob.glob(f"{temp}*.vtt") os.makedirs(settings.Youtubedl.videoDir, exist_ok=True) for item in subpath: namei = os.path.basename(item) namei = namei[:-3] newsubpath = f"{settings.Youtubedl.videoDir}{namei}srt" # I don't like this fix to a complain about var type if os.path.isfile(newsubpath): self.sub_output_console.insertPlainText( f"#yt-dl# file {item} already exists skipping...\n" ) else: cmd = ["-i", f"{item}", f"{newsubpath}"] floc = [ f"{spath+os.path.sep+'ffmpeg'}", "-hide_banner" ] if (fdir is True): cmd = floc + cmd else: cmd = ["ffmpeg", "-hide_banner"] + cmd self.sub_output_console.insertPlainText( "#yt-dl# starting ffmpeg please wait...\n") process_start(cmd, self.sub_output_console) running = False status("Ready.") self.tabWidget.setTabText(2, "Subs") else: messagePopup("Process warning", QMessageBox.Warning, "One process already running!") def sub_lang(): global running if running is False: running = True status("Busy.") self.sub_output_console.setHtml( "") # clearing the output_console url = self.sub_url_bar.text() cmd = ["youtube-dl", "--list-subs", "--no-playlist", f"{url}"] process_start(cmd, self.sub_output_console) running = False status("Ready.") else: messagePopup("Process warning", QMessageBox.Warning, "One process already running!") def sub_playlist_bar_toggle(): self.sub_lang_button.setEnabled( not self.sub_playlist_checkbox.isChecked()) self.sub_playlist_bar.setEnabled( self.sub_playlist_checkbox.isChecked()) if (self.sub_playlist_checkbox.isChecked()): self.sub_playlist_bar.setStyleSheet( "background-color: #909090;") else: self.sub_playlist_bar.setStyleSheet( "background-color: #707070;") def sub_lang_bar_toggle(): self.sub_lang_bar.setEnabled(self.sub_lang_checkbox.isChecked()) if (self.sub_lang_checkbox.isChecked()): self.sub_lang_bar.setStyleSheet("background-color: #909090;") else: self.sub_lang_bar.setStyleSheet("background-color: #707070;") # =====sub_controls=====# self.sub_folder_button.clicked.connect(vid_open) self.sub_download_button.clicked.connect(Subs) self.sub_lang_button.clicked.connect(sub_lang) self.sub_playlist_checkbox.toggled.connect(sub_playlist_bar_toggle) self.sub_lang_checkbox.toggled.connect(sub_lang_bar_toggle) self.sub_output_console.setHtml( "#yt-dl# Welcome to yt-dl-gui (Subtitles) paste a link and hit download." ) # endregion # region ==========💿RE-ENCODE💿========== def Reencode(): global running if running is False: running = True status("Busy.") self.tabWidget.setTabText(3, "*Re-encode") self.ree_output_console.setHtml( "") # clearing the output_console location = self.ree_location_bar.text() videoc = self.ree_videoc_bar.text() videoq = self.ree_videoq_bar.text() audioc = self.ree_audioc_bar.text() audiob = self.ree_audiob_bar.text() append = self.ree_append_bar.text() if location[-2:] == os.path.sep + "*": # whole folder VidsToRender = glob.glob(location) else: VidsToRender = [f"{location}"] for video in VidsToRender: if os.path.isfile(os.path.splitext(video)[0] + append): self.ree_output_console.insertPlainText( f"#yt-dl# file {video} already exists skipping...\n" ) else: cmd = [[ "-hwaccel", "auto", "-i", f"{video}", "-map", "0:v?", "-map", "0:a?", "-map", "0:s?" ], ["-max_muxing_queue_size", "9999", "-b:v", "0K"], [f"{os.path.splitext(video)[0]+append}"]] # //Video Quality\\# if "," in videoq: VQsplit = videoq.split(",") else: VQsplit = [videoq, videoq, videoq] # //Video Codec\\# if (videoc == "libx265"): VideoCodec = ["-c:v", f"{videoc}"] quality = [ "-crf", f"{int(VQsplit[0])-1}", "-qmin", f"{int(VQsplit[1])-1}", "-qmax", f"{int(VQsplit[2])-1}" ] Vformat = ["-vf", "format=yuv420p"] cmd = [ cmd[0] + VideoCodec + quality + cmd[1] + Vformat, cmd[2] ] elif (videoc == "copy"): VideoCodec = [f"-c:v", f"{videoc}"] cmd = [cmd[0] + VideoCodec + cmd[1], cmd[2]] elif (videoc == "remove"): VideoCodec = ["-vn"] cmd = [cmd[0] + VideoCodec + cmd[1], cmd[2]] elif (videoc == "hevc_nvenc"): VideoCodec = ["-c:v", f"{videoc}"] quality = [ "-rc:v", "vbr", "-qmin", f"{int(VQsplit[1])}", "-qmax", f"{int(VQsplit[2])}", "-bf", "1" ] Vformat = ["-vf", "format=yuv420p"] cmd = [ cmd[0] + VideoCodec + quality + cmd[1] + Vformat, cmd[2] ] elif (videoc == "h264_nvenc"): VideoCodec = ["-c:v", f"{videoc}"] quality = [ "-rc:v", "vbr", "-qmin", f"{int(VQsplit[1])}", "-qmax", f"{int(VQsplit[2])}" ] Vformat = ["-vf", "format=yuv420p"] cmd = [ cmd[0] + VideoCodec + quality + cmd[1] + Vformat, cmd[2] ] else: VideoCodec = ["-c:v", f"{videoc}"] quality = [ "-cq", f"{int(VQsplit[0])-1}", "-qmin", f"{int(VQsplit[1])-1}", "-qmax", f"{int(VQsplit[2])-1}" ] Vformat = ["-vf", "format=yuv420p"] cmd = [ cmd[0] + VideoCodec + quality + cmd[1] + Vformat, cmd[2] ] # //Audio\\# if (audioc == "remove"): AudioEverything = ["-an"] cmd = [cmd[0] + AudioEverything, cmd[1]] else: AudioEverything = [ "-c:a", f"{audioc}", "-strict", "-2", "-b:a", f"{audiob}" ] cmd = [cmd[0] + AudioEverything, cmd[1]] # //Subtitles\\# if (videoc == "remove"): cmd = cmd[0] + cmd[1] else: SubsC = ["-c:s", "copy"] cmd = cmd[0] + SubsC + cmd[1] floc = [ f"{spath+os.path.sep+'ffmpeg'}", "-hide_banner" ] if (fdir is True): cmd = floc + cmd else: cmd = ["ffmpeg", "-hide_banner"] + cmd process_start(cmd, self.ree_output_console) # print(cmd) running = False status("Ready.") self.tabWidget.setTabText(3, "Re-encode") else: messagePopup("Process warning", QMessageBox.Warning, "One process already running!") def ree_settings(): if self.ree_settings_combobox.currentIndex() == 4: # custom self.ree_videoc_bar.setText(settings.Ffmpeg.videoCodec) self.ree_videoq_bar.setText(settings.Ffmpeg.videoQuality) self.ree_audioc_bar.setText(settings.Ffmpeg.audioCodec) self.ree_audiob_bar.setText(settings.Ffmpeg.audioBitrate) self.ree_append_bar.setText(settings.Ffmpeg.append) elif self.ree_settings_combobox.currentIndex() == 0: # hevc_opus self.ree_videoc_bar.setText("libx265") self.ree_videoq_bar.setText("24,24,24") self.ree_audioc_bar.setText("opus") self.ree_audiob_bar.setText("190k") self.ree_append_bar.setText("_hevcopus.mkv") elif self.ree_settings_combobox.currentIndex() == 1: # h264_nvenc self.ree_videoc_bar.setText("h264_nvenc") self.ree_videoq_bar.setText("24,24,24") self.ree_audioc_bar.setText("aac") self.ree_audiob_bar.setText("190k") self.ree_append_bar.setText("_nvenc.mov") elif self.ree_settings_combobox.currentIndex() == 2: # hevc_nvenc self.ree_videoc_bar.setText("hevc_nvenc") self.ree_videoq_bar.setText("24,24,24") self.ree_audioc_bar.setText("opus") self.ree_audiob_bar.setText("190k") self.ree_append_bar.setText("_henc.mkv") elif self.ree_settings_combobox.currentIndex() == 3: # mp3 self.ree_videoc_bar.setText("remove") self.ree_videoq_bar.setText("none") self.ree_audioc_bar.setText("mp3") self.ree_audiob_bar.setText("190k") self.ree_append_bar.setText(".mp3") def ree_settings_save(): settings.Ffmpeg.videoCodec = self.ree_videoc_bar.text() settings.Ffmpeg.audioCodec = self.ree_audioc_bar.text() settings.Ffmpeg.videoQuality = self.ree_videoq_bar.text() settings.Ffmpeg.audioBitrate = self.ree_audiob_bar.text() settings.Ffmpeg.append = self.ree_append_bar.text() settings.defaultCodec = self.ree_settings_combobox.currentIndex() settings.toJson(settingsPath) def ree_choose(): self.ree_location_bar.setText(QFileDialog.getOpenFileName()[0]) def ree_open(): location = self.ree_location_bar.text() openFolder(os.path.dirname(location)) # =====ree_controls=====# self.ree_settings_combobox.addItem( "hevc_opus") # setting up items in combo list self.ree_settings_combobox.addItem("h264_nvenc") self.ree_settings_combobox.addItem("hevc_nvenc") self.ree_settings_combobox.addItem("mp3") self.ree_settings_combobox.addItem("custom") self.ree_settings_combobox.setCurrentIndex(settings.defaultCodec) ree_settings() # load option on startup self.ree_choose_button.clicked.connect(ree_choose) self.ree_reencode_button.clicked.connect(Reencode) self.ree_folder_button.clicked.connect(ree_open) self.ree_settings_combobox.activated.connect(ree_settings) self.ree_settings_button.clicked.connect(ree_settings_save) self.ree_output_console.setHtml( "#yt-dl# Welcome to yt-dl-gui (Re-encode) paste a link and hit download." ) # self.ree_location_bar.setDragEnabled(True) self.ree_location_bar.setAcceptDrops(True) # endregion # region ==========🔄UPDATE🔄========== def update_yt_dl(): cmd = ["git", "pull", "--recurse-submodules"] process_start(cmd, self.upd_output_console) def update_depend(): pips = settings.Python.pip.split(" ") cmd = [ f"{settings.Python.python}", "-m", "pip", "install", "-U", "pip" ] process_start(cmd, self.upd_output_console) cmd = pips + ["install", "-U", "-r", "req-gui5.txt"] process_start(cmd, self.upd_output_console) def Update(): global running if running is False: running = True status("Busy.") self.tabWidget.setTabText(4, "*Update") self.upd_output_console.setHtml( "") # clearing the output_console if self.upd_update_combobox.currentIndex() == 1: update_yt_dl() elif self.upd_update_combobox.currentIndex() == 2: update_depend() else: update_yt_dl() update_depend() running = False status("Ready.") self.tabWidget.setTabText(4, "Update") else: messagePopup("Process warning", QMessageBox.Warning, "One process already running!") def upd_auto_toggle(): # autoupdate is not a thing tho settings.autoUpdate = not settings.autoUpdate settings.toJson(settingsPath) self.upd_auto_button.setText( f"Autoupdate=\"{settings.autoUpdate}\"") # ======upd_controls======# self.upd_update_combobox.addItem( "All") # setting up items in combo list self.upd_update_combobox.addItem("yt-dl") self.upd_update_combobox.addItem("dependencies") if (sys.platform.startswith("haiku")): self.upd_update_combobox.setCurrentIndex(1) ''' #region (branch switching code) branches = os.listdir(spath+".git/refs/heads") for branch in branches: self.upd_branch_combobox.addItem(branch) process = subprocess.Popen(["git", "rev-parse", "--abbrev-ref", "HEAD"], stdout=subprocess.PIPE) gcurb = str(process.stdout.read()) curb = "Current branch: "+curb[2:-3] self.upd_branch_label.setText(curb) #endregion ''' QtWidgets.QApplication.processEvents() if settings.autoUpdate: self.tabWidget.setCurrentIndex(4) Update() self.upd_update_button.clicked.connect(Update) # self.upd_branch_button.clicked.connect(upd_branch) self.upd_auto_button.setText(f"Autoupdate=\"{settings.autoUpdate}\"") self.upd_auto_button.clicked.connect(upd_auto_toggle) # endregion # region ==========📈SETTINGS📈========== def set_save(): settings.Youtubedl.audioDir = self.set_audio_bar.text() settings.Youtubedl.videoDir = self.set_videos_bar.text() settings.Python.python = self.set_py_bar.text() settings.Python.pip = self.set_pip_bar.text() settings.Youtubedl.fromPip = self.set_ydpip_checkbox.isChecked() settings.autoUpdate = self.set_aup_checkbox.isChecked() settings.Ffmpeg.audioCodec = self.set_Acodec_bar.text() settings.Ffmpeg.videoCodec = self.set_Vcodec_bar.text() settings.Ffmpeg.audioBitrate = self.set_Abit_bar.text() settings.Ffmpeg.videoQuality = self.set_Vqual_bar.text() settings.Ffmpeg.append = self.set_Append_bar.text() settings.defaultTab = self.set_Tab_combobox.currentIndex() settings.toJson(settingsPath) def set_load(audio, video, py, pip, ydpip, aup, acodec, vcodec, abit, vqual, append, tab): self.set_audio_bar.setText(audio) self.set_videos_bar.setText(video) self.set_py_bar.setText(py) self.set_pip_bar.setText(pip) self.set_ydpip_checkbox.setChecked(ydpip) self.set_aup_checkbox.setChecked(aup) self.set_Acodec_bar.setText(acodec) self.set_Vcodec_bar.setText(vcodec) self.set_Abit_bar.setText(abit) self.set_Vqual_bar.setText(vqual) self.set_Append_bar.setText(append) self.set_Tab_combobox.setCurrentIndex(tab) def set_makeScript( ): # I had an issue getting the venv working with gui if (sys.platform.startswith("win")): f = open("yt-dl_gui.vbs", "w") f.write( f"Set WshShell = CreateObject(\"WScript.Shell\")\nWshShell.Run \"cmd /c cd /d {spath} & pythonw.exe gui.py\", 0\nSet WshShell = Nothing" ) f.close() f = open("yt-dl_gui.bat", "w") f.write(f"@echo off\n\nstart /b pythonw.exe gui5.py") f.close() else: # (sys.platform.startswith(("linux", "darwin", "freebsd"))): f = open("yt-dl", "w") f.write( f"#!/bin/sh\n\ncd {spath} && {settings.Python.python} gui5.py" ) f.close() def set_open(): openFolder(spath) # =====set_controls=====# self.set_loadcur_button.clicked.connect(lambda: set_load( settings.Youtubedl.audioDir, settings.Youtubedl.videoDir, settings. Python.python, settings.Python.pip, settings.Youtubedl.fromPip, settings.autoUpdate, settings.Ffmpeg.audioCodec, settings.Ffmpeg. videoCodec, settings.Ffmpeg.audioBitrate, settings.Ffmpeg. videoQuality, settings.Ffmpeg.append, settings.defaultTab)) self.set_loaddef_button.clicked.connect(lambda: set_load( audioDirDefault, videoDirDefault, "python", "pip", True, False, "opus", "libx265", "190k", "24,24,24", "_custom.mkv", 0)) self.set_folder_button.clicked.connect(set_open) self.set_launch_button.clicked.connect(set_makeScript) self.set_save_button.clicked.connect(set_save) self.set_Tab_combobox.addItem( "Audio") # setting up items in combo list self.set_Tab_combobox.addItem("Video") self.set_Tab_combobox.addItem("Subs") self.set_Tab_combobox.addItem("Re-encode") self.set_Tab_combobox.addItem("Update") self.set_Tab_combobox.addItem("Settings") self.set_Tab_combobox.addItem("About") set_load(settings.Youtubedl.audioDir, settings.Youtubedl.videoDir, settings.Python.python, settings.Python.pip, settings.Youtubedl.fromPip, settings.autoUpdate, settings.Ffmpeg.audioCodec, settings.Ffmpeg.videoCodec, settings.Ffmpeg.audioBitrate, settings.Ffmpeg.videoQuality, settings.Ffmpeg.append, settings.defaultTab) # endregion # region ==========🎓ABOUT🎓========== self.about_box.setHtml( f"<p style=\"font-size: 20px; white-space: pre\">HorseArmored Inc. (C){year}<br>" + f"Version: {ver} gui5 ({curb} branch)<br>" + f"Last updated on: {lstupdt}<br>" + f"My webpage: <a href=\"https://koleckolp.comli.com\">https://koleckolp.comli.com</a><br>" + f"Project page: <a href=\"https://github.com/KoleckOLP/yt-dl\">https://github.com/KoleckOLP/yt-dl</a><br>" + f"need help? ask here: <a href=\"https://github.com/KoleckOLP/yt-dl\">https://discord.gg/W88375j</a><br>" + f"youtube-dl (C)2008-2011 Ricardo Garcia Gonzalez<br>" + f" (C)2011-{year} youtube-dl developers<br>" + f"ffmpeg (C)2000-{year} FFmpeg team<br>" + f"Thanks to <a href=\"https://github.com/kangalioo\">kangalioo</a> who always helps a ton!<br>" + f"Thanks to <a href=\"https://github.com/siscodeorg\">siscode</a> for featuring my project<br>" + f"and helping me improve it.<br>" + f"You can read the changelog: <a href=\"https://github.com/KoleckOLP/yt-dl/blob/master/changelog.md\">here</a></pre></p>" )
def __iter__(self): p = [0] sen = re.compile(Settings.get('sentence_regex')) return ifilter(None, imap(lambda x: self.pars(p, x), sen.finditer(self.string)))