def _showhide_other(self, show): if show and not self._other_showing: self._w.contents.append( (Columns([(1, Text("/")), Color.string_input(self._other)]), self._w.options('pack'))) self._other_showing = True elif not show and self._other_showing: del self._w.contents[-1] self._other_showing = False
def add_event(self, text): walker = self.event_listbox.base_widget.body if len(walker) > 0: # Remove the spinner from the line it is currently on, if # there is one. walker[-1] = walker[-1][0] # Add spinner to the line we are inserting. new_line = Columns([('pack', Text(text)), ('pack', self.spinner)], dividechars=1) self._add_line(self.event_listbox, new_line)
def make_body(self): self.error_text = Text("", align="center") return Pile([ Text(_("Please press one of the following keys:")), Text(""), Columns([Text(s, align="center") for s in self.step.symbols], dividechars=1), Text(""), Color.info_error(self.error_text), ])
def make_body(self): self.error_text = Text("", align="center") self.pile = Pile([ ('pack', Text(_("Please press one of the following keys:"))), ('pack', Text("")), ('pack', Columns([Text(s, align="center") for s in self.step.symbols], dividechars=1)), ]) return self.pile
def _build_bondmode_configuration(self): log.debug('bond: _build_bondmode_configuration') items = [ Text("BOND CONFIGURATION"), Columns([("weight", 0.2, Text("Bonding Mode", align="right")), ("weight", 0.3, Color.string_input(self.bond_mode))], dividechars=4), ] log.debug('bond_mode: items: {}'.format(items)) return Pile(items)
def _build_model_inputs(self): partitioned_disks = [] def format_volume(label, part): size = humanize_size(part.size) if part.fs() is None: fstype = '-' mountpoint = '-' elif part.fs().mount() is None: fstype = part.fs().fstype mountpoint = '-' else: fstype = part.fs().fstype mountpoint = part.fs().mount().path if part.type == 'disk': part_btn = menu_btn(label, on_press=self._click_disk) else: part_btn = menu_btn(label, on_press=self._click_part, user_arg=part) return Columns([ (25, part_btn), (9, Text(size, align="right")), Text(fstype), Text(mountpoint), ], 2) if self.disk.fs() is not None: partitioned_disks.append(format_volume("entire disk", self.disk)) else: for part in self.disk.partitions(): partitioned_disks.append( format_volume("Partition {}".format(part.number), part)) if self.disk.free > 0: free_space = humanize_size(self.disk.free) if len(self.disk.partitions()) > 0: label = "Add another partition" else: label = "Add first partition" add_btn = menu_btn(label, on_press=self.add_partition) partitioned_disks.append( Columns([ (25, add_btn), (9, Text(free_space, align="right")), Text("free space"), ], 2)) if len(self.disk.partitions()) == 0 and \ self.disk.available: text = ("Format or create swap on entire " "device (unusual, advanced)") partitioned_disks.append(Text("")) partitioned_disks.append( menu_btn(label=text, on_press=self.format_entire)) return partitioned_disks
def event_start(self, context_id, context_parent_id, message): self.event_finish(context_parent_id) walker = self.event_listbox.base_widget.body spinner = Spinner(self.controller.app.aio_loop) spinner.start() new_line = Columns([ ('pack', Text(message)), ('pack', spinner), ], dividechars=1) self.ongoing[context_id] = len(walker) self._add_line(self.event_listbox, new_line)
def as_row(self, longest_caption): if self.pile is not None: raise RuntimeError("do not call as_row more than once!") self._longest_caption = longest_caption self.help_text.set_text(self.help) cols = [ (self._longest_caption, Text("")), self.help_text, ] self.pile = Pile([self._cols(), Columns(cols, dividechars=2)]) return self.pile
def _build_menu(self): items = [] for label, sig in self.model.get_menu(): items.append( Columns([("weight", 0.2, Text("")), ("weight", 0.3, Color.menu_button( menu_btn(label=label, on_press=self.confirm, user_data=sig)))])) return Pile(items)
def _build_model_inputs(self): sl = [ Columns( [ ("weight", 0.2, Text("Email address:", align="right")), ("weight", 0.3, Color.string_input(self.email)), ], dividechars=4 ), ] return Pile(sl)
def __init__(self, parent, snap, cur_channel): self.parent = parent self.snap = snap self.channels = [] self.needs_focus = True self.description = Text(snap.description.replace('\r', '').strip()) self.lb_description = ListBox([self.description]) radio_group = [] for csi in snap.channels: notes = '-' if csi.confinement != "strict": notes = csi.confinement btn = StarRadioButton(radio_group, "{}:".format(csi.channel_name), state=csi.channel_name == cur_channel, on_state_change=self.state_change, user_data=SnapSelection( channel=csi.channel_name, is_classic=csi.confinement == "classic")) self.channels.append( Color.menu_button( TableRow([ btn, Text(csi.version), Text("({})".format(csi.revision)), Text(humanize_size(csi.size)), Text(notes), ]))) self.lb_channels = Table(self.channels, container_maker=NoTabCyclingListBox) title = Columns([ Text(snap.name), ('pack', Text(_("Publisher: {}").format(snap.publisher), align='right')), ], dividechars=1) contents = [ ('pack', title), ('pack', Text("")), ('pack', Text(snap.summary)), ('pack', Text("")), self.lb_description, # overwritten in render() ('pack', Text("")), ('weight', 1, self.lb_channels), ] self.description_index = contents.index(self.lb_description) self.pile = Pile(contents) super().__init__(self.pile)
def as_row(self, view, longest_caption): if self.pile is not None: raise RuntimeError("do not call as_row more than once!") self.parent_view = view self._longest_caption = longest_caption self.help_text = Text(self.help, align="center") cols = [ (self._longest_caption, Text("")), self.help_text, ] self.pile = Pile([self._cols(), Columns(cols, dividechars=2)]) return self.pile
def __init__(self, parent, snap, max_name_len, max_publisher_len): self.parent = parent self.snap = snap self.box = StarCheckBox(snap.name, on_state_change=self.state_change) self.name_and_publisher_width = (max_name_len + self.box.reserve_columns + max_publisher_len + 2) self.two_column = Color.menu_button( Columns([ (max_name_len + self.box.reserve_columns, self.box), Text(snap.summary, wrap='clip'), ], dividechars=1)) self.three_column = Color.menu_button( Columns([ (max_name_len + 4, self.box), (max_publisher_len, Text(snap.publisher)), Text(snap.summary, wrap='clip'), ], dividechars=1)) super().__init__(self.two_column)
def _cols(self): text = Text(self.caption, align="right") if self._enabled: input = Color.string_input(_Validator(self, self.widget)) else: input = self.widget cols = [(self._longest_caption, text), input] cols = Columns(cols, dividechars=2) if self._enabled: return cols else: return WidgetDisable(Color.info_minor(cols))
def __init__(self): self.mode = "spinning" self.spinner = Spinner() self.label = Text("", wrap='clip') cols = Color.progress_incomplete(Columns([ (1, Text("")), self.label, (1, Text("")), (1, self.spinner), (1, Text("")), ])) super().__init__(cols)
def _build_model_inputs(self): netdevs = self.model.get_all_netdevs() ifname_width = 8 # default padding if netdevs: ifname_width += max(map(lambda dev: len(dev.name), netdevs)) if ifname_width > 20: ifname_width = 20 iface_menus = [] # Display each interface -- name in first column, then configured IPs # in the second. log.debug('interfaces: {}'.format(netdevs)) for dev in netdevs: col_1 = [] col_2 = [] col_1.append( menu_btn(label=dev.name, on_press=self.on_net_dev_press)) if dev.type == 'wlan': col_2.extend(_build_wifi_info(dev)) if len(dev.actual_ip_addresses) == 0 and (dev.type == 'eth' and not dev.is_connected): col_2.append(Color.info_primary(Text(_("Not connected")))) col_2.extend(_build_gateway_ip_info_for_version(dev, 4)) col_2.extend(_build_gateway_ip_info_for_version(dev, 6)) # Other device info (MAC, vendor/model, speed) template = '' if dev.hwaddr: template += '{} '.format(dev.hwaddr) # TODO is this to translate? if dev.is_bond_slave: template += '(Bonded) ' # TODO to check if this is affected by translations if not dev.vendor.lower().startswith('unknown'): vendor = textwrap.wrap(dev.vendor, 15)[0] template += '{} '.format(vendor) if not dev.model.lower().startswith('unknown'): model = textwrap.wrap(dev.model, 20)[0] template += '{} '.format(model) if dev.speed: template += '({})'.format(dev.speed) col_2.append(Color.info_minor(Text(template))) iface_menus.append( Columns([(ifname_width, Pile(col_1)), Pile(col_2)], 2)) return iface_menus
def event_start(self, context, message): self.event_finish(context.parent) walker = self.event_listbox.base_widget.body indent = context.full_name().count('/') - 2 if context.get('is-install-context'): indent -= 1 spinner = Spinner(self.controller.app.aio_loop) spinner.start() new_line = Columns([ ('pack', Text(' ' * indent + message)), ('pack', spinner), ], dividechars=1) self.ongoing[context] = len(walker) self._add_line(self.event_listbox, new_line)
def __init__(self, opts, index=0): self._icon = ClickableThing(Text("")) self._padding = UrwidPadding( AttrWrap( Columns([ (1, Text('[')), self._icon, (3, Text('\N{BLACK DOWN-POINTING SMALL TRIANGLE} ]')), ], dividechars=1), 'menu_button', 'menu_button focus')) options = [] for opt in opts: options.append(Option(opt)) self.options = options self._set_index(index) super().__init__(_Launcher(self, self._padding))
def _cols(self): text = Text(self.caption, align="right") if self._enabled: input = Color.string_input(_Validator(self, self.widget)) else: input = self.widget if self.help is not None: help = Help(self.parent_view, self.help) else: help = Text("") cols = [ (self._longest_caption, text), input, (3, help), ] cols = Columns(cols, dividechars=2) if self._enabled: return cols else: return WidgetDisable(Color.info_minor(cols))
def show_overlay(self, overlay_widget, **kw): args = dict(align='center', width=('relative', 60), min_width=80, valign='middle', height='pack') PADDING = 3 # Don't expect callers to account for the padding if # they pass a fixed width. if 'width' in kw: if isinstance(kw['width'], int): kw['width'] += 2 * PADDING args.update(kw) top = Pile([ ('pack', Text("")), Columns([(PADDING, Text("")), overlay_widget, (PADDING, Text(""))]), ('pack', Text("")), ]) self._w = Overlay(top_w=top, bottom_w=disabled(self._w), **args)
def _build_filesystem_list(self): log.debug('FileSystemView: building part list') cols = [] mount_point_text = _("MOUNT POINT") longest_path = len(mount_point_text) for m in sorted(self.model._mounts, key=lambda m: m.path): path = m.path longest_path = max(longest_path, len(path)) for p, *dummy in reversed(cols): if path.startswith(p): path = [('info_minor', p), path[len(p):]] break cols.append((m.path, path, humanize_size(m.device.volume.size), m.device.fstype, m.device.volume.desc())) for fs in self.model._filesystems: if fs.fstype == 'swap': cols.append( (None, _('SWAP'), humanize_size(fs.volume.size), fs.fstype, fs.volume.desc())) if len(cols) == 0: return Pile( [Color.info_minor(Text(_("No disks or partitions mounted.")))]) size_text = _("SIZE") type_text = _("TYPE") size_width = max(len(size_text), 9) type_width = max(len(type_text), self.model.longest_fs_name) cols.insert( 0, (None, mount_point_text, size_text, type_text, _("DEVICE TYPE"))) pl = [] for dummy, a, b, c, d in cols: if b == "SIZE": b = Text(b, align='center') else: b = Text(b, align='right') pl.append( Columns([(longest_path, Text(a)), (size_width, b), (type_width, Text(c)), Text(d)], 4)) return Pile(pl)
def format_volume(label, part): size = humanize_size(part.size) if part.fs() is None: fstype = '-' mountpoint = '-' elif part.fs().mount() is None: fstype = part.fs().fstype mountpoint = '-' else: fstype = part.fs().fstype mountpoint = part.fs().mount().path if part.type == 'disk': part_btn = menu_btn(label, on_press=self._click_disk) else: part_btn = menu_btn(label, on_press=self._click_part, user_arg=part) return Columns([ (label_width, part_btn), (9, Text(size, align="right")), Text(fstype), Text(mountpoint), ], 2)
def __init__(self, parent): self.parent = parent close_text = "(close)" close = ActionBackButton(close_text) connect_signal(close, "click", self.close) group = [Color.menu_button(close)] width = len(close_text) for i, action in enumerate(self.parent._actions): if action.enabled: if isinstance(action.label, Widget): btn = action.label elif action.opens_dialog: btn = Color.menu_button(ActionMenuOpenButton(action.label)) else: btn = Color.menu_button(ActionMenuButton(action.label)) width = max(width, len(btn.base_widget.label)) connect_signal(btn.base_widget, 'click', self.click, action.value) else: label = action.label if isinstance(label, Widget): label = label.base_widget.label width = max(width, len(label)) if action.opens_dialog: rhs = "\N{BLACK RIGHT-POINTING SMALL TRIANGLE}" else: rhs = "" btn = Columns([ ('fixed', 1, Text("")), Text(label), ('fixed', 1, Text(rhs)), ], dividechars=1) btn = AttrWrap(btn, 'info_minor') group.append(btn) self.width = width super().__init__(LineBox(ListBox(group)))
def __init__(self, parent, cur_index): self.parent = parent group = [] for i, option in enumerate(self.parent._options): if option.enabled: btn = ClickableIcon(" " + option.label) connect_signal(btn, 'click', self.click, i) if i == cur_index: rhs = '\N{BLACK LEFT-POINTING SMALL TRIANGLE} ' else: rhs = ' ' btn = Columns([ btn, (2, Text(rhs)), ]) btn = AttrWrap(btn, 'menu_button', 'menu_button focus') else: btn = Text(" " + option.label) btn = AttrWrap(btn, 'info_minor') btn = UrwidPadding(btn, width=self.parent._padding.width) group.append(btn) list_box = ListBox(group) list_box.base_widget.focus_position = cur_index super().__init__(Color.body(LineBox(list_box)))
def __init__(self, parent, snap, cur_channel): self.parent = parent self.snap = snap self.needs_focus = True self.description = Text(snap.description.replace('\r', '').strip()) self.lb_description = ListBox([self.description]) latest_update = datetime.datetime.min radio_group = [] channel_rows = [] for csi in snap.channels: latest_update = max(latest_update, csi.released_at) btn = StarRadioButton(radio_group, csi.channel_name, state=csi.channel_name == cur_channel, on_state_change=self.state_change, user_data=SnapSelection( channel=csi.channel_name, is_classic=csi.confinement == "classic")) channel_rows.append( Color.menu_button( TableRow([ btn, Text(csi.version), Text("(" + csi.revision + ")"), Text(humanize_size(csi.size)), Text(format_datetime(csi.released_at)), Text(csi.confinement), ]))) first_info_row = TableRow([ (3, Text([ ('info_minor', "LICENSE: "), snap.license, ], wrap='clip')), (3, Text([ ('info_minor', "LAST UPDATED: "), format_datetime(latest_update), ])), ]) heading_row = Color.info_minor( TableRow([ Text("CHANNEL"), (2, Text("VERSION")), Text("SIZE"), Text("PUBLISHED"), Text("CONFINEMENT"), ])) colspecs = { 1: ColSpec(can_shrink=True), } info_table = TablePile([ first_info_row, TableRow([Text("")]), heading_row, ], spacing=2, colspecs=colspecs) self.lb_channels = NoTabCyclingTableListBox(channel_rows, spacing=2, colspecs=colspecs) info_table.bind(self.lb_channels) self.info_padding = Padding.pull_1(info_table) publisher = [('info_minor header', "by: "), snap.publisher] if snap.verified: publisher.append(('verified header', ' \N{check mark}')) self.title = Columns([ Text(snap.name), ('pack', Text(publisher, align='right')), ], dividechars=1) contents = [ ('pack', Text(snap.summary)), ('pack', Text("")), self.lb_description, # overwritten in render() ('pack', Text("")), ('pack', self.info_padding), ('pack', Text("")), ('weight', 1, self.lb_channels), ] self.description_index = contents.index(self.lb_description) self.pile = Pile(contents) super().__init__(self.pile)
def col3(col1, col2, col3): inputs.append(Columns([(40, col1), (10, col2), (10, col3)], 2))
class SSHImport(WidgetWrap, WantsToKnowFormField): signals = ['change'] _helps = { None: _("You can import your SSH keys from Github, Launchpad or Ubuntu One."), "gh": _("Enter your github username."), "lp": _("Enter your Launchpad username."), "sso": _("Enter an email address associated with your Ubuntu One account."), } def __init__(self): choices = [ (_("No"), True, None), (_("from Github"), True, "gh"), (_("from Launchpad"), True, "lp"), (_("from Ubuntu One account"), True, "sso"), ] self.selector = Selector(choices) connect_signal(self.selector, 'select', self._select) self.username = UsernameEditor() self.email = EmailEditor() connect_signal(self.username, 'change', self._change) self.cols = Columns([ self.selector, (1, Text("")), (2, Color.body(Text(""))), Color.body(Text(""))]) super().__init__(self.cols) def _change(self, sender, val): self._emit('change', val) def set_bound_form_field(self, bff): self.bff = bff self.username.set_bound_form_field(bff) # Get things set up for the initial selection. self._select(self.selector, None) def _select(self, sender, val): label = sender.option_by_value(val).label self.cols.contents[0] = (self.cols.contents[0][0], self.cols.options('given', len(label) + 4)) if val is not None: if val == 'sso': editor = self.email else: editor = self.username self.cols.contents[3] = (editor, self.cols.options()) self.cols[1].set_text(":") self.cols.focus_position = 3 else: self.username.set_edit_text("") self.cols[1].set_text("") self.cols.contents[3] = (Color.body(Text("")), self.cols.options()) self.bff.help = self._helps[val] @property def value(self): v = self.selector.value if v is not None: return v + ":" + self.username.value
def col2(col1, col2): inputs.append(Columns([(40, col1), col2], 2))
def col1(col1): inputs.append(Columns([(40, col1)], 1))
class TableRow(WidgetWrap): """A row in a table. A wrapper around a Columns. The widths will be set when rendered. """ # To allow for variable padding between columns, we can't use # Columns.dividechars. Instead, the padding is implemented as # extra columns. This complicates things because "column i" # becomes a bit ambiguous -- does it mean the i'th column from the # user's POV, or does it mean the i'th column in the Columns from # urwid's POV? This code tries to refer to the former as the "user # index" and the latter as the "underlying index". Mapping from # one to the other is pretty simple: underlying-index = 2*user-index. def __init__(self, cells): """cells is a list of [widget] or [(colspan, widget)]. colspan is assumed to be 1 if omitted. """ self.cells = [] cols = [] for cell in cells: colspan = 1 if isinstance(cell, tuple): colspan, cell = cell assert colspan > 0 self.cells.append((colspan, cell)) cols.append(cell) cols.append(urwid.Text("")) del cols[-1] self.columns = Columns(cols) super().__init__(self.columns) def selectable(self): for w, _ in self._w.contents: if w.selectable(): return True return False def _user_indices_cells(self): """Yield the user indices each cell spans and the cell. """ user_i = 0 for colspan, cell in self.cells: yield range(user_i, user_i+colspan), cell user_i += colspan def get_natural_widths(self, unpacked_cols): """Return a mapping {underlying-index:natural-width}. Cells spanning multiple columns are ignored (handled in adjust_for_spanning_cells). """ widths = {} for user_indices, cell in self._user_indices_cells(): if len(user_indices) == 1 and user_indices[0] not in unpacked_cols: widths[2*user_indices[0]] = widget_width(cell) return widths def adjust_for_spanning_cells(self, unpacked_user_indices, widths): """Make sure columns are wide enough for cells with colspan > 1. This very roughly follows the approach in https://www.w3.org/TR/CSS2/tables.html#width-layout. """ for user_indices, cell in self._user_indices_cells(): if set(user_indices) & unpacked_user_indices: continue user_indices = [ user_i for user_i in user_indices if widths[2*user_i] > 0] if len(user_indices) <= 1: continue cur_width = _width(widths, user_indices) cell_width = widget_width(cell) if cur_width < cell_width: # Attempt to widen each column by about the same amount. # But widen the first few columns by more if that's # whats needed. div, mod = divmod(cell_width - cur_width, len(user_indices)) for i, user_j in enumerate(user_indices): widths[2*user_j] += div + int(i < mod) def set_widths(self, widths): """Configure row to given widths. `widths` is a mapping {underlying-index:width}. An index being missing means let the column shrink, a width being 0 means omit the column entirely. """ cols = [] for user_indices, cell in self._user_indices_cells(): try: width = _width(widths, user_indices) except KeyError: opt = self.columns.options('weight', 1) else: if width == 0: continue opt = self.columns.options('given', width) cols.append((cell, opt)) n = widths.get(2*max(user_indices) + 1, 0) if n: cols.append((urwid.Text(""), self.columns.options('given', n))) self.columns.contents[:] = cols