class VLANS: ''' Manage a bunch of VLANs, as a dictionary ''' def __init__(self, vlans=None, delemiter=",", range_delemiter="-"): super().__init__() self._delemiter = delemiter self._range_delemiter = range_delemiter self._vlans = AttrDict() if vlans: self.__iadd__(vlans) def __add__(self, other): """ Add two VLANS to each other """ if not isinstance(other, VLANS): raise TypeError("Error: Can only handle object of VLANS()") tmp = self.copy() for vlan in other._vlans.values(): tmp._vlans[vlan.id] = vlan return tmp def __iadd__(self, other): if isinstance(other, VLANS): for vlan in other._vlans.values(): self._vlans[vlan.id] = vlan elif isinstance(other, VLAN): self._vlans[other.id] = other else: raise TypeError( "Error: Can only handle object of VLANS() or VLAN() got %s" % type(other)) return self def __str__(self): return dict_to_vlan_str(self._vlans, delemiter=self._delemiter, range_delemiter=self._range_delemiter) def __repr__(self): s = "" for vlan in self._vlans.values(): s += "(%s)" % vlan.to_str() return "VLANS(%s)" % s def __iter__(self): return iter(self.__dict__) def items(self): for item in self._vlans.items(): yield item def keys(self): for item in self._vlans.keys(): yield item def values(self): for item in self._vlans.values(): yield item
class VLANS: ''' Manage a bunch of VLANs, as a dictionary ''' def __init__(self, vlans=None, delemiter=",", range_delemiter="-"): super().__init__() self._delemiter = delemiter self._range_delemiter = range_delemiter self._vlans = AttrDict() if vlans: self.__iadd__(vlans) def __add__(self, other): """ Add two VLANS to each other """ if not isinstance(other, VLANS): raise TypeError("Error: Can only handle object of VLANS()") tmp = self.copy() for vlan in other._vlans.values(): tmp._vlans[vlan.id] = vlan return tmp def __iadd__(self, other): if isinstance(other, VLANS): for vlan in other._vlans.values(): self._vlans[vlan.id] = vlan elif isinstance(other, VLAN): self._vlans[other.id] = other else: raise TypeError("Error: Can only handle object of VLANS() or VLAN() got %s" % type(other)) return self def __str__(self): return dict_to_vlan_str(self._vlans, delemiter=self._delemiter, range_delemiter=self._range_delemiter) def __repr__(self): s = "" for vlan in self._vlans.values(): s += "(%s)" % vlan.to_str() return "VLANS(%s)" % s def __iter__(self): return iter(self.__dict__) def items(self): for item in self._vlans.items(): yield item def keys(self): for item in self._vlans.keys(): yield item def values(self): for item in self._vlans.values(): yield item
class HighlightRuleConfig(object): # def __init__(self, config): def __init__(self, config_file): self._config_file = config_file self.config = config.Config( self._config_file ) self.flags = 0 if self.config.match.case_sensitive else re.IGNORECASE # import ipdb; ipdb.set_trace() self.rules = AttrDict([ (label, HighlightRuleList( self.highlight_config.get(label), [ dict(subject=k, **(v or {})) for k, v in rule_dict.items() ], config=self.config ) ) for label, rule_dict in self.label_config.items() ]) self.RE_MAP = dict() @property def highlight_config(self): return self.config.get("highlight", {}) @property def label_config(self): return self.config.get("label", {}) @property def labels(self): return self.label_config.keys() def __getitem__(self, key): return self.rules[key] def add_rule(self, label, subject, group=None, patterns=None): targets = [subject] + (patterns if patterns else []) self.remove_rule(targets) rule = HighlightRule(subject, group=group, patterns=patterns) self.rules[label].append(rule) self.save() def remove_rule(self, targets): if not isinstance(targets, list): targets = [targets] self.rules = AttrDict([ (label, HighlightRuleList( self.highlight_config.get(label), [ r for r in self.rules[label] if r.subject not in targets and not any(pattern in targets for pattern in r.patterns) ], config=self.config )) for label, rule_list in self.rules.items() ]) self.save() def save(self): self.config.label = { label: { d.subject: { k: v for k, v in d.items() if k != "subject" } or None for d in [ rule.to_dict() for rule in sorted(rule_list) ] } for label, rule_list in self.rules.items() } self.config.save() # temp_config = config.Config( # self._config_file + ".new.yaml" # ) # temp_config.update(self.config.tree) # temp_config.label = { # label: { # d.subject: { # k: v for k, v in d.items() # if k != "subject" # } or None # for d in [ # rule.to_dict() # for rule in sorted(rule_list) # ] # } # for label, rule_list in self.rules.items() # } # temp_config.save() def get_regex(self, rules): rules_set = frozenset(rules.items()) if rules_set not in self.RE_MAP: pattern = '|'.join( f"{rules_list.pattern}" for label, rules_list in rules.items() if len(rules_list) ) pattern_grouped = '|'.join( f"(?P<{label}>{rules_list.pattern})" for label, rules_list in rules.items() if len(rules_list) ) # pattern_tokens = f"{pattern_grouped}|(?P<none>(?!{pattern})(.+?))" pattern_tokens = "|".join([ p for p in [pattern_grouped, "(?P<none>(?!{pattern})(.+?))"] if len(p) ]) self.RE_MAP[rules_set] = tuple( re.compile(p, self.flags) for p in [pattern, pattern_grouped, pattern_tokens] ) return self.RE_MAP[rules_set] @property def pattern_rules(self): return self.get_regex(self.rules)[0] @property def pattern_rules_grouped(self): return self.get_regex(self.rules)[1] @property def pattern_rules_tokens(self): return self.get_regex(self.rules)[2] def search(self, text): return next( ( match for match in ( rules.search(text) for rules in self.rules.values() ) if match ), None ) def tokenize(self, text, candidates=[], aliases={}): for subject, alias_list in aliases.items(): text = re.sub("|".join( ( alias for alias in alias_list ) ), subject, text) if candidates: rules = AttrDict([ (label, HighlightRuleList( self.highlight_config[label], [ r for r in rule_list if r.subject in candidates ], config=self.config )) for label, rule_list in self.rules.items() ]) else: rules = self.rules (pattern, pattern_grouped, pattern_tokens) = self.get_regex(rules) tokens = ( (match.lastgroup, match.group()) for match in re.finditer(pattern_tokens, text) ) out = [] for k, g in groupby(tokens, lambda x: x[0] == "none"): if k: out.append(((None, None), "".join(item[1] for item in g))) else: (attr, text) = next(g) label, rule = self.rule_for_token(text) attr = rule.attr if (rule and rule.attr) else attr out.append(((label, attr), text)) return out def apply(self, text, candidates=[], aliases={}): return [ (attr, token) if attr else token for attr, token in [ (self.highlight_config.get(attr, attr), token) for ( (label, attr), token) in self.tokenize(text, candidates=candidates, aliases=aliases) ] ] def get_tokens(self, text, candidates=[], aliases={}): return [ token for (attr, token) in self.tokenize(text, candidates=candidates, aliases=aliases) if attr ] def rule_for_token(self, token): return next( ( (label, rule) for label, rule in ( (label, rules.rule_for_token(token)) for label, rules in self.rules.items() ) if rule ), (None, None) )
def main(): data = AttrDict([('Adipisci eius dolore consectetur.', 34), ('Aliquam consectetur velit dolore', 19), ('Amet ipsum quaerat numquam.', 25), ('Amet quisquam labore dolore.', 30), ('Amet velit consectetur.', 20), ('Consectetur consectetur aliquam voluptatem', 23), ('Consectetur ipsum aliquam.', 28), ('Consectetur sit neque est', 15), ('Dolore voluptatem etincidunt sit', 40), ('Dolorem porro tempora tempora.', 37), ('Eius numquam dolor ipsum', 26), ('Eius tempora etincidunt est', 12), ('Est adipisci numquam adipisci', 7), ('Est aliquam dolor.', 38), ('Etincidunt amet quisquam.', 33), ('Etincidunt consectetur velit.', 29), ('Etincidunt dolore eius.', 45), ('Etincidunt non amet.', 14), ('Etincidunt velit adipisci labore', 6), ('Ipsum magnam velit quiquia', 21), ('Ipsum modi eius.', 3), ('Labore voluptatem quiquia aliquam', 18), ('Magnam etincidunt porro magnam', 39), ('Magnam numquam amet.', 44), ('Magnam quisquam sit amet.', 27), ('Magnam voluptatem ipsum neque', 32), ('Modi est ipsum adipisci', 2), ('Neque eius voluptatem voluptatem', 42), ('Neque quisquam ipsum.', 10), ('Neque quisquam neque.', 48), ('Non dolore voluptatem.', 41), ('Non numquam consectetur voluptatem.', 35), ('Numquam eius dolorem.', 43), ('Numquam sed neque modi', 9), ('Porro voluptatem quaerat voluptatem', 11), ('Quaerat eius quiquia.', 17), ('Quiquia aliquam etincidunt consectetur.', 0), ('Quiquia ipsum sit.', 49), ('Quiquia non dolore quiquia', 8), ('Quisquam aliquam numquam dolore.', 1), ('Quisquam dolorem voluptatem adipisci.', 22), ('Sed magnam dolorem quisquam', 4), ('Sed tempora modi est.', 16), ('Sit aliquam dolorem.', 46), ('Sit modi dolor.', 31), ('Sit quiquia quiquia non.', 5), ('Sit quisquam numquam quaerat.', 36), ('Tempora etincidunt quiquia dolor', 13), ('Tempora velit etincidunt.', 24), ('Velit dolor velit.', 47)]) NORMAL_FG = 'light gray' NORMAL_BG = 'black' if os.environ.get("DEBUG"): logger.setLevel(logging.DEBUG) formatter = logging.Formatter( "%(asctime)s [%(levelname)8s] %(message)s", datefmt='%Y-%m-%d %H:%M:%S') fh = logging.FileHandler("dropdown.log") fh.setFormatter(formatter) logger.addHandler(fh) else: logger.addHandler(logging.NullHandler()) entries = { "dropdown_text": PaletteEntry( foreground="light gray", background="dark blue", foreground_high="light gray", background_high="#003", ), "dropdown_focused": PaletteEntry( foreground="white", background="light blue", foreground_high="white", background_high="#009", ), "dropdown_highlight": PaletteEntry( foreground="yellow", background="light blue", foreground_high="yellow", background_high="#009", ), "dropdown_label": PaletteEntry(foreground="white", background="black"), "dropdown_prompt": PaletteEntry(foreground="light blue", background="black"), } entries = DataTable.get_palette_entries(user_entries=entries) palette = Palette("default", **entries) screen = urwid.raw_display.Screen() screen.set_terminal_properties(256) boxes = [ TestDropdown( data, label="Foo", border=True, scrollbar=True, right_chars_top=u" \N{BLACK DOWN-POINTING TRIANGLE}", auto_complete=True, ), TestDropdown( data, border=False, margin=2, left_chars=u"\N{LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT}", right_chars=u"\N{LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT}", auto_complete=True), TestDropdown( data, initial_value=list(data.values())[10], label="Foo", border=True, scrollbar=False, auto_complete=False, ), TestDropdown([], ), ] grid = urwid.GridFlow([urwid.Padding(b) for b in boxes], 60, 1, 1, "left") main = urwid.Frame(urwid.Filler(grid)) def global_input(key): if key in ('q', 'Q'): raise urwid.ExitMainLoop() else: return False loop = urwid.MainLoop(main, palette, screen=screen, unhandled_input=global_input, pop_ups=True) loop.run()
class Dropdown(urwid.PopUpLauncher): # Based in part on SelectOne widget from # https://github.com/tuffy/python-audio-tools signals = ["change"] label = None empty_label = u"\N{EMPTY SET}" margin = 0 def __init__( self, items=None, label=None, default=None, border=False, scrollbar=False, margin=None, left_chars=None, right_chars=None, left_chars_top=None, right_chars_top=None, auto_complete=False, max_height=10, # keymap = {} ): if items is not None: self._items = items if label is not None: self.label = label self.default = default self.border = border self.scrollbar = scrollbar self.auto_complete = auto_complete # self.keymap = keymap if margin: self.margin = margin if isinstance(self.items, list): if len(self.items): if isinstance(self.items[0], tuple): self._items = AttrDict(self.items) else: logger.debug(self.items) self._items = AttrDict( ((item, n) for n, item in enumerate(self.items))) else: self._items = AttrDict() else: self._items = self.items self.button = DropdownItem( u"", None, margin=self.margin, left_chars=left_chars_top if left_chars_top else left_chars, right_chars=right_chars_top if right_chars_top else right_chars) self.pop_up = DropdownDialog( self, self._items, self.default, label=self.label, border=self.border, margin=self.margin, left_chars=left_chars, right_chars=right_chars, auto_complete=self.auto_complete, scrollbar=scrollbar, max_height=max_height, # keymap = self.KEYMAP ) urwid.connect_signal(self.pop_up, "select", lambda souce, selection: self.select(selection)) urwid.connect_signal(self.pop_up, "close", lambda button: self.close_pop_up()) if self.default is not None: try: if isinstance(self.default, str): self.select_label(self.default) else: raise StopIteration except StopIteration: try: self.select_value(self.default) except StopIteration: self.focus_position = 0 if len(self): self.select(self.selection) else: self.button.set_label(("dropdown_text", self.empty_label)) cols = [(self.button_width, self.button)] if self.label: cols[0:0] = [ ("pack", urwid.Text([("dropdown_label", "%s: " % (self.label)) ])), ] self.columns = urwid.Columns(cols, dividechars=0) w = self.columns if self.border: w = urwid.LineBox(self.columns) w = urwid.Padding(w, width=self.width) super(Dropdown, self).__init__(w) urwid.connect_signal(self.button, 'click', lambda button: self.open_pop_up()) @classmethod def get_palette_entries(cls): return { "dropdown_text": PaletteEntry( foreground="light gray", background="dark blue", foreground_high="light gray", background_high="#003", ), "dropdown_focused": PaletteEntry( foreground="white", background="light blue", foreground_high="white", background_high="#009", ), "dropdown_highlight": PaletteEntry( foreground="yellow", background="light blue", foreground_high="yellow", background_high="#009", ), "dropdown_label": PaletteEntry(foreground="white", background="black"), "dropdown_prompt": PaletteEntry(foreground="light blue", background="black") } @keymap_command() def complete_prefix(self): if not self.auto_complete: return self.open_pop_up() self.pop_up.complete_prefix() @keymap_command() def complete_substring(self): if not self.auto_complete: return self.open_pop_up() self.pop_up.complete_substring() def create_pop_up(self): # print("create") return self.pop_up @property def button_width(self): return self.pop_up.max_item_width + self.button.decoration_width @property def pop_up_width(self): w = self.button_width if self.border: w += 2 return w @property def contents_width(self): # raise Exception(self.button.width) w = self.button_width if self.label: w += len(self.label) + 2 return max(self.pop_up.width, w) @property def width(self): width = max(self.contents_width, self.pop_up.width) if self.border: width += 2 return width @property def height(self): height = self.pop_up.height + 1 return height def pack(self, size, focus=False): return (self.width, self.height) @property def page_size(self): return self.pop_up.height def open_pop_up(self): # print("open") super(Dropdown, self).open_pop_up() def close_pop_up(self): super(Dropdown, self).close_pop_up() def get_pop_up_parameters(self): return { 'left': (len(self.label) + 2 if self.label else 0), 'top': 0, 'overlay_width': self.pop_up_width, 'overlay_height': self.pop_up.height } @property def focus_position(self): return self.pop_up.focus_position @focus_position.setter def focus_position(self, pos): # self.select_index(pos) old_pos = self.focus_position self.pop_up.selected_button = self.pop_up.focus_position = pos self.select(self.selection) @property def items(self): return self._items @property def selection(self): return self.pop_up.selection def select_label(self, label, case_sensitive=False): old_value = self.value def f(x): return x if not case_sensitive: def f(x): return x.lower() index = next( itertools.dropwhile(lambda x: f(x[1]) != f(label), enumerate((self._items.keys()))))[0] self.focus_position = index @property def items(self): return self._items @property def selection(self): return self.pop_up.selection def select_label(self, label, case_sensitive=False): old_value = self.value def f(x): return x if not case_sensitive: def f(x): return x.lower() index = next( itertools.dropwhile(lambda x: f(x[1]) != f(label), enumerate((self._items.keys()))))[0] self.focus_position = index def select_value(self, value): index = next( itertools.dropwhile(lambda x: x[1] != value, enumerate((self._items.values()))))[0] self.focus_position = index @property def labels(self): return self._items.keys() @property def values(self): return self._items.values() @property def selected_label(self): return self.selection.label @selected_label.setter def selected_label(self, label): return self.select_label(label) @property def selected_value(self): if not self.selection: return None return self.selection.value @selected_value.setter def selected_value(self, value): return self.select_value(value) @property def value(self): return self.selected_value @value.setter def value(self, value): old_value = self.value # try to set by value. if not found, try to set by label try: self.selected_value = value except StopIteration: self.selected_label = value def cycle_prev(self): self.cycle(-1) @keymap_command("cycle") def cycle(self, n): pos = self.focus_position + n if pos > len(self) - 1: pos = len(self) - 1 elif pos < 0: pos = 0 # self.focus_position = pos self.focus_position = pos def select(self, button): logger.debug("select: %s" % (button)) self.button.set_label(("dropdown_text", button.label)) self.pop_up.dropdown_buttons.listbox.set_focus_valign("top") # if old_pos != pos: self._emit("change", self.selected_label, self.selected_value) # def set_items(self, items, selected_value): # self._items = items # self.make_selection([label for (label, value) in items if # value is selected_value][0], # selected_value) def __len__(self): return len(self.items)
class Dropdown(urwid.PopUpLauncher): # Based in part on SelectOne widget from # https://github.com/tuffy/python-audio-tools signals = ["change"] empty_label = u"\N{EMPTY SET}" margin = 0 def __init__(self, items=None, initial_value=None, label=None, border=False, scrollbar=False, margin=None, left_chars=None, right_chars=None, left_chars_top=None, right_chars_top=None, auto_complete=False, keymap={}): # raise Exception(self.KEYMAP_SCOPE) if items is not None: self._items = items self.initial_value = initial_value self.label = label self.border = border self.scrollbar = scrollbar self.auto_complete = auto_complete # self.keymap = keymap if margin: self.margin = margin if isinstance(self.items, list): if len(self.items): if isinstance(items[0], tuple): self.items = AttrDict(self.items) self._items = AttrDict( ((item, n) for n, item in enumerate(self.items))) else: self._items = AttrDict() self.button = DropdownItem( u"", None, margin=self.margin, left_chars=left_chars_top if left_chars_top else left_chars, right_chars=right_chars_top if right_chars_top else right_chars) self.pop_up = DropdownDialog( self, self._items, self.initial_value, label=self.label, border=self.border, margin=self.margin, left_chars=left_chars, right_chars=right_chars, auto_complete=self.auto_complete, scrollbar=scrollbar, # keymap = self.KEYMAP ) urwid.connect_signal(self.pop_up, "select", lambda souce, selection: self.select(selection)) urwid.connect_signal(self.pop_up, "close", lambda button: self.close_pop_up()) try: initial_index = next(v for v in self.items.values() if v == self.initial_value) self.focus_position = initial_index except StopIteration: initial_index = 0 if len(self): self.select(self.selection) else: self.button.set_label(("dropdown_text", self.empty_label)) cols = [(self.button_width, self.button)] if self.label: cols[0:0] = [ ("pack", urwid.Text([("dropdown_label", "%s: " % (self.label)) ])), ] self.columns = urwid.Columns(cols, dividechars=0) w = self.columns if self.border: w = urwid.LineBox(self.columns) w = urwid.Padding(w, width=self.width) super(Dropdown, self).__init__(w) urwid.connect_signal(self.button, 'click', lambda button: self.open_pop_up()) @keymap_command() def complete_prefix(self): if not self.auto_complete: return self.open_pop_up() self.pop_up.complete_prefix() @keymap_command() def complete_substring(self): if not self.auto_complete: return self.open_pop_up() self.pop_up.complete_substring() def create_pop_up(self): # print("create") return self.pop_up @property def button_width(self): return self.pop_up.max_item_width + self.button.decoration_width @property def pop_up_width(self): w = self.button_width if self.border: w += 2 return w @property def contents_width(self): # raise Exception(self.button.width) w = self.button_width if self.label: w += len(self.label) + 2 return max(self.pop_up.width, w) @property def width(self): width = max(self.contents_width, self.pop_up.width) if self.border: width += 2 return width @property def height(self): height = self.pop_up.height + 1 return height @property def page_size(self): return self.pop_up.height def open_pop_up(self): # print("open") super(Dropdown, self).open_pop_up() def close_pop_up(self): super(Dropdown, self).close_pop_up() def get_pop_up_parameters(self): return { 'left': (len(self.label) + 2 if self.label else 0), 'top': 0, 'overlay_width': self.pop_up_width, 'overlay_height': self.pop_up.height } @property def focus_position(self): return self.pop_up.focus_position @focus_position.setter def focus_position(self, pos): # self.select_index(pos) self.pop_up.selected_button = self.pop_up.focus_position = pos self.select(self.selection) @property def items(self): return self._items @property def selection(self): return self.pop_up.selection @property def selected_label(self): return self.selection.label @property def selected_value(self): return self.selection.value def cycle(self, n): pos = self.focus_position + n if pos > len(self) - 1: pos = len(self) - 1 elif pos < 0: pos = 0 # self.focus_position = pos self.focus_position = pos def select(self, button): logger.debug("select: %s" % (button)) self.button.set_label(("dropdown_text", button.label)) self.pop_up.dropdown_buttons.listbox.set_focus_valign("top") self._emit("change", button, button.value) # def set_items(self, items, selected_value): # self._items = items # self.make_selection([label for (label, value) in items if # value is selected_value][0], # selected_value) def __len__(self): return len(self.items)