Matthew Mosesohn
2013-08-12 17:11:31 UTC
Hi all,
I'm trying to make a useful tool that has 2 main columns: menu and
module. Modules get loaded dynamically from a folder and return an
urwid.Listbox with an urwid.SimpleListWalker inside it. The menu lets
me navigate between different modules.
I can get information displayed once, but I want to be able to click a
RadioButton and display information relevant to that object.
In this specific case, I want to display networking information. Here
is some relevant code:
---- start code ----
def getNetwork(self):
"""Uses netifaces module to get addr, broadcast, netmask about
network interfaces"""
import netifaces
for iface in netifaces.interfaces():
if 'lo' in iface or 'vir' in iface:
#if 'lo' in iface or 'vir' in iface or 'vbox' in iface:
continue
#print netifaces.ifaddresses(iface)
#print iface, netifaces.ifaddresses(iface)[netifaces.AF_INET][0]
self.netsettings.update({iface:
netifaces.ifaddresses(iface)[netifaces.AF_INET][0]})
def get_default_gateway_linux(self):
"""Read the default gateway directly from /proc."""
with open("/proc/net/route") as fh:
for line in fh:
fields = line.strip().split()
if fields[1] != '00000000' or not int(fields[3], 16) & 2:
continue
return socket.inet_ntoa(struct.pack("<L", int(fields[2], 16)))
def radioSelect(self, obj, other):
self.activeiface = obj.get_label()
self.gateway=self.get_default_gateway_linux()
self.getNetwork()
self.screenUI() ### called later to create text objects
self.listbox_content=[self.net_text1, self.net_text1,
self.net_text2, self.net_text3,
self.net_text4, self.net_choices]
self.listwalker[:]=[self.parent.cols]
...
def TextField(keyword, label, width, default_value=None):
"""Returns an Urwid Edit object"""
edit_obj = urwid.Edit(('important', label.ljust(width)), default_value)
wrapped_obj = urwid.AttrWrap(edit_obj, 'editbx', 'editfc')
return wrapped_obj
def ChoicesGroup(self, choices, default_value=None):
"""Returns a horizontal Urwid GridFlow with radio choices on one line."""
rb_group = []
for txt in choices:
if default_value == None:
is_default = "first True"
else:
is_default = True if txt == default_value else False
radio_button = urwid.AttrWrap(urwid.RadioButton(rb_group,
txt, is_default, on_state_change=self.radioSelect),
'buttn','buttnf')
wrapped_choices = urwid.Padding(urwid.GridFlow(rb_group, 13, 3, 1,
'left'), left=4, right=3, min_width=13)
return wrapped_choices
...
self.net_text1 = TextLabel("Current network settings for %s" %
self.activeiface)
self.net_text2 = TextLabel("IP address: %s" %
self.netsettings[self.activeiface]['addr'])
self.net_text3 = TextLabel("Netmask: %s" %
self.netsettings[self.activeiface]['netmask'])
self.net_text4 = TextLabel("Default gateway: %s" % (self.gateway))
self.net_choices = ChoicesGroup(self,
self.netsettings.keys(),self.activeiface)
self.listbox_content=[self.net_text1, self.net_text2, self.net_text3,
self.net_text4, self.net_choices]
self.listwalker=urwid.SimpleListWalker(self.listbox_content)
screen = urwid.ListBox(self.listwalker)
return screen
--- end code ---
I apologize for skimming base level code and my wrapper functions, but
it keeps the modules lighter and more readable. I can get changes
made if I manually set_text on each Text object, but it's quite manual
to do this. I would much rather call screenUI() and let every object
get recreated and rendered.
Updating the listwalker of the top level parent works if I change
objects in the very topmost layer, but if I change any listwalker that
isn't the very top one, I don't get any results. I wonder if it is a
bug or there are other ways to inform Urwid I want to redraw the
screen with my new widgets.
Does anyone have some great suggestions or examples where you can
update a nested listwalker?
-Matthew Mosesohn
I'm trying to make a useful tool that has 2 main columns: menu and
module. Modules get loaded dynamically from a folder and return an
urwid.Listbox with an urwid.SimpleListWalker inside it. The menu lets
me navigate between different modules.
I can get information displayed once, but I want to be able to click a
RadioButton and display information relevant to that object.
In this specific case, I want to display networking information. Here
is some relevant code:
---- start code ----
def getNetwork(self):
"""Uses netifaces module to get addr, broadcast, netmask about
network interfaces"""
import netifaces
for iface in netifaces.interfaces():
if 'lo' in iface or 'vir' in iface:
#if 'lo' in iface or 'vir' in iface or 'vbox' in iface:
continue
#print netifaces.ifaddresses(iface)
#print iface, netifaces.ifaddresses(iface)[netifaces.AF_INET][0]
self.netsettings.update({iface:
netifaces.ifaddresses(iface)[netifaces.AF_INET][0]})
def get_default_gateway_linux(self):
"""Read the default gateway directly from /proc."""
with open("/proc/net/route") as fh:
for line in fh:
fields = line.strip().split()
if fields[1] != '00000000' or not int(fields[3], 16) & 2:
continue
return socket.inet_ntoa(struct.pack("<L", int(fields[2], 16)))
def radioSelect(self, obj, other):
self.activeiface = obj.get_label()
self.gateway=self.get_default_gateway_linux()
self.getNetwork()
self.screenUI() ### called later to create text objects
self.listbox_content=[self.net_text1, self.net_text1,
self.net_text2, self.net_text3,
self.net_text4, self.net_choices]
self.listwalker[:]=[self.parent.cols]
...
def TextField(keyword, label, width, default_value=None):
"""Returns an Urwid Edit object"""
edit_obj = urwid.Edit(('important', label.ljust(width)), default_value)
wrapped_obj = urwid.AttrWrap(edit_obj, 'editbx', 'editfc')
return wrapped_obj
def ChoicesGroup(self, choices, default_value=None):
"""Returns a horizontal Urwid GridFlow with radio choices on one line."""
rb_group = []
for txt in choices:
if default_value == None:
is_default = "first True"
else:
is_default = True if txt == default_value else False
radio_button = urwid.AttrWrap(urwid.RadioButton(rb_group,
txt, is_default, on_state_change=self.radioSelect),
'buttn','buttnf')
wrapped_choices = urwid.Padding(urwid.GridFlow(rb_group, 13, 3, 1,
'left'), left=4, right=3, min_width=13)
return wrapped_choices
...
self.net_text1 = TextLabel("Current network settings for %s" %
self.activeiface)
self.net_text2 = TextLabel("IP address: %s" %
self.netsettings[self.activeiface]['addr'])
self.net_text3 = TextLabel("Netmask: %s" %
self.netsettings[self.activeiface]['netmask'])
self.net_text4 = TextLabel("Default gateway: %s" % (self.gateway))
self.net_choices = ChoicesGroup(self,
self.netsettings.keys(),self.activeiface)
self.listbox_content=[self.net_text1, self.net_text2, self.net_text3,
self.net_text4, self.net_choices]
self.listwalker=urwid.SimpleListWalker(self.listbox_content)
screen = urwid.ListBox(self.listwalker)
return screen
--- end code ---
I apologize for skimming base level code and my wrapper functions, but
it keeps the modules lighter and more readable. I can get changes
made if I manually set_text on each Text object, but it's quite manual
to do this. I would much rather call screenUI() and let every object
get recreated and rendered.
Updating the listwalker of the top level parent works if I change
objects in the very topmost layer, but if I change any listwalker that
isn't the very top one, I don't get any results. I wonder if it is a
bug or there are other ways to inform Urwid I want to redraw the
screen with my new widgets.
Does anyone have some great suggestions or examples where you can
update a nested listwalker?
-Matthew Mosesohn