From 23297b3f7ed52a326c06e9d5ae3fabcc36b3696f Mon Sep 17 00:00:00 2001 From: Andrew Psaltis Date: Sat, 13 Dec 2008 13:29:07 -0500 Subject: [PATCH 01/20] Let there be light! curses/wicd-curses.py: ADDED (new (very incomplete) curses UI) curses/README: ADDED (Simple README. Hope it says enough for the moment) curses/TODO: ADDED (Simple, incomplete, TODO list) --- curses/README | 13 ++ curses/TODO | 15 ++ curses/wicd-curses.py | 339 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 367 insertions(+) create mode 100644 curses/README create mode 100644 curses/TODO create mode 100644 curses/wicd-curses.py diff --git a/curses/README b/curses/README new file mode 100644 index 0000000..fae294b --- /dev/null +++ b/curses/README @@ -0,0 +1,13 @@ +This is a CURSES-based client for wicd. It is designed to imitate wicd-client +as much as can be done with a console-based interface. It is written using the +Urwid (http://excess.org/urwid) toolkit, and thus requires it. + +That's all there is to it, really. It's not going to install itself until I +work on it more and figure out how to use distutils. + +Right now, it only lists current available networks, and whether you are +connected to anything or not, and that is updated in real time. It does not +actually connect you to anything yet. I'll get on that when I have more free +time than I do now (which will be soon). + +~ ampsaltis diff --git a/curses/TODO b/curses/TODO new file mode 100644 index 0000000..0f9fd37 --- /dev/null +++ b/curses/TODO @@ -0,0 +1,15 @@ +Things to do (in no particular order): + +* Integrate a glib mainloop into the UI so that it can be aware of whether + we're connecting or not by some means. +* Implement the ability to connect/disconnect to/from stuff. + Yeah, I know that it's important... +* Make a settings dialog + * Implement something that resembles a combo box in urwid +* Make a network config dialog + +Oh, and most importantly: + +* Tell people how they can quit the app (F8, until I do all of that stuff) :-) + +Anything else? That's all I can think of now. diff --git a/curses/wicd-curses.py b/curses/wicd-curses.py new file mode 100644 index 0000000..f35d4cf --- /dev/null +++ b/curses/wicd-curses.py @@ -0,0 +1,339 @@ +#!/usr/bin/env python + +""" wicd-curses -- a (cursed-based) console interface to wicd + +Provides the a console UI for wicd, so that people with broken X servers can +at least get a network connection. Or for those who don't like using X. :-) + +""" + +# 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. + +""" + This contains/will contain A LOT of code from the other parts of wicd. + + This is probably due to the fact that I did not really know what I was doing + when I started writing this. It works, so I guess that's all that matters. + + Comments, criticisms, patches all welcome! +""" + +# UI stuff +#import urwid.raw_display +import urwid.curses_display +import urwid + +# DBus communication stuff +import dbus +import dbus.service +# It took me a while to figure out that I have to use this. +import gobject + +# Other stuff +import wicd.misc as misc +#import sys + +# Both of these are not currently used, until I can best resolve how to use them +#import functions +#from functions import language #,Functions + +# Translations for the text that people will see... as of yet. This code is +# already found in the gui.py file +# Stick into own ui_common file? +_ = misc.get_gettext() +language = {} +language['connected_to_wireless'] = _('Connected to $A at $B (IP: $C)') +language['connected_to_wired'] = _('Connected to wired network (IP: $A)') +language['not_connected'] = _('Not connected') + +# I might not need this... but I'm not sure so much yet. +if getattr(dbus, 'version', (0, 0, 0)) < (0, 80, 0): + import dbus.glib +else: + from dbus.mainloop.glib import DBusGMainLoop + DBusGMainLoop(set_as_default=True) + +# Look familiar? These two functions are clones of functions found in wicd's +# gui.py file, except that now set_status is a function passed to them. +def check_for_wired(wired_ip,set_status): + """ Determine if wired is active, and if yes, set the status. """ + if wired_ip and wired.CheckPluggedIn(): + set_status(language['connected_to_wired'].replace('$A',wired_ip)) + return True + else: + return False + +def check_for_wireless(iwconfig, wireless_ip, set_status): + """ Determine if wireless is active, and if yes, set the status. """ + if not wireless_ip: + return False + + network = wireless.GetCurrentNetwork(iwconfig) + if not network: + return False + + network = str(network) + if daemon.GetSignalDisplayType() == 0: + strength = wireless.GetCurrentSignalStrength(iwconfig) + else: + strength = wireless.GetCurrentDBMStrength(iwconfig) + + if strength is None: + return False + strength = str(strength) + ip = str(wireless_ip) + set_status(language['connected_to_wireless'].replace + ('$A', network).replace + ('$B', daemon.FormatSignalForPrinting(strength)).replace + ('$C', wireless_ip)) + return True + + +# Self explanitory, and not used until I can get some list sort function +# working... +def gen_list_header(): + return '%3s %4s %s %19s %s ' % ('NUM','STR','BSSID','CHANNEL','ESSID') + +# Generate the list of networks. +# Mostly borrowed/stolen from wpa_cli, since I had no clue what all of those +# DBUS interfaces do. ^_^ +def gen_network_list(): + #theList = [urwid.Text(gen_list_header())] + theList = [] + + id = 0 + for profile in config.GetWiredProfileList(): + if id == 0: + #theList.append(urwid.Text("Wired Network(s):")) + theList.append(ListElem("Wired Network(s):")) + theList.append(NetElem('%3s%*s' % (id, 33+len(profile),profile))) + ++id + for network_id in range(0, wireless.GetNumberOfNetworks()): + if network_id == 0: + theList.append(ListElem("Wireless Network(s):")) + elem = '%3s %3s%% %17s %3s %s' % ( network_id, + wireless.GetWirelessProperty(network_id, 'quality'), + wireless.GetWirelessProperty(network_id, 'bssid'), + wireless.GetWirelessProperty(network_id, 'channel'), + wireless.GetWirelessProperty(network_id, 'essid')) + theList.append(NetElem(elem)) + return theList + +# Widget representing an individual network element +# This will be more complicated later, once I know the rest of it works +class NetElem(urwid.WidgetWrap): + """Defines a selectable element, either a wireless or wired network profile, + in a NetList + """ + def __init__(self, theText): + self.label = urwid.AttrWrap(urwid.Text(theText),None) + w = self.label + self.__super.__init__(w) + self.selected = False + self.update_w() + + # Make the thing selectable. + def selectable(self): + return True + + # Update the widget. + # Called by NetList below pretty often + def update_w(self): + if self.selected: + self._w.attr = 'selected' + self._w.focus_attr = 'selected' + else: + self._w.attr = 'body' + self._w.focus_attr = 'body' + + # Don't handle any keys... yet + def keypress(self, size, key): + return key + +# Hackish. Designed to make my problems go away until I get around to cleaning +# this thing up. NetElem should be a subclass of ListElem. It'll make more +# sense later, once I start cleaning up some of the code... +class ListElem(NetElem): + """ Defines a non-selectable element that happens to be hanging out in a + NetList + """ + def selectable(self): + return False + +# Class representing the list of networks that appears in the middle. +# Just a listbox with some special features +class NetList(urwid.WidgetWrap): + """ The list of elements that sits in the middle of the screen most of the + time. + """ + def __init__(self, elems): + self.lbox = urwid.AttrWrap(urwid.ListBox(elems),'body') + w = self.lbox + self.__super.__init__(w) + #self.selected = False + # The first element in the list is to be selected first, since that one + # is a header + elems[1].selected = True + elems[1].update_w() + #widget.update_w() + + # Pick the selected-ness of the app + def update_selected(self,is_selected): + (elem, num) = self.w.get_focus() + elem.selected = is_selected + elem.update_w() + + # Updates the selected element, moves the focused element, and then selects + # that one, then updates its selection status + def keypress(self, size, key): + self.update_selected(False) + self.w.keypress(size,key) + (widget, num) = self.lbox.get_focus() + widget.selected = True + self.update_selected(True) + +# The Whole Shebang +class appGUI(): + """The UI itself, all glory belongs to it!""" + def __init__(self): + # Happy screen saying that you can't do anything because we're scanning + # for networks. :-) + # And I can't use it yet b/c of that blasted glib mainloop + self.screen_locker = urwid.Filler(urwid.Text(('important',"Scanning networks... stand by..."), align='center')) + + txt = urwid.Text("Wicd Curses Interface",align='right') + #wrap1 = urwid.AttrWrap(txt, 'black') + #fill = urwid.Filler(txt) + + header = urwid.AttrWrap(txt, 'header') + self.update_netlist() + #walker = urwid.SimpleListWalker(gen_network_list()) + + #thePile = urwid.Pile(walker) + footer = urwid.AttrWrap(urwid.Text("Something will go here... eventually!"),'important') + # Pop takes a number! + #walker.pop(1) + #self.listbox = urwid.AttrWrap(urwid.ListBox(netList),'body','selected') + self.frame = urwid.Frame(self.netList, header=header,footer=footer) + #self.frame = urwid.Frame(self.screen_locker, header=header,footer=footer) + self.update_status() + + # Does what it says it does + def lock_screen(self): + self.frame.set_body(self.screen_locker) + + def unlock_screen(self): + self.update_netlist() + self.frame.set_body(self.netList) + + # Be clunky until I get to a later stage of development. + def update_netlist(self): + netElems = gen_network_list() + self.netList = NetList(netElems) + + def update_status(self): + if check_for_wired(wired.GetWiredIP(),self.set_status): + return True + elif check_for_wireless(wireless.GetIwconfig(), + wireless.GetWirelessIP(), self.set_status): + return True + else: + self.set_status(language['not_connected']) + return True + + def set_status(self,text): + self.frame.set_footer(urwid.AttrWrap(urwid.Text(text),'important')) + + # Yeah, I'm copying code. Anything wrong with that? + #def dbus_scan_finished(self): + # #if not self.connecting: + # #self.refresh_networks(fresh=False) + # self.unlock_screen() + + # Same, same, same, same, same, same + #def dbus_scan_started(self): + # self.lock_screen() + + # Run the bleeding thing. + # Calls the main loop. This is how the thing should be started, at least + # until I decide to change it, whenever that is. + def main(self): + misc.RenameProcess('urwicd') + self.ui = urwid.curses_display.Screen() + self.ui.register_palette([ + ('body','light gray','black'), + ('selected','dark blue','light gray'), + ('header','light blue','black'), + ('important','light red','black')]) + self.ui.run_wrapper(self.run) + + # Main program loop + def run(self): + size = self.ui.get_cols_rows() + + # This doesn't do what I need it to do! + # What I will have to do is... (unless I totally misunderstand glib) + # 1. Stick all this loop into another function (probably update_ui) + # 2. Make a glib MainLoop object somewhere in this file + # 3. Connect the DBus Main Loop to that main loop + # 4. Throw update_ui into gobject.timeout() + # 5. Pray. :-) + while True: + self.update_status() + canvas = self.frame.render( (size) ) + self.ui.draw_screen((size),canvas) + keys = self.ui.get_input() + if "f8" in keys: + break + for k in keys: + if k == "window resize": + size = self.ui.get_cols_rows() + continue + self.frame.keypress( size, k ) + +# Mostly borrowed from gui.py, but also with the standard "need daemon first" +# check +def setup_dbus(): + global proxy_obj, daemon, wireless, wired, config, dbus_ifaces + try: + proxy_obj = bus.get_object('org.wicd.daemon', '/org/wicd/daemon') + except dbus.DBusException: + print 'Error: Could not connect to the daemon. Please make sure it is running.' + sys.exit(3) + daemon = dbus.Interface(proxy_obj, 'org.wicd.daemon') + wireless = dbus.Interface(proxy_obj, 'org.wicd.daemon.wireless') + wired = dbus.Interface(proxy_obj, 'org.wicd.daemon.wired') + config = dbus.Interface(proxy_obj, 'org.wicd.daemon.config') + + dbus_ifaces = {"daemon" : daemon, "wireless" : wireless, "wired" : wired, + "config" : config} + +bus = dbus.SystemBus() +setup_dbus() +# Main entry point +if __name__ == '__main__': + app = appGUI() + + # This stuff doesn't work yet. I have to stick a gobject mainloop in to get + # it to work... It'll be done soon enough + #bus.add_signal_receiver(app.dbus_scan_finished, 'SendEndScanSignal', + # 'org.wicd.daemon') + #bus.add_signal_receiver(app.dbus_scan_started, 'SendStartScanSignal', + # 'org.wicd.daemon') + + app.main() From 64fd3940d62a612d29e46dd5cb68c8ec8154bf40 Mon Sep 17 00:00:00 2001 From: Andrew Psaltis Date: Sat, 13 Dec 2008 16:06:11 -0500 Subject: [PATCH 02/20] curses/wicd-curses.py: Add gobject.MainLoop support, and enable the D-Bus function connections (It works!). Various comments/code cleanup done. curses/README: f5 refreshes the netlist now curses/TODO: Added a bunch of features to implement, and removed one of them. --- curses/README | 10 +++- curses/TODO | 6 ++- curses/wicd-curses.py | 103 ++++++++++++++++++++++++------------------ 3 files changed, 72 insertions(+), 47 deletions(-) diff --git a/curses/README b/curses/README index fae294b..4b6bdce 100644 --- a/curses/README +++ b/curses/README @@ -1,4 +1,4 @@ -This is a CURSES-based client for wicd. It is designed to imitate wicd-client +This is a curses-based client for wicd. It is designed to imitate wicd-client as much as can be done with a console-based interface. It is written using the Urwid (http://excess.org/urwid) toolkit, and thus requires it. @@ -10,4 +10,10 @@ connected to anything or not, and that is updated in real time. It does not actually connect you to anything yet. I'll get on that when I have more free time than I do now (which will be soon). -~ ampsaltis +Other important things: + +F5: refresh wireless networks +F8: quit + + +~nacl diff --git a/curses/TODO b/curses/TODO index 0f9fd37..579bfb6 100644 --- a/curses/TODO +++ b/curses/TODO @@ -1,12 +1,14 @@ Things to do (in no particular order): -* Integrate a glib mainloop into the UI so that it can be aware of whether - we're connecting or not by some means. * Implement the ability to connect/disconnect to/from stuff. Yeah, I know that it's important... * Make a settings dialog * Implement something that resembles a combo box in urwid * Make a network config dialog +* Make an about dialog +* Implement a keyhandler function for the overall frame + * Make keystrokes customizable +* Make color schemes customizable Oh, and most importantly: diff --git a/curses/wicd-curses.py b/curses/wicd-curses.py index f35d4cf..c7ba294 100644 --- a/curses/wicd-curses.py +++ b/curses/wicd-curses.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -""" wicd-curses -- a (cursed-based) console interface to wicd +""" wicd-curses -- a (curses-based) console interface to wicd Provides the a console UI for wicd, so that people with broken X servers can at least get a network connection. Or for those who don't like using X. :-) @@ -44,14 +44,10 @@ import dbus.service # It took me a while to figure out that I have to use this. import gobject -# Other stuff +# Other important wicd-related stuff import wicd.misc as misc #import sys -# Both of these are not currently used, until I can best resolve how to use them -#import functions -#from functions import language #,Functions - # Translations for the text that people will see... as of yet. This code is # already found in the gui.py file # Stick into own ui_common file? @@ -216,6 +212,8 @@ class appGUI(): # And I can't use it yet b/c of that blasted glib mainloop self.screen_locker = urwid.Filler(urwid.Text(('important',"Scanning networks... stand by..."), align='center')) + #self.update_ct = 0 + txt = urwid.Text("Wicd Curses Interface",align='right') #wrap1 = urwid.AttrWrap(txt, 'black') #fill = urwid.Filler(txt) @@ -224,7 +222,6 @@ class appGUI(): self.update_netlist() #walker = urwid.SimpleListWalker(gen_network_list()) - #thePile = urwid.Pile(walker) footer = urwid.AttrWrap(urwid.Text("Something will go here... eventually!"),'important') # Pop takes a number! #walker.pop(1) @@ -246,7 +243,9 @@ class appGUI(): netElems = gen_network_list() self.netList = NetList(netElems) + # Update the footer/status bar def update_status(self): + #self.update_ct += 1 if check_for_wired(wired.GetWiredIP(),self.set_status): return True elif check_for_wireless(wireless.GetIwconfig(), @@ -256,58 +255,77 @@ class appGUI(): self.set_status(language['not_connected']) return True + # Set the status text, called by the update_status method def set_status(self,text): self.frame.set_footer(urwid.AttrWrap(urwid.Text(text),'important')) # Yeah, I'm copying code. Anything wrong with that? - #def dbus_scan_finished(self): - # #if not self.connecting: - # #self.refresh_networks(fresh=False) - # self.unlock_screen() + def dbus_scan_finished(self): + # I'm pretty sure that I'll need this later. + #if not self.connecting: + #self.refresh_networks(fresh=False) + self.unlock_screen() # Same, same, same, same, same, same - #def dbus_scan_started(self): - # self.lock_screen() + def dbus_scan_started(self): + self.lock_screen() # Run the bleeding thing. # Calls the main loop. This is how the thing should be started, at least # until I decide to change it, whenever that is. def main(self): - misc.RenameProcess('urwicd') + misc.RenameProcess('wicd-curses') self.ui = urwid.curses_display.Screen() + # Color scheme + # Other potential color schemes can be found at: + # http://excess.org/urwid/wiki/RecommendedPalette self.ui.register_palette([ ('body','light gray','black'), - ('selected','dark blue','light gray'), + ('selected','dark magenta','light gray'), ('header','light blue','black'), ('important','light red','black')]) + # This is a wrapper around a function that calls another a function that is a + # wrapper around a infinite loop. Fun. self.ui.run_wrapper(self.run) # Main program loop def run(self): - size = self.ui.get_cols_rows() + self.size = self.ui.get_cols_rows() - # This doesn't do what I need it to do! - # What I will have to do is... (unless I totally misunderstand glib) - # 1. Stick all this loop into another function (probably update_ui) - # 2. Make a glib MainLoop object somewhere in this file - # 3. Connect the DBus Main Loop to that main loop - # 4. Throw update_ui into gobject.timeout() - # 5. Pray. :-) - while True: - self.update_status() - canvas = self.frame.render( (size) ) - self.ui.draw_screen((size),canvas) - keys = self.ui.get_input() - if "f8" in keys: - break - for k in keys: - if k == "window resize": - size = self.ui.get_cols_rows() - continue - self.frame.keypress( size, k ) + # This actually makes some things easier to do, amusingly enough + self.loop = gobject.MainLoop() + # Update what the interface looks like every 0.5 ms + gobject.timeout_add(0.5,self.update_ui) + # Update the connection status on the bottom every 0.5 s + gobject.timeout_add(500,self.update_status) + # Terminate the loop if the UI is terminated. + gobject.idle_add(self.stop_loop) + self.loop.run() -# Mostly borrowed from gui.py, but also with the standard "need daemon first" -# check + # Redraw the screen + def update_ui(self): + #self.update_status() + canvas = self.frame.render( (self.size) ) + self.ui.draw_screen((self.size),canvas) + keys = self.ui.get_input() + # Should make a keyhandler method, but this will do until I get around to + # that stage + if "f8" in keys: + return False + if "f5" in keys: + wireless.Scan() + for k in keys: + if k == "window resize": + self.size = self.ui.get_cols_rows() + continue + self.frame.keypress( self.size, k ) + return True + + # Terminate the loop, used as the glib mainloop's idle function + def stop_loop(self): + self.loop.quit() + +# Mostly borrowed from gui.py, but also with the "need daemon first" check def setup_dbus(): global proxy_obj, daemon, wireless, wired, config, dbus_ifaces try: @@ -329,11 +347,10 @@ setup_dbus() if __name__ == '__main__': app = appGUI() - # This stuff doesn't work yet. I have to stick a gobject mainloop in to get - # it to work... It'll be done soon enough - #bus.add_signal_receiver(app.dbus_scan_finished, 'SendEndScanSignal', - # 'org.wicd.daemon') - #bus.add_signal_receiver(app.dbus_scan_started, 'SendStartScanSignal', - # 'org.wicd.daemon') + # Connect signals and whatnot to UI screen control functions + bus.add_signal_receiver(app.dbus_scan_finished, 'SendEndScanSignal', + 'org.wicd.daemon') + bus.add_signal_receiver(app.dbus_scan_started, 'SendStartScanSignal', + 'org.wicd.daemon') app.main() From d8ae040ae664dc0f6ad2087d3d321ac13792fd2e Mon Sep 17 00:00:00 2001 From: Andrew Psaltis Date: Sun, 14 Dec 2008 01:04:23 -0500 Subject: [PATCH 03/20] curses/wicd-curses.py: Cleaned up code for the Net/ListElements. Added '>' to mark to currently selected network. Set gen_network_list() to output signal quality in units specified in the config. --- curses/TODO | 1 + curses/wicd-curses.py | 80 +++++++++++++++++++++++++++++-------------- 2 files changed, 56 insertions(+), 25 deletions(-) diff --git a/curses/TODO b/curses/TODO index 579bfb6..710ee79 100644 --- a/curses/TODO +++ b/curses/TODO @@ -9,6 +9,7 @@ Things to do (in no particular order): * Implement a keyhandler function for the overall frame * Make keystrokes customizable * Make color schemes customizable +* Add code to restore the screen if _anything_ bad happens Oh, and most importantly: diff --git a/curses/wicd-curses.py b/curses/wicd-curses.py index c7ba294..dbb527f 100644 --- a/curses/wicd-curses.py +++ b/curses/wicd-curses.py @@ -57,7 +57,6 @@ language['connected_to_wireless'] = _('Connected to $A at $B (IP: $C)') language['connected_to_wired'] = _('Connected to wired network (IP: $A)') language['not_connected'] = _('Not connected') -# I might not need this... but I'm not sure so much yet. if getattr(dbus, 'version', (0, 0, 0)) < (0, 80, 0): import dbus.glib else: @@ -112,35 +111,72 @@ def gen_network_list(): #theList = [urwid.Text(gen_list_header())] theList = [] + # Pick which strength measure to use based on what the daemon says + if daemon.GetSignalDisplayType() == 0: + strenstr = 'quality' + gap = 3 + else: + strenstr = 'strength' + gap = 5 + id = 0 for profile in config.GetWiredProfileList(): if id == 0: #theList.append(urwid.Text("Wired Network(s):")) theList.append(ListElem("Wired Network(s):")) - theList.append(NetElem('%3s%*s' % (id, 33+len(profile),profile))) - ++id + theString = '%4s%*s' % (id, 32+len(profile),profile) + #### THIS IS wired.blah() in experimental + #print config.GetLastUsedWiredNetwork() + # Tag if no wireless IP present, and wired one is + if wireless.GetWirelessIP() == None and wired.GetWiredIP() != None: + theString = '>'+theString[1:] + + theList.append(NetElem(theString,id )) + id+=1 for network_id in range(0, wireless.GetNumberOfNetworks()): if network_id == 0: theList.append(ListElem("Wireless Network(s):")) - elem = '%3s %3s%% %17s %3s %s' % ( network_id, - wireless.GetWirelessProperty(network_id, 'quality'), + + theString = '%4s %*s %17s %3s %s' % ( network_id, + gap,daemon.FormatSignalForPrinting( + str(wireless.GetWirelessProperty(network_id, strenstr))), wireless.GetWirelessProperty(network_id, 'bssid'), wireless.GetWirelessProperty(network_id, 'channel'), wireless.GetWirelessProperty(network_id, 'essid')) - theList.append(NetElem(elem)) + # This returns -1 if no ID is found, so we I could put this outside of this + # loop. I'll do that soon. + if wireless.GetCurrentNetworkID( wireless.GetIwconfig() ) == network_id: + theString = '>'+theString[1:] + theList.append(NetElem(theString,network_id)) return theList -# Widget representing an individual network element -# This will be more complicated later, once I know the rest of it works -class NetElem(urwid.WidgetWrap): - """Defines a selectable element, either a wireless or wired network profile, - in a NetList - """ +class ListElem(urwid.WidgetWrap): + """ Defines a (generic) non-selectable element that hangs out in a NetList""" def __init__(self, theText): self.label = urwid.AttrWrap(urwid.Text(theText),None) w = self.label self.__super.__init__(w) + #self.update_w() + def selectable(self): + return False + + def update_w(self): + pass + + # Don't handle any keys in the superclass + def keypress(self, size, key): + return key + +# Widget representing an individual network +# This will be more complicated later, once I know the rest of it works +class NetElem(ListElem): + """Defines a selectable element, either a wireless or wired network profile, + in a NetList + """ + def __init__(self, theText,theId): self.selected = False + self.id = theId + self.__super.__init__(theText) self.update_w() # Make the thing selectable. @@ -161,15 +197,6 @@ class NetElem(urwid.WidgetWrap): def keypress(self, size, key): return key -# Hackish. Designed to make my problems go away until I get around to cleaning -# this thing up. NetElem should be a subclass of ListElem. It'll make more -# sense later, once I start cleaning up some of the code... -class ListElem(NetElem): - """ Defines a non-selectable element that happens to be hanging out in a - NetList - """ - def selectable(self): - return False # Class representing the list of networks that appears in the middle. # Just a listbox with some special features @@ -182,7 +209,7 @@ class NetList(urwid.WidgetWrap): w = self.lbox self.__super.__init__(w) #self.selected = False - # The first element in the list is to be selected first, since that one + # The 1th element in the list is to be selected first, since that one # is a header elems[1].selected = True elems[1].update_w() @@ -190,15 +217,18 @@ class NetList(urwid.WidgetWrap): # Pick the selected-ness of the app def update_selected(self,is_selected): - (elem, num) = self.w.get_focus() + (elem, num) = self._w.get_focus() elem.selected = is_selected elem.update_w() # Updates the selected element, moves the focused element, and then selects - # that one, then updates its selection status + # that one, then updates its selection status. + # TODO: Pressing "Enter" would disconnect you from your current network, and + # connect you to the selected one def keypress(self, size, key): + #if key == 'down' or key == 'up': self.update_selected(False) - self.w.keypress(size,key) + self._w.keypress(size,key) (widget, num) = self.lbox.get_focus() widget.selected = True self.update_selected(True) From d23af8337122144a6f3e21cdd818f23aa4616096 Mon Sep 17 00:00:00 2001 From: Andrew Psaltis Date: Mon, 15 Dec 2008 17:50:00 -0500 Subject: [PATCH 04/20] curses/wicd-curses.py: Colorized the network we're currently connected to. That network is now updated every time that wicd tells us that the "status" has changed. Network connection status is now updated every 2 seconds, instead of every 0.5. --- curses/README | 1 - curses/TODO | 3 +- curses/wicd-curses.py | 106 ++++++++++++++++++++++++++++-------------- 3 files changed, 74 insertions(+), 36 deletions(-) diff --git a/curses/README b/curses/README index 4b6bdce..1db3021 100644 --- a/curses/README +++ b/curses/README @@ -15,5 +15,4 @@ Other important things: F5: refresh wireless networks F8: quit - ~nacl diff --git a/curses/TODO b/curses/TODO index 710ee79..d659e6b 100644 --- a/curses/TODO +++ b/curses/TODO @@ -9,7 +9,8 @@ Things to do (in no particular order): * Implement a keyhandler function for the overall frame * Make keystrokes customizable * Make color schemes customizable -* Add code to restore the screen if _anything_ bad happens +* Add code to restore the terminal if _anything_ bad happens. I have an idea about + how to do it. Ask me about it if you know some Python. Oh, and most importantly: diff --git a/curses/wicd-curses.py b/curses/wicd-curses.py index dbb527f..6baa552 100644 --- a/curses/wicd-curses.py +++ b/curses/wicd-curses.py @@ -3,7 +3,7 @@ """ wicd-curses -- a (curses-based) console interface to wicd Provides the a console UI for wicd, so that people with broken X servers can -at least get a network connection. Or for those who don't like using X. :-) +at least get a network connection. Or those who don't like using X. :-) """ @@ -30,7 +30,7 @@ at least get a network connection. Or for those who don't like using X. :-) This is probably due to the fact that I did not really know what I was doing when I started writing this. It works, so I guess that's all that matters. - Comments, criticisms, patches all welcome! + Comments, criticisms, patches, bug reports all welcome! """ # UI stuff @@ -128,10 +128,11 @@ def gen_network_list(): #### THIS IS wired.blah() in experimental #print config.GetLastUsedWiredNetwork() # Tag if no wireless IP present, and wired one is - if wireless.GetWirelessIP() == None and wired.GetWiredIP() != None: + is_active = wireless.GetWirelessIP() == None and wired.GetWiredIP() != None + if is_active: theString = '>'+theString[1:] - theList.append(NetElem(theString,id )) + theList.append(NetElem(theString,id,is_active)) id+=1 for network_id in range(0, wireless.GetNumberOfNetworks()): if network_id == 0: @@ -145,9 +146,10 @@ def gen_network_list(): wireless.GetWirelessProperty(network_id, 'essid')) # This returns -1 if no ID is found, so we I could put this outside of this # loop. I'll do that soon. - if wireless.GetCurrentNetworkID( wireless.GetIwconfig() ) == network_id: + is_active = wireless.GetPrintableSignalStrength("") != 0 and wireless.GetCurrentNetworkID(wireless.GetIwconfig())==network_id + if is_active: theString = '>'+theString[1:] - theList.append(NetElem(theString,network_id)) + theList.append(NetElem(theString,network_id,is_active)) return theList class ListElem(urwid.WidgetWrap): @@ -173,10 +175,18 @@ class NetElem(ListElem): """Defines a selectable element, either a wireless or wired network profile, in a NetList """ - def __init__(self, theText,theId): - self.selected = False + def __init__(self, theText,theId,is_active): + self.is_selected = False self.id = theId self.__super.__init__(theText) + + # Color the text differently if we are connected to that network + self.body = 'body' + self.selected = 'selected' + if is_active: + self.body = 'connected' + self.selected = 'connected_sel' + self.update_w() # Make the thing selectable. @@ -186,12 +196,12 @@ class NetElem(ListElem): # Update the widget. # Called by NetList below pretty often def update_w(self): - if self.selected: - self._w.attr = 'selected' - self._w.focus_attr = 'selected' + if self.is_selected: + self._w.attr = self.selected + self._w.focus_attr = self.selected else: - self._w.attr = 'body' - self._w.focus_attr = 'body' + self._w.attr = self.body + self._w.focus_attr = self.body # Don't handle any keys... yet def keypress(self, size, key): @@ -211,14 +221,14 @@ class NetList(urwid.WidgetWrap): #self.selected = False # The 1th element in the list is to be selected first, since that one # is a header - elems[1].selected = True + elems[1].is_selected = True elems[1].update_w() #widget.update_w() # Pick the selected-ness of the app def update_selected(self,is_selected): (elem, num) = self._w.get_focus() - elem.selected = is_selected + elem.is_selected = is_selected elem.update_w() # Updates the selected element, moves the focused element, and then selects @@ -229,8 +239,8 @@ class NetList(urwid.WidgetWrap): #if key == 'down' or key == 'up': self.update_selected(False) self._w.keypress(size,key) - (widget, num) = self.lbox.get_focus() - widget.selected = True + #(widget, num) = self.lbox.get_focus() + #widget.is_selected = True self.update_selected(True) # The Whole Shebang @@ -239,17 +249,17 @@ class appGUI(): def __init__(self): # Happy screen saying that you can't do anything because we're scanning # for networks. :-) - # And I can't use it yet b/c of that blasted glib mainloop self.screen_locker = urwid.Filler(urwid.Text(('important',"Scanning networks... stand by..."), align='center')) - #self.update_ct = 0 - + #self.update_ct = 0 txt = urwid.Text("Wicd Curses Interface",align='right') #wrap1 = urwid.AttrWrap(txt, 'black') #fill = urwid.Filler(txt) header = urwid.AttrWrap(txt, 'header') - self.update_netlist() + #self.update_netlist() + netElems = gen_network_list() + self.netList = NetList(netElems) #walker = urwid.SimpleListWalker(gen_network_list()) footer = urwid.AttrWrap(urwid.Text("Something will go here... eventually!"),'important') @@ -258,6 +268,7 @@ class appGUI(): #self.listbox = urwid.AttrWrap(urwid.ListBox(netList),'body','selected') self.frame = urwid.Frame(self.netList, header=header,footer=footer) #self.frame = urwid.Frame(self.screen_locker, header=header,footer=footer) + self.prev_state = False self.update_status() # Does what it says it does @@ -265,13 +276,22 @@ class appGUI(): self.frame.set_body(self.screen_locker) def unlock_screen(self): - self.update_netlist() + self.update_netlist(force_check=True) self.frame.set_body(self.netList) # Be clunky until I get to a later stage of development. - def update_netlist(self): - netElems = gen_network_list() - self.netList = NetList(netElems) + # Update the list of networks. Usually called by DBus. + # TODO: Preserve current focus when updating the list. + def update_netlist(self,state=None, x=None, force_check=False): + """ Updates the overall network list.""" + if not state: + state, x = daemon.GetConnectionStatus() + if self.prev_state != state or force_check: + netElems = gen_network_list() + self.netList = NetList(netElems) + self.frame.set_body(self.netList) + + self.prev_state = state # Update the footer/status bar def update_status(self): @@ -291,10 +311,14 @@ class appGUI(): # Yeah, I'm copying code. Anything wrong with that? def dbus_scan_finished(self): - # I'm pretty sure that I'll need this later. - #if not self.connecting: - #self.refresh_networks(fresh=False) - self.unlock_screen() + # I'm pretty sure that I'll need this later. + #if not self.connecting: + #self.refresh_networks(fresh=False) + self.unlock_screen() + # I'm hoping that this will resolve Adam's problem with the screen lock + # remaining onscreen until a key is pressed. It goes away perfectly well + # here. + self.update_ui() # Same, same, same, same, same, same def dbus_scan_started(self): @@ -306,14 +330,16 @@ class appGUI(): def main(self): misc.RenameProcess('wicd-curses') self.ui = urwid.curses_display.Screen() - # Color scheme + # Color scheme. # Other potential color schemes can be found at: # http://excess.org/urwid/wiki/RecommendedPalette self.ui.register_palette([ ('body','light gray','black'), ('selected','dark magenta','light gray'), ('header','light blue','black'), - ('important','light red','black')]) + ('important','light red','black'), + ('connected','dark green','black'), + ('connected_sel','black','dark green')]) # This is a wrapper around a function that calls another a function that is a # wrapper around a infinite loop. Fun. self.ui.run_wrapper(self.run) @@ -325,14 +351,25 @@ class appGUI(): # This actually makes some things easier to do, amusingly enough self.loop = gobject.MainLoop() # Update what the interface looks like every 0.5 ms + # Apparently this is deprecated. May have to change this. gobject.timeout_add(0.5,self.update_ui) - # Update the connection status on the bottom every 0.5 s - gobject.timeout_add(500,self.update_status) + # Update the connection status on the bottom every 2 s + gobject.timeout_add(2000,self.update_status) # Terminate the loop if the UI is terminated. gobject.idle_add(self.stop_loop) self.loop.run() # Redraw the screen + # There exists a problem with this where any exceptions that occur (especially of + # the DBus variety) will get spread out on the top of the screen, or not displayed + # at all. Urwid and the glib main loop don't mix all too well. I may need to + # consult the Urwid maintainer about this. + # + # I believe that I have a fix for this. It just involves wrapping every single + # function that might throw an exception with a try-except block, using a function + # wrapper. I have tested it, and it seems to work, but I'll save it until I can + # evaluate what I need to wrap. Probably a vast majority of stuff, until I am sure + # that this is stable. def update_ui(self): #self.update_status() canvas = self.frame.render( (self.size) ) @@ -382,5 +419,6 @@ if __name__ == '__main__': 'org.wicd.daemon') bus.add_signal_receiver(app.dbus_scan_started, 'SendStartScanSignal', 'org.wicd.daemon') - + bus.add_signal_receiver(app.update_netlist, 'StatusChanged', + 'org.wicd.daemon') app.main() From 868469ef90159c39a7018023a0d43f3a9e48fa5a Mon Sep 17 00:00:00 2001 From: Andrew Psaltis Date: Wed, 17 Dec 2008 11:27:09 -0500 Subject: [PATCH 05/20] curses/wicd-curses.py: Added code to restore the console on all errors, and then print them, including KeyboardInterrupts. curses/TODO: Removed the above from TODO --- curses/TODO | 2 - curses/wicd-curses.py | 85 +++++++++++++++++++++++++++++++++---------- 2 files changed, 66 insertions(+), 21 deletions(-) diff --git a/curses/TODO b/curses/TODO index d659e6b..579bfb6 100644 --- a/curses/TODO +++ b/curses/TODO @@ -9,8 +9,6 @@ Things to do (in no particular order): * Implement a keyhandler function for the overall frame * Make keystrokes customizable * Make color schemes customizable -* Add code to restore the terminal if _anything_ bad happens. I have an idea about - how to do it. Ask me about it if you know some Python. Oh, and most importantly: diff --git a/curses/wicd-curses.py b/curses/wicd-curses.py index 6baa552..639d931 100644 --- a/curses/wicd-curses.py +++ b/curses/wicd-curses.py @@ -46,11 +46,13 @@ import gobject # Other important wicd-related stuff import wicd.misc as misc -#import sys + +# Internal Python stuff +import sys # Translations for the text that people will see... as of yet. This code is # already found in the gui.py file -# Stick into own ui_common file? +# IN EXPERIMENTAL, THIS IS ALL IN wicd.misc _ = misc.get_gettext() language = {} language['connected_to_wireless'] = _('Connected to $A at $B (IP: $C)') @@ -63,8 +65,42 @@ else: from dbus.mainloop.glib import DBusGMainLoop DBusGMainLoop(set_as_default=True) +# A hack to get any errors that pop out of the program to appear ***AFTER*** the +# program exits. +# I also may have been a bit overkill about using this too, I guess I'll find that out +# soon enough. +class wrap_exceptions: + def __call__(self, f): + def wrap_exceptions(*args, **kargs): + try: + return f(*args, **kargs) + except KeyboardInterrupt: + gobject.source_remove(redraw_tag) + loop.quit() + ui.stop() + print "Terminated by user." + raise + except : + # Remove update_ui from the event queue + gobject.source_remove(redraw_tag) + # Quit the loop + loop.quit() + # Zap the screen + ui.stop() + # Print out standard notification: + print "EXCEPTION!" + print "Please report this to the maintainer and/or file a bug report with the backtrace below:" + # Flush the buffer so that the notification is always above the + # backtrace + sys.stdout.flush() + # Raise the exception + raise + + return wrap_exceptions + # Look familiar? These two functions are clones of functions found in wicd's # gui.py file, except that now set_status is a function passed to them. +@wrap_exceptions() def check_for_wired(wired_ip,set_status): """ Determine if wired is active, and if yes, set the status. """ if wired_ip and wired.CheckPluggedIn(): @@ -73,6 +109,7 @@ def check_for_wired(wired_ip,set_status): else: return False +@wrap_exceptions() def check_for_wireless(iwconfig, wireless_ip, set_status): """ Determine if wireless is active, and if yes, set the status. """ if not wireless_ip: @@ -107,6 +144,7 @@ def gen_list_header(): # Generate the list of networks. # Mostly borrowed/stolen from wpa_cli, since I had no clue what all of those # DBUS interfaces do. ^_^ +@wrap_exceptions() def gen_network_list(): #theList = [urwid.Text(gen_list_header())] theList = [] @@ -235,6 +273,7 @@ class NetList(urwid.WidgetWrap): # that one, then updates its selection status. # TODO: Pressing "Enter" would disconnect you from your current network, and # connect you to the selected one + #@wrap_exceptions() def keypress(self, size, key): #if key == 'down' or key == 'up': self.update_selected(False) @@ -249,6 +288,7 @@ class appGUI(): def __init__(self): # Happy screen saying that you can't do anything because we're scanning # for networks. :-) + # Will need a translation sooner or later self.screen_locker = urwid.Filler(urwid.Text(('important',"Scanning networks... stand by..."), align='center')) #self.update_ct = 0 @@ -278,10 +318,14 @@ class appGUI(): def unlock_screen(self): self.update_netlist(force_check=True) self.frame.set_body(self.netList) + # I'm hoping that this will get rid of Adam's problem with the NetList not + # redisplaying itself immediately upon completion. + update_ui() # Be clunky until I get to a later stage of development. # Update the list of networks. Usually called by DBus. # TODO: Preserve current focus when updating the list. + @wrap_exceptions() def update_netlist(self,state=None, x=None, force_check=False): """ Updates the overall network list.""" if not state: @@ -294,6 +338,7 @@ class appGUI(): self.prev_state = state # Update the footer/status bar + @wrap_exceptions() def update_status(self): #self.update_ct += 1 if check_for_wired(wired.GetWiredIP(),self.set_status): @@ -310,6 +355,7 @@ class appGUI(): self.frame.set_footer(urwid.AttrWrap(urwid.Text(text),'important')) # Yeah, I'm copying code. Anything wrong with that? + @wrap_exceptions() def dbus_scan_finished(self): # I'm pretty sure that I'll need this later. #if not self.connecting: @@ -321,6 +367,7 @@ class appGUI(): self.update_ui() # Same, same, same, same, same, same + @wrap_exceptions() def dbus_scan_started(self): self.lock_screen() @@ -328,12 +375,13 @@ class appGUI(): # Calls the main loop. This is how the thing should be started, at least # until I decide to change it, whenever that is. def main(self): + global ui misc.RenameProcess('wicd-curses') - self.ui = urwid.curses_display.Screen() + ui = urwid.curses_display.Screen() # Color scheme. # Other potential color schemes can be found at: # http://excess.org/urwid/wiki/RecommendedPalette - self.ui.register_palette([ + ui.register_palette([ ('body','light gray','black'), ('selected','dark magenta','light gray'), ('header','light blue','black'), @@ -342,22 +390,24 @@ class appGUI(): ('connected_sel','black','dark green')]) # This is a wrapper around a function that calls another a function that is a # wrapper around a infinite loop. Fun. - self.ui.run_wrapper(self.run) + ui.run_wrapper(self.run) # Main program loop def run(self): - self.size = self.ui.get_cols_rows() + global loop,redraw_tag + self.size = ui.get_cols_rows() # This actually makes some things easier to do, amusingly enough - self.loop = gobject.MainLoop() + loop = gobject.MainLoop() # Update what the interface looks like every 0.5 ms - # Apparently this is deprecated. May have to change this. - gobject.timeout_add(0.5,self.update_ui) + # Apparently this is use (with fractional seconds) is deprecated. May have to + # change this. + redraw_tag = gobject.timeout_add(0.5,self.update_ui) # Update the connection status on the bottom every 2 s gobject.timeout_add(2000,self.update_status) # Terminate the loop if the UI is terminated. gobject.idle_add(self.stop_loop) - self.loop.run() + loop.run() # Redraw the screen # There exists a problem with this where any exceptions that occur (especially of @@ -365,16 +415,13 @@ class appGUI(): # at all. Urwid and the glib main loop don't mix all too well. I may need to # consult the Urwid maintainer about this. # - # I believe that I have a fix for this. It just involves wrapping every single - # function that might throw an exception with a try-except block, using a function - # wrapper. I have tested it, and it seems to work, but I'll save it until I can - # evaluate what I need to wrap. Probably a vast majority of stuff, until I am sure - # that this is stable. + # The implementation of this solution + @wrap_exceptions() def update_ui(self): #self.update_status() canvas = self.frame.render( (self.size) ) - self.ui.draw_screen((self.size),canvas) - keys = self.ui.get_input() + ui.draw_screen((self.size),canvas) + keys = ui.get_input() # Should make a keyhandler method, but this will do until I get around to # that stage if "f8" in keys: @@ -383,14 +430,14 @@ class appGUI(): wireless.Scan() for k in keys: if k == "window resize": - self.size = self.ui.get_cols_rows() + self.size = ui.get_cols_rows() continue self.frame.keypress( self.size, k ) return True # Terminate the loop, used as the glib mainloop's idle function def stop_loop(self): - self.loop.quit() + loop.quit() # Mostly borrowed from gui.py, but also with the "need daemon first" check def setup_dbus(): From a72b99ad592e2f42db0b70e0edf95d967a298f8f Mon Sep 17 00:00:00 2001 From: Andrew Psaltis Date: Thu, 18 Dec 2008 00:07:00 -0500 Subject: [PATCH 06/20] curses/wicd-curses.py: fixed a bug (missing 'self.') in dbus_scan_finished --- curses/wicd-curses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/curses/wicd-curses.py b/curses/wicd-curses.py index 639d931..8b4fa9d 100644 --- a/curses/wicd-curses.py +++ b/curses/wicd-curses.py @@ -320,7 +320,7 @@ class appGUI(): self.frame.set_body(self.netList) # I'm hoping that this will get rid of Adam's problem with the NetList not # redisplaying itself immediately upon completion. - update_ui() + self.update_ui() # Be clunky until I get to a later stage of development. # Update the list of networks. Usually called by DBus. From 76fcc3e7c62635329540b770a28544e63cc2fd89 Mon Sep 17 00:00:00 2001 From: Andrew Psaltis Date: Thu, 18 Dec 2008 16:39:24 -0500 Subject: [PATCH 07/20] curses/wicd-curses.py: Removed a lot of redundant code related to focus on the main ListBox widget. Set focus=True in the frame's rendering function made everything so much easier. --- curses/wicd-curses.py | 163 ++++++++++++++---------------------------- 1 file changed, 54 insertions(+), 109 deletions(-) diff --git a/curses/wicd-curses.py b/curses/wicd-curses.py index 8b4fa9d..5a776a8 100644 --- a/curses/wicd-curses.py +++ b/curses/wicd-curses.py @@ -69,6 +69,8 @@ else: # program exits. # I also may have been a bit overkill about using this too, I guess I'll find that out # soon enough. +# I learned about this from this example: +# http://blog.lutzky.net/2007/09/16/exception-handling-decorators-and-python/ class wrap_exceptions: def __call__(self, f): def wrap_exceptions(*args, **kargs): @@ -98,6 +100,28 @@ class 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 + # Look familiar? These two functions are clones of functions found in wicd's # gui.py file, except that now set_status is a function passed to them. @wrap_exceptions() @@ -161,7 +185,7 @@ def gen_network_list(): for profile in config.GetWiredProfileList(): if id == 0: #theList.append(urwid.Text("Wired Network(s):")) - theList.append(ListElem("Wired Network(s):")) + theList.append(urwid.Text(('body',"Wired Network(s):") ) ) theString = '%4s%*s' % (id, 32+len(profile),profile) #### THIS IS wired.blah() in experimental #print config.GetLastUsedWiredNetwork() @@ -169,12 +193,13 @@ def gen_network_list(): is_active = wireless.GetWirelessIP() == None and wired.GetWiredIP() != None if is_active: theString = '>'+theString[1:] - - theList.append(NetElem(theString,id,is_active)) + theList.append(urwid.AttrWrap(SelText(theString),'connected','connected focus')) + else: + theList.append(urwid.AttrWrap(SelText(theString),'body','focus')) id+=1 for network_id in range(0, wireless.GetNumberOfNetworks()): if network_id == 0: - theList.append(ListElem("Wireless Network(s):")) + theList.append(urwid.Text(('body', "Wireless Network(s):")) ) theString = '%4s %*s %17s %3s %s' % ( network_id, gap,daemon.FormatSignalForPrinting( @@ -187,100 +212,11 @@ def gen_network_list(): is_active = wireless.GetPrintableSignalStrength("") != 0 and wireless.GetCurrentNetworkID(wireless.GetIwconfig())==network_id if is_active: theString = '>'+theString[1:] - theList.append(NetElem(theString,network_id,is_active)) - return theList - -class ListElem(urwid.WidgetWrap): - """ Defines a (generic) non-selectable element that hangs out in a NetList""" - def __init__(self, theText): - self.label = urwid.AttrWrap(urwid.Text(theText),None) - w = self.label - self.__super.__init__(w) - #self.update_w() - def selectable(self): - return False - - def update_w(self): - pass - - # Don't handle any keys in the superclass - def keypress(self, size, key): - return key - -# Widget representing an individual network -# This will be more complicated later, once I know the rest of it works -class NetElem(ListElem): - """Defines a selectable element, either a wireless or wired network profile, - in a NetList - """ - def __init__(self, theText,theId,is_active): - self.is_selected = False - self.id = theId - self.__super.__init__(theText) - - # Color the text differently if we are connected to that network - self.body = 'body' - self.selected = 'selected' - if is_active: - self.body = 'connected' - self.selected = 'connected_sel' - - self.update_w() - - # Make the thing selectable. - def selectable(self): - return True - - # Update the widget. - # Called by NetList below pretty often - def update_w(self): - if self.is_selected: - self._w.attr = self.selected - self._w.focus_attr = self.selected + theList.append(urwid.AttrWrap(SelText(theString),'connected','connected focus')) else: - self._w.attr = self.body - self._w.focus_attr = self.body - - # Don't handle any keys... yet - def keypress(self, size, key): - return key - - -# Class representing the list of networks that appears in the middle. -# Just a listbox with some special features -class NetList(urwid.WidgetWrap): - """ The list of elements that sits in the middle of the screen most of the - time. - """ - def __init__(self, elems): - self.lbox = urwid.AttrWrap(urwid.ListBox(elems),'body') - w = self.lbox - self.__super.__init__(w) - #self.selected = False - # The 1th element in the list is to be selected first, since that one - # is a header - elems[1].is_selected = True - elems[1].update_w() - #widget.update_w() - - # Pick the selected-ness of the app - def update_selected(self,is_selected): - (elem, num) = self._w.get_focus() - elem.is_selected = is_selected - elem.update_w() - - # Updates the selected element, moves the focused element, and then selects - # that one, then updates its selection status. - # TODO: Pressing "Enter" would disconnect you from your current network, and - # connect you to the selected one - #@wrap_exceptions() - def keypress(self, size, key): - #if key == 'down' or key == 'up': - self.update_selected(False) - self._w.keypress(size,key) - #(widget, num) = self.lbox.get_focus() - #widget.is_selected = True - self.update_selected(True) + theList.append(urwid.AttrWrap(SelText(theString),'body','focus')) + #theList.append(SelText(theString)) + return theList # The Whole Shebang class appGUI(): @@ -299,15 +235,17 @@ class appGUI(): header = urwid.AttrWrap(txt, 'header') #self.update_netlist() netElems = gen_network_list() - self.netList = NetList(netElems) + #self.netList = urwi/RecommendedPalette + netList = urwid.ListBox(netElems) #walker = urwid.SimpleListWalker(gen_network_list()) - + self.netList = urwid.ListBox(gen_network_list()) footer = urwid.AttrWrap(urwid.Text("Something will go here... eventually!"),'important') # Pop takes a number! #walker.pop(1) - #self.listbox = urwid.AttrWrap(urwid.ListBox(netList),'body','selected') + #self.listbox = urwid.AttrWrap(urwid.ListBox(netList),'body','focus') self.frame = urwid.Frame(self.netList, header=header,footer=footer) #self.frame = urwid.Frame(self.screen_locker, header=header,footer=footer) + self.frame.set_focus('body') self.prev_state = False self.update_status() @@ -318,7 +256,7 @@ class appGUI(): def unlock_screen(self): self.update_netlist(force_check=True) self.frame.set_body(self.netList) - # I'm hoping that this will get rid of Adam's problem with the NetList not + # I'm hoping that this will get rid of Adam's problem with the ListBox not # redisplaying itself immediately upon completion. self.update_ui() @@ -332,7 +270,7 @@ class appGUI(): state, x = daemon.GetConnectionStatus() if self.prev_state != state or force_check: netElems = gen_network_list() - self.netList = NetList(netElems) + self.netList = urwid.ListBox(netElems) self.frame.set_body(self.netList) self.prev_state = state @@ -352,6 +290,8 @@ class appGUI(): # Set the status text, called by the update_status method def set_status(self,text): + #wid,pos = self.frame.body.get_focus() + #text = text +' '+ str(pos) self.frame.set_footer(urwid.AttrWrap(urwid.Text(text),'important')) # Yeah, I'm copying code. Anything wrong with that? @@ -375,19 +315,21 @@ class appGUI(): # Calls the main loop. This is how the thing should be started, at least # until I decide to change it, whenever that is. def main(self): - global ui + # We are _not_ python. misc.RenameProcess('wicd-curses') + + global ui ui = urwid.curses_display.Screen() # Color scheme. # Other potential color schemes can be found at: # http://excess.org/urwid/wiki/RecommendedPalette ui.register_palette([ ('body','light gray','black'), - ('selected','dark magenta','light gray'), + ('focus','dark magenta','light gray'), ('header','light blue','black'), ('important','light red','black'), ('connected','dark green','black'), - ('connected_sel','black','dark green')]) + ('connected focus','black','dark green')]) # This is a wrapper around a function that calls another a function that is a # wrapper around a infinite loop. Fun. ui.run_wrapper(self.run) @@ -402,7 +344,7 @@ class appGUI(): # Update what the interface looks like every 0.5 ms # Apparently this is use (with fractional seconds) is deprecated. May have to # change this. - redraw_tag = gobject.timeout_add(0.5,self.update_ui) + redraw_tag = gobject.timeout_add(1,self.update_ui) # Update the connection status on the bottom every 2 s gobject.timeout_add(2000,self.update_status) # Terminate the loop if the UI is terminated. @@ -415,11 +357,13 @@ class appGUI(): # at all. Urwid and the glib main loop don't mix all too well. I may need to # consult the Urwid maintainer about this. # - # The implementation of this solution + # The implementation of this solution is active in this program, and it appears to + # be functioning well. @wrap_exceptions() def update_ui(self): #self.update_status() - canvas = self.frame.render( (self.size) ) + canvas = self.frame.render( (self.size),True ) + ### GRRRRRRRRRRRRRRRRRRRRR ^^^^ ui.draw_screen((self.size),canvas) keys = ui.get_input() # Should make a keyhandler method, but this will do until I get around to @@ -457,8 +401,9 @@ def setup_dbus(): bus = dbus.SystemBus() setup_dbus() -# Main entry point +# Main entry point. Probably going to be moved soon. if __name__ == '__main__': + app = appGUI() # Connect signals and whatnot to UI screen control functions From c7e63acbfd22d28e269d7d51895216115827f1e0 Mon Sep 17 00:00:00 2001 From: Andrew Psaltis Date: Thu, 18 Dec 2008 22:55:20 -0500 Subject: [PATCH 08/20] curses/wicd-curses.py: Moved the primary entry point to outside of the appGUI class, added some comments to improve code readability --- curses/wicd-curses.py | 144 ++++++++++++++++++++++-------------------- 1 file changed, 74 insertions(+), 70 deletions(-) diff --git a/curses/wicd-curses.py b/curses/wicd-curses.py index 5a776a8..b38f141 100644 --- a/curses/wicd-curses.py +++ b/curses/wicd-curses.py @@ -65,10 +65,13 @@ else: from dbus.mainloop.glib import DBusGMainLoop DBusGMainLoop(set_as_default=True) +######################################## +##### SUPPORT CLASSES +######################################## # A hack to get any errors that pop out of the program to appear ***AFTER*** the # program exits. -# I also may have been a bit overkill about using this too, I guess I'll find that out -# soon enough. +# I also may have been a bit overkill about using this too, I guess I'll find that +# out soon enough. # I learned about this from this example: # http://blog.lutzky.net/2007/09/16/exception-handling-decorators-and-python/ class wrap_exceptions: @@ -105,23 +108,20 @@ class wrap_exceptions: # its due. # http://excess.org/urwid/browser/contrib/trunk/rbreu_filechooser.py class SelText(urwid.Text): - """ - A selectable text widget. See urwid.Text. - """ - + """A selectable text widget. See urwid.Text.""" def selectable(self): - """ - Make widget selectable. - """ + """Make widget selectable.""" return True def keypress(self, size, key): - """ - Don't handle any keys. - """ + """Don't handle any keys.""" return key +######################################## +##### SUPPORT FUNCTIONS +######################################## + # Look familiar? These two functions are clones of functions found in wicd's # gui.py file, except that now set_status is a function passed to them. @wrap_exceptions() @@ -168,7 +168,7 @@ def gen_list_header(): # Generate the list of networks. # Mostly borrowed/stolen from wpa_cli, since I had no clue what all of those # DBUS interfaces do. ^_^ -@wrap_exceptions() +# Whatever calls this needs to be exception-wrapped def gen_network_list(): #theList = [urwid.Text(gen_list_header())] theList = [] @@ -218,25 +218,27 @@ def gen_network_list(): #theList.append(SelText(theString)) return theList +######################################## +##### APPLICATION INTERFACE CLASS +######################################## # The Whole Shebang class appGUI(): """The UI itself, all glory belongs to it!""" def __init__(self): + self.size = ui.get_cols_rows() # Happy screen saying that you can't do anything because we're scanning # for networks. :-) # Will need a translation sooner or later self.screen_locker = urwid.Filler(urwid.Text(('important',"Scanning networks... stand by..."), align='center')) #self.update_ct = 0 - txt = urwid.Text("Wicd Curses Interface",align='right') + self.TITLE = 'Wicd Curses Interface' #wrap1 = urwid.AttrWrap(txt, 'black') #fill = urwid.Filler(txt) - header = urwid.AttrWrap(txt, 'header') + header = urwid.AttrWrap(urwid.Text(self.TITLE,align='right'), 'header') #self.update_netlist() - netElems = gen_network_list() #self.netList = urwi/RecommendedPalette - netList = urwid.ListBox(netElems) #walker = urwid.SimpleListWalker(gen_network_list()) self.netList = urwid.ListBox(gen_network_list()) footer = urwid.AttrWrap(urwid.Text("Something will go here... eventually!"),'important') @@ -311,46 +313,6 @@ class appGUI(): def dbus_scan_started(self): self.lock_screen() - # Run the bleeding thing. - # Calls the main loop. This is how the thing should be started, at least - # until I decide to change it, whenever that is. - def main(self): - # We are _not_ python. - misc.RenameProcess('wicd-curses') - - global ui - ui = urwid.curses_display.Screen() - # Color scheme. - # Other potential color schemes can be found at: - # http://excess.org/urwid/wiki/RecommendedPalette - ui.register_palette([ - ('body','light gray','black'), - ('focus','dark magenta','light gray'), - ('header','light blue','black'), - ('important','light red','black'), - ('connected','dark green','black'), - ('connected focus','black','dark green')]) - # This is a wrapper around a function that calls another a function that is a - # wrapper around a infinite loop. Fun. - ui.run_wrapper(self.run) - - # Main program loop - def run(self): - global loop,redraw_tag - self.size = ui.get_cols_rows() - - # This actually makes some things easier to do, amusingly enough - loop = gobject.MainLoop() - # Update what the interface looks like every 0.5 ms - # Apparently this is use (with fractional seconds) is deprecated. May have to - # change this. - redraw_tag = gobject.timeout_add(1,self.update_ui) - # Update the connection status on the bottom every 2 s - gobject.timeout_add(2000,self.update_status) - # Terminate the loop if the UI is terminated. - gobject.idle_add(self.stop_loop) - loop.run() - # Redraw the screen # There exists a problem with this where any exceptions that occur (especially of # the DBus variety) will get spread out on the top of the screen, or not displayed @@ -363,7 +325,7 @@ class appGUI(): def update_ui(self): #self.update_status() canvas = self.frame.render( (self.size),True ) - ### GRRRRRRRRRRRRRRRRRRRRR ^^^^ + ### GRRRRRRRRRRRRRRRRRRRRR ->^^^^ ui.draw_screen((self.size),canvas) keys = ui.get_input() # Should make a keyhandler method, but this will do until I get around to @@ -383,6 +345,55 @@ class appGUI(): def stop_loop(self): loop.quit() +######################################## +##### INITIALIZATION FUNCTIONS +######################################## + +def main(): + global ui + + # We are _not_ python. + misc.RenameProcess('wicd-curses') + + ui = urwid.curses_display.Screen() + # Color scheme. + # Other potential color schemes can be found at: + # http://excess.org/urwid/wiki/RecommendedPalette + ui.register_palette([ + ('body','light gray','black'), + ('focus','dark magenta','light gray'), + ('header','light blue','black'), + ('important','light red','black'), + ('connected','dark green','black'), + ('connected focus','black','dark green')]) + # This is a wrapper around a function that calls another a function that is a + # wrapper around a infinite loop. Fun. + ui.run_wrapper(run) + +def run(): + global loop,redraw_tag + + app = appGUI() + + # Connect signals and whatnot to UI screen control functions + bus.add_signal_receiver(app.dbus_scan_finished, 'SendEndScanSignal', + 'org.wicd.daemon') + bus.add_signal_receiver(app.dbus_scan_started, 'SendStartScanSignal', + 'org.wicd.daemon') + #bus.add_signal_receiver(app.update_netlist, 'StatusChanged', + # 'org.wicd.daemon') + loop = gobject.MainLoop() + # Update what the interface looks like every 0.5 ms + # Apparently this is use (with fractional seconds) is deprecated. May have to + # change this. + redraw_tag = gobject.timeout_add(1,app.update_ui) + # Update the connection status on the bottom every 2 s + gobject.timeout_add(2000,app.update_status) + # Terminate the loop if the UI is terminated. + gobject.idle_add(app.stop_loop) + loop.run() + + # Mostly borrowed from gui.py, but also with the "need daemon first" check def setup_dbus(): global proxy_obj, daemon, wireless, wired, config, dbus_ifaces @@ -401,16 +412,9 @@ def setup_dbus(): bus = dbus.SystemBus() setup_dbus() -# Main entry point. Probably going to be moved soon. -if __name__ == '__main__': - - app = appGUI() - # Connect signals and whatnot to UI screen control functions - bus.add_signal_receiver(app.dbus_scan_finished, 'SendEndScanSignal', - 'org.wicd.daemon') - bus.add_signal_receiver(app.dbus_scan_started, 'SendStartScanSignal', - 'org.wicd.daemon') - bus.add_signal_receiver(app.update_netlist, 'StatusChanged', - 'org.wicd.daemon') - app.main() +######################################## +##### MAIN ENTRY POINT +######################################## +if __name__ == '__main__': + main() From f80c04c98de37879645b0786edeec7d7c821abc5 Mon Sep 17 00:00:00 2001 From: Andrew Psaltis Date: Fri, 19 Dec 2008 12:34:03 -0500 Subject: [PATCH 09/20] curses/wicd-curses.py: Redesigned the internal list so that the wired network information is always at the top, no matter the number of wireless networks present. --- curses/wicd-curses.py | 65 ++++++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/curses/wicd-curses.py b/curses/wicd-curses.py index b38f141..36c2bdc 100644 --- a/curses/wicd-curses.py +++ b/curses/wicd-curses.py @@ -70,8 +70,8 @@ else: ######################################## # A hack to get any errors that pop out of the program to appear ***AFTER*** the # program exits. -# I also may have been a bit overkill about using this too, I guess I'll find that -# out soon enough. +# I also may have been a bit overkill about using this too, I guess I'll find +# that out soon enough. # I learned about this from this example: # http://blog.lutzky.net/2007/09/16/exception-handling-decorators-and-python/ class wrap_exceptions: @@ -109,6 +109,7 @@ class wrap_exceptions: # 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 @@ -168,10 +169,10 @@ def gen_list_header(): # Generate the list of networks. # Mostly borrowed/stolen from wpa_cli, since I had no clue what all of those # DBUS interfaces do. ^_^ -# Whatever calls this needs to be exception-wrapped +# Whatever calls this must be exception-wrapped if it is run if the UI is up def gen_network_list(): #theList = [urwid.Text(gen_list_header())] - theList = [] + #theList = [] # Pick which strength measure to use based on what the daemon says if daemon.GetSignalDisplayType() == 0: @@ -182,10 +183,11 @@ def gen_network_list(): gap = 5 id = 0 + wiredL = [] for profile in config.GetWiredProfileList(): - if id == 0: + #if id == 0: #theList.append(urwid.Text("Wired Network(s):")) - theList.append(urwid.Text(('body',"Wired Network(s):") ) ) + #wired.append(urwid.Text(('body',"Wired Network(s):") ) ) theString = '%4s%*s' % (id, 32+len(profile),profile) #### THIS IS wired.blah() in experimental #print config.GetLastUsedWiredNetwork() @@ -193,13 +195,15 @@ def gen_network_list(): is_active = wireless.GetWirelessIP() == None and wired.GetWiredIP() != None if is_active: theString = '>'+theString[1:] - theList.append(urwid.AttrWrap(SelText(theString),'connected','connected focus')) + wiredL.append(urwid.AttrWrap(SelText(theString),'connected','connected focus')) else: - theList.append(urwid.AttrWrap(SelText(theString),'body','focus')) + wiredL.append(urwid.AttrWrap(SelText(theString),'body','focus')) id+=1 + + wlessL = [] for network_id in range(0, wireless.GetNumberOfNetworks()): - if network_id == 0: - theList.append(urwid.Text(('body', "Wireless Network(s):")) ) + #if network_id == 0: + #wireless.append(urwid.Text(('body', "Wireless Network(s):")) ) theString = '%4s %*s %17s %3s %s' % ( network_id, gap,daemon.FormatSignalForPrinting( @@ -212,11 +216,12 @@ def gen_network_list(): is_active = wireless.GetPrintableSignalStrength("") != 0 and wireless.GetCurrentNetworkID(wireless.GetIwconfig())==network_id if is_active: theString = '>'+theString[1:] - theList.append(urwid.AttrWrap(SelText(theString),'connected','connected focus')) + wlessL.append(urwid.AttrWrap(SelText(theString),'connected','connected focus')) else: - theList.append(urwid.AttrWrap(SelText(theString),'body','focus')) + wlessL.append(urwid.AttrWrap(SelText(theString),'body','focus')) #theList.append(SelText(theString)) - return theList + return (wiredL,wlessL) + ######################################## ##### APPLICATION INTERFACE CLASS @@ -230,22 +235,32 @@ class appGUI(): # for networks. :-) # Will need a translation sooner or later self.screen_locker = urwid.Filler(urwid.Text(('important',"Scanning networks... stand by..."), align='center')) + self.TITLE = 'Wicd Curses Interface' #self.update_ct = 0 - self.TITLE = 'Wicd Curses Interface' #wrap1 = urwid.AttrWrap(txt, 'black') #fill = urwid.Filler(txt) header = urwid.AttrWrap(urwid.Text(self.TITLE,align='right'), 'header') - #self.update_netlist() - #self.netList = urwi/RecommendedPalette + self.wiredH=urwid.Filler(urwid.Text("Wired Networks")) + self.wlessH=urwid.Filler(urwid.Text("Wireless Networks")) + + wiredL,wlessL = gen_network_list() + self.wiredLB = urwid.ListBox(wiredL) + self.wlessLB = urwid.ListBox(wlessL) + #spam = SelText('spam') + #spamL = [ urwid.AttrWrap( w, None, 'focus' ) for w in [spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam] ] + self.thePile = urwid.Pile([('fixed',1,self.wiredH), + ('fixed',1,self.wiredLB), + ('fixed',1,self.wlessH), + self.wlessLB] ) + #self.netList = urwid.ListBox(wlessL) #walker = urwid.SimpleListWalker(gen_network_list()) - self.netList = urwid.ListBox(gen_network_list()) - footer = urwid.AttrWrap(urwid.Text("Something will go here... eventually!"),'important') + footer = urwid.AttrWrap(urwid.Text("If you are seeing this, then something has gone wrong!"),'important') # Pop takes a number! #walker.pop(1) #self.listbox = urwid.AttrWrap(urwid.ListBox(netList),'body','focus') - self.frame = urwid.Frame(self.netList, header=header,footer=footer) + self.frame = urwid.Frame(self.thePile, header=header,footer=footer) #self.frame = urwid.Frame(self.screen_locker, header=header,footer=footer) self.frame.set_focus('body') self.prev_state = False @@ -257,7 +272,7 @@ class appGUI(): def unlock_screen(self): self.update_netlist(force_check=True) - self.frame.set_body(self.netList) + self.frame.set_body(self.thePile) # I'm hoping that this will get rid of Adam's problem with the ListBox not # redisplaying itself immediately upon completion. self.update_ui() @@ -271,10 +286,10 @@ class appGUI(): if not state: state, x = daemon.GetConnectionStatus() if self.prev_state != state or force_check: - netElems = gen_network_list() - self.netList = urwid.ListBox(netElems) - self.frame.set_body(self.netList) - + wiredL,wlessL = gen_network_list() + self.wiredLB.body = urwid.SimpleListWalker(wiredL) + self.wlessLB.body = urwid.SimpleListWalker(wlessL) + self.prev_state = state # Update the footer/status bar @@ -386,7 +401,7 @@ def run(): # Update what the interface looks like every 0.5 ms # Apparently this is use (with fractional seconds) is deprecated. May have to # change this. - redraw_tag = gobject.timeout_add(1,app.update_ui) + redraw_tag = gobject.timeout_add(0.5,app.update_ui) # Update the connection status on the bottom every 2 s gobject.timeout_add(2000,app.update_status) # Terminate the loop if the UI is terminated. From b1475ce12c0b990f7b6a595934f0c2377bb8c973 Mon Sep 17 00:00:00 2001 From: Andrew Psaltis Date: Sat, 20 Dec 2008 16:32:19 -0500 Subject: [PATCH 10/20] Big one this time. Hopefully I got everything. curses/wicd.curses.py: Added the full language component from wicd.misc into the file. Added support for connecting to networks :-). Added statusbar-updating support during connections. Fixed a problem where an exception found before the UI is on-screen will cause another exception in wrap_exceptions. Turned the footer into a ListBox, so that I can add more than more stuff to it more easily. Rearranged the order of strings in the wireless connection part of the UI. Added a bunch of keymappings to support all of the new functionality. Made the UI updating function into an idle function, to better support the new functionality (and it eats up less CPU, too). Some minor code cleanup throughout. curses/README: Updated to correspond with new features curses/TODO: Removed connection support from the TODO, added a few other things. --- curses/README | 16 +- curses/TODO | 7 +- curses/wicd-curses.py | 355 ++++++++++++++++++++++++++++++++++-------- 3 files changed, 307 insertions(+), 71 deletions(-) diff --git a/curses/README b/curses/README index 1db3021..4e3e9be 100644 --- a/curses/README +++ b/curses/README @@ -5,14 +5,16 @@ Urwid (http://excess.org/urwid) toolkit, and thus requires it. That's all there is to it, really. It's not going to install itself until I work on it more and figure out how to use distutils. -Right now, it only lists current available networks, and whether you are -connected to anything or not, and that is updated in real time. It does not -actually connect you to anything yet. I'll get on that when I have more free -time than I do now (which will be soon). +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 +connect you to networks now. Configuring them is something soon to come soon. -Other important things: +Controls: -F5: refresh wireless networks -F8: quit +F5: refresh wireless networks +F8 or Q: quit +D : disconnect from active network +ESC : if connecting to a network, stop doing so +ENTER : Attempt connection to selected network ~nacl diff --git a/curses/TODO b/curses/TODO index 579bfb6..b306e15 100644 --- a/curses/TODO +++ b/curses/TODO @@ -1,7 +1,5 @@ Things to do (in no particular order): -* Implement the ability to connect/disconnect to/from stuff. - Yeah, I know that it's important... * Make a settings dialog * Implement something that resembles a combo box in urwid * Make a network config dialog @@ -9,9 +7,12 @@ Things to do (in no particular order): * Implement a keyhandler function for the overall frame * Make keystrokes customizable * Make color schemes customizable +* Integrate this with the my local copy of the experimental branch +* Give some indication of activity during the connection process Oh, and most importantly: -* Tell people how they can quit the app (F8, until I do all of that stuff) :-) +* Tell people how they can quit the app in the app (F8 or Q, until I do all of + that stuff) :-) Anything else? That's all I can think of now. diff --git a/curses/wicd-curses.py b/curses/wicd-curses.py index 36c2bdc..6af93dc 100644 --- a/curses/wicd-curses.py +++ b/curses/wicd-curses.py @@ -33,6 +33,10 @@ at least get a network connection. Or those who don't like using X. :-) Comments, criticisms, patches, bug reports all welcome! """ +##### NOTICE: THIS DOES NOT WORK WITH THE EXPERIMENTAL BRANCH, DESPITE THE FACT +##### THAT THIS FILE COMES WITH A FULL COPY OF THE EXPERIMENTAL BRANCH! +##### I WILL PROBABLY BE REMEDYING THIS SOMETIME IN JANUARY. + # UI stuff #import urwid.raw_display import urwid.curses_display @@ -50,21 +54,131 @@ import wicd.misc as misc # Internal Python stuff import sys -# Translations for the text that people will see... as of yet. This code is -# already found in the gui.py file -# IN EXPERIMENTAL, THIS IS ALL IN wicd.misc -_ = misc.get_gettext() -language = {} -language['connected_to_wireless'] = _('Connected to $A at $B (IP: $C)') -language['connected_to_wired'] = _('Connected to wired network (IP: $A)') -language['not_connected'] = _('Not connected') - if getattr(dbus, 'version', (0, 0, 0)) < (0, 80, 0): import dbus.glib else: from dbus.mainloop.glib import DBusGMainLoop DBusGMainLoop(set_as_default=True) +# Translations for the text that people will see. This code is +# already found in the gui.py file +# IN EXPERIMENTAL, THIS IS ALL IN wicd.misc +# (Yeah... um... all 102 of them ^_^) +_ = misc.get_gettext() +language = {} +language['connect'] = _("Connect") +language['ip'] = _("IP") +language['netmask'] = _("Netmask") +language['gateway'] = _('Gateway') +language['dns'] = _('DNS') +language['use_static_ip'] = _('Use Static IPs') +language['use_static_dns'] = _('Use Static DNS') +language['use_encryption'] = _('Use Encryption') +language['advanced_settings'] = _('Advanced Settings') +language['wired_network'] = _('Wired Network') +language['wired_network_instructions'] = _('To connect to a wired network,' +' you must create a network profile. To create a network profile, type a' +' name that describes this network, and press Add.') +language['automatic_connect'] = _('Automatically connect to this network') +language['secured'] = _('Secured') +language['unsecured'] = _('Unsecured') +language['channel'] = _('Channel') +language['preferences'] = _('Preferences') +language['wpa_supplicant_driver'] = _('WPA Supplicant Driver') +language['wireless_interface'] = _('Wireless Interface') +language['wired_interface'] = _('Wired Interface') +language['hidden_network'] = _('Hidden Network') +language['hidden_network_essid'] = _('Hidden Network ESSID') +language['connected_to_wireless'] = _('Connected to $A at $B (IP: $C)') +language['connected_to_wired'] = _('Connected to wired network (IP: $A)') +language['not_connected'] = _('Not connected') +language['no_wireless_networks_found'] = _('No wireless networks found.') +language['killswitch_enabled'] = _('Wireless Kill Switch Enabled') +language['key'] = _('Key') +language['username'] = _('Username') +language['password'] = _('Password') +language['anonymous_identity'] = _('Anonymous Identity') +language['identity'] = _('Identity') +language['authentication'] = _('Authentication') +language['path_to_pac_file'] = _('Path to PAC File') +language['select_a_network'] = _('Choose from the networks below:') +language['connecting'] = _('Connecting...') +language['wired_always_on'] = _('Always show wired interface') +language['auto_reconnect'] = _('Automatically reconnect on connection loss') +language['create_adhoc_network'] = _('Create an Ad-Hoc Network') +language['essid'] = _('ESSID') +language['use_wep_encryption'] = _('Use Encryption (WEP only)') +language['before_script'] = _('Run script before connect') +language['after_script'] = _('Run script after connect') +language['disconnect_script'] = _('Run disconnect script') +language['script_settings'] = _('Scripts') +language['use_ics'] = _('Activate Internet Connection Sharing') +language['madwifi_for_adhoc'] = _('Check if using madwifi/atheros drivers') +language['default_wired'] = _('Use as default profile (overwrites any previous default)') +language['use_debug_mode'] = _('Enable debug mode') +language['use_global_dns'] = _('Use global DNS servers') +language['use_default_profile'] = _('Use default profile on wired autoconnect') +language['show_wired_list'] = _('Prompt for profile on wired autoconnect') +language['use_last_used_profile'] = _('Use last used profile on wired autoconnect') +language['choose_wired_profile'] = _('Select or create a wired profile to connect with') +language['wired_network_found'] = _('Wired connection detected') +language['stop_showing_chooser'] = _('Stop Showing Autoconnect pop-up temporarily') +language['display_type_dialog'] = _('Use dBm to measure signal strength') +language['scripts'] = _('Scripts') +language['invalid_address'] = _('Invalid address in $A entry.') +language['global_settings'] = _('Use these settings for all networks sharing this essid') +language['encrypt_info_missing'] = _('Required encryption information is missing.') +language['enable_encryption'] = _('This network requires encryption to be enabled.') +language['wicd_auto_config'] = _('Automatic (recommended)') +language["gen_settings"] = _("General Settings") +language["ext_programs"] = _("External Programs") +language["dhcp_client"] = _("DHCP Client") +language["wired_detect"] = _("Wired Link Detection") +language["route_flush"] = _("Route Table Flushing") +language["backend"] = _("Backend") +language["backend_alert"] = _("Changes to your backend won't occur until the daemon is restarted.") +language['search_domain'] = _("Search Domain") +language['scripts_need_pass'] = _('You must enter your password to configure scripts') +language['no_sudo_prog'] = _("Could not find a graphical sudo program. The script editor could not be launched." + + "You'll have to edit scripts directly your configuration file.") + +language['0'] = _('0') +language['1'] = _('1') +language['2'] = _('2') +language['3'] = _('3') +language['4'] = _('4') +language['5'] = _('5') +language['6'] = _('6') +language['7'] = _('7') +language['8'] = _('8') +language['9'] = _('9') + +language['interface_down'] = _('Putting interface down...') +language['resetting_ip_address'] = _('Resetting IP address...') +language['interface_up'] = _('Putting interface up...') +language['setting_encryption_info'] = _('Setting encryption info') +language['removing_old_connection'] = _('Removing old connection...') +language['generating_psk'] = _('Generating PSK...') +language['generating_wpa_config'] = _('Generating WPA configuration file...') +language['flushing_routing_table'] = _('Flushing the routing table...') +language['configuring_interface'] = _('Configuring wireless interface...') +language['validating_authentication'] = _('Validating authentication...') +language['setting_broadcast_address'] = _('Setting broadcast address...') +language['setting_static_dns'] = _('Setting static DNS servers...') +language['setting_static_ip'] = _('Setting static IP addresses...') +language['running_dhcp'] = _('Obtaining IP address...') +language['dhcp_failed'] = _('Connection Failed: Unable to Get IP Address') +language['aborted'] = _('Connection Cancelled') +language['bad_pass'] = _('Connection Failed: Bad password') +language['done'] = _('Done connecting...') +language['scanning'] = _('Scanning') +language['cannot_start_daemon'] = _("Unable to connect to wicd daemon DBus interface." + \ + "This typically means there was a problem starting the daemon." + \ + "Check the wicd log for more info") +language['lost_dbus'] = _("The wicd daemon has shut down, the UI will not function properly until it is restarted.") + +# Whew. Now on to more interesting stuff: + ######################################## ##### SUPPORT CLASSES ######################################## @@ -86,18 +200,22 @@ class wrap_exceptions: print "Terminated by user." raise except : - # Remove update_ui from the event queue - gobject.source_remove(redraw_tag) - # Quit the loop - loop.quit() - # Zap the screen - ui.stop() - # Print out standard notification: - print "EXCEPTION!" - print "Please report this to the maintainer and/or file a bug report with the backtrace below:" - # Flush the buffer so that the notification is always above the - # backtrace - sys.stdout.flush() + # If the UI isn't inactive (redraw_tag wouldn't normally be + # set), then don't try to stop it, just gracefully die. + if redraw_tag != -1: + # Remove update_ui from the event queue + gobject.source_remove(redraw_tag) + # Quit the loop + loop.quit() + # Zap the screen + ui.stop() + # Print out standard notification: + print "EXCEPTION!" + print "Please report this to the maintainer and/or file a bug report with the backtrace below:" + print redraw_tag + # Flush the buffer so that the notification is always above the + # backtrace + sys.stdout.flush() # Raise the exception raise @@ -163,6 +281,8 @@ def check_for_wireless(iwconfig, wireless_ip, set_status): # Self explanitory, and not used until I can get some list sort function # working... +# Also defunct. +# Current list header is STR,ESSID,ENCRYPT,BSSID,TYPE,CHANNEL def gen_list_header(): return '%3s %4s %s %19s %s ' % ('NUM','STR','BSSID','CHANNEL','ESSID') @@ -188,14 +308,15 @@ def gen_network_list(): #if id == 0: #theList.append(urwid.Text("Wired Network(s):")) #wired.append(urwid.Text(('body',"Wired Network(s):") ) ) - theString = '%4s%*s' % (id, 32+len(profile),profile) + theString = '%4s %25s' % (id, profile) #### THIS IS wired.blah() in experimental #print config.GetLastUsedWiredNetwork() # Tag if no wireless IP present, and wired one is is_active = wireless.GetWirelessIP() == None and wired.GetWiredIP() != None if is_active: theString = '>'+theString[1:] - wiredL.append(urwid.AttrWrap(SelText(theString),'connected','connected focus')) + wiredL.append(urwid.AttrWrap(SelText(theString),'connected', + 'connected focus')) else: wiredL.append(urwid.AttrWrap(SelText(theString),'body','focus')) id+=1 @@ -204,15 +325,19 @@ def gen_network_list(): for network_id in range(0, wireless.GetNumberOfNetworks()): #if network_id == 0: #wireless.append(urwid.Text(('body', "Wireless Network(s):")) ) - - theString = '%4s %*s %17s %3s %s' % ( network_id, - gap,daemon.FormatSignalForPrinting( + + # ?: in python + encryption = wireless.GetWirelessProperty(network_id, 'encryption_method') if wireless.GetWirelessProperty(network_id, 'encryption') else 'Unsecured' + theString = ' %*s %25s %9s %17s %6s: %s' % ( gap, + daemon.FormatSignalForPrinting( str(wireless.GetWirelessProperty(network_id, strenstr))), + wireless.GetWirelessProperty(network_id, 'essid'), + #wireless.GetWirelessProperty(network_id, 'encryption_method'), + encryption, wireless.GetWirelessProperty(network_id, 'bssid'), - wireless.GetWirelessProperty(network_id, 'channel'), - wireless.GetWirelessProperty(network_id, 'essid')) - # This returns -1 if no ID is found, so we I could put this outside of this - # loop. I'll do that soon. + wireless.GetWirelessProperty(network_id, 'mode'), # Master, Ad-Hoc + wireless.GetWirelessProperty(network_id, 'channel') + ) is_active = wireless.GetPrintableSignalStrength("") != 0 and wireless.GetCurrentNetworkID(wireless.GetIwconfig())==network_id if is_active: theString = '>'+theString[1:] @@ -237,42 +362,59 @@ class appGUI(): self.screen_locker = urwid.Filler(urwid.Text(('important',"Scanning networks... stand by..."), align='center')) self.TITLE = 'Wicd Curses Interface' - #self.update_ct = 0 #wrap1 = urwid.AttrWrap(txt, 'black') #fill = urwid.Filler(txt) header = urwid.AttrWrap(urwid.Text(self.TITLE,align='right'), 'header') - self.wiredH=urwid.Filler(urwid.Text("Wired Networks")) - self.wlessH=urwid.Filler(urwid.Text("Wireless Networks")) + self.wiredH=urwid.Filler(urwid.Text("Wired Network(s)")) + self.wlessH=urwid.Filler(urwid.Text("Wireless Network(s)")) wiredL,wlessL = gen_network_list() self.wiredLB = urwid.ListBox(wiredL) self.wlessLB = urwid.ListBox(wlessL) + # Stuff I used to simulate large lists #spam = SelText('spam') - #spamL = [ urwid.AttrWrap( w, None, 'focus' ) for w in [spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam] ] + #spamL = [ urwid.AttrWrap( w, None, 'focus' ) for w in [spam,spam,spam, + # spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam, + # spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam, + # spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam, + # spam,spam,spam,spam] ] + #self.spamLB = urwid.ListBox(spamL) self.thePile = urwid.Pile([('fixed',1,self.wiredH), ('fixed',1,self.wiredLB), ('fixed',1,self.wlessH), self.wlessLB] ) #self.netList = urwid.ListBox(wlessL) #walker = urwid.SimpleListWalker(gen_network_list()) - footer = urwid.AttrWrap(urwid.Text("If you are seeing this, then something has gone wrong!"),'important') + self.footer1 = urwid.AttrWrap(urwid.Text("Something important will eventually go here."),'body') + self.footer2 = urwid.AttrWrap(urwid.Text("If you are seeing this, then something has gone wrong!"),'important') + self.footerList = urwid.ListBox([self.footer1,self.footer2]) # Pop takes a number! #walker.pop(1) #self.listbox = urwid.AttrWrap(urwid.ListBox(netList),'body','focus') - self.frame = urwid.Frame(self.thePile, header=header,footer=footer) + self.frame = urwid.Frame(self.thePile, + header=header, + footer=urwid.BoxAdapter(self.footerList,2)) #self.frame = urwid.Frame(self.screen_locker, header=header,footer=footer) self.frame.set_focus('body') - self.prev_state = False + # Booleans gallore! + self.prev_state = False + self.connecting = False + self.screen_locked = False + self.connecting = False + self.update_status() + # Does what it says it does def lock_screen(self): self.frame.set_body(self.screen_locker) + self.screen_locked = True def unlock_screen(self): self.update_netlist(force_check=True) self.frame.set_body(self.thePile) + self.screen_locked = False # I'm hoping that this will get rid of Adam's problem with the ListBox not # redisplaying itself immediately upon completion. self.update_ui() @@ -295,24 +437,70 @@ class appGUI(): # Update the footer/status bar @wrap_exceptions() def update_status(self): - #self.update_ct += 1 - if check_for_wired(wired.GetWiredIP(),self.set_status): - return True - elif check_for_wireless(wireless.GetIwconfig(), - wireless.GetWirelessIP(), self.set_status): + wired_connecting = wired.CheckIfWiredConnecting() + wireless_connecting = wireless.CheckIfWirelessConnecting() + self.connecting = wired_connecting or wireless_connecting + + # IN EXPERIMENTAL + #fast = not daemon.NeedsExternalCalls() + if self.connecting: + #self.lock_screen() + #if self.statusID: + # gobject.idle_add(self.status_bar.remove, 1, self.statusID) + if wireless_connecting: + #if not fast: + iwconfig = wireless.GetIwconfig() + #else: + # iwconfig = '' + # set_status is rigged to return false when it is not + # connecting to anything, so this should work. + gobject.idle_add(self.set_status, wireless.GetCurrentNetwork(iwconfig) + + ': ' + + language[str(wireless.CheckWirelessConnectingMessage())], + True ) + if wired_connecting: + gobject.idle_add(self.set_status, language['wired_network'] + + ': ' + + language[str(wired.CheckWiredConnectingMessage())], + True) return True else: - self.set_status(language['not_connected']) - return True + if check_for_wired(wired.GetWiredIP(),self.set_status): + return True + elif check_for_wireless(wireless.GetIwconfig(), + wireless.GetWirelessIP(), self.set_status): + return True + else: + self.set_status(language['not_connected']) + return True # Set the status text, called by the update_status method - def set_status(self,text): - #wid,pos = self.frame.body.get_focus() - #text = text +' '+ str(pos) - self.frame.set_footer(urwid.AttrWrap(urwid.Text(text),'important')) + # from_idle : a check to see if we are being called directly from the + # mainloop + def set_status(self,text,from_idle=False): + # If we are being called as the result of trying to connect to + # something return False immediately. + if from_idle and not self.connecting: + return False + self.footer2 = urwid.AttrWrap(urwid.Text(text),'important') + self.frame.set_footer(urwid.BoxAdapter( + urwid.ListBox([self.footer1,self.footer2]),2)) + return True + + # Make sure the screen is still working by providing a pretty counter. + # Not necessary in the end, but I will be using footer1 for stuff in + # the long run. + incr = 0 + def idle_incr(self): + theText = "" + if self.connecting: + theText = "-- Connecting -- Press ESC to cancel" + self.footer1 = urwid.Text(str(self.incr) + ' '+theText) + self.incr+=1 + return True # Yeah, I'm copying code. Anything wrong with that? - @wrap_exceptions() + #@wrap_exceptions() def dbus_scan_finished(self): # I'm pretty sure that I'll need this later. #if not self.connecting: @@ -324,7 +512,7 @@ class appGUI(): self.update_ui() # Same, same, same, same, same, same - @wrap_exceptions() + #@wrap_exceptions() def dbus_scan_started(self): self.lock_screen() @@ -345,10 +533,25 @@ class appGUI(): keys = ui.get_input() # Should make a keyhandler method, but this will do until I get around to # that stage - if "f8" in keys: + if "f8" in keys or 'Q' in keys: + loop.quit() return False if "f5" in keys: wireless.Scan() + if "enter" in keys: + # Should be a function of the labels, I think. + self.call_connect() + if "D" in keys: + # Disconnect from all networks. + daemon.Disconnect() + self.update_netlist() + if "esc" in keys: + # Force disconnect here if connection in progress + if self.connecting: + daemon.CancelConnect() + # Prevents automatic reconnecting if that option is enabled + daemon.SetForcedDisconnect(True) + pass for k in keys: if k == "window resize": self.size = ui.get_cols_rows() @@ -360,6 +563,35 @@ class appGUI(): def stop_loop(self): loop.quit() + # Bring back memories, anyone? + def call_connect(self): + wid = self.thePile.get_focus() + if wid is self.wiredLB: + wid2,pos = self.wiredLB.get_focus() + self.connect(self,'wired',pos) + #return "Wired network %i" % pos + if wid is self.wlessLB: + #self.footer1 = urwid.Text("Wireless!") + wid2,pos = self.wlessLB.get_focus() + self.connect(self,'wireless',pos) + else: + return "Failure!" + + def connect(self, event, nettype, networkid): + """ Initiates the connection process in the daemon. """ + if nettype == "wireless": + # I need to do something that is similar to this in this UI, but + # I don't have an "advanced settings" dialog yet. + #if not self.check_encryption_valid(networkid, + # networkentry.advanced_dialog): + # self.edit_advanced(None, None, nettype, networkid, networkentry) + # return False + wireless.ConnectWireless(networkid) + elif nettype == "wired": + wired.ConnectWired() + self.update_status() + + ######################################## ##### INITIALIZATION FUNCTIONS ######################################## @@ -388,6 +620,7 @@ def main(): def run(): global loop,redraw_tag + redraw_tag = -1 app = appGUI() # Connect signals and whatnot to UI screen control functions @@ -395,17 +628,17 @@ def run(): 'org.wicd.daemon') bus.add_signal_receiver(app.dbus_scan_started, 'SendStartScanSignal', 'org.wicd.daemon') - #bus.add_signal_receiver(app.update_netlist, 'StatusChanged', - # 'org.wicd.daemon') + # I've left this commented out many times. + bus.add_signal_receiver(app.update_netlist, 'StatusChanged', + 'org.wicd.daemon') loop = gobject.MainLoop() - # Update what the interface looks like every 0.5 ms - # Apparently this is use (with fractional seconds) is deprecated. May have to - # change this. - redraw_tag = gobject.timeout_add(0.5,app.update_ui) - # Update the connection status on the bottom every 2 s - gobject.timeout_add(2000,app.update_status) - # Terminate the loop if the UI is terminated. - gobject.idle_add(app.stop_loop) + # Update what the interface looks like as an idle function + redraw_tag = gobject.idle_add(app.update_ui) + # Update the connection status on the bottom every 1.5 s. + gobject.timeout_add(1500,app.update_status) + gobject.idle_add(app.idle_incr) + # DEFUNCT: Terminate the loop if the UI is terminated. + #gobject.idle_add(app.stop_loop) loop.run() From 780a05cef3fc1cf387d5d87d5d380c2fcbcd4db4 Mon Sep 17 00:00:00 2001 From: Andrew Psaltis Date: Fri, 26 Dec 2008 13:48:26 -0500 Subject: [PATCH 11/20] curses/prefs_curses.py: ADDED. A basic global preferences dialog. Has a tabbed interface. It is missing things such as buttons, external program controls, advanced settings, and the ability to save information. :-) curses/wicd-curses.py: Some code cleanup, replaced the language mess with the GUI list in misc, and added support for running the Preferences dialog with 'P'. curses/README: Added the keybindings to bring up the preferences dialog. --- curses/README | 8 +- curses/prefs_curses.py | 249 +++++++++++++++++++++++++++++++++++++++++ curses/wicd-curses.py | 133 +++------------------- 3 files changed, 271 insertions(+), 119 deletions(-) create mode 100644 curses/prefs_curses.py diff --git a/curses/README b/curses/README index 4e3e9be..d4d1444 100644 --- a/curses/README +++ b/curses/README @@ -11,10 +11,16 @@ connect you to networks now. Configuring them is something soon to come soon. Controls: -F5: refresh wireless networks +F5 : refresh wireless networks F8 or Q: quit D : disconnect from active network ESC : if connecting to a network, stop doing so ENTER : Attempt connection to selected network +P : Display preferences dialog + +IN DIALOGS: +ESC or Q: Quit dialog without saving information (if present) + + ~nacl diff --git a/curses/prefs_curses.py b/curses/prefs_curses.py new file mode 100644 index 0000000..a995c29 --- /dev/null +++ b/curses/prefs_curses.py @@ -0,0 +1,249 @@ +#!/usr/bin/env python +import urwid + +from wicd import misc + +# Will work for now, I guess. +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): + def __init__(self,body,pos): + # We are on a VT100, I presume. + width = 80 + height = 20 + # Stuff that goes at the top + header0_t = language["gen_settings"] + header1_t = language["ext_programs"] + header2_t = language["advanced_settings"] + self.header0 = urwid.AttrWrap(SelText(header0_t),'body','focus') + self.header1 = urwid.AttrWrap(SelText(header1_t),'body','focus') + self.header2 = urwid.AttrWrap(SelText(header2_t),'body','focus') + title = language['preferences'] + + # Blank line + self._blank = urwid.Text('') + + #### + #### Text in the widgets + #### + + # General Settings + wired_t=('editcp',language['wired_interface']+':') + wless_t=('editcp',language['wireless_interface']+':') + global_dns_t=(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:') + + always_show_wired_t = 'wired always on' #language['wired always on'] + auto_reconnect_t = language['auto_reconnect'] + + #wired_autoconnect_header = 'Wired Autoconnect Setting' + wired_auto_1_t = language['use_default_profile'] + wired_auto_2_t = language['show_wired_list'] + wired_auto_3_t = language['use_last_used_profile'] + + #### External Programs + automatic_t = language['wicd_auto_config'] + + dhcp_header = language["dhcp_client"] + # Automatic + dhcp1_t = 'dhclient' + dhcp2_t = 'dhcpcd' + dhcp3_t = 'pump' + + wired_detect_header = language["wired_detect"] + wired1_t = 'ethtool' + wired2_t = 'mii-tool' + + route_table_header = language["route_flush"] + route1_t = 'ip' + route2_t = 'route' + + # Advanced Settings + wpa_t=('editcp',language['wpa_supplicant_driver']+':') + debug_mode_t = language['use_debug_mode'] + use_dbm_t = language['display_type_dialog'] + # backend_sel_t = + + #### + #### UI Widgets + #### + + # General Settings + self.wpa_edit = urwid.AttrWrap(urwid.Edit(wpa_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') + global_dns_state = False + self.global_dns = urwid.CheckBox(global_dns_t,global_dns_state, + on_state_change=self.global_dns_trigger) + self.search_dom = ToggleEdit(search_dom_t,global_dns_state) + self.dns1 = ToggleEdit(dns1_t,global_dns_state) + self.dns2 = ToggleEdit(dns2_t,global_dns_state) + self.dns3 = ToggleEdit(dns3_t,global_dns_state) + + 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 = [] + self.wired_auto_1_r = urwid.RadioButton(wired_auto_l,wired_auto_1_t) + self.wired_auto_2_r = urwid.RadioButton(wired_auto_l,wired_auto_2_t) + self.wired_auto_3_r = urwid.RadioButton(wired_auto_l,wired_auto_3_t) + generalPile = urwid.Pile([ + self.wired_iface,#self._blank, + self.wless_iface,self._blank, + self.global_dns,#self._blank, + self.search_dom, + self.dns1,self.dns2,self.dns3,self._blank, + self.always_show_wired, + self.auto_reconnect, + self.debug_mode, + self.use_dbm,self._blank, + self.wired_auto_1_r, + self.wired_auto_2_r, + self.wired_auto_3_r + ]) + + #externalPile = urwid.Pile() + + + + # Advanced Settings + # WPA Supplicant: Combo Box + # Backend: Combo box + # Debugging + # Enable debug mode + # Wireless Interface + # Use DBM to measure signal strength + + advancedPile = urwid.Pile([self.wpa_edit,self._blank]) + + 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.tab_map = {self.header0 : generalPile, + self.header1 : advancedPile, + self.header2 : advancedPile} + + content = [self.columns,generalPile] + #self._label = urwid.AttrWrap(SelText(titles),attr[0],attr[1]) + self.walker = urwid.SimpleListWalker(content) + self._listbox = urwid.ListBox(self.walker) + self._boxadap = urwid.BoxAdapter + #self._linebox = urwid.LineBox(self._listbox) + overlay = urwid.Overlay(self._listbox, body, ('fixed left', pos[0]), + width + 2, ('fixed top', pos[1]), height) + self.__super.__init__(overlay) + + def global_dns_trigger(self,check_box,new_state,user_data=None): + for w in self.search_dom,self.dns1,self.dns2,self.dns3: + w.set_sensitive(new_state) + # Normal keypress, but if we are at the top, then be "tabbish" instead + def keypress(self,size,ui): + self._w.keypress(size,ui) + (wid,pos) = self._listbox.get_focus() + if wid is self.columns: + lw = self._listbox.body + lw.pop(1) + lw.append(self.tab_map[self.columns.get_focus()]) + self._listbox.body = lw + +#@wrap_exceptions() + def run(self,ui, dim, display): + + global app + #dialog = TabbedOverlay(["Foo", "Bar", "Quit"], + # ('body', 'focus'), (1, 1), display) + + #dialog = PrefOverlay(display,(0,1)) + keys = True + 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 or 'Q' in keys: + return + + for k in keys: + #Send key to underlying widget: + self.keypress(dim, k) + + #if program_menu.selected == "Quit": + # return + + #if program_menu.selected == "Foo": + #Do something + # return + + #if program_menu.selected == "Bar": + #Do something + #return + +#@wrap_exceptions() +#def run_dialog(ui,dim,display,dialog): +# pass + #Event loop: diff --git a/curses/wicd-curses.py b/curses/wicd-curses.py index c754a58..e07e9bb 100644 --- a/curses/wicd-curses.py +++ b/curses/wicd-curses.py @@ -54,129 +54,17 @@ from wicd import dbusmanager # Internal Python stuff import sys +# Curses UIs for other stuff +import prefs_curses +from prefs_curses import PrefOverlay + if getattr(dbus, 'version', (0, 0, 0)) < (0, 80, 0): import dbus.glib else: from dbus.mainloop.glib import DBusGMainLoop DBusGMainLoop(set_as_default=True) -# Translations for the text that people will see. This code is -# already found in the gui.py file -# IN EXPERIMENTAL, THIS IS ALL IN wicd.misc -# (Yeah... um... all 102 of them ^_^) -_ = misc.get_gettext() -language = {} -language['connect'] = _("Connect") -language['ip'] = _("IP") -language['netmask'] = _("Netmask") -language['gateway'] = _('Gateway') -language['dns'] = _('DNS') -language['use_static_ip'] = _('Use Static IPs') -language['use_static_dns'] = _('Use Static DNS') -language['use_encryption'] = _('Use Encryption') -language['advanced_settings'] = _('Advanced Settings') -language['wired_network'] = _('Wired Network') -language['wired_network_instructions'] = _('To connect to a wired network,' -' you must create a network profile. To create a network profile, type a' -' name that describes this network, and press Add.') -language['automatic_connect'] = _('Automatically connect to this network') -language['secured'] = _('Secured') -language['unsecured'] = _('Unsecured') -language['channel'] = _('Channel') -language['preferences'] = _('Preferences') -language['wpa_supplicant_driver'] = _('WPA Supplicant Driver') -language['wireless_interface'] = _('Wireless Interface') -language['wired_interface'] = _('Wired Interface') -language['hidden_network'] = _('Hidden Network') -language['hidden_network_essid'] = _('Hidden Network ESSID') -language['connected_to_wireless'] = _('Connected to $A at $B (IP: $C)') -language['connected_to_wired'] = _('Connected to wired network (IP: $A)') -language['not_connected'] = _('Not connected') -language['no_wireless_networks_found'] = _('No wireless networks found.') -language['killswitch_enabled'] = _('Wireless Kill Switch Enabled') -language['key'] = _('Key') -language['username'] = _('Username') -language['password'] = _('Password') -language['anonymous_identity'] = _('Anonymous Identity') -language['identity'] = _('Identity') -language['authentication'] = _('Authentication') -language['path_to_pac_file'] = _('Path to PAC File') -language['select_a_network'] = _('Choose from the networks below:') -language['connecting'] = _('Connecting...') -language['wired_always_on'] = _('Always show wired interface') -language['auto_reconnect'] = _('Automatically reconnect on connection loss') -language['create_adhoc_network'] = _('Create an Ad-Hoc Network') -language['essid'] = _('ESSID') -language['use_wep_encryption'] = _('Use Encryption (WEP only)') -language['before_script'] = _('Run script before connect') -language['after_script'] = _('Run script after connect') -language['disconnect_script'] = _('Run disconnect script') -language['script_settings'] = _('Scripts') -language['use_ics'] = _('Activate Internet Connection Sharing') -language['madwifi_for_adhoc'] = _('Check if using madwifi/atheros drivers') -language['default_wired'] = _('Use as default profile (overwrites any previous default)') -language['use_debug_mode'] = _('Enable debug mode') -language['use_global_dns'] = _('Use global DNS servers') -language['use_default_profile'] = _('Use default profile on wired autoconnect') -language['show_wired_list'] = _('Prompt for profile on wired autoconnect') -language['use_last_used_profile'] = _('Use last used profile on wired autoconnect') -language['choose_wired_profile'] = _('Select or create a wired profile to connect with') -language['wired_network_found'] = _('Wired connection detected') -language['stop_showing_chooser'] = _('Stop Showing Autoconnect pop-up temporarily') -language['display_type_dialog'] = _('Use dBm to measure signal strength') -language['scripts'] = _('Scripts') -language['invalid_address'] = _('Invalid address in $A entry.') -language['global_settings'] = _('Use these settings for all networks sharing this essid') -language['encrypt_info_missing'] = _('Required encryption information is missing.') -language['enable_encryption'] = _('This network requires encryption to be enabled.') -language['wicd_auto_config'] = _('Automatic (recommended)') -language["gen_settings"] = _("General Settings") -language["ext_programs"] = _("External Programs") -language["dhcp_client"] = _("DHCP Client") -language["wired_detect"] = _("Wired Link Detection") -language["route_flush"] = _("Route Table Flushing") -language["backend"] = _("Backend") -language["backend_alert"] = _("Changes to your backend won't occur until the daemon is restarted.") -language['search_domain'] = _("Search Domain") -language['scripts_need_pass'] = _('You must enter your password to configure scripts') -language['no_sudo_prog'] = _("Could not find a graphical sudo program. The script editor could not be launched." + - "You'll have to edit scripts directly your configuration file.") - -language['0'] = _('0') -language['1'] = _('1') -language['2'] = _('2') -language['3'] = _('3') -language['4'] = _('4') -language['5'] = _('5') -language['6'] = _('6') -language['7'] = _('7') -language['8'] = _('8') -language['9'] = _('9') - -language['interface_down'] = _('Putting interface down...') -language['resetting_ip_address'] = _('Resetting IP address...') -language['interface_up'] = _('Putting interface up...') -language['setting_encryption_info'] = _('Setting encryption info') -language['removing_old_connection'] = _('Removing old connection...') -language['generating_psk'] = _('Generating PSK...') -language['generating_wpa_config'] = _('Generating WPA configuration file...') -language['flushing_routing_table'] = _('Flushing the routing table...') -language['configuring_interface'] = _('Configuring wireless interface...') -language['validating_authentication'] = _('Validating authentication...') -language['setting_broadcast_address'] = _('Setting broadcast address...') -language['setting_static_dns'] = _('Setting static DNS servers...') -language['setting_static_ip'] = _('Setting static IP addresses...') -language['running_dhcp'] = _('Obtaining IP address...') -language['dhcp_failed'] = _('Connection Failed: Unable to Get IP Address') -language['aborted'] = _('Connection Cancelled') -language['bad_pass'] = _('Connection Failed: Bad password') -language['done'] = _('Done connecting...') -language['scanning'] = _('Scanning') -language['cannot_start_daemon'] = _("Unable to connect to wicd daemon DBus interface." + \ - "This typically means there was a problem starting the daemon." + \ - "Check the wicd log for more info") -language['lost_dbus'] = _("The wicd daemon has shut down, the UI will not function properly until it is restarted.") - +language = misc.get_language_list_gui() # Whew. Now on to more interesting stuff: ######################################## @@ -392,6 +280,8 @@ class appGUI(): self.update_status() + #self.dialog = PrefOverlay(self.frame,self.size) + # Does what it says it does def lock_screen(self): @@ -512,6 +402,11 @@ class appGUI(): #self.update_status() canvas = self.frame.render( (self.size),True ) ### GRRRRRRRRRRRRRRRRRRRRR ->^^^^ + # It looks like if I wanted to get the statusbar to update itself + # continuously, I would have to use overlay the canvasses and redirect + # the input. I'll try to get that working at a later time, if people + # want that "feature". + #canvaso = urwid.CanvasOverlay(self.dialog.render( (80,20),True),canvas,0,1) ui.draw_screen((self.size),canvas) keys = ui.get_input() # Should make a keyhandler method, but this will do until I get around to @@ -534,6 +429,9 @@ class appGUI(): daemon.CancelConnect() # Prevents automatic reconnecting if that option is enabled daemon.SetForcedDisconnect(True) + if "P" in keys: + dialog = PrefOverlay(self.frame,(0,1)) + dialog.run(ui,self.size,self.frame) for k in keys: if k == "window resize": self.size = ui.get_cols_rows() @@ -648,7 +546,6 @@ def setup_dbus(force=True): return True -bus = dbus.SystemBus() setup_dbus() ######################################## From 5d11be8eae180c49fb7ebfde6a157f16c6e5b1d3 Mon Sep 17 00:00:00 2001 From: Andrew Psaltis Date: Fri, 26 Dec 2008 14:18:40 -0500 Subject: [PATCH 12/20] curses/prefs_curses.py: Forgot the license. ^_^ --- curses/prefs_curses.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/curses/prefs_curses.py b/curses/prefs_curses.py index a995c29..a7cc9db 100644 --- a/curses/prefs_curses.py +++ b/curses/prefs_curses.py @@ -1,4 +1,21 @@ #!/usr/bin/env python +# 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 from wicd import misc From 0d4811b1291f22971a1d7915acb74f0dec62e0c2 Mon Sep 17 00:00:00 2001 From: Andrew Psaltis Date: Sat, 27 Dec 2008 00:18:03 -0500 Subject: [PATCH 13/20] 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. --- curses/README | 4 +- curses/TODO | 5 +- curses/curses_misc.py | 176 ++++++++++++++++++++++++++++++ curses/prefs_curses.py | 237 ++++++++++++++++++++++------------------- curses/wicd-curses.py | 38 +++---- 5 files changed, 321 insertions(+), 139 deletions(-) create mode 100644 curses/curses_misc.py diff --git a/curses/README b/curses/README index d4d1444..8ee7f80 100644 --- a/curses/README +++ b/curses/README @@ -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 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: @@ -23,4 +23,4 @@ ESC or Q: Quit dialog without saving information (if present) -~nacl +~NaCl diff --git a/curses/TODO b/curses/TODO index b306e15..6b3e9fb 100644 --- a/curses/TODO +++ b/curses/TODO @@ -1,7 +1,7 @@ Things to do (in no particular order): -* Make a settings dialog - * Implement something that resembles a combo box in urwid +* Make a settings dialog -- Finish the backend. The frontend is pretty much + done. * Make a network config dialog * Make an about dialog * Implement a keyhandler function for the overall frame @@ -9,6 +9,7 @@ Things to do (in no particular order): * Make color schemes customizable * Integrate this with the my local copy of the experimental branch * Give some indication of activity during the connection process +* Make a man page for the UI. Oh, and most importantly: diff --git a/curses/curses_misc.py b/curses/curses_misc.py new file mode 100644 index 0000000..d34fedb --- /dev/null +++ b/curses/curses_misc.py @@ -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 diff --git a/curses/prefs_curses.py b/curses/prefs_curses.py index a7cc9db..1fb36be 100644 --- a/curses/prefs_curses.py +++ b/curses/prefs_curses.py @@ -1,4 +1,5 @@ #!/usr/bin/env python + # Copyright (C) 2008 Andrew Psaltis # This program is free software; you can redistribute it and/or modify @@ -19,71 +20,14 @@ import urwid from wicd import misc +from curses_misc import SelText,ToggleEdit,ComboText # Will work for now, I guess. 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): - def __init__(self,body,pos): + def __init__(self,body,pos,ui): + self.ui = ui # We are on a VT100, I presume. width = 80 height = 20 @@ -91,7 +35,7 @@ class PrefOverlay(urwid.WidgetWrap): header0_t = language["gen_settings"] header1_t = language["ext_programs"] 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.header2 = urwid.AttrWrap(SelText(header2_t),'body','focus') title = language['preferences'] @@ -104,18 +48,20 @@ class PrefOverlay(urwid.WidgetWrap): #### # General Settings - wired_t=('editcp',language['wired_interface']+':') - wless_t=('editcp',language['wireless_interface']+':') - global_dns_t=(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:') + net_cat_t = ('header','Network Interfaces') + wired_t = ('editcp',language['wired_interface']+': ') + wless_t = ('editcp',language['wireless_interface']+':') + always_show_wired_t = 'Always show wired interface' - always_show_wired_t = 'wired always on' #language['wired always on'] - auto_reconnect_t = language['auto_reconnect'] + global_dns_cat_t = ('header','Global DNS Servers') + 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_2_t = language['show_wired_list'] wired_auto_3_t = language['use_last_used_profile'] @@ -123,34 +69,51 @@ class PrefOverlay(urwid.WidgetWrap): #### External Programs automatic_t = language['wicd_auto_config'] - dhcp_header = language["dhcp_client"] + dhcp_header_t = ('header',language["dhcp_client"]) # Automatic dhcp1_t = 'dhclient' dhcp2_t = 'dhcpcd' dhcp3_t = 'pump' - wired_detect_header = language["wired_detect"] - wired1_t = 'ethtool' - wired2_t = 'mii-tool' + wired_detect_header_t = ('header',language["wired_detect"]) + wired1_t = 'ethtool' + wired2_t = 'mii-tool' - route_table_header = language["route_flush"] + route_table_header_t = ('header',language["route_flush"]) route1_t = 'ip' route2_t = 'route' - # Advanced Settings - wpa_t=('editcp',language['wpa_supplicant_driver']+':') + #### Advanced Settings + #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'] + + wless_cat_t = ('header','Wireless Interface') 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 #### # 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.wless_iface = urwid.AttrWrap(urwid.Edit(wless_t),'editbx','editfc') + + self.global_dns_cat = urwid.Text(global_dns_cat_t) global_dns_state = False self.global_dns = urwid.CheckBox(global_dns_t,global_dns_state, on_state_change=self.global_dns_trigger) @@ -160,58 +123,109 @@ class PrefOverlay(urwid.WidgetWrap): self.dns3 = ToggleEdit(dns3_t,global_dns_state) 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 = [] - self.wired_auto_1_r = urwid.RadioButton(wired_auto_l,wired_auto_1_t) - self.wired_auto_2_r = urwid.RadioButton(wired_auto_l,wired_auto_2_t) - self.wired_auto_3_r = urwid.RadioButton(wired_auto_l,wired_auto_3_t) - generalPile = urwid.Pile([ - self.wired_iface,#self._blank, - self.wless_iface,self._blank, + self.wired_auto_cat = urwid.Text(wired_auto_cat_t) + self.wired_auto_1 = urwid.RadioButton(wired_auto_l,wired_auto_1_t) + self.wired_auto_2 = urwid.RadioButton(wired_auto_l,wired_auto_2_t) + self.wired_auto_3 = urwid.RadioButton(wired_auto_l,wired_auto_3_t) + generalPile = urwid.Pile([self.net_cat, + self.wless_iface,#self._blank, + self.wired_iface, + self.always_show_wired,self._blank, + self.global_dns_cat, self.global_dns,#self._blank, self.search_dom, self.dns1,self.dns2,self.dns3,self._blank, - self.always_show_wired, - self.auto_reconnect, - self.debug_mode, - self.use_dbm,self._blank, - self.wired_auto_1_r, - self.wired_auto_2_r, - self.wired_auto_3_r + self.wired_auto_cat, + self.wired_auto_1, + self.wired_auto_2, + self.wired_auto_3 ]) - #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 - # WPA Supplicant: Combo Box - # Backend: Combo box - # Debugging - # Enable debug mode - # Wireless Interface - # Use DBM to measure signal strength + #### 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) - advancedPile = urwid.Pile([self.wpa_edit,self._blank]) - - 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.backend_cat = urwid.Text(backend_cat_t) + self.backend_cbox = ComboText(backend_t,backend_list,self,ui,8) + self.debug_cat = urwid.Text(debug_cat_t) + self.debug_mode = urwid.CheckBox(debug_mode_t) + + 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.header1 : advancedPile, + self.header1 : externalPile, 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] #self._label = urwid.AttrWrap(SelText(titles),attr[0],attr[1]) self.walker = urwid.SimpleListWalker(content) self._listbox = urwid.ListBox(self.walker) - self._boxadap = urwid.BoxAdapter #self._linebox = urwid.LineBox(self._listbox) overlay = urwid.Overlay(self._listbox, body, ('fixed left', pos[0]), width + 2, ('fixed top', pos[1]), height) self.__super.__init__(overlay) + + def global_dns_trigger(self,check_box,new_state,user_data=None): for w in self.search_dom,self.dns1,self.dns2,self.dns3: @@ -223,6 +237,9 @@ class PrefOverlay(urwid.WidgetWrap): if wid is self.columns: lw = self._listbox.body 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()]) self._listbox.body = lw diff --git a/curses/wicd-curses.py b/curses/wicd-curses.py index e07e9bb..b605256 100644 --- a/curses/wicd-curses.py +++ b/curses/wicd-curses.py @@ -55,6 +55,7 @@ from wicd import dbusmanager import sys # Curses UIs for other stuff +from curses_misc import SelText import prefs_curses from prefs_curses import PrefOverlay @@ -109,22 +110,6 @@ class 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 ######################################## @@ -430,7 +415,7 @@ class appGUI(): # Prevents automatic reconnecting if that option is enabled daemon.SetForcedDisconnect(True) if "P" in keys: - dialog = PrefOverlay(self.frame,(0,1)) + dialog = PrefOverlay(self.frame,(0,1),ui) dialog.run(ui,self.size,self.frame) for k in keys: if k == "window resize": @@ -486,17 +471,20 @@ def main(): # Color scheme. # Other potential color schemes can be found at: # 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([ - ('body','light gray','black'), + ('body','light gray','default'), ('focus','dark magenta','light gray'), - ('header','light blue','black'), - ('important','light red','black'), - ('connected','dark green','black'), - ('connected focus','black','dark green'), - # I'll be needing these soon, so I'll leave them here for now. - ('editcp', 'light gray', 'black', 'standout'), + ('header','light blue','default'), + ('important','light red','default'), + ('connected','dark green','default'), + ('connected focus','default','dark green'), + ('editcp', 'default', 'default', 'standout'), ('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 # wrapper around a infinite loop. Fun. ui.run_wrapper(run) From 689d31b098de0a159c9c634aac665028c5c8a977 Mon Sep 17 00:00:00 2001 From: Andrew Psaltis Date: Sat, 27 Dec 2008 22:35:58 -0500 Subject: [PATCH 14/20] curses/curses_misc.py: Added a tabbed interface widget for use in the preferences dialog. curses/prefs_curses.py: Converted the code to use the tabbed interface found in curses_misc.py. The dialog now fills up the terminal, but it still does nothing. curses/wicd-curses.py: Turned the "list" wired section of the interface to a combo box. --- curses/curses_misc.py | 68 +++++++++++++++++++++++++++++-------- curses/prefs_curses.py | 65 ++++++++++++++++++++---------------- curses/wicd-curses.py | 76 +++++++++++++++++++++++++----------------- 3 files changed, 135 insertions(+), 74 deletions(-) diff --git a/curses/curses_misc.py b/curses/curses_misc.py index d34fedb..0ee8e96 100644 --- a/curses/curses_misc.py +++ b/curses/curses_misc.py @@ -72,16 +72,42 @@ class ToggleEdit(urwid.WidgetWrap): 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 +# Tabbed interface class TabColumns(urwid.WidgetWrap): - def __init__(self): - pass + """ + titles_dict = dictionary of tab_contents (a SelText) : tab_widget (box) + attr = normal attributes + attrsel = attribute when active + """ + def __init__(self,tab_str,tab_wid,title,attr=('body','focus'),attrsel='tab active', + attrtitle='header'): + #title_wid = urwid.Text((attrtitle,title),align='right') + column_list = [] + for w in tab_str: + text,trash = w.get_text() + column_list.append(('fixed',len(text),w)) + column_list.append(urwid.Text((attrtitle,title),align='right')) + + self.tab_map = dict(zip(tab_str,tab_wid)) + self.active_tab = tab_str[0] + self.columns = urwid.Columns(column_list,dividechars=1) + walker = urwid.SimpleListWalker([self.columns,tab_wid[0]]) + self.listbox = urwid.ListBox(walker) + self.__super.__init__(self.listbox) + def selectable(self): return True def keypress(self,size,key): - pass + self._w.keypress(size,key) + (wid,pos) = self.listbox.get_focus() + if wid is self.columns: + lw = self.listbox.body + 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()]) + self.listbox.body = lw # A "combo box" of SelTexts # I based this off of the code found here: @@ -106,7 +132,7 @@ class ComboText(urwid.WidgetWrap): for entry in list: if len(entry) > width: width = len(entry) - content = [urwid.AttrWrap(SelText(" " + w), attr[0], attr[1]) + content = [urwid.AttrWrap(SelText(w), attr[0], attr[1]) for w in list] self._listbox = urwid.ListBox(content) @@ -141,9 +167,11 @@ class ComboText(urwid.WidgetWrap): #def get_size(self): - def __init__(self,label,list,body,ui,row = 0,show_first=0,attr=('body','focus')): + def __init__(self,label,list,body,ui,row = 0,show_first=0,attr=('body','focus'), + use_enter=True): """ - label : bit of text that preceeds the combobox + label : bit of text that preceeds the combobox. If it is "", then + ignore it list : stuff to include in the combobox body : parent widget ui : the screen @@ -154,23 +182,35 @@ class ComboText(urwid.WidgetWrap): 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)) + self.cbox = urwid.AttrWrap(SelText([list[show_first]+' vvv']),attr[0],attr[1]) # Unicode will kill me sooner or later. ^_^ - w = urwid.Columns([('fixed',len(str),self.label),self.cbox,('fixed',3,urwid.Text("vvv"))],dividechars=1) + if label != '': + w = urwid.Columns([('fixed',len(str),self.label),self.cbox],dividechars=1) + self.overlay = self.ComboSpace(list,body,ui,show_first,pos=(len(str)+1,row)) + else: + w = urwid.Columns([self.cbox]) + self.overlay = self.ComboSpace(list,body,ui,show_first,pos=(0,row)) self.__super.__init__(w) # We need this to control the keypress self.body = body self.ui = ui + self.use_enter = use_enter # If we press space or enter, be a combo box! def keypress(self,size,key): - if key == ' ' or key == 'enter': + activate = key == ' ' + if self.use_enter: + activate = activate or key == 'enter' + if activate: retval = self.overlay.show(self.ui,self.body) if retval != None: - self.cbox.set_w(SelText(retval)) + self.cbox.set_w(SelText(retval+' vvv')) return self._w.keypress(size,key) # Most obvious thing ever. :-) def selectable(self): return True + + # Return a tuple of (widget,position) + def get_selected(self): + return self.overlay._listbox.get_focus() diff --git a/curses/prefs_curses.py b/curses/prefs_curses.py index 1fb36be..e360b9c 100644 --- a/curses/prefs_curses.py +++ b/curses/prefs_curses.py @@ -20,7 +20,7 @@ import urwid from wicd import misc -from curses_misc import SelText,ToggleEdit,ComboText +from curses_misc import SelText,ToggleEdit,ComboText,TabColumns # Will work for now, I guess. language = misc.get_language_list_gui() @@ -28,9 +28,11 @@ language = misc.get_language_list_gui() class PrefOverlay(urwid.WidgetWrap): def __init__(self,body,pos,ui): self.ui = ui - # We are on a VT100, I presume. - width = 80 - height = 20 + + width,height = ui.get_cols_rows() + height -= 3 + #width = 80 + #height = 20 # Stuff that goes at the top header0_t = language["gen_settings"] header1_t = language["ext_programs"] @@ -205,48 +207,53 @@ class PrefOverlay(urwid.WidgetWrap): self.auto_reconn_cat, self.auto_reconn]) + + headerList = [self.header0,self.header1,self.header2] + pileList = [generalPile,externalPile,advancedPile] self.tab_map = {self.header0 : generalPile, self.header1 : externalPile, self.header2 : advancedPile} - self.active_tab = self.header0 + #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) + #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.walker = urwid.SimpleListWalker(content) - self._listbox = urwid.ListBox(self.walker) + #self.walker = urwid.SimpleListWalker(content) + #self.listbox = urwid.ListBox(self.walker) #self._linebox = urwid.LineBox(self._listbox) - overlay = urwid.Overlay(self._listbox, body, ('fixed left', pos[0]), + self.tabs = TabColumns(headerList,pileList,'Preferences') + overlay = urwid.Overlay(self.tabs, body, ('fixed left', pos[0]), width + 2, ('fixed top', pos[1]), height) self.__super.__init__(overlay) - - def global_dns_trigger(self,check_box,new_state,user_data=None): for w in self.search_dom,self.dns1,self.dns2,self.dns3: w.set_sensitive(new_state) # Normal keypress, but if we are at the top, then be "tabbish" instead - def keypress(self,size,ui): - self._w.keypress(size,ui) - (wid,pos) = self._listbox.get_focus() - if wid is self.columns: - lw = self._listbox.body - 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()]) - self._listbox.body = lw + #def keypress(self,size,ui): + # self._w.keypress(size,ui) + # (wid,pos) = self._listbox.get_focus() + # if wid is self.columns: + # lw = self.listbox.body + # 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()]) + # self.listbox.body = lw #@wrap_exceptions() + # Put the widget into an overlay, and run! def run(self,ui, dim, display): - - global app + # If we are small, "tabbify" the interface + + # Else, pile it together + #dialog = TabbedOverlay(["Foo", "Bar", "Quit"], # ('body', 'focus'), (1, 1), display) diff --git a/curses/wicd-curses.py b/curses/wicd-curses.py index b605256..d24f1d5 100644 --- a/curses/wicd-curses.py +++ b/curses/wicd-curses.py @@ -53,9 +53,10 @@ from wicd import dbusmanager # Internal Python stuff import sys +from time import sleep # Curses UIs for other stuff -from curses_misc import SelText +from curses_misc import SelText,ComboText import prefs_curses from prefs_curses import PrefOverlay @@ -106,6 +107,7 @@ class wrap_exceptions: # backtrace sys.stdout.flush() # Raise the exception + #sleep(2) raise return wrap_exceptions @@ -182,10 +184,12 @@ def gen_network_list(): is_active = wireless.GetWirelessIP('') == None and wired.GetWiredIP('') != None if is_active: theString = '>'+theString[1:] - wiredL.append(urwid.AttrWrap(SelText(theString),'connected', - 'connected focus')) - else: - wiredL.append(urwid.AttrWrap(SelText(theString),'body','focus')) + + #wiredL.append(urwid.AttrWrap(SelText(theString),'connected', + # 'connected focus')) + #else: + # wiredL.append(urwid.AttrWrap(SelText(theString),'body','focus')) + wiredL.append(theString) id+=1 wlessL = [] @@ -232,8 +236,21 @@ class appGUI(): self.wiredH=urwid.Filler(urwid.Text("Wired Network(s)")) self.wlessH=urwid.Filler(urwid.Text("Wireless Network(s)")) + self.footer1 = urwid.AttrWrap(urwid.Text("Something important will eventually go here."),'body') + self.footer2 = urwid.AttrWrap(urwid.Text("If you are seeing this, then something has gone wrong!"),'important') + self.footerList = urwid.ListBox([self.footer1,self.footer2]) + # Pop takes a number! + #walker.pop(1) + nothingness = urwid.Filler(urwid.Text('Hello, world!')) + self.frame = urwid.Frame(nothingness, + header=header, + footer=urwid.BoxAdapter(self.footerList,2)) + self.frame.set_focus('body') + + # Miiiiiiiiiiight be changing this back to something like how it was + # originally wiredL,wlessL = gen_network_list() - self.wiredLB = urwid.ListBox(wiredL) + self.wiredCB = urwid.Filler(ComboText('',wiredL,self.frame,ui,3,use_enter=False)) self.wlessLB = urwid.ListBox(wlessL) # Stuff I used to simulate large lists #spam = SelText('spam') @@ -244,19 +261,11 @@ class appGUI(): # spam,spam,spam,spam] ] #self.spamLB = urwid.ListBox(spamL) self.thePile = urwid.Pile([('fixed',1,self.wiredH), - ('fixed',1,self.wiredLB), + ('fixed',1,self.wiredCB), ('fixed',1,self.wlessH), self.wlessLB] ) - self.footer1 = urwid.AttrWrap(urwid.Text("Something important will eventually go here."),'body') - self.footer2 = urwid.AttrWrap(urwid.Text("If you are seeing this, then something has gone wrong!"),'important') - self.footerList = urwid.ListBox([self.footer1,self.footer2]) - # Pop takes a number! - #walker.pop(1) - self.frame = urwid.Frame(self.thePile, - header=header, - footer=urwid.BoxAdapter(self.footerList,2)) - self.frame.set_focus('body') + self.frame.set_body(self.thePile) # Booleans gallore! self.prev_state = False self.connecting = False @@ -290,9 +299,10 @@ class appGUI(): if not state: state, x = daemon.GetConnectionStatus() if self.prev_state != state or force_check: - wiredL,wlessL = gen_network_list() - self.wiredLB.body = urwid.SimpleListWalker(wiredL) - self.wlessLB.body = urwid.SimpleListWalker(wlessL) + wiredL,wlessL = gen_network_list() + self.wiredCB = urwid.Filler(ComboText('',wiredL,self.frame,ui,3, + use_enter=False)) + self.wlessLB.body = urwid.SimpleListWalker(wlessL) self.prev_state = state @@ -410,10 +420,9 @@ class appGUI(): self.update_netlist() if "esc" in keys: # Force disconnect here if connection in progress - if self.connecting: - daemon.CancelConnect() - # Prevents automatic reconnecting if that option is enabled - daemon.SetForcedDisconnect(True) + daemon.CancelConnect() + # Prevents automatic reconnecting if that option is enabled + daemon.SetForcedDisconnect(True) if "P" in keys: dialog = PrefOverlay(self.frame,(0,1),ui) dialog.run(ui,self.size,self.frame) @@ -422,18 +431,23 @@ class appGUI(): self.size = ui.get_cols_rows() continue self.frame.keypress( self.size, k ) - return True - # Terminate the loop, used as the glib mainloop's idle function - def stop_loop(self): - loop.quit() + if " " in keys: + # I can't really tell if this works ^_^. + if self.thePile.get_focus() == self.wiredCB: + wid,pos = self.wiredCB.get_body().get_selected() + text,attr = wid.get_text() + wired.ReadWiredNetworkProfile(text) + + return True # Bring back memories, anyone? def call_connect(self): wid = self.thePile.get_focus() - if wid is self.wiredLB: - wid2,pos = self.wiredLB.get_focus() - self.connect(self,'wired',pos) + if wid is self.wiredCB: + #wid2,pos = self.wiredCB.get_focus() + # Apparently, connect() doesn't care about the networkid + self.connect(self,'wired',0) #return "Wired network %i" % pos if wid is self.wlessLB: #self.footer1 = urwid.Text("Wireless!") @@ -472,7 +486,7 @@ def main(): # Other potential color schemes can be found at: # 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. + # For example, this looks particularly bad on a default-colored XTerm. # NB: To find current terminal background use variable COLORFGBG ui.register_palette([ ('body','light gray','default'), From 98a1a74c493a023886a50d68d5a7fdfe7651f65a Mon Sep 17 00:00:00 2001 From: Andrew Psaltis Date: Sun, 28 Dec 2008 11:36:41 -0500 Subject: [PATCH 15/20] curses/wicd-curses.py: Fixed a problem where any use of the wired network combo box would prevent connecting to wired networks. Added some semblance of a "working" indicator while connecting, a simple |,/,-,\,|,/,... --- curses/wicd-curses.py | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/curses/wicd-curses.py b/curses/wicd-curses.py index d24f1d5..82504b5 100644 --- a/curses/wicd-curses.py +++ b/curses/wicd-curses.py @@ -349,15 +349,25 @@ class appGUI(): self.set_status(language['not_connected']) return True - # Set the status text, called by the update_status method - # from_idle : a check to see if we are being called directly from the - # mainloop + + # Cheap little indicator stating that we are actually connecting + twirl = ['|','/','-','\\'] def set_status(self,text,from_idle=False): + # Set the status text, usually called by the update_status method + # from_idle : a check to see if we are being called directly from the + # mainloop # If we are being called as the result of trying to connect to - # something, return False immediately. + # something, and we aren't connecting to something, return False + # immediately. if from_idle and not self.connecting: return False - self.footer2 = urwid.AttrWrap(urwid.Text(text),'important') + toAppend = '' + # If we are connecting and being called from the idle function, spin + # the wheel. + if from_idle and self.connecting: + # This is probably the wrong way to do this, but ir works for now. + toAppend=self.twirl[self.incr % 4] + self.footer2 = urwid.AttrWrap(urwid.Text(text+' '+toAppend),'important') self.frame.set_footer(urwid.BoxAdapter( urwid.ListBox([self.footer1,self.footer2]),2)) return True @@ -433,11 +443,13 @@ class appGUI(): self.frame.keypress( self.size, k ) if " " in keys: - # I can't really tell if this works ^_^. - if self.thePile.get_focus() == self.wiredCB: + self.set_status('space pressed on wiredCB!') wid,pos = self.wiredCB.get_body().get_selected() text,attr = wid.get_text() wired.ReadWiredNetworkProfile(text) + # Make sure our internal reference to the combobox matches the + # one found in the pile. + self.wiredCB = self.thePile.get_focus() return True @@ -449,12 +461,13 @@ class appGUI(): # Apparently, connect() doesn't care about the networkid self.connect(self,'wired',0) #return "Wired network %i" % pos - if wid is self.wlessLB: + elif wid is self.wlessLB: #self.footer1 = urwid.Text("Wireless!") wid2,pos = self.wlessLB.get_focus() self.connect(self,'wireless',pos) else: - return "Failure!" + self.set_status("call_connect() failed! This is definitely a bug!") + #return "Failure!" def connect(self, event, nettype, networkid): """ Initiates the connection process in the daemon. """ From 2db3b3e60dc8a2a403160d8e08556041a9b36f92 Mon Sep 17 00:00:00 2001 From: Andrew Psaltis Date: Mon, 29 Dec 2008 22:04:40 -0500 Subject: [PATCH 16/20] Checkpoint in getting the Preferences dialog functional. There's still some tweaking left to do. curses/curses_misc.py: Changed the internal layout of the widgets to allow me to stick buttons on the bottom. curses/prefs_curses.py: Added rudimentary Dbus support to the dialog. Started getting the config settings to save to wicd. Added buttons (which don't do anything yet). The PrefOverlay has been renamed to PrefsDialog. The PrefsDialog widget is wrapped around a TabColumns widget. Added a main entry point into the file to allow for somewhat easier testing. It can now be called indepentently of wicd-curses, if needed. curses/wicd-curses.py: Undid a change that caused the ESC key to disconnect from the current network, in addition to its current function. --- curses/curses_misc.py | 41 ++++++--- curses/prefs_curses.py | 188 +++++++++++++++++++++++++++-------------- curses/wicd-curses.py | 23 +++-- 3 files changed, 163 insertions(+), 89 deletions(-) diff --git a/curses/curses_misc.py b/curses/curses_misc.py index 0ee8e96..64de914 100644 --- a/curses/curses_misc.py +++ b/curses/curses_misc.py @@ -79,8 +79,9 @@ class TabColumns(urwid.WidgetWrap): attr = normal attributes attrsel = attribute when active """ - def __init__(self,tab_str,tab_wid,title,attr=('body','focus'),attrsel='tab active', - attrtitle='header'): + def __init__(self,tab_str,tab_wid,title,bottom_part,attr=('body','focus'), + attrsel='tab active', attrtitle='header'): + self.bottom_part = bottom_part #title_wid = urwid.Text((attrtitle,title),align='right') column_list = [] for w in tab_str: @@ -91,23 +92,38 @@ class TabColumns(urwid.WidgetWrap): self.tab_map = dict(zip(tab_str,tab_wid)) self.active_tab = tab_str[0] self.columns = urwid.Columns(column_list,dividechars=1) - walker = urwid.SimpleListWalker([self.columns,tab_wid[0]]) - self.listbox = urwid.ListBox(walker) - self.__super.__init__(self.listbox) + #walker = urwid.SimpleListWalker([self.columns,tab_wid[0]]) + #self.listbox = urwid.ListBox(walker) + self.gen_pile(tab_wid[0],True) + self.frame = urwid.Frame(self.pile) + self.__super.__init__(self.frame) + # Make the pile in the middle + def gen_pile(self,lbox,firstrun=False): + self.pile = urwid.Pile([ + ('fixed',1,urwid.Filler(self.columns,'top')), + urwid.Filler(lbox,'top',height=('relative',99)), + ('fixed',1,urwid.Filler(self.bottom_part,'bottom')) + ]) + if not firstrun: + self.frame.set_body(self.pile) + self.set_w(self.frame) + def selectable(self): return True + def keypress(self,size,key): self._w.keypress(size,key) - (wid,pos) = self.listbox.get_focus() - if wid is self.columns: - lw = self.listbox.body - lw.pop(1) + wid = self.pile.get_focus().get_body() + if wid == self.columns: + # lw = self.listbox.body + # 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()]) - self.listbox.body = lw + self.gen_pile(self.tab_map[self.active_tab]) + return key + # self.listbox.body = lw # A "combo box" of SelTexts # I based this off of the code found here: @@ -182,7 +198,8 @@ class ComboText(urwid.WidgetWrap): self.label = urwid.Text(label) str,trash = self.label.get_text() - self.cbox = urwid.AttrWrap(SelText([list[show_first]+' vvv']),attr[0],attr[1]) + self.cbox = urwid.AttrWrap(SelText([list[show_first]+' vvv']), + attr[0],attr[1]) # Unicode will kill me sooner or later. ^_^ if label != '': w = urwid.Columns([('fixed',len(str),self.label),self.cbox],dividechars=1) diff --git a/curses/prefs_curses.py b/curses/prefs_curses.py index e360b9c..67ae0bd 100644 --- a/curses/prefs_curses.py +++ b/curses/prefs_curses.py @@ -18,17 +18,27 @@ # MA 02110-1301, USA. import urwid +import urwid.curses_display from wicd import misc +from wicd import dbusmanager from curses_misc import SelText,ToggleEdit,ComboText,TabColumns +daemon = None +wireless = None +wired = None # Will work for now, I guess. language = misc.get_language_list_gui() -class PrefOverlay(urwid.WidgetWrap): - def __init__(self,body,pos,ui): +class PrefsDialog(urwid.WidgetWrap): + def __init__(self,body,pos,ui,dbus=None): + global daemon, wireless, wired self.ui = ui + daemon = dbus['daemon'] + wireless = dbus['wireless'] + wired = dbus['wired'] + width,height = ui.get_cols_rows() height -= 3 #width = 80 @@ -117,32 +127,32 @@ class PrefOverlay(urwid.WidgetWrap): self.global_dns_cat = urwid.Text(global_dns_cat_t) global_dns_state = False - self.global_dns = urwid.CheckBox(global_dns_t,global_dns_state, + self.global_dns_checkb = urwid.CheckBox(global_dns_t,global_dns_state, on_state_change=self.global_dns_trigger) self.search_dom = ToggleEdit(search_dom_t,global_dns_state) self.dns1 = ToggleEdit(dns1_t,global_dns_state) self.dns2 = ToggleEdit(dns2_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_checkb = urwid.CheckBox(always_show_wired_t) wired_auto_l = [] self.wired_auto_cat = urwid.Text(wired_auto_cat_t) self.wired_auto_1 = urwid.RadioButton(wired_auto_l,wired_auto_1_t) self.wired_auto_2 = urwid.RadioButton(wired_auto_l,wired_auto_2_t) self.wired_auto_3 = urwid.RadioButton(wired_auto_l,wired_auto_3_t) - generalPile = urwid.Pile([self.net_cat, - self.wless_iface,#self._blank, - self.wired_iface, - self.always_show_wired,self._blank, - self.global_dns_cat, - self.global_dns,#self._blank, - self.search_dom, - self.dns1,self.dns2,self.dns3,self._blank, - self.wired_auto_cat, - self.wired_auto_1, - self.wired_auto_2, - self.wired_auto_3 + generalLB = urwid.ListBox([self.net_cat, + self.wless_iface,#self._blank, + self.wired_iface, + self.always_show_wired_checkb,self._blank, + self.global_dns_cat, + self.global_dns_checkb,#self._blank, + self.search_dom, + self.dns1,self.dns2,self.dns3,self._blank, + self.wired_auto_cat, + self.wired_auto_1, + self.wired_auto_2, + self.wired_auto_3 ]) #### External Programs tab @@ -168,15 +178,15 @@ class PrefOverlay(urwid.WidgetWrap): 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 - ]) + externalLB = urwid.ListBox([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 @@ -187,32 +197,44 @@ class PrefOverlay(urwid.WidgetWrap): self.backend_cat = urwid.Text(backend_cat_t) self.backend_cbox = ComboText(backend_t,backend_list,self,ui,8) - self.debug_cat = urwid.Text(debug_cat_t) - self.debug_mode = urwid.CheckBox(debug_mode_t) + self.debug_cat = urwid.Text(debug_cat_t) + self.debug_mode_checkb = urwid.CheckBox(debug_mode_t) - self.wless_cat = urwid.Text(wless_cat_t) - self.use_dbm = urwid.CheckBox(use_dbm_t) + self.wless_cat = urwid.Text(wless_cat_t) + self.use_dbm_checkb = urwid.CheckBox(use_dbm_t) - self.auto_reconn_cat = urwid.Text(auto_reconn_cat_t) - self.auto_reconn = urwid.CheckBox(auto_reconn_t) + self.auto_reconn_cat = urwid.Text(auto_reconn_cat_t) + self.auto_reconn_checkb = 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]) + advancedLB = urwid.ListBox([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_checkb, self._blank, + self.wless_cat, + self.use_dbm_checkb, self._blank, + self.auto_reconn_cat, + self.auto_reconn_checkb]) headerList = [self.header0,self.header1,self.header2] - pileList = [generalPile,externalPile,advancedPile] - self.tab_map = {self.header0 : generalPile, - self.header1 : externalPile, - self.header2 : advancedPile} + lbList = [generalLB,externalLB,advancedLB] + self.tab_map = {self.header0 : generalLB, + self.header1 : externalLB, + self.header2 : advancedLB} + self.load_settings() + + # Now for the buttons: + + ok_t = 'OK' + cancel_t = 'Cancel' + + ok_button = urwid.AttrWrap(urwid.Button('OK'),'body','focus') + cancel_button = urwid.AttrWrap(urwid.Button('Cancel'),'body','focus') + + + self.button_cols = urwid.Columns([ok_button,cancel_button]) #self.active_tab = self.header0 #self.columns = urwid.Columns([('fixed',len(header0_t),self.header0), @@ -226,11 +248,21 @@ class PrefOverlay(urwid.WidgetWrap): #self.walker = urwid.SimpleListWalker(content) #self.listbox = urwid.ListBox(self.walker) #self._linebox = urwid.LineBox(self._listbox) - self.tabs = TabColumns(headerList,pileList,'Preferences') - overlay = urwid.Overlay(self.tabs, body, ('fixed left', pos[0]), - width + 2, ('fixed top', pos[1]), height) - self.__super.__init__(overlay) + self.tabs = TabColumns(headerList,lbList,'Preferences',self.button_cols) + #overlay = urwid.Overlay(self.tabs, body, ('fixed left', pos[0]), + # width, ('fixed top', pos[1]), height) + self.__super.__init__(self.tabs) + def load_settings(self): + self.always_show_wired_checkb.set_state( + daemon.GetAlwaysShowWiredInterface()) + self.auto_reconn_checkb.set_state(daemon.GetAutoReconnect()) + self.debug_mode_checkb.set_state(daemon.GetDebugMode()) + self.use_dbm_checkb.set_state(daemon.GetSignalDisplayType()) + + def store_results(self): + daemon.SetAlwaysShowWiredInterface(self.always_show_wired_checkb.get_state()) + def global_dns_trigger(self,check_box,new_state,user_data=None): for w in self.search_dom,self.dns1,self.dns2,self.dns3: w.set_sensitive(new_state) @@ -250,9 +282,12 @@ class PrefOverlay(urwid.WidgetWrap): #@wrap_exceptions() # Put the widget into an overlay, and run! def run(self,ui, dim, display): + width,height = ui.get_cols_rows() # If we are small, "tabbify" the interface # Else, pile it together + overlay = urwid.Overlay(self.tabs, display, ('fixed left', 0),width + , ('fixed top',1), height-3) #dialog = TabbedOverlay(["Foo", "Bar", "Quit"], # ('body', 'focus'), (1, 1), display) @@ -261,7 +296,7 @@ class PrefOverlay(urwid.WidgetWrap): keys = True while True: if keys: - ui.draw_screen(dim, self.render(dim, True)) + ui.draw_screen(dim, overlay.render(dim, True)) keys = ui.get_input() if "window resize" in keys: @@ -271,20 +306,45 @@ class PrefOverlay(urwid.WidgetWrap): for k in keys: #Send key to underlying widget: - self.keypress(dim, k) + overlay.keypress(dim, k) - #if program_menu.selected == "Quit": - # return - - #if program_menu.selected == "Foo": - #Do something - # return +def run(): + dialog = PrefsDialog(None,(0,0),ui,dbusmanager.get_dbus_ifaces()) + keys = True + dim = ui.get_cols_rows() + while True: + if keys: + ui.draw_screen(dim, dialog.render(dim, True)) + keys = ui.get_input() - #if program_menu.selected == "Bar": - #Do something - #return + if "window resize" in keys: + dim = ui.get_cols_rows() + if "esc" in keys or 'Q' in keys: + return -#@wrap_exceptions() -#def run_dialog(ui,dim,display,dialog): -# pass - #Event loop: + for k in keys: + #Send key to underlying widget: + dialog.keypress(dim, k) + +if __name__=='__main__': + try: + dbusmanager.connect_to_dbus() + except DBusException: + # I may need to be a little more verbose here. + # Suggestions as to what should go here + print "Can't connect to the daemon. Are you sure it is running?" + print "Please check the wicd log for error messages." + raise + ui = urwid.curses_display.Screen() + ui.register_palette([ + ('body','light gray','default'), + ('focus','dark magenta','light gray'), + ('header','light blue','default'), + ('important','light red','default'), + ('connected','dark green','default'), + ('connected focus','default','dark green'), + ('editcp', 'default', 'default', 'standout'), + ('editbx', 'light gray', 'dark blue'), + ('editfc', 'white','dark blue', 'bold'), + ('tab active','dark green','light gray')]) + ui.run_wrapper(run) diff --git a/curses/wicd-curses.py b/curses/wicd-curses.py index 82504b5..06f5d04 100644 --- a/curses/wicd-curses.py +++ b/curses/wicd-curses.py @@ -42,8 +42,8 @@ import urwid.curses_display import urwid # DBus communication stuff -import dbus -import dbus.service +from dbus import DBusException +from dbus import version as dbus_version # It took me a while to figure out that I have to use this. import gobject @@ -58,13 +58,7 @@ from time import sleep # Curses UIs for other stuff from curses_misc import SelText,ComboText import prefs_curses -from prefs_curses import PrefOverlay - -if getattr(dbus, 'version', (0, 0, 0)) < (0, 80, 0): - import dbus.glib -else: - from dbus.mainloop.glib import DBusGMainLoop - DBusGMainLoop(set_as_default=True) +from prefs_curses import PrefsDialog language = misc.get_language_list_gui() # Whew. Now on to more interesting stuff: @@ -430,12 +424,15 @@ class appGUI(): self.update_netlist() if "esc" in keys: # Force disconnect here if connection in progress - daemon.CancelConnect() - # Prevents automatic reconnecting if that option is enabled - daemon.SetForcedDisconnect(True) + if self.connecting: + daemon.CancelConnect() + # Prevents automatic reconnecting if that option is enabled + daemon.SetForcedDisconnect(True) if "P" in keys: - dialog = PrefOverlay(self.frame,(0,1),ui) + dialog = PrefsDialog(self.frame,(0,1),ui, + dbusmanager.get_dbus_ifaces()) dialog.run(ui,self.size,self.frame) + dialog.store_results() for k in keys: if k == "window resize": self.size = ui.get_cols_rows() From 5fd6cca50b786562ffefbbe884cd65b37f3c42f5 Mon Sep 17 00:00:00 2001 From: Andrew Psaltis Date: Tue, 30 Dec 2008 21:27:41 -0500 Subject: [PATCH 17/20] Yet another checkpoint in building the Preferences Dialog up to completion. Some of the code isn't used yet, but this should all be done relatively soon. curses/curses_misc.py: Added a function in the ToggleEdit to set its text to something Changed the name of ComboText to ComboBox Provided the ability to generate the initial parts of a ComboBox w/o needing the screen. Added ComboBoxException, a simple derived exception for the ComboBox. Used it to die of the user never called build_combobox() curses/prefs_curses.py: Changed the names of some of the widgets. Adjusted the code to use the modified ComboBox widget curses/wicd-curses.py: Adjusted the code to use the modified ComboBox widget --- curses/curses_misc.py | 59 +++++++++++++++++++++++++------ curses/prefs_curses.py | 79 +++++++++++++++++++++++------------------- curses/wicd-curses.py | 37 ++++++++++---------- 3 files changed, 110 insertions(+), 65 deletions(-) diff --git a/curses/curses_misc.py b/curses/curses_misc.py index 64de914..898cd89 100644 --- a/curses/curses_misc.py +++ b/curses/curses_misc.py @@ -63,8 +63,11 @@ class ToggleEdit(urwid.WidgetWrap): self._w.set_attr('editbx') else: self._w.set_attr('body') + + def set_edit_text(self,text): + self._w.set_edit_text(text) - # If we aren't sensitive, don't be selectab;e + # If we aren't sensitive, don't be selectable def selectable(self): return self.sensitive @@ -125,10 +128,16 @@ class TabColumns(urwid.WidgetWrap): return key # self.listbox.body = lw + +### Combo box code begins here + +class ComboBoxException(Exception): + 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): +class ComboBox(urwid.WidgetWrap): """A ComboBox of text objects""" class ComboSpace(urwid.WidgetWrap): """The actual menu-like space that comes down from the ComboText""" @@ -183,8 +192,7 @@ class ComboText(urwid.WidgetWrap): #def get_size(self): - def __init__(self,label,list,body,ui,row = 0,show_first=0,attr=('body','focus'), - use_enter=True): + def __init__(self,label='',list=[],attr=('body','focus'),use_enter=True,show_first=0): """ label : bit of text that preceeds the combobox. If it is "", then ignore it @@ -194,31 +202,60 @@ class ComboText(urwid.WidgetWrap): 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) + self.attr = attr + self.list = list str,trash = self.label.get_text() - self.cbox = urwid.AttrWrap(SelText([list[show_first]+' vvv']), - attr[0],attr[1]) + self.overlay = None + + self.cbox = urwid.AttrWrap(SelText(' vvv'),attr[0],attr[1]) # Unicode will kill me sooner or later. ^_^ if label != '': w = urwid.Columns([('fixed',len(str),self.label),self.cbox],dividechars=1) - self.overlay = self.ComboSpace(list,body,ui,show_first,pos=(len(str)+1,row)) else: w = urwid.Columns([self.cbox]) - self.overlay = self.ComboSpace(list,body,ui,show_first,pos=(0,row)) self.__super.__init__(w) - # We need this to control the keypress + # We need this to pick our keypresses + self.use_enter = use_enter + + # Set the focus at the beginning to 0 + self.show_first = show_first + + def set_list(self,list): + self.list = list + + def set_show_first(self,show_first): + self.show_first = show_first + + def build_combobox(self,body,ui,row,show_first=0): + str,trash = self.label.get_text() + self.cbox = urwid.AttrWrap(SelText([self.list[show_first]+' vvv']), + self.attr[0],self.attr[1]) + if str != '': + w = urwid.Columns([('fixed',len(str),self.label),self.cbox],dividechars=1) + self.overlay = self.ComboSpace(self.list,body,ui,self.show_first, + pos=(len(str)+1,row)) + else: + w = urwid.Columns([self.cbox]) + self.overlay = self.ComboSpace(self.list,body,ui,self.show_first, + pos=(0,row)) + + self.set_w(w) self.body = body self.ui = ui - self.use_enter = use_enter + # If we press space or enter, be a combo box! def keypress(self,size,key): activate = key == ' ' if self.use_enter: activate = activate or key == 'enter' if activate: + # Die if the user didn't prepare the combobox overlay + if self.overlay == None: + raise ComboBoxException('ComboBox must be built before use!') retval = self.overlay.show(self.ui,self.body) if retval != None: self.cbox.set_w(SelText(retval+' vvv')) diff --git a/curses/prefs_curses.py b/curses/prefs_curses.py index 67ae0bd..3d1eee9 100644 --- a/curses/prefs_curses.py +++ b/curses/prefs_curses.py @@ -22,7 +22,7 @@ import urwid.curses_display from wicd import misc from wicd import dbusmanager -from curses_misc import SelText,ToggleEdit,ComboText,TabColumns +from curses_misc import SelText,ToggleEdit,ComboBox,TabColumns daemon = None wireless = None @@ -33,7 +33,6 @@ language = misc.get_language_list_gui() class PrefsDialog(urwid.WidgetWrap): def __init__(self,body,pos,ui,dbus=None): global daemon, wireless, wired - self.ui = ui daemon = dbus['daemon'] wireless = dbus['wireless'] @@ -91,9 +90,9 @@ class PrefsDialog(urwid.WidgetWrap): wired1_t = 'ethtool' wired2_t = 'mii-tool' - route_table_header_t = ('header',language["route_flush"]) - route1_t = 'ip' - route2_t = 'route' + flush_header_t = ('header',language["route_flush"]) + flush1_t = 'ip' + flush2_t = 'route' #### Advanced Settings #wpa_t=('editcp',language['wpa_supplicant_driver']+':') @@ -122,10 +121,12 @@ class PrefsDialog(urwid.WidgetWrap): # General Settings self.net_cat = urwid.Text(net_cat_t) - self.wired_iface = urwid.AttrWrap(urwid.Edit(wired_t),'editbx','editfc') - self.wless_iface = urwid.AttrWrap(urwid.Edit(wless_t),'editbx','editfc') + self.wired_edit = urwid.AttrWrap(urwid.Edit(wired_t),'editbx','editfc') + self.wless_edit = urwid.AttrWrap(urwid.Edit(wless_t),'editbx','editfc') self.global_dns_cat = urwid.Text(global_dns_cat_t) + # Default the global DNS settings to off. They will be reenabled later + # if so required. global_dns_state = False self.global_dns_checkb = urwid.CheckBox(global_dns_t,global_dns_state, on_state_change=self.global_dns_trigger) @@ -142,8 +143,8 @@ class PrefsDialog(urwid.WidgetWrap): self.wired_auto_2 = urwid.RadioButton(wired_auto_l,wired_auto_2_t) self.wired_auto_3 = urwid.RadioButton(wired_auto_l,wired_auto_3_t) generalLB = urwid.ListBox([self.net_cat, - self.wless_iface,#self._blank, - self.wired_iface, + self.wless_edit,#self._blank, + self.wired_edit, self.always_show_wired_checkb,self._blank, self.global_dns_cat, self.global_dns_checkb,#self._blank, @@ -159,24 +160,24 @@ class PrefsDialog(urwid.WidgetWrap): automatic_t = language['wicd_auto_config'] self.dhcp_header = urwid.Text(dhcp_header_t) - dhcp_l = [] + self.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) + self.dhcp0 = urwid.RadioButton(self.dhcp_l,automatic_t) + self.dhcp1 = urwid.RadioButton(self.dhcp_l,dhcp1_t) + self.dhcp2 = urwid.RadioButton(self.dhcp_l,dhcp2_t) + self.dhcp3 = urwid.RadioButton(self.dhcp_l,dhcp3_t) - wired_l = [] + self.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) + self.wired0 = urwid.RadioButton(self.wired_l,automatic_t) + self.wired1 = urwid.RadioButton(self.wired_l,wired1_t) + self.wired2 = urwid.RadioButton(self.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) + self.flush_l = [] + self.flush_header = urwid.Text(flush_header_t) + self.flush0 = urwid.RadioButton(self.flush_l,automatic_t) + self.flush1 = urwid.RadioButton(self.flush_l,flush1_t) + self.flush2 = urwid.RadioButton(self.flush_l,flush2_t) externalLB = urwid.ListBox([self.dhcp_header, self.dhcp0,self.dhcp1,self.dhcp2,self.dhcp3, @@ -184,18 +185,18 @@ class PrefsDialog(urwid.WidgetWrap): self.wired_detect_header, self.wired0,self.wired1,self.wired2, self._blank, - self.route_table_header, - self.route0,self.route1,self.route2 + self.flush_header, + self.flush0,self.flush1,self.flush2 ]) #### Advanced settings self.wpa_cat = urwid.Text(wpa_cat_t) - self.wpa_cbox = ComboText(wpa_t,wpa_list,self,ui,4) + self.wpa_cbox = ComboBox(wpa_t) self.wpa_warn = urwid.Text(wpa_warn_t) self.backend_cat = urwid.Text(backend_cat_t) - self.backend_cbox = ComboText(backend_t,backend_list,self,ui,8) + self.backend_cbox = ComboBox(backend_t) self.debug_cat = urwid.Text(debug_cat_t) self.debug_mode_checkb = urwid.CheckBox(debug_mode_t) @@ -223,7 +224,7 @@ class PrefsDialog(urwid.WidgetWrap): self.tab_map = {self.header0 : generalLB, self.header1 : externalLB, self.header2 : advancedLB} - self.load_settings() + #self.load_settings() # Now for the buttons: @@ -266,6 +267,11 @@ class PrefsDialog(urwid.WidgetWrap): def global_dns_trigger(self,check_box,new_state,user_data=None): for w in self.search_dom,self.dns1,self.dns2,self.dns3: w.set_sensitive(new_state) + + def ready_comboboxes(self,ui,body): + self.wpa_cbox.build_combobox(body,ui,4) + self.backend_cbox.build_combobox(body,ui,8) + # Normal keypress, but if we are at the top, then be "tabbish" instead #def keypress(self,size,ui): # self._w.keypress(size,ui) @@ -283,16 +289,16 @@ class PrefsDialog(urwid.WidgetWrap): # Put the widget into an overlay, and run! def run(self,ui, dim, display): width,height = ui.get_cols_rows() + self.load_settings() + # TODO: The below, if things go 'well' # If we are small, "tabbify" the interface - # Else, pile it together + overlay = urwid.Overlay(self.tabs, display, ('fixed left', 0),width , ('fixed top',1), height-3) + # Will need once we actually get the comboboxes filled with stuff + #self.ready_comboboxes(ui,overlay) - #dialog = TabbedOverlay(["Foo", "Bar", "Quit"], - # ('body', 'focus'), (1, 1), display) - - #dialog = PrefOverlay(display,(0,1)) keys = True while True: if keys: @@ -308,10 +314,13 @@ class PrefsDialog(urwid.WidgetWrap): #Send key to underlying widget: overlay.keypress(dim, k) -def run(): +def run_it(): dialog = PrefsDialog(None,(0,0),ui,dbusmanager.get_dbus_ifaces()) keys = True dim = ui.get_cols_rows() + dialog.load_settings() + # Will need once we actually get the comboboxes filled with stuff + #self.ready_comboboxes(ui,overlay) while True: if keys: ui.draw_screen(dim, dialog.render(dim, True)) @@ -347,4 +356,4 @@ if __name__=='__main__': ('editbx', 'light gray', 'dark blue'), ('editfc', 'white','dark blue', 'bold'), ('tab active','dark green','light gray')]) - ui.run_wrapper(run) + ui.run_wrapper(run_it) diff --git a/curses/wicd-curses.py b/curses/wicd-curses.py index 06f5d04..dd19dde 100644 --- a/curses/wicd-curses.py +++ b/curses/wicd-curses.py @@ -56,7 +56,7 @@ import sys from time import sleep # Curses UIs for other stuff -from curses_misc import SelText,ComboText +from curses_misc import SelText,ComboBox import prefs_curses from prefs_curses import PrefsDialog @@ -230,21 +230,8 @@ class appGUI(): self.wiredH=urwid.Filler(urwid.Text("Wired Network(s)")) self.wlessH=urwid.Filler(urwid.Text("Wireless Network(s)")) - self.footer1 = urwid.AttrWrap(urwid.Text("Something important will eventually go here."),'body') - self.footer2 = urwid.AttrWrap(urwid.Text("If you are seeing this, then something has gone wrong!"),'important') - self.footerList = urwid.ListBox([self.footer1,self.footer2]) - # Pop takes a number! - #walker.pop(1) - nothingness = urwid.Filler(urwid.Text('Hello, world!')) - self.frame = urwid.Frame(nothingness, - header=header, - footer=urwid.BoxAdapter(self.footerList,2)) - self.frame.set_focus('body') - - # Miiiiiiiiiiight be changing this back to something like how it was - # originally wiredL,wlessL = gen_network_list() - self.wiredCB = urwid.Filler(ComboText('',wiredL,self.frame,ui,3,use_enter=False)) + self.wiredCB = urwid.Filler(ComboBox(list=wiredL,use_enter=False)) self.wlessLB = urwid.ListBox(wlessL) # Stuff I used to simulate large lists #spam = SelText('spam') @@ -259,6 +246,17 @@ class appGUI(): ('fixed',1,self.wlessH), self.wlessLB] ) + self.footer1 = urwid.AttrWrap(urwid.Text("Something important will eventually go here."),'body') + self.footer2 = urwid.AttrWrap(urwid.Text("If you are seeing this, then something has gone wrong!"),'important') + self.footerList = urwid.ListBox([self.footer1,self.footer2]) + # Pop takes a number! + #walker.pop(1) + nothingness = urwid.Filler(urwid.Text('Hello, world!')) + self.frame = urwid.Frame(self.thePile, + header=header, + footer=urwid.BoxAdapter(self.footerList,2)) + self.wiredCB.get_body().build_combobox(self.frame,ui,3) + self.frame.set_body(self.thePile) # Booleans gallore! self.prev_state = False @@ -270,7 +268,6 @@ class appGUI(): #self.dialog = PrefOverlay(self.frame,self.size) - # Does what it says it does def lock_screen(self): self.frame.set_body(self.screen_locker) @@ -294,8 +291,10 @@ class appGUI(): state, x = daemon.GetConnectionStatus() if self.prev_state != state or force_check: wiredL,wlessL = gen_network_list() - self.wiredCB = urwid.Filler(ComboText('',wiredL,self.frame,ui,3, - use_enter=False)) + #self.wiredCB = urwid.Filler(ComboBox(wiredL,self.frame,ui,3, + # use_enter=False)) + self.wiredCB.get_body().set_list(wiredL) + self.wiredCB.get_body().build_combobox(self.frame,ui,3) self.wlessLB.body = urwid.SimpleListWalker(wlessL) self.prev_state = state @@ -440,7 +439,7 @@ class appGUI(): self.frame.keypress( self.size, k ) if " " in keys: - self.set_status('space pressed on wiredCB!') + #self.set_status('space pressed on wiredCB!') wid,pos = self.wiredCB.get_body().get_selected() text,attr = wid.get_text() wired.ReadWiredNetworkProfile(text) From 64741b032a122c900549b38f8004ca3c11aa1327 Mon Sep 17 00:00:00 2001 From: Andrew Psaltis Date: Tue, 30 Dec 2008 23:10:18 -0500 Subject: [PATCH 18/20] curses/curses_misc.py: Added a get_edit_text function to ToggleEdit. Changed the get_selected function in ComboBox to return the index only. curses/prefs_curses.py: Completed the load+save functions of the UI Buttons are now functional. Only selecting the OK button will save the data for now. curses/wicd-curses.py: Added support for the "Always Show Wired Interface" config option Completed support for the preferences dialog curses/TODO,README: Preferences dialog is done. :-) --- curses/TODO | 4 +- curses/curses_misc.py | 10 ++- curses/prefs_curses.py | 192 +++++++++++++++++++++++++++++++---------- curses/wicd-curses.py | 33 +++++-- 4 files changed, 181 insertions(+), 58 deletions(-) diff --git a/curses/TODO b/curses/TODO index 6b3e9fb..1c80fde 100644 --- a/curses/TODO +++ b/curses/TODO @@ -1,8 +1,6 @@ Things to do (in no particular order): -* Make a settings dialog -- Finish the backend. The frontend is pretty much - done. -* Make a network config dialog +* Make a network config dialog for both wireless and wired interfaces * Make an about dialog * Implement a keyhandler function for the overall frame * Make keystrokes customizable diff --git a/curses/curses_misc.py b/curses/curses_misc.py index 898cd89..9469cfd 100644 --- a/curses/curses_misc.py +++ b/curses/curses_misc.py @@ -67,6 +67,9 @@ class ToggleEdit(urwid.WidgetWrap): def set_edit_text(self,text): self._w.set_edit_text(text) + def get_edit_text(self): + return self._w.get_edit_text() + # If we aren't sensitive, don't be selectable def selectable(self): return self.sensitive @@ -75,7 +78,7 @@ class ToggleEdit(urwid.WidgetWrap): def keypress(self,size,key): return self._w.keypress(size,key) -# Tabbed interface +# Tabbed interface, mostly for use in the Preferences Dialog class TabColumns(urwid.WidgetWrap): """ titles_dict = dictionary of tab_contents (a SelText) : tab_widget (box) @@ -265,6 +268,7 @@ class ComboBox(urwid.WidgetWrap): def selectable(self): return True - # Return a tuple of (widget,position) + # Return the index of the selected element def get_selected(self): - return self.overlay._listbox.get_focus() + wid,pos = self.overlay._listbox.get_focus() + return pos diff --git a/curses/prefs_curses.py b/curses/prefs_curses.py index 3d1eee9..d12d82e 100644 --- a/curses/prefs_curses.py +++ b/curses/prefs_curses.py @@ -43,6 +43,7 @@ class PrefsDialog(urwid.WidgetWrap): #width = 80 #height = 20 # Stuff that goes at the top + header0_t = language["gen_settings"] header1_t = language["ext_programs"] header2_t = language["advanced_settings"] @@ -52,7 +53,7 @@ class PrefsDialog(urwid.WidgetWrap): title = language['preferences'] # Blank line - self._blank = urwid.Text('') + _blank = urwid.Text('') #### #### Text in the widgets @@ -137,19 +138,19 @@ class PrefsDialog(urwid.WidgetWrap): self.always_show_wired_checkb = urwid.CheckBox(always_show_wired_t) - wired_auto_l = [] + self.wired_auto_l = [] self.wired_auto_cat = urwid.Text(wired_auto_cat_t) - self.wired_auto_1 = urwid.RadioButton(wired_auto_l,wired_auto_1_t) - self.wired_auto_2 = urwid.RadioButton(wired_auto_l,wired_auto_2_t) - self.wired_auto_3 = urwid.RadioButton(wired_auto_l,wired_auto_3_t) + self.wired_auto_1 = urwid.RadioButton(self.wired_auto_l,wired_auto_1_t) + self.wired_auto_2 = urwid.RadioButton(self.wired_auto_l,wired_auto_2_t) + self.wired_auto_3 = urwid.RadioButton(self.wired_auto_l,wired_auto_3_t) generalLB = urwid.ListBox([self.net_cat, - self.wless_edit,#self._blank, + self.wless_edit,#_blank, self.wired_edit, - self.always_show_wired_checkb,self._blank, + self.always_show_wired_checkb,_blank, self.global_dns_cat, - self.global_dns_checkb,#self._blank, + self.global_dns_checkb,#_blank, self.search_dom, - self.dns1,self.dns2,self.dns3,self._blank, + self.dns1,self.dns2,self.dns3,_blank, self.wired_auto_cat, self.wired_auto_1, self.wired_auto_2, @@ -181,10 +182,10 @@ class PrefsDialog(urwid.WidgetWrap): externalLB = urwid.ListBox([self.dhcp_header, self.dhcp0,self.dhcp1,self.dhcp2,self.dhcp3, - self._blank, + _blank, self.wired_detect_header, self.wired0,self.wired1,self.wired2, - self._blank, + _blank, self.flush_header, self.flush0,self.flush1,self.flush2 ]) @@ -208,13 +209,13 @@ class PrefsDialog(urwid.WidgetWrap): self.auto_reconn_checkb = urwid.CheckBox(auto_reconn_t) advancedLB = urwid.ListBox([self.wpa_cat, - self.wpa_cbox,self.wpa_warn,self._blank, + self.wpa_cbox,self.wpa_warn,_blank, self.backend_cat, - self.backend_cbox,self._blank, + self.backend_cbox,_blank, self.debug_cat, - self.debug_mode_checkb, self._blank, + self.debug_mode_checkb, _blank, self.wless_cat, - self.use_dbm_checkb, self._blank, + self.use_dbm_checkb, _blank, self.auto_reconn_cat, self.auto_reconn_checkb]) @@ -227,15 +228,17 @@ class PrefsDialog(urwid.WidgetWrap): #self.load_settings() # Now for the buttons: - ok_t = 'OK' cancel_t = 'Cancel' - ok_button = urwid.AttrWrap(urwid.Button('OK'),'body','focus') - cancel_button = urwid.AttrWrap(urwid.Button('Cancel'),'body','focus') + ok_button = urwid.AttrWrap(urwid.Button('OK',self.ok_callback),'body','focus') + cancel_button = urwid.AttrWrap(urwid.Button('Cancel',self.cancel_callback),'body','focus') + # Variables set by the buttons' callback functions + self.CANCEL_PRESSED = False + self.OK_PRESSED = False - self.button_cols = urwid.Columns([ok_button,cancel_button]) + self.button_cols = urwid.Columns([ok_button,cancel_button],dividechars=1) #self.active_tab = self.header0 #self.columns = urwid.Columns([('fixed',len(header0_t),self.header0), @@ -255,37 +258,130 @@ class PrefsDialog(urwid.WidgetWrap): self.__super.__init__(self.tabs) def load_settings(self): + ### General Settings + # Urwid does not like dbus.Strings as text markups + wless_iface = unicode(daemon.GetWirelessInterface()) + wired_iface = unicode(daemon.GetWiredInterface()) + self.wless_edit.set_edit_text(wless_iface) + self.wired_edit.set_edit_text(wired_iface) + self.always_show_wired_checkb.set_state( daemon.GetAlwaysShowWiredInterface()) - self.auto_reconn_checkb.set_state(daemon.GetAutoReconnect()) + + # DNS + self.global_dns_checkb.set_state(daemon.GetUseGlobalDNS()) + theDNS = daemon.GetGlobalDNSAddresses() + + i = 0 + for w in self.dns1,self.dns2,self.dns3,self.search_dom : + w.set_edit_text(misc.noneToBlankString(theDNS[i])) + i+=1 + + # Wired Automatic Connection + self.wired_auto_l[daemon.GetWiredAutoConnectMethod()-1] + + ### External Programs + dhcp_method = daemon.GetDHCPClient() + self.dhcp_l[dhcp_method].set_state(True) + + wired_link_method = daemon.GetLinkDetectionTool() + self.wired_l[wired_link_method].set_state(True) + + flush_method = daemon.GetFlushTool() + self.flush_l[flush_method].set_state(True) + + ### Advanced settings + # wpa_supplicant janx + self.wpadrivers = ["wext", "hostap", "madwifi", "atmel", + "ndiswrapper", "ipw"] + self.wpadrivers = wireless.GetWpaSupplicantDrivers(self.wpadrivers) + self.wpadrivers.append("ralink_legacy") + # Same as above with the dbus.String + self.thedrivers = [unicode(w) for w in self.wpadrivers] + self.wpa_cbox.set_list(self.thedrivers) + + # Pick where to begin first: + def_driver = daemon.GetWPADriver() + try: + self.wpa_cbox.set_show_first(self.wpadrivers.index(def_driver)) + except ValueError: + pass # It defaults to 0 anyway + + self.backends = daemon.GetBackendList() + # Remove the blank string b/c of some dbus mess + self.backends.remove('') + self.thebackends= [unicode(w) for w in self.backends] + self.backend_cbox.set_list(self.thebackends) + + # Three last checkboxes self.debug_mode_checkb.set_state(daemon.GetDebugMode()) self.use_dbm_checkb.set_state(daemon.GetSignalDisplayType()) + self.auto_reconn_checkb.set_state(daemon.GetAutoReconnect()) - def store_results(self): + def save_results(self): + """ Pushes the selected settings to the daemon. + This exact order is found in prefs.py""" + daemon.SetUseGlobalDNS(self.global_dns_checkb.get_state()) + daemon.SetGlobalDNS(self.dns1.get_edit_text(), self.dns2.get_edit_text(), + self.dns3.get_edit_text(), self.search_dom.get_edit_text()) + daemon.SetWirelessInterface(self.wless_edit.get_edit_text()) + daemon.SetWiredInterface(self.wired_edit.get_edit_text()) + daemon.SetWPADriver(self.wpadrivers[self.wpa_cbox.get_selected()]) daemon.SetAlwaysShowWiredInterface(self.always_show_wired_checkb.get_state()) + daemon.SetAutoReconnect(self.auto_reconn_checkb.get_state()) + daemon.SetDebugMode(self.debug_mode_checkb.get_state()) + daemon.SetSignalDisplayType(int(self.use_dbm_checkb.get_state())) + if self.wired_auto_2.get_state(): + daemon.SetWiredAutoConnectMethod(2) + elif self.wired_auto_3.get_state(): + daemon.SetWiredAutoConnectMethod(3) + else: + daemon.SetWiredAutoConnectMethod(1) + daemon.SetBackend(self.backends[self.backend_cbox.get_selected()]) + + # External Programs Tab + if self.dhcp0.get_state(): + dhcp_client = misc.AUTO + elif self.dhcp1.get_state(): + dhcp_client = misc.DHCLIENT + elif self.dhcp2.get_state(): + dhcp_client = misc.DHCPCD + else: + dhcp_client = misc.PUMP + daemon.SetDHCPClient(dhcp_client) + + if self.wired0.get_state(): + link_tool = misc.AUTO + elif self.wired1.get_state(): + link_tool = misc.ETHTOOL + else: + link_tool = misc.MIITOOL + daemon.SetLinkDetectionTool(link_tool) + + if self.flush0.get_state(): + flush_tool = misc.AUTO + elif self.flush1.get_state(): + flush_tool = misc.IP + else: + flush_tool = misc.ROUTE + daemon.SetFlushTool(flush_tool) + + # DNS CheckBox callback def global_dns_trigger(self,check_box,new_state,user_data=None): for w in self.search_dom,self.dns1,self.dns2,self.dns3: w.set_sensitive(new_state) + # Button callbacks + def ok_callback(self,button_object,user_data=None): + self.OK_PRESSED = True + def cancel_callback(self,button_object,user_data=None): + self.CANCEL_PRESSED = True + def ready_comboboxes(self,ui,body): self.wpa_cbox.build_combobox(body,ui,4) self.backend_cbox.build_combobox(body,ui,8) - # Normal keypress, but if we are at the top, then be "tabbish" instead - #def keypress(self,size,ui): - # self._w.keypress(size,ui) - # (wid,pos) = self._listbox.get_focus() - # if wid is self.columns: - # lw = self.listbox.body - # 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()]) - # self.listbox.body = lw - -#@wrap_exceptions() # Put the widget into an overlay, and run! def run(self,ui, dim, display): width,height = ui.get_cols_rows() @@ -296,8 +392,7 @@ class PrefsDialog(urwid.WidgetWrap): overlay = urwid.Overlay(self.tabs, display, ('fixed left', 0),width , ('fixed top',1), height-3) - # Will need once we actually get the comboboxes filled with stuff - #self.ready_comboboxes(ui,overlay) + self.ready_comboboxes(ui,overlay) keys = True while True: @@ -308,19 +403,25 @@ class PrefsDialog(urwid.WidgetWrap): if "window resize" in keys: dim = ui.get_cols_rows() if "esc" in keys or 'Q' in keys: - return - + return False for k in keys: #Send key to underlying widget: overlay.keypress(dim, k) + if self.CANCEL_PRESSED: + return False + if self.OK_PRESSED: + return True + +### +### EXTERNAL ENTRY POINT STUFF +### def run_it(): dialog = PrefsDialog(None,(0,0),ui,dbusmanager.get_dbus_ifaces()) keys = True dim = ui.get_cols_rows() dialog.load_settings() - # Will need once we actually get the comboboxes filled with stuff - #self.ready_comboboxes(ui,overlay) + dialog.ready_comboboxes(ui,dialog) while True: if keys: ui.draw_screen(dim, dialog.render(dim, True)) @@ -329,11 +430,14 @@ def run_it(): if "window resize" in keys: dim = ui.get_cols_rows() if "esc" in keys or 'Q' in keys: - return - + return False for k in keys: - #Send key to underlying widget: dialog.keypress(dim, k) + if dialog.CANCEL_PRESSED: + return False + if dialog.OK_PRESSED: + dialog.save_results() + return True if __name__=='__main__': try: diff --git a/curses/wicd-curses.py b/curses/wicd-curses.py index dd19dde..a32b242 100644 --- a/curses/wicd-curses.py +++ b/curses/wicd-curses.py @@ -241,11 +241,15 @@ class appGUI(): # spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam, # spam,spam,spam,spam] ] #self.spamLB = urwid.ListBox(spamL) - self.thePile = urwid.Pile([('fixed',1,self.wiredH), - ('fixed',1,self.wiredCB), - ('fixed',1,self.wlessH), - self.wlessLB] ) + # Choose whether to show the wired part of the interface. + if daemon.GetAlwaysShowWiredInterface(): + self.thePile = urwid.Pile([('fixed',1,self.wiredH), + ('fixed',1,self.wiredCB), + ('fixed',1,self.wlessH), + self.wlessLB] ) + else: + self.thePile = urwid.Pile([('fixed',1,self.wlessH),self.wlessLB] ) self.footer1 = urwid.AttrWrap(urwid.Text("Something important will eventually go here."),'body') self.footer2 = urwid.AttrWrap(urwid.Text("If you are seeing this, then something has gone wrong!"),'important') self.footerList = urwid.ListBox([self.footer1,self.footer2]) @@ -263,7 +267,7 @@ class appGUI(): self.connecting = False self.screen_locked = False self.connecting = False - + self.always_show_wired = daemon.GetAlwaysShowWiredInterface() self.update_status() #self.dialog = PrefOverlay(self.frame,self.size) @@ -296,7 +300,18 @@ class appGUI(): self.wiredCB.get_body().set_list(wiredL) self.wiredCB.get_body().build_combobox(self.frame,ui,3) self.wlessLB.body = urwid.SimpleListWalker(wlessL) - + # If the "Always Show Wired" part of the interface changes, change + # along with it. + if daemon.GetAlwaysShowWiredInterface() != self.always_show_wired: + if daemon.GetAlwaysShowWiredInterface(): + self.thePile = urwid.Pile([('fixed',1,self.wiredH), + ('fixed',1,self.wiredCB), + ('fixed',1,self.wlessH), + self.wlessLB] ) + else: + self.thePile = urwid.Pile([('fixed',1,self.wlessH),self.wlessLB] ) + self.frame.body = self.thePile + self.always_show_wired = not self.always_show_wired self.prev_state = state # Update the footer/status bar @@ -430,8 +445,10 @@ class appGUI(): if "P" in keys: dialog = PrefsDialog(self.frame,(0,1),ui, dbusmanager.get_dbus_ifaces()) - dialog.run(ui,self.size,self.frame) - dialog.store_results() + # There is some lag in using the buttons. Not my fault. + if dialog.run(ui,self.size,self.frame): + dialog.save_results() + self.update_ui() for k in keys: if k == "window resize": self.size = ui.get_cols_rows() From f0466be6b8cf2d6517b1a9a68cd109acda70f06f Mon Sep 17 00:00:00 2001 From: Andrew Psaltis Date: Thu, 1 Jan 2009 00:30:53 -0500 Subject: [PATCH 19/20] curses/curses_misc.py: Added a Dialog class, mostly borrowed from a urwid example curses/prefs_curses.py: Added the DNS domain to the dialog, as was done in the GTK UI curses/wicd-curses.py: Added a semi-pretty about dialog. curses/README: Activating about dialog is done by "A" --- curses/README | 1 + curses/curses_misc.py | 72 ++++++++++++++++++++++++++++++++++++++++++ curses/prefs_curses.py | 13 +++++--- curses/wicd-curses.py | 49 +++++++++++++++++++++++++--- 4 files changed, 125 insertions(+), 10 deletions(-) diff --git a/curses/README b/curses/README index f3832a6..7586196 100644 --- a/curses/README +++ b/curses/README @@ -18,6 +18,7 @@ D : disconnect from active network ESC : if connecting to a network, stop doing so ENTER : Attempt connection to selected network P : Display preferences dialog +A : Display "About" dialog IN DIALOGS: ESC or Q: Quit dialog without saving information (if present) diff --git a/curses/curses_misc.py b/curses/curses_misc.py index 9469cfd..45e3a46 100644 --- a/curses/curses_misc.py +++ b/curses/curses_misc.py @@ -272,3 +272,75 @@ class ComboBox(urwid.WidgetWrap): def get_selected(self): wid,pos = self.overlay._listbox.get_focus() return pos + + +# Almost completely ripped from rbreu_filechooser.py: +# http://excess.org/urwid/browser/contrib/trunk/rbreu_menus.py +class Dialog(urwid.WidgetWrap): + """ + Creates a BoxWidget that displays a message + + Attributes: + + b_pressed -- Contains the label of the last button pressed or None if no + button has been pressed. + edit_text -- After a button is pressed, this contains the text the user + has entered in the edit field + """ + + b_pressed = None + edit_text = None + + _blank = urwid.Text("") + _edit_widget = None + _mode = None + + def __init__(self, msg, buttons, attr, width, height, body, ): + """ + msg -- content of the message widget, one of: + plain string -- string is displayed + (attr, markup2) -- markup2 is given attribute attr + [markupA, markupB, ... ] -- list items joined together + buttons -- a list of strings with the button labels + attr -- a tuple (background, button, active_button) of attributes + width -- width of the message widget + height -- height of the message widget + body -- widget displayed beneath the message widget + """ + + # Text widget containing the message: + msg_widget = urwid.Padding(urwid.Text(msg), 'center', width - 4) + + # GridFlow widget containing all the buttons: + button_widgets = [] + + for button in buttons: + button_widgets.append(urwid.AttrWrap( + urwid.Button(button, self._action), attr[1], attr[2])) + + button_grid = urwid.GridFlow(button_widgets, 12, 2, 1, 'center') + + # Combine message widget and button widget: + widget_list = [msg_widget, self._blank, button_grid] + self._combined = urwid.AttrWrap(urwid.Filler( + urwid.Pile(widget_list, 2)), attr[0]) + + # This was the real thing I added to this class + self._linebox = urwid.LineBox(self._combined) + # Place the dialog widget on top of body: + # Width and height are increased to accomidate the linebox + overlay = urwid.Overlay(self._linebox, body, 'center', width+2, + 'middle', height+2) + + urwid.WidgetWrap.__init__(self, overlay) + + + def _action(self, button): + """ + Function called when a button is pressed. + Should not be called manually. + """ + + self.b_pressed = button.get_label() + if self._edit_widget: + self.edit_text = self._edit_widget.get_edit_text() diff --git a/curses/prefs_curses.py b/curses/prefs_curses.py index d12d82e..f6a8dfb 100644 --- a/curses/prefs_curses.py +++ b/curses/prefs_curses.py @@ -67,6 +67,7 @@ class PrefsDialog(urwid.WidgetWrap): global_dns_cat_t = ('header','Global DNS Servers') global_dns_t = ('editcp',language['use_global_dns']) + dns_dom_t = ('editcp',' DNS Domain: ') search_dom_t = ('editcp',' Search domain:') dns1_t = ('editcp',' DNS server 1: ') dns2_t = ('editcp',' DNS server 2: ') @@ -132,6 +133,7 @@ class PrefsDialog(urwid.WidgetWrap): self.global_dns_checkb = urwid.CheckBox(global_dns_t,global_dns_state, on_state_change=self.global_dns_trigger) self.search_dom = ToggleEdit(search_dom_t,global_dns_state) + self.dns_dom = ToggleEdit(dns_dom_t,global_dns_state) self.dns1 = ToggleEdit(dns1_t,global_dns_state) self.dns2 = ToggleEdit(dns2_t,global_dns_state) self.dns3 = ToggleEdit(dns3_t,global_dns_state) @@ -149,7 +151,7 @@ class PrefsDialog(urwid.WidgetWrap): self.always_show_wired_checkb,_blank, self.global_dns_cat, self.global_dns_checkb,#_blank, - self.search_dom, + self.search_dom,self.dns_dom, self.dns1,self.dns2,self.dns3,_blank, self.wired_auto_cat, self.wired_auto_1, @@ -273,7 +275,7 @@ class PrefsDialog(urwid.WidgetWrap): theDNS = daemon.GetGlobalDNSAddresses() i = 0 - for w in self.dns1,self.dns2,self.dns3,self.search_dom : + for w in self.dns1,self.dns2,self.dns3,self.dns_dom,self.search_dom : w.set_edit_text(misc.noneToBlankString(theDNS[i])) i+=1 @@ -323,7 +325,8 @@ class PrefsDialog(urwid.WidgetWrap): This exact order is found in prefs.py""" daemon.SetUseGlobalDNS(self.global_dns_checkb.get_state()) daemon.SetGlobalDNS(self.dns1.get_edit_text(), self.dns2.get_edit_text(), - self.dns3.get_edit_text(), self.search_dom.get_edit_text()) + self.dns3.get_edit_text(), self.dns_dom.get_edit_text(), + self.search_dom.get_edit_text()) daemon.SetWirelessInterface(self.wless_edit.get_edit_text()) daemon.SetWiredInterface(self.wired_edit.get_edit_text()) daemon.SetWPADriver(self.wpadrivers[self.wpa_cbox.get_selected()]) @@ -369,7 +372,7 @@ class PrefsDialog(urwid.WidgetWrap): # DNS CheckBox callback 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.dns1,self.dns2,self.dns3,self.dns_dom,self.search_dom: w.set_sensitive(new_state) # Button callbacks @@ -409,7 +412,7 @@ class PrefsDialog(urwid.WidgetWrap): overlay.keypress(dim, k) if self.CANCEL_PRESSED: return False - if self.OK_PRESSED: + if self.OK_PRESSED in keys: return True diff --git a/curses/wicd-curses.py b/curses/wicd-curses.py index a32b242..2582371 100644 --- a/curses/wicd-curses.py +++ b/curses/wicd-curses.py @@ -33,9 +33,6 @@ at least get a network connection. Or those who don't like using X. :-) Comments, criticisms, patches, bug reports all welcome! """ -##### NOTICE: THIS ONLY WORKS WITH THE SOURCE IN WICD 1.6 AS FOUND IN THE BZR -##### REPOSITORIES! - # UI stuff #import urwid.raw_display import urwid.curses_display @@ -56,7 +53,7 @@ import sys from time import sleep # Curses UIs for other stuff -from curses_misc import SelText,ComboBox +from curses_misc import SelText,ComboBox,Dialog import prefs_curses from prefs_curses import PrefsDialog @@ -208,6 +205,42 @@ def gen_network_list(): wlessL.append(urwid.AttrWrap(SelText(theString),'body','focus')) return (wiredL,wlessL) +def about_dialog(body): + # This looks A LOT better when it is actually displayed. I promise :-). + # The ASCII Art "Wicd" was made from the "smslant" font on one of those + # online ASCII big text generators. + theText = [ +('green'," /// \\\\\\")," _ ___ __\n", +('green'," /// \\\\\\")," | | /| / (_)______/ /\n", +('green'," /// \\\\\\")," | |/ |/ / / __/ _ / \n", +('green',"/|| // \\\\ ||\\")," |__/|__/_/\__/\_,_/ \n", +('green',"||| ||"),"(|^|)",('green',"|| |||"), +" ($VERSION) \n".replace("$VERSION",daemon.Hello()), + +('green',"\\|| \\\\")," |+| ",('green',"// ||/ \n"), +('green'," \\\\\\")," |+| ",('green',"///")," http://wicd.net \n", +('green'," \\\\\\")," |+| ",('green',"///")," Brought to you by:\n", +('green'," \\\\\\")," |+| "('green',"///")," Adam Blackburn (wicd)\n", +" ___|+|___ Dan O'Reilly (wicd)\n", +" |---------| Andrew Psaltis (this ui)\n", +"---------------------------------------------------"] + about = Dialog(theText,['OK'],('body','body','focus'),55,14,body) + + keys = True + dim = ui.get_cols_rows() + while True: + if keys: + ui.draw_screen(dim, about.render(dim, True)) + + keys = ui.get_input() + if "window resize" in keys: + dim = ui.get_cols_rows() + if "esc" in keys: + return False + for k in keys: + about.keypress(dim, k) + if about.b_pressed == 'OK': + return False ######################################## ##### APPLICATION INTERFACE CLASS @@ -449,6 +482,8 @@ class appGUI(): if dialog.run(ui,self.size,self.frame): dialog.save_results() self.update_ui() + if "A" in keys: + about_dialog(self.frame) for k in keys: if k == "window resize": self.size = ui.get_cols_rows() @@ -524,7 +559,11 @@ def main(): ('editcp', 'default', 'default', 'standout'), ('editbx', 'light gray', 'dark blue'), ('editfc', 'white','dark blue', 'bold'), - ('tab active','dark green','light gray')]) + ('tab active','dark green','light gray'), + # Simple colors around text + ('green','dark green','default'), + ('blue','dark blue','default'), + ('red','dark red','default')]) # This is a wrapper around a function that calls another a function that is a # wrapper around a infinite loop. Fun. ui.run_wrapper(run) From 8ed394abf3ac5c5ea70819cdb2801f6ed6a9b749 Mon Sep 17 00:00:00 2001 From: Andrew Psaltis Date: Thu, 1 Jan 2009 00:36:08 -0500 Subject: [PATCH 20/20] curses/*.py: updated copyrights to include 2009 curses/wicd-curses.py: Fixed missing comma in the About Dialog text --- curses/curses_misc.py | 2 +- curses/prefs_curses.py | 2 +- curses/wicd-curses.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/curses/curses_misc.py b/curses/curses_misc.py index 45e3a46..91d9c2a 100644 --- a/curses/curses_misc.py +++ b/curses/curses_misc.py @@ -5,7 +5,7 @@ wicd-curses. """ -# Copyright (C) 2008 Andrew Psaltis +# Copyright (C) 2008-9 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 diff --git a/curses/prefs_curses.py b/curses/prefs_curses.py index f6a8dfb..3dd275a 100644 --- a/curses/prefs_curses.py +++ b/curses/prefs_curses.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -# Copyright (C) 2008 Andrew Psaltis +# Copyright (C) 2008-9 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 diff --git a/curses/wicd-curses.py b/curses/wicd-curses.py index 2582371..aa5ac35 100644 --- a/curses/wicd-curses.py +++ b/curses/wicd-curses.py @@ -7,7 +7,7 @@ at least get a network connection. Or those who don't like using X. :-) """ -# Copyright (C) 2008 Andrew Psaltis +# Copyright (C) 2008-9 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 @@ -220,7 +220,7 @@ def about_dialog(body): ('green',"\\|| \\\\")," |+| ",('green',"// ||/ \n"), ('green'," \\\\\\")," |+| ",('green',"///")," http://wicd.net \n", ('green'," \\\\\\")," |+| ",('green',"///")," Brought to you by:\n", -('green'," \\\\\\")," |+| "('green',"///")," Adam Blackburn (wicd)\n", +('green'," \\\\\\")," |+| ",('green',"///")," Adam Blackburn (wicd)\n", " ___|+|___ Dan O'Reilly (wicd)\n", " |---------| Andrew Psaltis (this ui)\n", "---------------------------------------------------"]