def _build_widget(self, **kwargs): total_items = [] for _item in self.radio_items.keys(): desc = AttrWrap( Text(" {}".format( self.radio_items[_item][1])), 'input', 'input focus') total_items.append( AttrWrap(self.radio_items[_item][0], 'input', 'input focus')) total_items.append(AttrWrap(desc, 'input')) total_items.append(Divider('-')) self.input_lbox = ListBox(SimpleListWalker(total_items[:-1])) self.add_buttons() self.container_box_adapter = BoxAdapter(self.input_lbox, len(total_items)) self.container_lbox = ListBox( [self.container_box_adapter, Divider(), self.btn_pile]) return LineBox( BoxAdapter(self.container_lbox, height=len(total_items) + 3), title=self.title)
def __init__(self): self.screen = Screen() self.screen.set_input_timeouts(max_wait=0) self.steps = GridFlow([], 20, 2, 1, 'left') self.progress = SimpleFocusListWalker([]) self.log = SimpleFocusListWalker([]) self.widget = AttrMap( LineBox(Pile([ ('fixed', 6, AttrMap(Filler(self.steps), 'default')), ('fixed', 1, Filler(Divider('\u2500'))), ('fixed', 3, ListBox(self.progress)), AttrMap(LineBox(ListBox(self.log), title='Message log'), 'default') ]), title='Indico 1.2 -> 2.0 migration'), 'global_frame') self.screen.register_palette( [('green', 'light green', ''), ('white', 'white', ''), ('red', 'dark red', ''), ('yellow', 'yellow', ''), ('progress_empty', 'black', 'light gray'), ('progress_progress', 'light cyan', 'light gray'), ('progress_done', 'black', 'light cyan'), ('box', 'white', 'dark gray'), ('step_done', 'light green', ''), ('step_working', 'dark gray', ''), ('global_frame', 'light cyan', ''), ('fill', 'light cyan', 'dark cyan'), ('done', 'white', 'dark green'), ('eta', 'yellow', 'dark gray')] + generate_urwid_palette(PALETTE))
def render(self, size, focus=False): self.body.sync() canvas = ListBox.render(self, size, focus) if self.body.event: self.body.sync() canvas = ListBox.render(self, size, focus) assert not self.body.event return canvas
def __init__(self, choice_callback=None, command_callback=None, help_callback=None): self.palette = [ ('brick', 'light red', 'black'), ('rubble', 'yellow', 'black'), ('wood', 'light green', 'black'), ('concrete', 'white', 'black'), ('stone', 'light cyan', 'black'), ('marble', 'light magenta', 'black'), ('jack', 'dark gray', 'white'), ('msg_info', 'white', 'black'), ('msg_err', 'light red', 'black'), ('msg_debug', 'light green', 'black'), ] self.choice_callback = choice_callback self.command_callback = command_callback self.help_callback = help_callback self.screen = None self.loop = None self.called_loop_stop = False self.reactor_stop_fired = False self.quit_flag = False self.edit_msg = "Make selection ('q' to quit): " self.roll_list = SimpleListWalker([]) self.game_log_list = SimpleListWalker([]) self.choices_list = SimpleListWalker([]) self.state_text = SimpleListWalker([Text('Connecting...')]) self.edit_widget = Edit(self.edit_msg) self.roll = ListBox(self.roll_list) self.game_log = ListBox(self.game_log_list) self.choices = ListBox(self.choices_list) self.state = ListBox(self.state_text) self.left_frame = Pile([ LineBox(self.state), (13, LineBox(self.choices)), ]) self.right_frame = Pile([LineBox(self.game_log), LineBox(self.roll)]) self.state.set_focus(len(self.state_text) - 1) self.columns = Columns([('weight', 0.75, self.left_frame), ('weight', 0.25, self.right_frame)]) self.frame_widget = Frame(footer=self.edit_widget, body=self.columns, focus_part='footer') self.exc_info = None
class AddCharmDialog(Overlay): """ Adding charm dialog """ def __init__(self, underlying, juju_state, destroy, command_runner=None): import cloudinstall.charms charm_modules = [import_module('cloudinstall.charms.' + mname) for (_, mname, _) in pkgutil.iter_modules(cloudinstall.charms.__path__)] charm_classes = [m.__charm_class__ for m in charm_modules if m.__charm_class__.allow_multi_units] self.cr = command_runner self.underlying = underlying self.destroy = destroy self.boxes = [] self.bgroup = [] first_index = 0 for i, charm_class in enumerate(charm_classes): charm = charm_class(juju_state=juju_state) if charm.name() and not first_index: first_index = i r = RadioButton(self.bgroup, charm.name()) r.text_label = charm.name() self.boxes.append(r) self.count_editor = IntEdit("Number of units to add: ", 1) self.boxes.append(self.count_editor) wrapped_boxes = _wrap_focus(self.boxes) bs = [Button("Ok", self.yes), Button("Cancel", self.no)] wrapped_buttons = _wrap_focus(bs) self.buttons = Columns(wrapped_buttons) self.items = ListBox(wrapped_boxes) self.items.set_focus(first_index) ba = BoxAdapter(self.items, height=len(wrapped_boxes)) self.lb = ListBox([ba, Text(""), self.buttons]) self.w = LineBox(self.lb, title="Add unit") self.w = AttrMap(self.w, "dialog") Overlay.__init__(self, self.w, self.underlying, 'center', 45, 'middle', len(wrapped_boxes) + 4) def yes(self, button): selected = [r for r in self.boxes if r is not self.count_editor and r.get_state()][0] _charm_to_deploy = selected.label n = self.count_editor.value() log.info("Adding {n} units of {charm}".format( n=n, charm=_charm_to_deploy)) self.cr.add_unit(_charm_to_deploy, count=int(n)) self.destroy() def no(self, button): self.destroy()
def __init__(self, walker, **kwargs): """ :param walker: tree of widgets to be displayed. In case we are given a raw `TreeWalker`, it will be used though `TreeListWalker` which means no decoration. :type walker: TreeWalker or TreeListWalker """ if not isinstance(walker, TreeListWalker): walker = TreeListWalker(walker) self._walker = walker self._outer_list = ListBox(walker) self.__super.__init__(self._outer_list)
def __init__(self, tree, focus=None): """ :param tree: tree of widgets to be displayed. :type tree: Tree :param focus: initially focussed position """ self._tree = tree self._walker = TreeListWalker(tree) self._outer_list = ListBox(self._walker) if focus is not None: self._outer_list.set_focus(focus) self.__super.__init__(self._outer_list)
def __init__(self, contents, offset=1): """ Arguments: `contents` is a list with the elements contained in the `ScrollableListBox`. `offset` is the number of position that `scroll_up` and `scroll_down` shift the cursor. """ self.offset = offset ListBox.__init__(self, SimpleListWalker(contents))
def __init__(self, height, directory=".", file="", attr=(None, None), show_hidden=False): """ height -- height of the directory list and the file list directory, file -- default selection attr -- (inner selectable widgets, selected widgets) show_hidden -- If True, hidden files are shown by default. """ self.directory = abspath(directory) self.file = "" self.attr = attr self.height = height self.show_hidden = show_hidden # Create dummy widgets for directory and file display: self.dir_widget = AttrWrap( BoxAdapter(ListBox([self._blank]), self.height), self.attr[0]) self.file_widget = AttrWrap( BoxAdapter(ListBox([self._blank]), self.height), self.attr[0]) columns = Columns([self.dir_widget, self.file_widget], 1) # Selection widget: self.select_widget = AttrWrap(Edit("", ""), self.attr[0], self.attr[1]) # Buttons and checkbox: button_widgets = [ AttrWrap(Button(button, self._action), attr[0], attr[1]) for button in ["OK", "Cancel"] ] button_grid = GridFlow(button_widgets, 12, 2, 1, 'center') button_cols = Columns([ CheckBox(self.SHOW_HIDDEN_TEXT, self.show_hidden, False, self._toggle_hidden), button_grid ]) self.outer_widget = Pile([ columns, self._blank, Text(self.SELECTION_TEXT), self.select_widget, self._blank, button_cols ]) self.update_widgets() WidgetWrap.__init__(self, self.outer_widget)
def __init__(self, model, signal, iface): self.model = model self.signal = signal self.iface = iface self.iface_obj = self.model.get_interface(iface) self._build_widgets() super().__init__(ListBox(self._build_body()))
def __init__(self, list_data=None): self.is_editing = False self.tasks = [] self.name = None self.group = None self.id = None if list_data: # Parse the data. self.parse_data(list_data) else: # Must be a new list self.name = 'untitled' self.group = 'none' self.id = uuid.uuid4().hex # AttrSpecs self.attr_spec = AttrSpec('', '') self.focus_nav = AttrSpec('h12', '') self.focus_ins = AttrSpec('h160', '') # Build widget stack self.title = TitleBar(self.name) self.group_foot = GroupFoot(self.group) self.body = urwid.SimpleFocusListWalker(self.tasks) self.list_box = ListBox(self.body) self.list_frame = Frame(self.list_box, header=self.title, footer=self.group_foot) self.line_box = LineBox(self.list_frame) self.line_attr = AttrMap(self.line_box, self.attr_spec, self.focus_nav) super().__init__(self.line_attr)
def __init__(self, model, signal, selected_disk): log.debug('AddPartitionView: selected_disk=[{}]'.format(selected_disk)) self.model = model self.signal = signal self.selected_disk = selected_disk self.disk_obj = self.model.get_disk(selected_disk) self.partnum = IntegerEditor(caption="", default=self.disk_obj.lastpartnumber + 1) self.size_str = _humanize_size(self.disk_obj.freespace) self.size = StringEditor(caption="".format(self.size_str)) self.mountpoint = MountEditor(caption="", edit_text="/") self.fstype = Selector(opts=self.model.supported_filesystems) body = [ Columns([ ("weight", 0.2, Text("Adding partition to {}".format(self.disk_obj.devpath), align="right")), ("weight", 0.3, Text("")) ]), Padding.line_break(""), self._container(), Padding.line_break(""), Padding.fixed_10(self._build_buttons()) ] partition_box = Padding.center_50(ListBox(body)) super().__init__(partition_box)
def __init__(self, executor_widgets): self.log_widget = ScrollingLog() self.latest_stats = LatestStats() self.cumulative_stats = CumulativeStats() stats_pane = Pile([(WEIGHT, 0.333, self.latest_stats), (WEIGHT, 0.667, self.cumulative_stats)]) self.graphs = ThreeGraphs() self.logo = TaurusLogo() ordered_widgets = sorted(executor_widgets, key=lambda x: x.priority) right_widgets = ListBox( SimpleListWalker([Pile([x, Divider()]) for x in ordered_widgets])) widget_pile = Pile([ (7, self.logo), right_widgets, ]) log_block = Pile([(1, Filler(Divider('─'))), self.log_widget]) right_pane = Pile([(WEIGHT, 0.667, widget_pile), (WEIGHT, 0.333, log_block)]) columns = [(WEIGHT, 0.25, self.graphs), (WEIGHT, 0.50, stats_pane), (WEIGHT, 0.25, right_pane)] super(TaurusConsole, self).__init__(columns)
def __init__(self, msg=None, height=10): if not msg: msg = "Processing." listbox = ListBox([Text(msg)]) box_adapter = BoxAdapter(listbox, height=height) linebox = LineBox(box_adapter, title="Info") super().__init__(AttrWrap(linebox, 'dialog'))
def __init__(self, tui, locator, device): super().__init__(tui) self.body.contents.append( (Text("Select file to load on \"{}\"".format(device)), ('pack', None))) self.body.contents.append((Divider(), ('pack', None))) self.device = device self.locator = locator self.device = device self.all_files = [] def limit(regexp): self.regexp = regexp self.populate_list() def enter(edit_text): if len(self.walker) < 1: self.tui.pop_window() return button = self.walker[0] button.keypress(1, 'enter') editbox = CB_Edit("Limit (regexp): ", "", limit, enter) self.body.contents.append((editbox, ('pack', 1))) self.body.contents.append((Divider(), ('pack', 1))) self.walker = SimpleFocusListWalker([]) listbox = ListBox(self.walker) self.body.contents.append((listbox, ('weight', 1))) self.body.focus_position = 2 self.update() self.regexp = ".*"
def __init__(self, model, signal, iface): self.model = model self.signal = signal self.ifname = iface self.iface = self.model.get_interface(self.ifname) self.is_gateway = False self.gateway_input = StringEditor(caption="") # FIXME: ipaddr_editor self.address_input = StringEditor(caption="") # FIXME: ipaddr_editor self.subnet_input = StringEditor(caption="") # FIXME: ipaddr_editor self.error = Text("", align='center') self.nameserver_input = \ StringEditor(caption="") # FIXME: ipaddr_editor self.searchdomains_input = \ StringEditor(caption="") # FIXME: ipaddr_editor self.set_as_default_gw_button = Pile( self._build_set_as_default_gw_button()) body = [ Padding.center_79(self._build_iface_inputs()), Padding.line_break(""), Padding.center_79(self.set_as_default_gw_button), Padding.line_break(""), Padding.center_90(Color.info_error(self.error)), Padding.line_break(""), Padding.fixed_10(self._build_buttons()) ] super().__init__(ListBox(body))
def build_widgets(self): readme_files = glob(os.path.join(self.spell_dir, 'README.*')) if len(readme_files) == 0: self.readme_w = Text("No README found for bundle.") else: readme_file = readme_files[0] if len(readme_files) != 1: utils.warning("Unexpected: {} files matching README.*" "- using {}".format(len(readme_files), readme_file)) with open(readme_file) as rf: rlines = [Text(l) for l in rf.readlines()] self.readme_w = BoxAdapter(ListBox(rlines), self.initial_height) ws = [ Text("About {}:".format(self.spell_name)), Padding.right_50( Color.button_primary(PlainButton("Continue", self.do_continue), focus_map='button_primary focus')), Padding.center(HR()), Padding.center(self.readme_w, left=2), Padding.center(HR()), Padding.center( Text("Use arrow keys to scroll text " "and TAB to select the button.")) ] self.pile = Pile(ws) return Padding.center_90(Filler(self.pile, valign="top"))
def __init__(self, tui, locator, device): super().__init__(tui) self.body.contents.append( (Text("Selected device \"{}\"".format(device)), ('pack', None))) fn = self.tui.controller.get_filename(locator) self.filename_txt = Text("Selected file \"{}\"".format(fn)) self.body.contents.append((self.filename_txt, ('pack', None))) self.body.contents.append((Divider(), ('pack', None))) self.statuswidget = Pile([]) self.body.contents.append((self.statuswidget, ('pack', None))) self.body.contents.append((Divider(), ('pack', None))) self.locator = locator self.device = device self.walker = SimpleFocusListWalker([]) listbox = ListBox(self.walker) self.body.contents.append((listbox, ('weight', 1))) self.body.focus_position = 5 self.add_hotkey('U', self.update, "update") self.add_hotkey('w', self.webcams, "webcams") self.add_hotkey('a', self.actions, "actions") self.status = defaultdict(str) self.update()
def __init__(self, choice_callback=None, command_callback=None, help_callback=None): self.palette = [ ('brick', 'light red', 'black'), ('rubble', 'yellow', 'black'), ('wood', 'light green', 'black'), ('concrete', 'white', 'black'), ('stone', 'light cyan', 'black'), ('marble', 'light magenta', 'black'), ('jack', 'dark gray', 'white'), ('msg_info', 'white', 'black'), ('msg_err', 'light red', 'black'), ('msg_debug', 'light green', 'black'), ] self.choice_callback = choice_callback self.command_callback = command_callback self.help_callback = help_callback self.screen = None self.loop = None self.called_loop_stop = False self.reactor_stop_fired = False self.quit_flag = False self.edit_msg = "Make selection ('q' to quit): " self.roll_list = SimpleListWalker([]) self.game_log_list = SimpleListWalker([]) self.choices_list = SimpleListWalker([]) self.state_text = SimpleListWalker([Text('Connecting...')]) self.edit_widget = Edit(self.edit_msg) self.roll = ListBox(self.roll_list) self.game_log = ListBox(self.game_log_list) self.choices = ListBox(self.choices_list) self.state = ListBox(self.state_text) self.left_frame = Pile([ LineBox(self.state), (13, LineBox(self.choices)), ]) self.right_frame = Pile([ LineBox(self.game_log), LineBox(self.roll) ]) self.state.set_focus(len(self.state_text)-1) self.columns = Columns([('weight', 0.75, self.left_frame), ('weight', 0.25, self.right_frame) ]) self.frame_widget = Frame(footer=self.edit_widget, body=self.columns, focus_part='footer') self.exc_info = None
def __init__(self, num_rows=20, w=(14, 14, 18, 16, 16, 16, 20)): """ @method __init__ Initializes the widget """ self.m_process_list = ProcessList(w) self.prev_sort_item = None self.w_status = HeaderButton('Status', 'status', self.handle_click) self.w_pid = HeaderButton('PID', 'pid', self.handle_click) self.w_name = HeaderButton('Name', 'name', self.handle_click) self.w_cpu = HeaderButton('CPU %', 'cpu_perc', self.handle_click) self.w_mem = HeaderButton('MEM %', 'mem_perc', self.handle_click) self.w_up = HeaderButton('Uptime', 'uptime', self.handle_click) self.w_pname = HeaderButton('Process', 'pname', self.handle_click) self.w_cpu.activate() self.prev_sort_item = self.w_cpu self.header_buttons = h = [ self.w_status, self.w_pid, self.w_name, self.w_cpu, self.w_mem, self.w_up, self.w_pname ] m_header = AttrMap( Columns([('fixed', w[i], h[i]) for i in range(0, len(h))]), 'invert') m_lb = ListBox( SimpleListWalker( [m_header, BoxAdapter(self.m_process_list, num_rows)])) super(ProcessTable, self).__init__(m_lb, None) self.update()
def create_interface(self): self.screen = Screen() self.screen.start() self.screen.register_palette([ ("title", "white", "dark blue", "standout"), ("line", "light gray", "black"), ("help", "white", "dark blue")] ) self.body = ListBox(SimpleListWalker([])) self.lines = self.body.body self.title = Text(MAIN_TITLE) self.header = AttrWrap(self.title, "title") self.help = AttrWrap( Text(HELP_STRINGS["main"]), "help" ) self.input = Edit(caption="%s> " % self.ircchannel) self.footer = Pile([self.help, self.input]) self.top = Frame(self.body, self.header, self.footer)
class ChangeStateDialog(Overlay): def __init__(self, underlying, juju_state, on_success, on_cancel): import cloudinstall.charms charm_modules = [import_module('cloudinstall.charms.' + mname) for (_, mname, _) in pkgutil.iter_modules(cloudinstall.charms.__path__)] charm_classes = sorted([m.__charm_class__ for m in charm_modules], key=attrgetter('deploy_priority')) self.boxes = [] first_index = 0 for i, charm_class in enumerate(charm_classes): charm = charm_class(juju_state=juju_state) if charm.name() and not first_index: first_index = i r = CheckBox(charm.name()) r.text_label = charm.name() self.boxes.append(r) wrapped_boxes = _wrap_focus(self.boxes) def ok(button): selected = filter(lambda r: r.get_state(), self.boxes) on_success([s.text_label for s in selected]) def cancel(button): on_cancel() bs = [Button("Ok", ok), Button("Cancel", cancel)] wrapped_buttons = _wrap_focus(bs) self.buttons = Columns(wrapped_buttons) self.items = ListBox(wrapped_boxes) self.items.set_focus(first_index) ba = BoxAdapter(self.items, height=len(wrapped_boxes)) self.lb = ListBox([ba, self.count_editor, self.buttons]) root = LineBox(self.lb, title="Select new charm") root = AttrMap(root, "dialog") Overlay.__init__(self, root, underlying, 'center', 30, 'middle', len(wrapped_boxes) + 4) def keypress(self, size, key): if key == 'tab': if self.lb.get_focus()[0] == self.buttons: self.keypress(size, 'page up') else: self.keypress(size, 'page down') return Overlay.keypress(self, size, key)
def show_copybook(body): listbox = ListBox([*body]) main_widget.original_widget = Overlay(LineBox(listbox), box, align="center", width=62, height=31, valign="middle") #меняем фокус на последний зарисованный участок лабиринта listbox.change_focus((20, 20), len(body) - 1, offset_inset=0, coming_from=None, cursor_coords=None, snap_rows=None) button_power.overlay = 1
def _insert_charm_selections(self): first_index = 0 bgroup = [] for i, charm_class in enumerate(self.charms): charm = charm_class if charm.name() and not first_index: first_index = i r = RadioButton(bgroup, charm.name()) r.text_label = charm.name() self.boxes.append(r) # Add input widget for specifying NumUnits self.count_editor = IntEdit("Number of units to add: ", 1) self.boxes.append(self.count_editor) wrapped_boxes = self._wrap_focus(self.boxes) items = ListBox(SimpleListWalker(wrapped_boxes)) items.set_focus(first_index) return (len(self.boxes), BoxAdapter(items, len(self.boxes)))
def __init__(self, model, signal): self.model = model self.signal = signal self.items = [] self.error = Text("", align='center') self.body = [ Padding.center_79(self._build_model_inputs()), Padding.line_break(""), Padding.center_79(self._build_additional_options()), Padding.line_break(""), Padding.center_79(Color.info_error(self.error)), Padding.line_break(""), Padding.fixed_10(self._build_buttons()), ] # FIXME determine which UX widget should have focus self.lb = ListBox(self.body) self.lb.set_focus(4) # _build_buttons super().__init__(self.lb)
def __init__(self, app, results, cb): self.app = app self.results = results self.cb = cb self.result_pile = [Padding.line_break("")] self.result_pile += [ Padding.center_90(s) for s in self.build_results() ] super().__init__(ListBox(self.result_pile))
def start(config): """Start the application and handle user input. Blocks until the application exits.""" def item_chosen(button, server): global choice choice = server response = Text( [u'Connecting to: ', server.connection_string(), u'\n']) done = Button(u'Ok') urwid.connect_signal(done, 'click', exit_program) main.original_widget = Filler( Pile([response, AttrMap(done, None, focus_map='reversed')])) def exit_program(button): raise urwid.ExitMainLoop() def unhandled(key): vim_map = {'h': 'left', 'j': 'down', 'k': 'up', 'l': 'right'} if key in vim_map.keys(): list_box.keypress((0, 1), vim_map[key]) elif key in ['left', 'right']: pass elif key in ['esc', 'q']: raise ExitMainLoop() body = [urwid.Text(u'\nServers'), Divider(u'-')] for server in config.get_servers(): button = Button(server.name) urwid.connect_signal(button, 'click', item_chosen, server) body.append(AttrMap(button, None, focus_map='reversed')) list_box = ListBox(urwid.SimpleFocusListWalker(body)) main = Padding(list_box, left=2, right=2) overlay = Overlay(main, SolidFill(u'\N{MEDIUM SHADE}'), align='center', width=('relative', 60), valign='middle', height=('relative', 60), min_width=20, min_height=9) header = AttrMap(Text(u' ssh-menu'), 'header') footer = AttrMap(Text(u'this is the footer'), 'footer') frame = Frame(overlay, header=header, footer=footer) urwid.MainLoop(urwid.AttrMap(frame, 'body'), palette=palette, unhandled_input=unhandled).run() return choice
def __init__(self, model, signal): self.model = model self.signal = signal self.items = [] self.body = [ Padding.center_79(self._build_model_inputs()), Padding.line_break(""), Padding.fixed_10(self._build_buttons()), ] super().__init__(ListBox(self.body))
def __init__(self, signal): self.signal = signal self.body = [ Padding.center_79(Text("This view is not yet implemented.")), Padding.line_break(""), Padding.center_79(Color.info_minor(Text("A place holder widget"))), Padding.line_break(""), Padding.center_79(self._build_buttons()) ] super().__init__(ListBox(self.body))
def _build_widget(self, **kwargs): # Charm selections num_of_items, charm_sel = self._insert_charm_selections() # Control buttons buttons = self._insert_buttons() return LineBox(BoxAdapter(ListBox([charm_sel, Divider(), buttons]), height=num_of_items + 2), title="Add unit")
def __init__(self, parser): self.parser = parser self.find = {'ref': '', 'text': ''} self.isolate_filter = (None, None) self.line_collection = None self.jump_stack = [] self.expansions = {} self.show_sip_level = 2 self.show_ladder = False self.show_only_filtered = False self.show_verbose = True self.show_channel = True self.line_no_before_isolate = 0 self.showing_help = False self.walker = LogLineWalker([('Select call on the left side', None)], self.jump, self.expand, self.isolate) self.header = AttrWrap(Text([('key', 'F6'), ' Log']), 'bar') self.listbox = ListBox(self.walker) self.frame = Frame(self.listbox, header=self.header)
def _build_widget(self, **kwargs): total_items = [] for _item in self.input_items.keys(): total_items.append( AttrWrap(self.input_items[_item], 'input', 'input focus')) self.input_lbox = ListBox(SimpleListWalker(total_items)) # Add buttons self.add_buttons() self.container_box_adapter = BoxAdapter(self.input_lbox, len(total_items)) self.container_lbox = ListBox( [self.container_box_adapter, Divider(), self.btn_pile]) return LineBox(BoxAdapter(self.container_lbox, height=len(total_items) + 1 + len(self.btn_pile.contents)), title=self.title)
def __init__(self, common, cb): self.common = common self.cb = cb _pile = [ Padding.center_90(Text("Choose a solution to get started:")), Padding.center_90(Divider("\N{BOX DRAWINGS LIGHT HORIZONTAL}")), Padding.center_90(self.build_menuable_items()), Padding.line_break(""), Padding.center_20(self.buttons()) ] super().__init__(ListBox(_pile))
def keypress(self, size, key): key = self.body.keypress(size, key) if key == 'home': pos = self.body.get_home() if pos: self.change_focus(size, pos, coming_from='below') elif key == 'end': pos = self.body.get_end() if pos: self.change_focus(size, pos, coming_from='above') elif key: return ListBox.keypress(self, size, key)
def __init__(self, **kwargs):#{{{ self.search_box = SearchBox() self.items_count = Text("", align='right') self.search_items = SimpleListWalker(self._null_list_item()) connect_signal(self.search_items, "modified", self._update_items_count) self.search_list = ListBox(self.search_items) connect_signal(self.search_box, "edit-done", self.on_edit_done) connect_signal(self.search_box, "edit-cancel", lambda w: self.quit()) connect_signal(self.search_box, "change", lambda sb, term: self.set_search_term(term)) self._constr = self.get_item_constructor() self.multiple_selection = kwargs.pop('multiple_selection', False) self._selected_items = OrderedSet([]) opts = { 'height': kwargs.get('height', None), 'width': kwargs.get('width', ('relative', 90)), 'title': kwargs.get('title', self.title), 'subtitle': kwargs.get('subtitle', self.subtitle), 'compact_header': kwargs.get('compact_header', True), } kwargs.update(opts) self.pile = Pile([ ('fixed', 15, AttrMap(self.search_list, 'dialog.search.item')), Columns([ AttrMap(self.search_box, 'dialog.search.input'), ('fixed', 1, AttrMap(Divider(), 'dialog.search.input')), ('fixed', 4, AttrMap(self.items_count, 'dialog.search.input')), ]), ], focus_item=0) self.__super.__init__(self.pile, **kwargs) self.attr_style = "dialog.search" self.title_attr_style = "dialog.search.title" self.subtitle_attr_style = "dialog.search.subtitle"
class CursesGUI(object): def __init__(self, choice_callback=None, command_callback=None, help_callback=None): self.palette = [ ('brick', 'light red', 'black'), ('rubble', 'yellow', 'black'), ('wood', 'light green', 'black'), ('concrete', 'white', 'black'), ('stone', 'light cyan', 'black'), ('marble', 'light magenta', 'black'), ('jack', 'dark gray', 'white'), ('msg_info', 'white', 'black'), ('msg_err', 'light red', 'black'), ('msg_debug', 'light green', 'black'), ] self.choice_callback = choice_callback self.command_callback = command_callback self.help_callback = help_callback self.screen = None self.loop = None self.called_loop_stop = False self.reactor_stop_fired = False self.quit_flag = False self.edit_msg = "Make selection ('q' to quit): " self.roll_list = SimpleListWalker([]) self.game_log_list = SimpleListWalker([]) self.choices_list = SimpleListWalker([]) self.state_text = SimpleListWalker([Text('Connecting...')]) self.edit_widget = Edit(self.edit_msg) self.roll = ListBox(self.roll_list) self.game_log = ListBox(self.game_log_list) self.choices = ListBox(self.choices_list) self.state = ListBox(self.state_text) self.left_frame = Pile([ LineBox(self.state), (13, LineBox(self.choices)), ]) self.right_frame = Pile([ LineBox(self.game_log), LineBox(self.roll) ]) self.state.set_focus(len(self.state_text)-1) self.columns = Columns([('weight', 0.75, self.left_frame), ('weight', 0.25, self.right_frame) ]) self.frame_widget = Frame(footer=self.edit_widget, body=self.columns, focus_part='footer') self.exc_info = None def register_loggers(self): """Gets the global loggers and sets up log handlers. """ self.game_logger_handler = RollLogHandler(self._roll_write) self.logger_handler = RollLogHandler(self._roll_write) self.game_logger = logging.getLogger('gtr.game') self.logger = logging.getLogger('gtr') self.logger.addHandler(self.logger_handler) self.game_logger.addHandler(self.game_logger_handler) #self.set_log_level(logging.INFO) def unregister_loggers(self): self.game_logger.removeHandler(self.game_logger_handler) self.logger.removeHandler(self.logger_handler) def fail_safely(f): """Wraps functions in this class to catch arbitrary exceptions, shut down the event loop and reset the terminal to a normal state. It then re-raises the exception. """ @wraps(f) def wrapper(self, *args, **kwargs): retval = None try: retval = f(self, *args, **kwargs) except urwid.ExitMainLoop: from twisted.internet import reactor if not self.reactor_stop_fired and reactor.running: # Make sure to call reactor.stop once reactor.stop() self.reactor_stop_fired = True except: #pdb.set_trace() from twisted.internet import reactor if not self.reactor_stop_fired and reactor.running: # Make sure to call reactor.stop once reactor.stop() self.reactor_stop_fired = True if not self.called_loop_stop: self.called_loop_stop = True self.loop.stop() # Save exception info for printing later outside the GUI. self.exc_info = sys.exc_info() raise return retval return wrapper def set_log_level(self, level): """Set the log level as per the standard library logging module. Default is logging.INFO. """ logging.getLogger('gtr.game').setLevel(level) logging.getLogger('gtr').setLevel(level) def run(self): loop = MainLoop(self.frame_widget, unhandled_input=self.handle_input) loop.run() def run_twisted(self): from twisted.internet import reactor evloop = urwid.TwistedEventLoop(reactor, manage_reactor=False) self.screen = urwid.raw_display.Screen() self.screen.register_palette(self.palette) self.loop = MainLoop(self.frame_widget, unhandled_input=self.handle_input, screen = self.screen, event_loop = evloop) self.loop.set_alarm_in(0.1, lambda loop, _: loop.draw_screen()) self.loop.start() # The loggers get a Handler that writes to the screen. We want this to only # happen if the screen exists, so de-register them after the reactor stops. reactor.addSystemEventTrigger('after','startup', self.register_loggers) reactor.addSystemEventTrigger('before','shutdown', self.unregister_loggers) reactor.run() # We might have stopped the screen already, and the stop() method # doesn't check for stopping twice. if self.called_loop_stop: self.logger.warn('Internal error!') else: self.loop.stop() self.called_loop_stop = True @fail_safely def handle_input(self, key): if key == 'enter': text = self.edit_widget.edit_text if text in ['q', 'Q']: self.handle_quit_request() else: self.quit_flag = False try: i = int(text) except ValueError: i = None self.handle_invalid_choice(text) if i is not None: self.handle_choice(i) self.edit_widget.set_edit_text('') def _roll_write(self, line, attr=None): """Add a line to the roll with palette attributes 'attr'. If no attr is specified, None is used. Default attr is plain text """ text = Text((attr, '* ' + line)) self.roll_list.append(text) self.roll_list.set_focus(len(self.roll_list)-1) self._modified() @fail_safely def update_state(self, state): """Sets the game state window via one large string. """ self.logger.debug('Drawing game state.') self.state_text[:] = [self.colorize(s) for s in state.split('\n')] self._modified() @fail_safely def update_game_log(self, log): """Sets the game log window via one large string. """ self.logger.debug('Drawing game log.') self.game_log_list[:] = [self.colorize(s) for s in log.split('\n')] self.game_log_list.set_focus(len(self.game_log_list)-1) self._modified() @fail_safely def update_choices(self, choices): """Update choices list. """ self.choices_list[:] = [self.colorize(str(c)) for c in choices] self._modified() length = len([c for c in choices if c[2] == '[']) i = randint(1,length) if length else 0 self.choices_list.append(self.colorize('\nPicking random choice: {0} in 1s'.format(i))) self._modified() #from twisted.internet import reactor #reactor.callLater(1, self.handle_choice, i) @fail_safely def update_prompt(self, prompt): """Set the prompt for the input field. """ self.edit_widget.set_caption(prompt) self._modified() def _modified(self): if self.loop: self.loop.draw_screen() @fail_safely def quit(self): """Quit the program. """ #import pdb; pdb.set_trace() #raise TypeError('Artificial TypeError') raise urwid.ExitMainLoop() def handle_invalid_choice(self, s): if len(s): text = 'Invalid choice: "' + s + '". Please enter an integer.' self.logger.warn(text) def handle_quit_request(self): if True or self.quit_flag: self.quit() else: self.quit_flag = True text = 'Are you sure you want to quit? Press Q again to confirm.' self.logger.warn(text) def handle_choice(self, i): if self.choice_callback: self.choice_callback(i) def colorize(self, s): """Applies color to roles found in a string. A string with attributes applied looks like Text([('attr1', 'some text'), 'some more text']) so we need to split into a list of tuples of text. """ regex_color_dict = { r'\b([Ll]egionaries|[Ll]egionary|[Ll]eg|LEGIONARIES|LEGIONARY|LEG)\b' : 'brick', r'\b([Ll]aborers?|[Ll]ab|LABORERS?|LAB)\b' : 'rubble', r'\b([Cc]raftsmen|[Cc]raftsman|[Cc]ra|CRAFTSMEN|CRAFTSMAN|CRA)\b' : 'wood', r'\b([Aa]rchitects?|[Aa]rc|ARCHITECTS?|ARC)\b' : 'concrete', r'\b([Mm]erchants?|[Mm]er|MERCHANTS?|MER)\b' : 'stone', r'\b([Pp]atrons?|[Pp]at|PATRONS?|PAT)\b' : 'marble', r'\b([Jj]acks?|JACKS?)\b' : 'jack', r'\b([Bb]ricks?|[Bb]ri|BRICKS?|BRI)\b' : 'brick', r'\b([Rr]ubble|[Rr]ub|RUBBLE|RUB)\b' : 'rubble', r'\b([Ww]ood|[Ww]oo|WOOD|WOO)\b' : 'wood', r'\b([Cc]oncrete|[Cc]on|CONCRETE|CON)\b' : 'concrete', r'\b([Ss]tone|[Ss]to|STONE|STO)\b' : 'stone', r'\b([Mm]arble|[Mm]ar|MARBLE|MAR)\b' : 'marble', } def _colorize(s, regex, attr): """s is a tuple of ('attr', 'text'). This splits based on the regex and adds attr to any matches. Returns a list of tuples [('attr1', 'text1'), ('attr2', 'text2'), ('attr3','text3')] with some attributes being None if they aren't colored. """ output = [] a, t = s # Make a list of all tokens, split by matches tokens = re.split(regex, t) for tok in tokens: m = re.match(regex, tok) if m: # matches get the new attributes output.append( (attr,tok) ) else: # non-matches keep the old ones output.append( (a, tok) ) return output output = [ (None, s) ] for k,v in regex_color_dict.items(): new_output = [] for token in output: new_output.extend(_colorize(token, k, v)) output[:] = new_output return Text(output)
class TreeBox(WidgetWrap): """ A widget that displays a given :class:`Tree`. This is essentially a :class:`ListBox` with the ability to move the focus based on directions in the Tree and to collapse/expand subtrees if possible. TreeBox interprets `left/right` as well as `page up/`page down` to move the focus to parent/first child and next/previous sibling respectively. All other keys are passed to the underlying ListBox. """ def __init__(self, tree, focus=None): """ :param tree: tree of widgets to be displayed. :type tree: Tree :param focus: initially focussed position """ self._tree = tree self._walker = TreeListWalker(tree) self._outer_list = ListBox(self._walker) if focus is not None: self._outer_list.set_focus(focus) self.__super.__init__(self._outer_list) # Widget API def get_focus(self): return self._outer_list.get_focus() def set_focus(self, pos): return self._outer_list.set_focus(pos) def refresh(self): self._walker.clear_cache() signals.emit_signal(self._walker, "modified") def keypress(self, size, key): key = self._outer_list.keypress(size, key) if key in ["left", "right", "[", "]", "-", "+", "C", "E"]: if key == "left": self.focus_parent() elif key == "right": self.focus_first_child() elif key == "[": self.focus_prev_sibling() elif key == "]": self.focus_next_sibling() elif key == "-": self.collapse_focussed() elif key == "+": self.expand_focussed() elif key == "C": self.collapse_all() elif key == "E": self.expand_all() # This is a hack around ListBox misbehaving: # it seems impossible to set the focus without calling keypress as # otherwise the change becomes visible only after the next render() return self._outer_list.keypress(size, None) else: return self._outer_list.keypress(size, key) # Collapse operations def collapse_focussed(self): """ Collapse currently focussed position; works only if the underlying tree allows it. """ if implementsCollapseAPI(self._tree): w, focuspos = self.get_focus() self._tree.collapse(focuspos) self._walker.clear_cache() self.refresh() def expand_focussed(self): """ Expand currently focussed position; works only if the underlying tree allows it. """ if implementsCollapseAPI(self._tree): w, focuspos = self.get_focus() self._tree.expand(focuspos) self._walker.clear_cache() self.refresh() def collapse_all(self): """ Collapse all positions; works only if the underlying tree allows it. """ if implementsCollapseAPI(self._tree): self._tree.collapse_all() self.set_focus(self._tree.root) self._walker.clear_cache() self.refresh() def expand_all(self): """ Expand all positions; works only if the underlying tree allows it. """ if implementsCollapseAPI(self._tree): self._tree.expand_all() self._walker.clear_cache() self.refresh() # Tree based focus movement def focus_parent(self): """move focus to parent node of currently focussed one""" w, focuspos = self.get_focus() parent = self._tree.parent_position(focuspos) if parent is not None: self.set_focus(parent) def focus_first_child(self): """move focus to first child of currently focussed one""" w, focuspos = self.get_focus() child = self._tree.first_child_position(focuspos) if child is not None: self.set_focus(child) def focus_last_child(self): """move focus to last child of currently focussed one""" w, focuspos = self.get_focus() child = self._tree.last_child_position(focuspos) if child is not None: self.set_focus(child) def focus_next_sibling(self): """move focus to next sibling of currently focussed one""" w, focuspos = self.get_focus() sib = self._tree.next_sibling_position(focuspos) if sib is not None: self.set_focus(sib) def focus_prev_sibling(self): """move focus to previous sibling of currently focussed one""" w, focuspos = self.get_focus() sib = self._tree.prev_sibling_position(focuspos) if sib is not None: self.set_focus(sib) def focus_next(self): """move focus to next position (DFO)""" w, focuspos = self.get_focus() next = self._tree.next_position(focuspos) if next is not None: self.set_focus(next) def focus_prev(self): """move focus to previous position (DFO)""" w, focuspos = self.get_focus() prev = self._tree.prev_position(focuspos) if prev is not None: self.set_focus(prev)
class TreeBox(WidgetWrap): """ A widget representing something in a nested tree display. This is essentially a ListBox with the ability to move the focus based on directions in the Tree. TreeBox interprets `left/right` as well as page `up/down` to move the focus to parent/first child and next/previous sibling respectively. All other keys are passed to the underlying ListBox. """ _selectable = True def __init__(self, walker, **kwargs): """ :param walker: tree of widgets to be displayed. In case we are given a raw `TreeWalker`, it will be used though `TreeListWalker` which means no decoration. :type walker: TreeWalker or TreeListWalker """ if not isinstance(walker, TreeListWalker): walker = TreeListWalker(walker) self._walker = walker self._outer_list = ListBox(walker) self.__super.__init__(self._outer_list) # Widget API def get_focus(self): return self._outer_list.get_focus() def keypress(self, size, key): key = self._outer_list.keypress(size, key) if key in ['left', 'right', '[', ']', '-', '+', 'C', 'E']: if key == 'left': self.focus_parent() elif key == 'right': self.focus_first_child() elif key == '[': self.focus_prev_sibling() elif key == ']': self.focus_next_sibling() if isinstance(self._walker, CollapseMixin): if key == '-': w, focuspos = self._walker.get_focus() self._walker.collapse(focuspos) elif key == '+': w, focuspos = self._walker.get_focus() self._walker.expand(focuspos) elif key == 'C': self._walker.collapse_all() elif key == 'E': self._walker.expand_all() # This is a hack around ListBox misbehaving: # it seems impossible to set the focus without calling keypress as # otherwise the change becomes visible only after the next render() return self._outer_list.keypress(size, None) else: return self._outer_list.keypress(size, key) # Tree based focus movement def focus_parent(self): w, focuspos = self._walker.get_focus() parent = self._walker.parent_position(focuspos) if parent is not None: self._outer_list.set_focus(parent) def focus_first_child(self): w, focuspos = self._walker.get_focus() child = self._walker.first_child_position(focuspos) if child is not None: self._outer_list.set_focus(child) def focus_next_sibling(self): w, focuspos = self._walker.get_focus() sib = self._walker.next_sibling_position(focuspos) if sib is not None: self._outer_list.set_focus(sib) def focus_prev_sibling(self): w, focuspos = self._walker.get_focus() sib = self._walker.prev_sibling_position(focuspos) if sib is not None: self._outer_list.set_focus(sib)
def __init__(self, charset, root, preload_fields, focus, options={}): ListBox.__init__(self, Walker(charset, root, preload_fields, focus, options))
class SearchDialog(Dialog):#{{{ title = "SearchDialog" subtitle = "GenericWindow" _items = [] def __init__(self, **kwargs):#{{{ self.search_box = SearchBox() self.items_count = Text("", align='right') self.search_items = SimpleListWalker(self._null_list_item()) connect_signal(self.search_items, "modified", self._update_items_count) self.search_list = ListBox(self.search_items) connect_signal(self.search_box, "edit-done", self.on_edit_done) connect_signal(self.search_box, "edit-cancel", lambda w: self.quit()) connect_signal(self.search_box, "change", lambda sb, term: self.set_search_term(term)) self._constr = self.get_item_constructor() self.multiple_selection = kwargs.pop('multiple_selection', False) self._selected_items = OrderedSet([]) opts = { 'height': kwargs.get('height', None), 'width': kwargs.get('width', ('relative', 90)), 'title': kwargs.get('title', self.title), 'subtitle': kwargs.get('subtitle', self.subtitle), 'compact_header': kwargs.get('compact_header', True), } kwargs.update(opts) self.pile = Pile([ ('fixed', 15, AttrMap(self.search_list, 'dialog.search.item')), Columns([ AttrMap(self.search_box, 'dialog.search.input'), ('fixed', 1, AttrMap(Divider(), 'dialog.search.input')), ('fixed', 4, AttrMap(self.items_count, 'dialog.search.input')), ]), ], focus_item=0) self.__super.__init__(self.pile, **kwargs) self.attr_style = "dialog.search" self.title_attr_style = "dialog.search.title" self.subtitle_attr_style = "dialog.search.subtitle" #}}} def keypress(self, key):#{{{ if key == 'insert' and self.multiple_selection: if self.pile.get_focus().original_widget is self.search_list: wid, pos = self.search_list.get_focus() else: pos = 0 current_item = self.search_items[pos] article = self.get_data_for(pos) current_item.original_widget.selected = not current_item.original_widget.selected if current_item.original_widget.selected: current_item.attr_map = {None: 'dialog.search.item.selected'} current_item.focus_map = {None: 'dialog.search.item.focus.selected'} self._selected_items.add(article) else: current_item.attr_map = {None: 'dialog.search.item'} current_item.focus_map = {None: 'dialog.search.item.focus'} self._selected_items.discard(article) self.search_list.set_focus(pos+1) self._update_items_count() #}}} def on_edit_done(self, widget, text):#{{{ result = [] if self.pile.get_focus().original_widget is self.search_list: wid, pos = self.search_list.get_focus() else: pos = 0 if self.multiple_selection: result = list(self._selected_items) if len(result) < 1: if self.get_data_for(pos): result = [self.get_data_for(pos)] self.dialog_result = result self.quit() #}}} def set_search_term(self, term):#{{{ self._clear_search_items() query = self.get_query(term) if query is not None: self._items = tuple(query[:150]) if len(self._items) > 0: l_items = map(self._constr, self._items) for i in l_items: i.set_search_box(self.search_box) self.search_items.extend([AttrMap(i, 'dialog.search.item',\ 'dialog.search.item.focus') for i in l_items]) if self.multiple_selection: for a in (self._selected_items & set(self._items)): idx = self._items.index(a) self.search_items[idx].attr_map = {None: 'dialog.search.item.selected'} self.search_items[idx].focus_map = {None: 'dialog.search.item.focus.selected'} self.search_items[idx].original_widget.selected = True return self.search_items.extend(self._null_list_item()) #}}} def _clear_search_items(self):#{{{ self.search_items[:] = [] self._update_items_count() #}}} def _null_list_item(self):#{{{ null = SearchListItem([Text("")]) null.set_search_box(self.search_box) return [null] #}}} def _update_items_count(self):#{{{ if len(self.search_items) > 149: self.items_count.set_text("+150") else: self.items_count.set_text("") selected_count = len(self._selected_items) if selected_count > 0: self._title_widget.set_text(self.title + (" (+%d)" % selected_count)) else: self._title_widget.set_text(self.title) #}}} def get_data_for(self, index):#{{{ try: return self._items[index] except IndexError as e: # index out of range return None #}}} def get_query(self, term):#{{{ raise NotImplementedError("This must be implemented by subclass") #}}} def get_item_constructor(self):#{{{ return None
def __init__(self): self._contents = SimpleListWalker([]) ListBox.__init__(self, self._contents) self.client = JujuClient()