def _changed_name(self, *obj): """ callback to changes typed by user to the person name. Update the window title, and default name in name tab """ self.update_title(self.get_menu_title()) self.preview_name.set_markup( "<span size='x-large' weight='bold'>%s</span>" % markup_escape_text(self.get_preview_name(), -1)) self.name_list.update_defname()
def display_repo(self, repo): """ Display details of the active repository. """ self.name.set_markup("<span size='large' weight='bold'>%s</span>" % markup_escape_text(repo.get_name(), -1)) self.clear_grid() address_list = repo.get_address_list() if len(address_list) > 0: self.display_address(address_list[0]) self.display_separator() phone = address_list[0].get_phone() if phone: self.add_row(_('Phone'), phone) self.display_url(repo, UrlType(UrlType.EMAIL)) self.display_url(repo, UrlType(UrlType.WEB_HOME)) self.display_url(repo, UrlType(UrlType.WEB_SEARCH)) self.display_url(repo, UrlType(UrlType.WEB_FTP))
def display_person(self, active_person): """ Display details of the active person. """ self.load_person_image(active_person) self.name.set_markup( "<span size='large' weight='bold'>%s</span>" % markup_escape_text(name_displayer.display(active_person), -1)) self.clear_grid() self.display_alternate_names(active_person) self.display_parents(active_person) self.display_separator() self.display_type(active_person, EventType(EventType.BIRTH)) self.display_type(active_person, EventType(EventType.BAPTISM)) self.display_type(active_person, EventType(EventType.DEATH)) self.display_type(active_person, EventType(EventType.BURIAL)) self.display_separator() self.display_attribute(active_person, _('Occupation')) self.display_attribute(active_person, _('Title')) self.display_attribute(active_person, _('Religion'))
def display_place(self, place): """ Display details of the active place. """ self.load_place_image(place) title = place_displayer.display(self.dbstate.db, place) self.title.set_markup("<span size='large' weight='bold'>%s</span>" % markup_escape_text(title)) self.clear_grid() self.add_row(_('Name'), place.get_name().get_value()) self.add_row(_('Type'), place.get_type()) self.display_separator() self.display_alt_names(place) self.display_separator() lat, lon = conv_lat_lon(place.get_latitude(), place.get_longitude(), format='DEG') if lat: self.add_row(_('Latitude'), lat) if lon: self.add_row(_('Longitude'), lon)
def __populate_reg_list(self): """ Build list of plugins""" self.addons = [] new_addons_file = os.path.join(VERSION_DIR, "new_addons.txt") if not os.path.isfile(new_addons_file) and not static.check_done: if QuestionDialog2(TITLE, _("3rd party addons are not shown until the " "addons update list is downloaded. Would " "you like to check for updated addons now?"), _("Yes"), _("No"), parent=self.window).run(): self._check_for_updates() else: static.check_done = True try: with open(new_addons_file, encoding='utf-8') as filep: for line in filep: try: plugin_dict = safe_eval(line) if not isinstance(plugin_dict, dict): raise TypeError("Line with addon metadata is not " "a dictionary") except: # pylint: disable=bare-except LOG.warning("Skipped a line in the addon listing: " + str(line)) continue self.addons.append(plugin_dict) except FileNotFoundError: pass except Exception as err: # pylint: disable=broad-except LOG.warning("Failed to open addon status file: %s", err) addons = [] updateable = [] for plugin_dict in self.addons: pid = plugin_dict["i"] plugin = self._pmgr.get_plugin(pid) if plugin: # check for an already registered plugin LOG.debug("Comparing %s > %s", version_str_to_tup(plugin_dict["v"], 3), version_str_to_tup(plugin.version, 3)) # Check for a update if (version_str_to_tup(plugin_dict["v"], 3) > version_str_to_tup(plugin.version, 3)): LOG.debug("Update for '%s'...", plugin_dict["z"]) updateable.append(pid) else: # current plugin is up to date. LOG.debug(" '%s' is up to date", plugin_dict["n"]) else: # new addon LOG.debug(" '%s' is not installed", plugin_dict["n"]) hidden = pid in self.hidden status_str = _("*Available") status = AVAILABLE if hidden: status_str = "<s>%s</s>" % status_str status |= HIDDEN row = [_(plugin_dict["t"]), status_str, markup_escape_text(plugin_dict["n"]), markup_escape_text(plugin_dict["d"]), plugin_dict["i"], status] addons.append(row) fail_list = self._pmgr.get_fail_list() for (_type, typestr) in PTYPE_STR.items(): for pdata in self._preg.type_plugins(_type): # model: plugintype, hidden, pluginname, plugindescr, pluginid if 'gramps/plugins' in pdata.fpath.replace('\\', '/'): status_str = _("Built-in") status = BUILTIN if not self._show_builtins: continue else: status_str = _("*Installed") status = INSTALLED # i = (filename, (exception-type, exception, traceback), pdata) for i in fail_list: if pdata == i[2]: status_str += ', ' + '<span color="red">%s</span>' % \ _("Failed") break if pdata.id in updateable: status_str += ', ' + _("Update Available") status |= UPDATE hidden = pdata.id in self.hidden if hidden: status_str = "<s>%s</s>" % status_str status |= HIDDEN addons.append([typestr, status_str, markup_escape_text(pdata.name), markup_escape_text(pdata.description), pdata.id, status]) for row in sorted(addons, key=itemgetter(R_TYPE, R_NAME)): if self._show_hidden or (row[R_ID] not in self.hidden): self._model_reg.append(row) self._selection_reg.select_path('0')
def _get_preview_text(self, lines, query): max_lines = 200 # check if the file is a Zim markup file and if so, skip header if lines[0] == 'Content-Type: text/x-zim-wiki': for i, line in enumerate(lines): if line == "": lines = lines[i + 1:] break if query.strip() == "": return "\n".join(line for line in lines[:max_lines]) # searching for "a" cannot match "&a", since markup_escape_text("&") -> "'" # Ignoring q == "b", it would interfere with multiple queries: # Ex: query "f b", text "foo", matched with "f" -> "<b>f</b>oo", matched with "b" -> "<<b>b</b>>f</<b>b</b>>" query_match = (re.compile("(" + re.escape(q) + ")", re.IGNORECASE) for q in query.split(" ") if q != "b") # too long lines caused strange Gtk behaviour – monitor brightness set to maximum, without any logged warning # so that I decided to put just extract of such long lines in preview # This regex matches query chunk in the line, prepends characters before and after. # When there should be the same query chunk after the first, it stops. # Otherwise, the second chunk might be halved and thus not highlighted. # Ex: query "test", text: "lorem ipsum text dolor text text sit amet consectetur" -> # ["ipsum text dolor ", "text ", "text sit amet"] (words "lorem" and "consectetur" are strip) line_extract = [ re.compile( "(.{0,80}" + re.escape(q) + "(?:(?!" + re.escape(q) + ").){0,80})", re.IGNORECASE) for q in query.split(" ") if q != "b" ] # grep some lines keep_all = not self.plugin.preferences["preview_short"] and len( lines) < max_lines lines_iter = iter(lines) chosen = [ next(lines_iter) ] # always include header as the first line, even if it does not contain the query for line in lines_iter: if len( chosen ) > max_lines: # file is too long which would result the preview to not be smooth break elif keep_all or any(q in line.lower() for q in query.split(" ")): # keep this line since it contains a query chunk if len(line) > 100: # however, this line is too long to display, try to extract query and its neighbourhood s = "...".join("...".join(q.findall(line)) for q in line_extract).strip(".") if not s: # no query chunk was find on this line, the keep_all is True for sure chosen.append(line[:100] + "...") else: chosen.append("..." + s + "...") else: chosen.append(line) if not keep_all or len(chosen) > max_lines: # note that query might not been found, ex: query "foo" would not find line with a bold 'o': "f**o**o" chosen.append("...") txt = markup_escape_text("\n".join(line for line in chosen)) # bold query chunks in the text for q in query_match: txt = q.sub(r"<b>\g<1></b>", txt) # preserve markup_escape_text entities # correct ex: '&a<b>m</b>p;' -> '&' if searching for 'm' bold_tag = re.compile("</?b>") broken_entity = re.compile("&[a-z]*<b[^;]*;") txt = broken_entity.sub(lambda m: bold_tag.sub("", m.group(0)), txt) return txt
def show_ink(self, ink, switchpage): inknamet = '...' inktagst = '-' inkdesct = '' inkavailt = '-' inkstatust = '-' inkcolordesc = '-' # тут когда-нибудь будет человекочитаемое описание цвета inkcolor = None inkmaincolor = '-' inkusagecnt = '...' self.chosenInk = ink self.randominkusageview.refresh_begin() if not ink: inknamet = 'ничего подходящего не нашлось' else: inkName, inkTags, inkDescription, inkAvailability = self.stats.get_ink_description(ink) inknamet = inkName inktagst = markup_escape_text(inkTags) if inkTags else '-' inkdesct = inkDescription inkavailt = markup_escape_text(inkAvailability) if ink.done is None: inkstatust = 'не нужны' elif ink.done: inkstatust = 'испытаны' else: inkstatust = 'планируется покупка' if ink.color: inkcolor = ColorValue.get_rgb32_value(ink.color) inkcolordesc = ColorValue.new_from_rgb24(ink.color).get_description() if ink.maincolor: inkmaincolor = self.stats.tagNames.get(ink.maincolor, ink.maincolor) dnow = datetime.datetime.now().date() inkusagecnt = 'заправок: %d' % len(ink.usage) for unfo in ink.usage: _dd = dnow - unfo.date self.randominkusageview.store.append((str(unfo.date), 'сегодня' if _dd.days <= 1 else '%d дн. назад' % _dd.days, unfo.comment)) self.randominkusageview.refresh_end() self.randominkname.set_text(inknamet) self.randominktags.set_markup(inktagst) self.randominkdescbuf.set_text(inkdesct) self.randominkavail.set_markup(inkavailt) self.randominkstatus.set_text(inkstatust) self.randominkcolordesc.set_text(inkcolordesc) self.randominkmaincolor.set_text(inkmaincolor) self.randominkusagecnt.set_text(inkusagecnt) if inkcolor is None: self.randominkcolorimg.set_from_pixbuf(self.nocoloricon) else: self.randominkColorPixbuf.fill(inkcolor) self.randominkcolorimg.set_from_pixbuf(self.randominkColorPixbuf) if switchpage: self.pages.set_visible_child(self.pageChooser)
def load_db(self): self.totalstatview.set_model(None) self.totalstatlstore.clear() self.detailstats.view.set_model(None) self.detailstats.store.clear() expand = [] def __bool_s(b, clr=None): st = '√' if clr is None else '<span color="%s"><b>√</b></span>' % clr return st if b else '' CTODO = '#fd0' CNODO = '#c00' CDONE = '#0f0' try: self.db = load_ink_db(self.cfg.databaseFileName) self.stats = get_ink_stats(self.db) self.rndchooser = None # статистика if self.stats: dfname = os.path.split(self.cfg.databaseFileName)[-1] # # общая статистика # totals = self.stats.get_total_result_table() for row in totals: self.totalstatlstore.append(row) # # детали # for tagstat in self.stats.tagStats: itr = self.detailstats.store.append(None, (None, tagstat.title, '', '', '', '', None, None)) expand.append(self.detailstats.store.get_path(itr)) _items = tagstat.stats.items() # мелкий костылинг: сортироваться должны только списки, # полученные обработкой директив TAGSTATS if tagstat.issortable: # порядок сортировки: название метки, наличие # на кой чорт питонщики сделали key вместо cmp? # в случае cmp не пришлось бы тратить память на # значение временного ключа сортировки # и фрагментировать кучу def __group_key_f(r): return '%5d%s' % (r[1].available, self.stats.get_tag_display_name(r[0]).lower()) _items = sorted(_items, key=__group_key_f, reverse=True) for tag, nfo in _items: row = (None, self.stats.get_tag_display_name(tag), *nfo.counter_strs(), None, None) subitr = self.detailstats.store.append(itr, row) # конкретные марки чернил сортируем уже по названию в алфавитном порядке for ink in sorted(nfo.inks, key=lambda i: i.text.lower()): if ink.color: pbuf = Pixbuf.new(GdkPixbuf.Colorspace.RGB, False, 8, self.samplePixbufSize, self.samplePixbufSize) pbuf.fill(int(ColorValue.get_rgb32_value(ink.color))) else: pbuf = self.nocoloricon # 'название', 'отсортированный список человекочитаемых меток', 'описание', 'наличие' _inkname, _inktags, _inkdesc, _inkavail = self.stats.get_ink_description(ink) hint = ['<b>%s</b>' % markup_escape_text(_inkname)] if ink.color: hint.append('Цвет: <span color="#%.6x">██</span> %s' % (ink.color, markup_escape_text(ColorValue.new_from_rgb24(ink.color).get_description()))) if _inkdesc: hint.append(markup_escape_text(_inkdesc)) if _inkavail: hint.append('В наличии: %s' % markup_escape_text(_inkavail)) if ink.done == False: hint.append('Запланирована покупка этих чернил') if ink.missing: hint.append('Отсутствуют данные: %s' % self.stats.get_ink_missing_data_str(ink)) bunwanted = ink.done is None self.detailstats.store.append(subitr, (ink, ink.text, # avail __bool_s(ink.avail, CDONE), # unavail __bool_s(not ink.avail, None if bunwanted else CTODO), # wanted __bool_s(ink.done == False, CTODO), # прямое сравнение, т.к. иначе None будет воспринято тоже как False # unwanted __bool_s(bunwanted, CNODO), pbuf, '\n\n'.join(hint))) self.rndchooser = RandomInkChooser(self.stats, None, None) else: dfname = '' # # метки # self.tagchecklistbox.clear_items() def __add_tag(tagname): tagdisp = tagname if tagname not in self.stats.tagNames else self.stats.tagNames[tagname] self.tagchecklistbox.add_item(False, tagdisp, tagname) # в первую очередь используем только метки, учитываемые в статистике ntags = 0 for tsinfo in self.stats.tagStats: for tagname in sorted(tsinfo.tags): __add_tag(tagname) ntags += 1 # ...а вот если там меток не было - берём весь список меток if not ntags: for tagname in sorted(self.stats.tags): __add_tag(tagname) self.includetags.clear() # пустое множество - выбирать все self.excludetags.clear() # пустое множество - не исключать ничего self.includetagstxt.set_text(self.INCLUDE_ANY) self.excludetagstxt.set_text(self.EXCLUDE_NOTHING) # self.openorgfiledlg.select_filename(self.cfg.databaseFileName) self.headerbar.set_subtitle(dfname) self.cfg.add_recent_file(self.cfg.databaseFileName) self.update_recent_files_menu() finally: self.detailstats.view.set_model(self.detailstats.store) for path in expand: self.detailstats.view.expand_row(path, False) self.totalstatview.set_model(self.totalstatlstore) self.choose_random_ink()
def view_encode(self, string, limit=25): if (len(string)) > limit: string = string[:limit] + '…' string = markup_escape_text(string) return string