1
0
mirror of https://github.com/gryf/wicd.git synced 2025-12-21 05:18:02 +01:00

curses/curses_misc.py: ADDED. Various urwid classes that I use throughout the program.

curses/prefs_curses.py: Frontend is complete.  However, it is still missing buttons and the ability to save information.  Removed code that is now in curses_misc.py.
curses/wicd-curses.py: Removed code that is now in curses_misc.py.  Tweaked the visuals a little bit.
curses/README: Preferences configuration is a WIP now.
curses/TODO: A combo box has been implemented in curses_misc.py, so that part has been removed.  Also added a part about making a man page.
This commit is contained in:
Andrew Psaltis
2008-12-27 00:18:03 -05:00
parent 5d11be8eae
commit 0d4811b129
5 changed files with 321 additions and 139 deletions

View File

@@ -7,7 +7,7 @@ work on it more and figure out how to use distutils.
Right now, it lists current available networks, and whether you are connected Right now, it lists current available networks, and whether you are connected
to anything or not, all of which is updated in real time. It will actually to anything or not, all of which is updated in real time. It will actually
connect you to networks now. Configuring them is something soon to come soon. connect you to networks now. Configuring them is a work in progress.
Controls: Controls:
@@ -23,4 +23,4 @@ ESC or Q: Quit dialog without saving information (if present)
~nacl ~NaCl

View File

@@ -1,7 +1,7 @@
Things to do (in no particular order): Things to do (in no particular order):
* Make a settings dialog * Make a settings dialog -- Finish the backend. The frontend is pretty much
* Implement something that resembles a combo box in urwid done.
* Make a network config dialog * Make a network config dialog
* Make an about dialog * Make an about dialog
* Implement a keyhandler function for the overall frame * Implement a keyhandler function for the overall frame
@@ -9,6 +9,7 @@ Things to do (in no particular order):
* Make color schemes customizable * Make color schemes customizable
* Integrate this with the my local copy of the experimental branch * Integrate this with the my local copy of the experimental branch
* Give some indication of activity during the connection process * Give some indication of activity during the connection process
* Make a man page for the UI.
Oh, and most importantly: Oh, and most importantly:

176
curses/curses_misc.py Normal file
View File

@@ -0,0 +1,176 @@
#!/usr/bin/env python
# -* coding: utf-8 -*-
""" curses_misc.py: Module for various widgets that are used throughout
wicd-curses.
"""
# Copyright (C) 2008 Andrew Psaltis
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
import urwid
# My savior. :-)
# Although I could have made this myself pretty easily, just want to give credit where
# its due.
# http://excess.org/urwid/browser/contrib/trunk/rbreu_filechooser.py
class SelText(urwid.Text):
"""A selectable text widget. See urwid.Text."""
def selectable(self):
"""Make widget selectable."""
return True
def keypress(self, size, key):
"""Don't handle any keys."""
return key
class ToggleEdit(urwid.WidgetWrap):
"""A edit that can be rendered unselectable by somethhing like a checkbox"""
def __init__(self, caption='', state=True,
attr=('editbx','editfc'),attrnsens='body'):
"""
caption : the Edit's caption
state : the Edit's current sensitivity
attr : tuple of (attr_no_focus, attr_focus)
attrnsens: attr to use when not sensitive
"""
edit = urwid.Edit(caption)
curattr = attr[0] if state == True else attrnsens
w = urwid.AttrWrap(edit,curattr,attr[1])
self.sensitive=state
self.__super.__init__(w)
# Kinda like the thing in PyGTK
def set_sensitive(self,state):
self.sensitive=state
if state:
self._w.set_attr('editbx')
else:
self._w.set_attr('body')
# If we aren't sensitive, don't be selectab;e
def selectable(self):
return self.sensitive
# Do what an edit does with keys
def keypress(self,size,key):
return self._w.keypress(size,key)
# Would seem to complicate things a little bit, but could be very useful. ^_^
# Not used yet. Will be used very shortly, as a superclass of some future
# overlays
class TabColumns(urwid.WidgetWrap):
def __init__(self):
pass
def selectable(self):
return True
def keypress(self,size,key):
pass
# A "combo box" of SelTexts
# I based this off of the code found here:
# http://excess.org/urwid/browser/contrib/trunk/rbreu_menus.py
class ComboText(urwid.WidgetWrap):
"""A ComboBox of text objects"""
class ComboSpace(urwid.WidgetWrap):
"""The actual menu-like space that comes down from the ComboText"""
def __init__(self,list,body,ui,show_first=0,pos=(0,0),attr=('body','focus')):
"""
body : parent widget
list : stuff to include in the combobox
ui : the screen
show_first: index of the element in the list to pick first
pos : a tuple of (row,col) where to put the list
attr : a tuple of (attr_no_focus,attr_focus)
"""
#Calculate width and height of the menu widget:
height = len(list)
width = 0
for entry in list:
if len(entry) > width:
width = len(entry)
content = [urwid.AttrWrap(SelText(" " + w), attr[0], attr[1])
for w in list]
self._listbox = urwid.ListBox(content)
overlay = urwid.Overlay(self._listbox, body, ('fixed left', pos[0]),
width + 2, ('fixed top', pos[1]), height)
self.__super.__init__(overlay)
def show(self,ui,display):
dim = ui.get_cols_rows()
keys = True
#Event loop:
while True:
if keys:
ui.draw_screen(dim, self.render(dim, True))
keys = ui.get_input()
if "window resize" in keys:
dim = ui.get_cols_rows()
if "esc" in keys:
return None
if "enter" in keys:
(wid,pos) = self._listbox.get_focus()
(text,attr) = wid.get_text()
return text
for k in keys:
#Send key to underlying widget:
self._w.keypress(dim, k)
#def get_size(self):
def __init__(self,label,list,body,ui,row = 0,show_first=0,attr=('body','focus')):
"""
label : bit of text that preceeds the combobox
list : stuff to include in the combobox
body : parent widget
ui : the screen
row : where this object is to be found onscreen
show_first: index of the element in the list to pick first
"""
self.label = urwid.Text(label)
str,trash = self.label.get_text()
self.cbox = urwid.AttrWrap(SelText(list[show_first]),attr[0],attr[1])
self.overlay = self.ComboSpace(list,body,ui,show_first,pos=(len(str)+1,row))
# Unicode will kill me sooner or later. ^_^
w = urwid.Columns([('fixed',len(str),self.label),self.cbox,('fixed',3,urwid.Text("vvv"))],dividechars=1)
self.__super.__init__(w)
# We need this to control the keypress
self.body = body
self.ui = ui
# If we press space or enter, be a combo box!
def keypress(self,size,key):
if key == ' ' or key == 'enter':
retval = self.overlay.show(self.ui,self.body)
if retval != None:
self.cbox.set_w(SelText(retval))
return self._w.keypress(size,key)
# Most obvious thing ever. :-)
def selectable(self):
return True

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env python #!/usr/bin/env python
# Copyright (C) 2008 Andrew Psaltis # Copyright (C) 2008 Andrew Psaltis
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@@ -19,71 +20,14 @@
import urwid import urwid
from wicd import misc from wicd import misc
from curses_misc import SelText,ToggleEdit,ComboText
# Will work for now, I guess. # Will work for now, I guess.
language = misc.get_language_list_gui() language = misc.get_language_list_gui()
class SelText(urwid.Text):
"""A selectable text widget. See urwid.Text."""
def selectable(self):
"""Make widget selectable."""
return True
def keypress(self, size, key):
"""Don't handle any keys."""
return key
class ToggleEdit(urwid.WidgetWrap):
"""A edit that can be rendered unselectable by somethhing like a checkbox"""
def __init__(self, caption='', state=True,attr=('editbx','editfc'),attrnfoc='body'):
edit = urwid.Edit(caption)
curattr = attr[0] if state == True else attrnfoc
w = urwid.AttrWrap(edit,curattr,attr[1])
self.sensitive=state
self.__super.__init__(w)
def set_sensitive(self,state):
self.sensitive=state
if state:
self._w.set_attr('editbx')
else:
self._w.set_attr('body')
def selectable(self):
return self.sensitive
def keypress(self,size,key):
return self._w.keypress(size,key)
# Would seem to complicate things a little bit...
class TabColumns(urwid.WidgetWrap):
def __init__(self):
pass
def selectable(self):
return True
def keypress(self,size,key):
pass
# A "combo box" of SelTexts
class ComboText(urwid.WidgetWrap):
class ComboSpace(urwid.WidgetWrap):
def init(self,body,list,show_first=0,pos=(0,0)):
#Calculate width and height of the menu widget:
height = len(list)
width = 0
for entry in list:
if len(entry) > width:
width = len(entry)
self._listbox = urwid.ListBox(list)
overlay = urwid.Overlay(self._listbox, body, ('fixed left', pos[0]),
width + 2, ('fixed top', pos[1]), height)
def init(self,list,show_first=0):
pass
class PrefOverlay(urwid.WidgetWrap): class PrefOverlay(urwid.WidgetWrap):
def __init__(self,body,pos): def __init__(self,body,pos,ui):
self.ui = ui
# We are on a VT100, I presume. # We are on a VT100, I presume.
width = 80 width = 80
height = 20 height = 20
@@ -91,7 +35,7 @@ class PrefOverlay(urwid.WidgetWrap):
header0_t = language["gen_settings"] header0_t = language["gen_settings"]
header1_t = language["ext_programs"] header1_t = language["ext_programs"]
header2_t = language["advanced_settings"] header2_t = language["advanced_settings"]
self.header0 = urwid.AttrWrap(SelText(header0_t),'body','focus') self.header0 = urwid.AttrWrap(SelText(header0_t),'tab active','focus')
self.header1 = urwid.AttrWrap(SelText(header1_t),'body','focus') self.header1 = urwid.AttrWrap(SelText(header1_t),'body','focus')
self.header2 = urwid.AttrWrap(SelText(header2_t),'body','focus') self.header2 = urwid.AttrWrap(SelText(header2_t),'body','focus')
title = language['preferences'] title = language['preferences']
@@ -104,18 +48,20 @@ class PrefOverlay(urwid.WidgetWrap):
#### ####
# General Settings # General Settings
wired_t=('editcp',language['wired_interface']+':') net_cat_t = ('header','Network Interfaces')
wless_t=('editcp',language['wireless_interface']+':') wired_t = ('editcp',language['wired_interface']+': ')
global_dns_t=(language['use_global_dns']) wless_t = ('editcp',language['wireless_interface']+':')
search_dom_t= ('editcp','Search domain:') always_show_wired_t = 'Always show wired interface'
dns1_t = ('editcp','DNS server 1:')
dns2_t = ('editcp','DNS server 2:')
dns3_t = ('editcp','DNS server 3:')
always_show_wired_t = 'wired always on' #language['wired always on'] global_dns_cat_t = ('header','Global DNS Servers')
auto_reconnect_t = language['auto_reconnect'] global_dns_t = ('editcp',language['use_global_dns'])
search_dom_t = ('editcp',' Search domain:')
dns1_t = ('editcp',' DNS server 1: ')
dns2_t = ('editcp',' DNS server 2: ')
dns3_t = ('editcp',' DNS server 3: ')
#wired_autoconnect_header = 'Wired Autoconnect Setting'
wired_auto_cat_t= ('header','Wired Autoconnect Settings')
wired_auto_1_t = language['use_default_profile'] wired_auto_1_t = language['use_default_profile']
wired_auto_2_t = language['show_wired_list'] wired_auto_2_t = language['show_wired_list']
wired_auto_3_t = language['use_last_used_profile'] wired_auto_3_t = language['use_last_used_profile']
@@ -123,34 +69,51 @@ class PrefOverlay(urwid.WidgetWrap):
#### External Programs #### External Programs
automatic_t = language['wicd_auto_config'] automatic_t = language['wicd_auto_config']
dhcp_header = language["dhcp_client"] dhcp_header_t = ('header',language["dhcp_client"])
# Automatic # Automatic
dhcp1_t = 'dhclient' dhcp1_t = 'dhclient'
dhcp2_t = 'dhcpcd' dhcp2_t = 'dhcpcd'
dhcp3_t = 'pump' dhcp3_t = 'pump'
wired_detect_header = language["wired_detect"] wired_detect_header_t = ('header',language["wired_detect"])
wired1_t = 'ethtool' wired1_t = 'ethtool'
wired2_t = 'mii-tool' wired2_t = 'mii-tool'
route_table_header = language["route_flush"] route_table_header_t = ('header',language["route_flush"])
route1_t = 'ip' route1_t = 'ip'
route2_t = 'route' route2_t = 'route'
# Advanced Settings #### Advanced Settings
wpa_t=('editcp',language['wpa_supplicant_driver']+':') #wpa_t=('editcp',language['wpa_supplicant_driver']+':')
wpa_cat_t=('header','WPA_Supplicant')
wpa_t=('editcp','Driver:')
wpa_list = ['spam','double spam','triple spam','quadruple spam']
wpa_warn_t = ('important','You should almost always use wext as the WPA Supplicant Driver')
backend_cat_t = ('header',language['backend'])
backend_t = language['backend']+':'
backend_list = ['spam','double spam','triple spam','quadruple spam']
debug_cat_t = ('header','Debugging')
debug_mode_t = language['use_debug_mode'] debug_mode_t = language['use_debug_mode']
wless_cat_t = ('header','Wireless Interface')
use_dbm_t = language['display_type_dialog'] use_dbm_t = language['display_type_dialog']
# backend_sel_t =
auto_reconn_cat_t = ('header','Automatic Reconnect')
auto_reconn_t = 'Automatically reconnect on connection loss'
#### ####
#### UI Widgets #### UI Widgets
#### ####
# General Settings # General Settings
self.wpa_edit = urwid.AttrWrap(urwid.Edit(wpa_t),'editbx','editfc') self.net_cat = urwid.Text(net_cat_t)
self.wired_iface = urwid.AttrWrap(urwid.Edit(wired_t),'editbx','editfc') self.wired_iface = urwid.AttrWrap(urwid.Edit(wired_t),'editbx','editfc')
self.wless_iface = urwid.AttrWrap(urwid.Edit(wless_t),'editbx','editfc') self.wless_iface = urwid.AttrWrap(urwid.Edit(wless_t),'editbx','editfc')
self.global_dns_cat = urwid.Text(global_dns_cat_t)
global_dns_state = False global_dns_state = False
self.global_dns = urwid.CheckBox(global_dns_t,global_dns_state, self.global_dns = urwid.CheckBox(global_dns_t,global_dns_state,
on_state_change=self.global_dns_trigger) on_state_change=self.global_dns_trigger)
@@ -160,59 +123,110 @@ class PrefOverlay(urwid.WidgetWrap):
self.dns3 = ToggleEdit(dns3_t,global_dns_state) self.dns3 = ToggleEdit(dns3_t,global_dns_state)
self.always_show_wired = urwid.CheckBox(always_show_wired_t) self.always_show_wired = urwid.CheckBox(always_show_wired_t)
self.auto_reconnect = urwid.CheckBox(auto_reconnect_t)
self.debug_mode = urwid.CheckBox(debug_mode_t)
self.use_dbm = urwid.CheckBox(use_dbm_t)
wired_auto_l = [] wired_auto_l = []
self.wired_auto_1_r = urwid.RadioButton(wired_auto_l,wired_auto_1_t) self.wired_auto_cat = urwid.Text(wired_auto_cat_t)
self.wired_auto_2_r = urwid.RadioButton(wired_auto_l,wired_auto_2_t) self.wired_auto_1 = urwid.RadioButton(wired_auto_l,wired_auto_1_t)
self.wired_auto_3_r = urwid.RadioButton(wired_auto_l,wired_auto_3_t) self.wired_auto_2 = urwid.RadioButton(wired_auto_l,wired_auto_2_t)
generalPile = urwid.Pile([ self.wired_auto_3 = urwid.RadioButton(wired_auto_l,wired_auto_3_t)
self.wired_iface,#self._blank, generalPile = urwid.Pile([self.net_cat,
self.wless_iface,self._blank, self.wless_iface,#self._blank,
self.wired_iface,
self.always_show_wired,self._blank,
self.global_dns_cat,
self.global_dns,#self._blank, self.global_dns,#self._blank,
self.search_dom, self.search_dom,
self.dns1,self.dns2,self.dns3,self._blank, self.dns1,self.dns2,self.dns3,self._blank,
self.always_show_wired, self.wired_auto_cat,
self.auto_reconnect, self.wired_auto_1,
self.debug_mode, self.wired_auto_2,
self.use_dbm,self._blank, self.wired_auto_3
self.wired_auto_1_r,
self.wired_auto_2_r,
self.wired_auto_3_r
]) ])
#externalPile = urwid.Pile() #### External Programs tab
automatic_t = language['wicd_auto_config']
self.dhcp_header = urwid.Text(dhcp_header_t)
dhcp_l = []
# Automatic
self.dhcp0 = urwid.RadioButton(dhcp_l,automatic_t)
self.dhcp1 = urwid.RadioButton(dhcp_l,dhcp1_t)
self.dhcp2 = urwid.RadioButton(dhcp_l,dhcp2_t)
self.dhcp3 = urwid.RadioButton(dhcp_l,dhcp3_t)
wired_l = []
self.wired_detect_header = urwid.Text(wired_detect_header_t)
self.wired0 = urwid.RadioButton(wired_l,automatic_t)
self.wired1 = urwid.RadioButton(wired_l,wired1_t)
self.wired2 = urwid.RadioButton(wired_l,wired2_t)
route_l = []
self.route_table_header = urwid.Text(route_table_header_t)
self.route0 = urwid.RadioButton(route_l,automatic_t)
self.route1 = urwid.RadioButton(route_l,route1_t)
self.route2 = urwid.RadioButton(route_l,route2_t)
externalPile = urwid.Pile([self.dhcp_header,
self.dhcp0,self.dhcp1,self.dhcp2,self.dhcp3,
self._blank,
self.wired_detect_header,
self.wired0,self.wired1,self.wired2,
self._blank,
self.route_table_header,
self.route0,self.route1,self.route2
])
#### Advanced settings
self.wpa_cat = urwid.Text(wpa_cat_t)
self.wpa_cbox = ComboText(wpa_t,wpa_list,self,ui,4)
self.wpa_warn = urwid.Text(wpa_warn_t)
# Advanced Settings self.backend_cat = urwid.Text(backend_cat_t)
# WPA Supplicant: Combo Box self.backend_cbox = ComboText(backend_t,backend_list,self,ui,8)
# Backend: Combo box
# Debugging
# Enable debug mode
# Wireless Interface
# Use DBM to measure signal strength
advancedPile = urwid.Pile([self.wpa_edit,self._blank]) self.debug_cat = urwid.Text(debug_cat_t)
self.debug_mode = urwid.CheckBox(debug_mode_t)
self.columns = urwid.Columns([('fixed',len(header0_t),self.header0),('fixed',len(header1_t),self.header1),urwid.Text(('header',title),align='right')],dividechars=1) self.wless_cat = urwid.Text(wless_cat_t)
self.use_dbm = urwid.CheckBox(use_dbm_t)
self.auto_reconn_cat = urwid.Text(auto_reconn_cat_t)
self.auto_reconn = urwid.CheckBox(auto_reconn_t)
advancedPile = urwid.Pile([self.wpa_cat,
self.wpa_cbox,self.wpa_warn,self._blank,
self.backend_cat,
self.backend_cbox,self._blank,
self.debug_cat,
self.debug_mode, self._blank,
self.wless_cat,
self.use_dbm, self._blank,
self.auto_reconn_cat,
self.auto_reconn])
self.tab_map = {self.header0 : generalPile, self.tab_map = {self.header0 : generalPile,
self.header1 : advancedPile, self.header1 : externalPile,
self.header2 : advancedPile} self.header2 : advancedPile}
self.active_tab = self.header0
self.columns = urwid.Columns([('fixed',len(header0_t),self.header0),
('fixed',len(header1_t),self.header1),
('fixed',len(header2_t),self.header2),
urwid.Text(('header',title),align='right')],
dividechars=1)
content = [self.columns,generalPile] content = [self.columns,generalPile]
#self._label = urwid.AttrWrap(SelText(titles),attr[0],attr[1]) #self._label = urwid.AttrWrap(SelText(titles),attr[0],attr[1])
self.walker = urwid.SimpleListWalker(content) self.walker = urwid.SimpleListWalker(content)
self._listbox = urwid.ListBox(self.walker) self._listbox = urwid.ListBox(self.walker)
self._boxadap = urwid.BoxAdapter
#self._linebox = urwid.LineBox(self._listbox) #self._linebox = urwid.LineBox(self._listbox)
overlay = urwid.Overlay(self._listbox, body, ('fixed left', pos[0]), overlay = urwid.Overlay(self._listbox, body, ('fixed left', pos[0]),
width + 2, ('fixed top', pos[1]), height) width + 2, ('fixed top', pos[1]), height)
self.__super.__init__(overlay) self.__super.__init__(overlay)
def global_dns_trigger(self,check_box,new_state,user_data=None): def global_dns_trigger(self,check_box,new_state,user_data=None):
for w in self.search_dom,self.dns1,self.dns2,self.dns3: for w in self.search_dom,self.dns1,self.dns2,self.dns3:
w.set_sensitive(new_state) w.set_sensitive(new_state)
@@ -223,6 +237,9 @@ class PrefOverlay(urwid.WidgetWrap):
if wid is self.columns: if wid is self.columns:
lw = self._listbox.body lw = self._listbox.body
lw.pop(1) lw.pop(1)
self.active_tab.set_attr('body')
self.columns.get_focus().set_attr('tab active')
self.active_tab = self.columns.get_focus()
lw.append(self.tab_map[self.columns.get_focus()]) lw.append(self.tab_map[self.columns.get_focus()])
self._listbox.body = lw self._listbox.body = lw

View File

@@ -55,6 +55,7 @@ from wicd import dbusmanager
import sys import sys
# Curses UIs for other stuff # Curses UIs for other stuff
from curses_misc import SelText
import prefs_curses import prefs_curses
from prefs_curses import PrefOverlay from prefs_curses import PrefOverlay
@@ -109,22 +110,6 @@ class wrap_exceptions:
return wrap_exceptions return wrap_exceptions
# My savior. :-)
# Although I could have made this myself pretty easily, just want to give credit where
# its due.
# http://excess.org/urwid/browser/contrib/trunk/rbreu_filechooser.py
class SelText(urwid.Text):
"""A selectable text widget. See urwid.Text."""
def selectable(self):
"""Make widget selectable."""
return True
def keypress(self, size, key):
"""Don't handle any keys."""
return key
######################################## ########################################
##### SUPPORT FUNCTIONS ##### SUPPORT FUNCTIONS
######################################## ########################################
@@ -430,7 +415,7 @@ class appGUI():
# Prevents automatic reconnecting if that option is enabled # Prevents automatic reconnecting if that option is enabled
daemon.SetForcedDisconnect(True) daemon.SetForcedDisconnect(True)
if "P" in keys: if "P" in keys:
dialog = PrefOverlay(self.frame,(0,1)) dialog = PrefOverlay(self.frame,(0,1),ui)
dialog.run(ui,self.size,self.frame) dialog.run(ui,self.size,self.frame)
for k in keys: for k in keys:
if k == "window resize": if k == "window resize":
@@ -486,17 +471,20 @@ def main():
# Color scheme. # Color scheme.
# Other potential color schemes can be found at: # Other potential color schemes can be found at:
# http://excess.org/urwid/wiki/RecommendedPalette # http://excess.org/urwid/wiki/RecommendedPalette
# Note: the current palette below is optimized for the linux console.
# For example, this will look like crap on a default-colored XTerm.
# NB: To find current terminal background use variable COLORFGBG
ui.register_palette([ ui.register_palette([
('body','light gray','black'), ('body','light gray','default'),
('focus','dark magenta','light gray'), ('focus','dark magenta','light gray'),
('header','light blue','black'), ('header','light blue','default'),
('important','light red','black'), ('important','light red','default'),
('connected','dark green','black'), ('connected','dark green','default'),
('connected focus','black','dark green'), ('connected focus','default','dark green'),
# I'll be needing these soon, so I'll leave them here for now. ('editcp', 'default', 'default', 'standout'),
('editcp', 'light gray', 'black', 'standout'),
('editbx', 'light gray', 'dark blue'), ('editbx', 'light gray', 'dark blue'),
('editfc', 'white','dark blue', 'bold') ]) ('editfc', 'white','dark blue', 'bold'),
('tab active','dark green','light gray')])
# This is a wrapper around a function that calls another a function that is a # This is a wrapper around a function that calls another a function that is a
# wrapper around a infinite loop. Fun. # wrapper around a infinite loop. Fun.
ui.run_wrapper(run) ui.run_wrapper(run)