Discussion:
[Urwid] ListBox and focus problem
Jason But
2013-01-28 23:26:02 UTC
Permalink
Hello all

I've been hacking away for the last three days with urwid, my program is
visually organised as follows (with extra stuff but this is the most
important bit)

---------------------------------------------------------------
|listbox widget | terminal widget |
| | |
| | |
| | |
| | |
| | |
| | |
---------------------------------------------------------------

The terminal widget is tied to a callable which executes a series of
external commands (using subprocess.call). The callable also sends progress
information to the main program using a watch_pipe where a handler takes
progress information to update the currently selected item in the listbox. A
summary of the process is that the listbox displays a list of tasks to
execute, which are executed in the terminal window, the selected item is the
listbox is updated to indicate what the current task is.

The problem is this.

1) The program works fine ONLY if the applications executed in the terminal
widget do NOT require any keyboard input.

2) The two widgets above are in a Columns, if O use a "cols.focus_col = 1"
(giving focus to the terminal widget), then all of a sudden any applications
that require keyboard input start working as the terminal is the selected
widget. However, because the listbox has lost focus, the currently selected
item is no longer highlighted

Any ideas on how I can achieve both?

Thanks in advance

Jason
Ian Ward
2013-01-28 23:49:17 UTC
Permalink
Post by Jason But
1) The program works fine ONLY if the applications executed in the terminal
widget do NOT require any keyboard input.
2) The two widgets above are in a Columns, if O use a "cols.focus_col = 1"
(giving focus to the terminal widget), then all of a sudden any applications
that require keyboard input start working as the terminal is the selected
widget. However, because the listbox has lost focus, the currently selected
item is no longer highlighted
Any ideas on how I can achieve both?
Don't use the focus_map AttrMap setting for the ListBox items. You
could just put an AttrMap(task, 'active task) around the task that is
active, maybe also add some text like an arrow or something to help
people with trouble distinguishing colours. You can continue forcing
the ListBox to change focus when the task changes (to keep the active
task visible within the ListBox)

You should also make the ListBox not selectable by wrapping it with a
WidgetDisable() so that the user doesn't accidentally move focus away
from the Terminal.

Ian
Jason But
2013-01-29 00:50:34 UTC
Permalink
Thanks Ian for your quick reply
Post by Ian Ward
Post by Jason But
1) The program works fine ONLY if the applications executed in the terminal
widget do NOT require any keyboard input.
2) The two widgets above are in a Columns, if O use a "cols.focus_col = 1"
(giving focus to the terminal widget), then all of a sudden any applications
that require keyboard input start working as the terminal is the selected
widget. However, because the listbox has lost focus, the currently selected
item is no longer highlighted
Any ideas on how I can achieve both?
Don't use the focus_map AttrMap setting for the ListBox items. You
could just put an AttrMap(task, 'active task) around the task that is
active, maybe also add some text like an arrow or something to help
people with trouble distinguishing colours. You can continue forcing
the ListBox to change focus when the task changes (to keep the active
task visible within the ListBox)
I am a little unsure of your suggestion here. At the moment my code looks like:

self.task_listbox = urwid.ListBox(urwid.SimpleListWalker(
[urwid.AttrWrap(urwid.Text(item), 'default', 'focus') for item in item_list]
))

Where 'default' and 'focus' are the two palette attributes and item_list is
a list of text information.

Are you suggesting I run AttrMap on the old selection and the new selection
each time the selection changes? If so, what would be the cleanest way to do
this, something like:

urwid.AttrMap(self.task_listbox.contents[0][item_no], ?????)
Post by Ian Ward
You should also make the ListBox not selectable by wrapping it with a
WidgetDisable() so that the user doesn't accidentally move focus away
from the Terminal.
Good idea and thanks

Jason
Post by Ian Ward
Ian
_______________________________________________
Urwid mailing list
Urwid at lists.excess.org
http://lists.excess.org/mailman/listinfo/urwid
--
----------
Dr. Jason But
Program Co-Ordinator, Bachelor of ICT (Network Design and Security)
Telecommunications Engineering Academic Group
Faculty of Information and Communication Technologies
Swinburne University of Technology

Phone: +61 3 9214 4839
Email: jbut at swin.edu.au
www: http://caia.swin.edu.au
Jason But
2013-01-29 04:37:11 UTC
Permalink
OK, I managed to get it working

Attached is some sample code if you would like to add it to your respository
or put somewhere for others to use

Jason
Post by Jason But
Thanks Ian for your quick reply
Post by Ian Ward
Post by Jason But
1) The program works fine ONLY if the applications executed in the terminal
widget do NOT require any keyboard input.
2) The two widgets above are in a Columns, if O use a "cols.focus_col = 1"
(giving focus to the terminal widget), then all of a sudden any applications
that require keyboard input start working as the terminal is the selected
widget. However, because the listbox has lost focus, the currently selected
item is no longer highlighted
Any ideas on how I can achieve both?
Don't use the focus_map AttrMap setting for the ListBox items. You
could just put an AttrMap(task, 'active task) around the task that is
active, maybe also add some text like an arrow or something to help
people with trouble distinguishing colours. You can continue forcing
the ListBox to change focus when the task changes (to keep the active
task visible within the ListBox)
self.task_listbox = urwid.ListBox(urwid.SimpleListWalker(
[urwid.AttrWrap(urwid.Text(item), 'default', 'focus') for item in item_list]
))
Where 'default' and 'focus' are the two palette attributes and item_list is
a list of text information.
Are you suggesting I run AttrMap on the old selection and the new selection
each time the selection changes? If so, what would be the cleanest way to do
urwid.AttrMap(self.task_listbox.contents[0][item_no], ?????)
Post by Ian Ward
You should also make the ListBox not selectable by wrapping it with a
WidgetDisable() so that the user doesn't accidentally move focus away
from the Terminal.
Good idea and thanks
Jason
Post by Ian Ward
Ian
-------------- next part --------------
A non-text attachment was scrubbed...
Name: tasks_ui.py
Type: text/x-python
Size: 10070 bytes
Desc: not available
Url : http://lists.excess.org/pipermail/urwid/attachments/20130129/0aae6b6d/attachment.py
-------------- next part --------------
#!/usr/bin/python
#

import tasks_ui

################################################################################
## Sample program
################################################################################
def main():
information = [('Program:', 'Test program'),
('Message:', 'Hello World'),
('Meaning of life:', '42'),
('South:', 'Not north')
]

dir_list = 'ls -l'
whoami = '/usr/bin/whoami'
date_time = '/bin/date'
sleep = '/bin/sleep 2'
msgbox = '/usr/bin/dialog --title \"Hello to my test\" --msgbox \"Press enter to continue\" 0 0'


my_tasks = []

## Create a set of arbitrary tasks, delay two seconds after each command so
## the program doesn't execute too quickly

my_tasks.append({'description' : u"File Listing",
'commands' : [dir_list, sleep]
})

my_tasks.append({'description' : u"Dialog Box",
'commands' : [msgbox, sleep]
})

my_tasks.append({'description' : u"Basic Information",
'commands' : [whoami, sleep, date_time, sleep]
})

my_tasks.append({'description' : u"File Listing 2",
'commands' : [dir_list, sleep]
})

my_tasks.append({'description' : u"All at once",
'commands' : [dir_list, sleep, whoami, sleep, date_time, sleep]
})




UI = tasks_ui.JobInterface('URWID Task Processor Demo', 'Task List', information, my_tasks, progress_bar = True)

UI.main()


if __name__ == '__main__':
main()

Loading...