def _build_gateway_ipv6_info(self): addresses = self.iface_obj.ipv6_addresses ips = self.iface_obj.ip6 methods = self.iface_obj.ip6_methods providers = self.iface_obj.ip6_providers dhcp6 = self.iface_obj.dhcp6 if dhcp6: punct = ":" if len(ips) else "." p = [Text("Will use DHCP for IPv6" + punct)] for idx in range(len(ips)): if methods[idx] == "manual": gw_info = (str(ips[idx]) + (" provided by ") + methods[idx]) else: gw_info = (str(ips[idx]) + (" provided by ") + methods[idx] + (" from ") + providers[idx]) p.append(Color.info_minor(Text(gw_info))) elif not dhcp6 and len(addresses) > 0: p = [Text("Will use static addresses for IPv6:")] for idx in range(len(addresses)): p.append(Color.info_minor(Text(addresses[idx]))) else: p = [Text("IPv6 is not configured.")] return p
def _build_iface_inputs(self): if len(self.dev.actual_ssids) > 0: networks_btn = menu_btn("Choose a visible network", on_press=self.show_ssid_list) else: networks_btn = Color.info_minor( Columns([('fixed', 1, Text("")), Text("No visible networks"), ('fixed', 1, Text(">"))], dividechars=1)) if not self.dev.scan_state: scan_btn = menu_btn("Scan for networks", on_press=self.start_scan) else: scan_btn = Color.info_minor( Columns([('fixed', 1, Text("")), Text("Scanning for networks"), ('fixed', 1, Text(">"))], dividechars=1)) col = [ Padding.center_79( Color.info_minor( Text( "Only open or WPA2/PSK networks are supported at this time." ))), Padding.line_break(""), self.ssid_row, Padding.fixed_30(networks_btn), Padding.fixed_30(scan_btn), self.psk_row, ] return col
def refresh_model_inputs(self): devices = [ d for d in self.parent.model.all_devices() if (d.available() == self.show_available or ( not self.show_available and d.has_unavailable_partition())) ] if len(devices) == 0: self._w = Padding.push_2(self._no_devices_content) self.table.table_rows = [] return self._w = self.table log.debug('FileSystemView: building device list') rows = [] rows.append( Color.info_minor( TableRow([ Text(""), (2, Text(_("DEVICE"))), Text(_("TYPE")), Text(_("SIZE"), align="center"), Text(""), Text(""), ]))) for device in devices: for obj, cells in summarize_device( device, lambda part: part.available() == self.show_available): if obj is not None: menu = self._action_menu_for_device(obj) else: menu = Text("") if obj is device: start, end = '[', ']' else: start, end = '', '' cells = [Text(start)] + cells + [menu, Text(end)] if obj is not None: rows.append(make_action_menu_row(cells, menu)) else: rows.append(TableRow(cells)) if (self.show_available and device.used > 0 and device.free_for_partitions > 0): free = humanize_size(device.free_for_partitions) rows.append( TableRow([ Text(""), (3, Color.info_minor(Text(_("free space")))), Text(free, align="right"), Text(""), Text(""), ])) rows.append(TableRow([Text("")])) self.table.set_contents(rows[:-1]) if self.table._w.focus_position >= len(rows): self.table._w.focus_position = len(rows) - 1 while not self.table._w.focus.selectable(): self.table._w.focus_position -= 1
def refresh_model_inputs(self): zdevinfos = self.parent.controller.get_zdevinfos() rows = [ TableRow([ Color.info_minor(heading) for heading in [ Text(_("ID")), Text(_("ONLINE")), Text(_("NAMES")), ] ]) ] typeclass = '' for i, zdevinfo in enumerate(zdevinfos): if zdevinfo.typeclass != typeclass: rows.append(TableRow([ Text(""), ])) rows.append( TableRow([ Color.info_minor(Text(zdevinfo.type)), Text(""), Text("") ])) typeclass = zdevinfo.typeclass if zdevinfo.type == 'zfcp-lun': rows.append( TableRow([ Color.info_minor(Text(zdevinfo.id[9:])), zdevinfo.status, Text(zdevinfo.names), ])) continue actions = [(_("Enable"), not zdevinfo.on, 'enable'), (_("Disable"), zdevinfo.on, 'disable')] menu = ActionMenu(actions) connect_signal(menu, 'action', self._zdev_action, zdevinfo) cells = [ Text(zdevinfo.id), zdevinfo.status, Text(zdevinfo.names), menu, ] row = make_action_menu_row(cells, menu, attr_map='menu_button', focus_map={ None: 'menu_button focus', 'info_minor': 'menu_button focus', }, cursor_x=0) rows.append(row) self.table.set_contents(rows) if self.table._w.base_widget.focus_position >= len(rows): self.table._w.base_widget.focus_position = len(rows) - 1
def _build_additional_options(self): labels = [] netdevs = self.model.get_all_netdevs() # Display default route status if self.model.default_v4_gateway is not None: v4_route_source = "via " + self.model.default_v4_gateway default_v4_route_w = Color.info_minor( Text(_(" IPv4 default route %s." % v4_route_source))) labels.append(default_v4_route_w) if self.model.default_v6_gateway is not None: v6_route_source = "via " + self.model.default_v6_gateway default_v6_route_w = Color.info_minor( Text(" IPv6 default route " + v6_route_source + ".")) labels.append(default_v6_route_w) max_btn_len = 0 buttons = [] for opt, sig in self.model.get_menu(): if ':set-default-route' in sig: if len(netdevs) < 2: log.debug('Skipping default route menu option' ' (only one nic)') continue if ':bond-interfaces' in sig: not_bonded = [dev for dev in netdevs if not dev.is_bonded] if len(not_bonded) < 2: log.debug('Skipping bonding menu option' ' (not enough available nics)') continue if len(opt) > max_btn_len: max_btn_len = len(opt) buttons.append( menu_btn(label=opt, on_press=self.additional_menu_select, user_data=sig)) from urwid import Padding buttons = [ Padding(button, align='left', width=max_btn_len + 6) for button in buttons ] r = labels + buttons if len(r) > 0: r[0:0] = [Text("")] return r
def _build_additional_options(self): labels = [] ifaces = self.model.get_all_interface_names() # Display default route status if self.model.default_v4_gateway is not None: v4_route_source = "via " + self.model.default_v4_gateway default_v4_route_w = Color.info_minor( Text(" IPv4 default route " + v4_route_source + ".")) labels.append(default_v4_route_w) if self.model.default_v6_gateway is not None: v6_route_source = "via " + self.model.default_v6_gateway default_v6_route_w = Color.info_minor( Text(" IPv6 default route " + v6_route_source + ".")) labels.append(default_v6_route_w) max_btn_len = 0 buttons = [] for opt, sig in self.model.get_menu(): if ':set-default-route' in sig: if len(ifaces) < 2: log.debug('Skipping default route menu option' ' (only one nic)') continue if ':bond-interfaces' in sig: not_bonded = [iface for iface in ifaces if not self.model.iface_is_bonded(iface)] if len(not_bonded) < 2: log.debug('Skipping bonding menu option' ' (not enough available nics)') continue if len(opt) > max_btn_len: max_btn_len = len(opt) buttons.append( Color.menu_button( menu_btn(label=opt, on_press=self.additional_menu_select, user_data=sig), focus_map='button focus')) padding = getattr(Padding, 'left_{}'.format(max_btn_len + 10)) buttons = [ padding(button) for button in buttons ] return Pile(labels + buttons)
def _build_filesystem_list(self): log.debug('FileSystemView: building part list') cols = [] longest_path = len("MOUNT POINT") for m in sorted(self.model._mounts, key=lambda m:m.path): path = m.path longest_path = max(longest_path, len(path)) for p, *_ 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."))]) cols.insert(0, (None, "MOUNT POINT", "SIZE", "TYPE", "DEVICE TYPE")) pl = [] for _, 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)), (9, b), (self.model.longest_fs_name, Text(c)), Text(d)], 4)) return Pile(pl)
def _build_disk_selection(self): log.debug('lvm: _build_disk_selection') items = [Text("DISK SELECTION")] # lvm can use empty whole disks, or empty partitions avail_disks = self.model.get_empty_disk_names() avail_parts = self.model.get_empty_partition_names() avail_devs = sorted(avail_disks + avail_parts) if len(avail_devs) == 0: return items.append( [Color.info_minor(Text("No available disks."))]) for dname in avail_devs: device = self.model.get_disk(dname) if device.path != dname: # we've got a partition lvmdev = device.get_partition(dname) else: lvmdev = device disk_sz = humanize_size(lvmdev.size) disk_string = "{} {}, {}".format(dname, disk_sz, device.model) log.debug('lvm: disk_string={}'.format(disk_string)) self.selected_disks.append(CheckBox(disk_string)) items += self.selected_disks return Pile(items)
def __init__(self, parent, obj): self.parent = parent self.obj = obj delete_ok, reason = can_delete(obj) if delete_ok: title = _("Confirm deletion of {}").format(obj.desc()) lines = [ _("Do you really want to delete {}?").format(obj.label), "", ] new_lines, delete_funcs = delete_consequences( self.parent.controller, obj) lines.extend(new_lines) self.delete_funcs = delete_funcs else: title = "Cannot delete {}".format(obj.desc()) lines = [ _("Cannot delete {} because {}").format(obj.label, reason) ] delete_btn = danger_btn(label=_("Delete"), on_press=self.confirm) if not delete_ok: delete_btn = WidgetDisable( Color.info_minor(delete_btn.original_widget)) widgets = [ Text("\n".join(lines)), Text(""), button_pile([ delete_btn, other_btn(label=_("Cancel"), on_press=self.cancel), ]), ] super().__init__(title, widgets, 0, 2)
def summarize_device(device, part_filter=lambda p: True): """Return content for a table summarizing device. This (obj, cells) where obj is either device itself, a partition of device or None and cells is part of an argument to TableRow that will span 4 columns that describes device or a partition of device. This sounds a bit strange but hopefully you can figure it out by looking at the uses of this function. """ label = labels.label(device) anns = labels.annotations(device) if anns: label = "{} ({})".format(label, ", ".join(anns)) rows = [(device, [ (2, Text(label)), Text(labels.desc(device)), Text(humanize_size(device.size), align="right"), ])] partitions = device.partitions() if partitions: for part in device.partitions(): if not part_filter(part): continue details = ", ".join( labels.annotations(part) + labels.usage_labels(part)) rows.append((part, [ Text(labels.label(part, short=True)), (2, Text(details)), Text(humanize_size(part.size), align="right"), ])) else: rows.append((None, [ (4, Color.info_minor(Text(", ".join(labels.usage_labels(device))))) ])) return rows
def __init__(self, model, controller): self.model = model self.controller = controller cancel = cancel_btn(_("Cancel"), on_press=self.cancel) disks = [] for disk in self.model.all_disks(): label = "%-42s %s" % (disk.label, humanize_size( disk.size).rjust(9)) if disk.size >= model.lower_size_limit: disk_btn = forward_btn(label, on_press=self.choose_disk, user_arg=disk) else: disk_btn = Color.info_minor(Text(" " + label)) disks.append(disk_btn) body = Pile([ ('pack', Text("")), ('pack', Padding.center_70(Text(_("Choose the disk to install to:")))), ('pack', Text("")), Padding.center_70(ListBox(disks)), ('pack', Text("")), ('pack', button_pile([cancel])), ('pack', Text("")), ]) super().__init__(body)
def _build_iface_selection(self): log.debug('bond: _build_iface_selection') items = [Text("INTERFACE SELECTION")] all_iface_names = self.model.get_all_interface_names() avail_ifnames = [ iface for iface in all_iface_names if not self.model.iface_is_bonded(iface) ] log.debug('available for bonding: {}'.format(avail_ifnames)) if len(avail_ifnames) == 0: log.debug('Nothing available...') return Pile([Color.info_minor(Text("No available interfaces."))]) for ifname in avail_ifnames: device = self.model.get_interface(ifname) device_speed = self.model.iface_get_speed(ifname) iface_string = "{} {}, {}".format(device.ifname, device.ip4, device_speed) log.debug('bond: iface_string={}'.format(iface_string)) self.selected_ifaces.append(CheckBox(iface_string)) items += self.selected_ifaces log.debug('iface_select: items: {}'.format(items)) return Pile(items)
def __init__(self, model, controller, opts, loop): self.model = model self.controller = controller self.opts = opts self.loop = loop self.items = [] self.email = EmailEditor() self.error = Text("", align="center") self.progress = Text("", align="center") body = [ ('pack', Text("")), ListBox([ self._build_model_inputs(), Padding.line_break(""), Padding.center_79( Color.info_minor( Text("If you do not have an account, visit " "https://login.ubuntu.com to create one."))), Padding.line_break(""), Padding.center_90(Color.info_error(self.error)), Padding.center_90(self.progress), ]), ('pack', Pile([ ('pack', Text("")), button_pile(self._build_buttons()), ('pack', Text("")), ])), ] super().__init__(Pile(body))
def _build_model_inputs(self): rows = [] rows.append(TableRow([ Color.info_minor(Text(header)) for header in ["", "NAME", "TYPE", "NOTES / ADDRESSES", ""]])) for dev in self.model.get_all_netdevs(): rows.extend(self._rows_for_device(dev)) return rows
def _create(self): # Create the widget for a nic. This consists of a Pile containing a # table, an info line and a blank line. The first row of the table is # the one that can be focused and has a menu for manipulating the nic, # the other rows summarize its address config. # [ name type notes ▸ ] \ # address info | <- table # more address info / # mac / vendor info / model info # <blank line> actions = [] for action in NetDevAction: meth = getattr(self.parent, '_action_' + action.name) opens_dialog = getattr(meth, 'opens_dialog', False) if action in self.dev_info.enabled_actions: actions.append( (action.str(), True, (action, meth), opens_dialog)) menu = ActionMenu(actions) connect_signal(menu, 'action', self.parent._action, self) trows = [ make_action_menu_row([ Text("["), Text(self.dev_info.name), Text(self.dev_info.type), Text(self._notes(), wrap='clip'), menu, Text("]"), ], menu) ] + self._address_rows() self.table = TablePile(trows, colspecs=self.parent.device_colspecs, spacing=2) self.table.bind(self.parent.heading_table) if self.dev_info.type == "vlan": info = _("VLAN {id} on interface {link}").format( id=self.dev_info.vlan.id, link=self.dev_info.vlan.link) elif self.dev_info.type == "bond": info = _("bond master for {interfaces}").format( interfaces=', '.join(self.dev_info.bond.interfaces)) else: info = " / ".join([ self.dev_info.hwaddr, self.dev_info.vendor, self.dev_info.model, ]) return Pile([ ('pack', self.table), ('pack', Color.info_minor(Text(" " + info))), ('pack', Text("")), ])
def __init__(self, controller, netdev_infos): self.controller = controller self.dev_name_to_table = {} self.cur_netdev_names = [] self.error = Text("", align='center') self.device_colspecs = { 0: ColSpec(rpad=1), 3: ColSpec(min_width=15), 4: ColSpec(can_shrink=True, rpad=1), } self.heading_table = TablePile([ TableRow([ Color.info_minor(Text(header)) for header in [ "", "NAME", "TYPE", "NOTES", "", ] ]) ], spacing=2, colspecs=self.device_colspecs) self.device_pile = Pile([self.heading_table]) for dev_info in netdev_infos: self.new_link(dev_info) self._create_bond_btn = menu_btn(_("Create bond"), on_press=self._create_bond) bp = button_pile([self._create_bond_btn]) bp.align = 'left' rows = [ self.device_pile, bp, ] self.buttons = button_pile([ done_btn("TBD", on_press=self.done), # See _route_watcher back_btn(_("Back"), on_press=self.cancel), ]) self.bottom = Pile([ ('pack', self.buttons), ]) self.error_showing = False super().__init__( screen(rows=rows, buttons=self.bottom, focus_buttons=True, excerpt=_(self.excerpt)))
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 _summarize(self, prefix, device): if device.fs() is not None: fs = device.fs() text = prefix + _("formatted as {}").format(fs.fstype) if fs.mount(): text += _(", mounted at {}").format(fs.mount().path) else: text += _(", not mounted") else: text = prefix + _("unused {}").format(device.desc()) return TableRow([(2, Color.info_minor(Text(text)))])
def __init__(self, parent): self.parent = parent self.table = TableListBox([], spacing=2, colspecs={ 0: ColSpec(rpad=2), 1: ColSpec(rpad=2), 2: ColSpec(rpad=2), 3: ColSpec(rpad=2), }) self._no_zdev_content = Color.info_minor( Text(_("No zdev devices found."))) super().__init__(self.table)
def status(zdevinfo): if zdevinfo.failed: # for translator: failed is a zdev device status return Color.info_error(Text(_("failed"), align="center")) if zdevinfo.auto and zdevinfo.on: # for translator: auto is a zdev device status return Color.info_minor(Text(_("auto"), align="center")) if zdevinfo.pers and zdevinfo.on: # for translator: online is a zdev device status return Text(_("online"), align="center") return Text("", align="center")
def _device_widget(self, dev, netdev_i=None): # Create the widget for a nic. This consists of a Pile containing a # table, an info line and a blank line. The first row of the table is # the one that can be focused and has a menu for manipulating the nic, # the other rows summarize its address config. # [ name type notes ▸ ] \ # address info | <- table # more address info / # mac / vendor info / model info # <blank line> if netdev_i is None: netdev_i = len(self.cur_netdevs) self.cur_netdevs[netdev_i:netdev_i] = [dev] actions = [] for action in NetDevAction: meth = getattr(self, '_action_' + action.name) opens_dialog = getattr(meth, 'opens_dialog', False) if dev.supports_action(action): actions.append( (_(action.value), True, (action, meth), opens_dialog)) menu = ActionMenu(actions) connect_signal(menu, 'action', self._action, dev) trows = [ make_action_menu_row([ Text("["), Text(dev.name), Text(dev.type), Text(self._notes_for_device(dev), wrap='clip'), menu, Text("]"), ], menu) ] + self._address_rows_for_device(dev) table = TablePile(trows, colspecs=self.device_colspecs, spacing=2) self.dev_to_table[dev] = table table.bind(self.heading_table) if dev.type == "vlan": info = _("VLAN {id} on interface {link}").format(**dev.config) elif dev.type == "bond": info = _("bond master for {interfaces}").format( interfaces=', '.join(dev.config['interfaces'])) else: info = " / ".join( [dev.info.hwaddr, dev.info.vendor, dev.info.model]) return Pile([ ('pack', table), ('pack', Color.info_minor(Text(" " + info))), ('pack', Text("")), ])
def __init__(self, parent): self.parent = parent self.table = Table([], spacing=2, colspecs={ 0: ColSpec(can_shrink=True), 1: ColSpec(min_width=9), }) self._no_mounts_content = Color.info_minor( Text(_("No disks or partitions mounted."))) super().__init__(self.table) self.refresh_model_inputs()
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, signal, error_message): suggestion = "Check the installer log files for details and try again" self.signal = signal self.body = [ Padding.center_79(Text(error_message)), Padding.line_break(""), Padding.center_79(Color.info_minor(Text(suggestion))), Padding.line_break(""), Padding.line_break(""), Padding.center_79(self._build_buttons()) ] super().__init__(ListBox(self.body))
def make_body(self): body = super().make_body() if self.partition is not None: btn = delete_btn(_("Delete"), on_press=self.delete) if self.partition.flag == "boot": btn = WidgetDisable(Color.info_minor(btn.original_widget)) body.extend([ Text(""), button_pile([btn]), ]) pass return body
def _rows_for_device(self, dev, netdev_i=None): if netdev_i is None: netdev_i = len(self.cur_netdevs) rows = [] name, typ, addresses = self._cells_for_device(dev) actions = [] for action in NetDevAction: meth = getattr(self, '_action_' + action.name) opens_dialog = getattr(meth, 'opens_dialog', False) if dev.supports_action(action): actions.append( (_(action.value), True, (action, meth), opens_dialog)) menu = ActionMenu(actions) connect_signal(menu, 'action', self._action, dev) row = make_action_menu_row([ Text("["), Text(name), Text(typ), Text(addresses, wrap='clip'), menu, Text("]"), ], menu) self.dev_to_row[dev] = row.base_widget self.cur_netdevs[netdev_i:netdev_i] = [dev] rows.append(row) if dev.type == "vlan": info = _("VLAN {id} on interface {link}").format( **dev.config) elif dev.type == "bond": info = _("bond master for {}").format( ', '.join(dev.config['interfaces'])) else: info = " / ".join([ dev.info.hwaddr, dev.info.vendor, dev.info.model]) rows.append(Color.info_minor(TableRow([ Text(""), (4, Text(info)), Text("")]))) rows.append(Color.info_minor(TableRow([(4, Text(""))]))) return rows
def __init__(self, controller, systems): self.controller = controller heading_table = TablePile([ TableRow([ Color.info_minor(Text(header)) for header in ["LABEL", "MODEL", "PUBLISHER", ""] ]) ], spacing=2) trows = [] systems = sorted( systems, key=lambda s: (s.brand.display_name, s.model.display_name, s.current, s.label)) for s in systems: actions = [] log.debug('actions: %s', s.actions) for act in sorted(s.actions, key=by_preferred_action_type): actions.append( Action(label=act.title.capitalize(), value=act, enabled=True)) menu = ActionMenu(actions) connect_signal(menu, 'action', self._system_action, s) srow = make_action_menu_row([ Text(s.label), Text(s.model.display_name), Text(s.brand.display_name), Text("(installed)" if s.current else ""), menu, ], menu) trows.append(srow) systems_table = TablePile(trows, spacing=2) systems_table.bind(heading_table) rows = [ Pile([heading_table, systems_table]), ] buttons = [] if controller.model.current is not None: # back to options of current system buttons.append(back_btn("BACK", on_press=self.back)) super().__init__( controller.model.current, screen(rows=rows, buttons=button_pile(buttons), focus_buttons=False, excerpt=self.excerpt))
def _build_model_inputs(self): self.heading_table = TablePile([ TableRow([ Color.info_minor(Text(header)) for header in [ "", "NAME", "TYPE", "NOTES", "", ] ]) ], spacing=2, colspecs=self.device_colspecs) rows = [self.heading_table] for dev in self.model.get_all_netdevs(): rows.append(self._device_widget(dev)) return rows
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 _build_disk_selection(self, section): log.debug('bcache: _build_disk_selection, section:' + section) items = [Text(section + " DISK SELECTION")] avail_devs = self._get_available_devs(section) if len(avail_devs) == 0: return items.append( [Color.info_minor(Text("No available disks."))]) selector = Selector(avail_devs) self.selected_disks[section] = selector items.append(Color.string_input(selector)) return Pile(items)