uick Guide to Python's Snack Module

Quick Guide to Python's Snack Module

uick Guide to Python's Snack Module

Written by Jared Riley

Snack is a python library based on newt that can be used to
create a simple text based User interface. This is the package
that was used by Red Hat to create their installation along with
a number of configuration tools. It is an ideal platform upon
which to create installation and configuration scripts,
particularly if you don't want to rely on X, or you want to
avoid complexity.

The newt RPM on Red Hat Linux comes with two sample python
programs: peanuts.py, and popcorn.py. From these it is expected
that the programmer will be able to figure out what to do.
There is also an SGML guide to the newt C library. From this
you can also make some inferences, but some things are
different in the python world. You should read this document
anyway because it gives an overview of how newt/snack programs work.

I have used snack to create a configuration program for
software that will run on a server without X Windows installed.
Along the way, I had to read the sample programs, Red Hat's
installation program, and the C Library documentation.
Hopefully the information here will help you do it with far less
effort.

Other Resources

I have only documented what I have used. If you would like to
undertake more complex programs or use features not discussed,
here are some other places to look. Unless otherwise mentioned,
these files are contained in the newt or newt-devel RPMS on Red
Hat. Their location may be different on your version of Linux.

  1. snack.py

    This is the interface to the newt library. Whatever is in
    this file is what you are allowed to access from your
    program.

  2. tutorial.sgml

    This file documents some of the calls in the newt library
    for C. Not every call is documented, and not every call is
    available through snack.

    If you are like me and you can't figure out how to get
    something readable out of sgml in less than half a day, you
    can find it on the internet. I found it
    here.

  3. peanuts.py and popcorn.py

    These sample programs come with the newt-devel RPM on Red
    Hat. Between them, they use most of the widgets that are
    available in snack.

Loading Snack

To use the snack module, import it into your program.

from snack import *

Snack provides the SnackScreen class, which you must allocate
before you can do anything.

screen = SnackScreen()

This call will paint your console a lovely blue. It will stay
this way forever, unless you call the finish method.

screen.finish()

Calling the finish method is very important. If you don't call
it, then your screen will be left in a fairly unusable state
when you exit python. To recover, you will have to call
reset from the shell. You may also have to fix your
interrupt and end of file keys (stty intr ^C; stty eof ^D).

Another caveat is that newt takes over your terminal and
prevents signals due to keystrokes. ^Z and ^C will not work, so
you should make an effort to provide a way to exit the program
cleanly. There is a way to allow job control keystrokes to
function correctly that will be discussed later.

Forms and Grids

You can't do very much with snack without using Forms and
Grids. These two items have been combined into the GridFormHelp
and GridForm classes, but I will leave those for you to figure
out. Instead, I will show how to use basic Forms and Grids to
create your display. In a later section I will show the types
of Widgets that can be used.


screen = SnackScreen()
upperleft = Widget()
upperright = Widget()
lowerleft = Widget()
lowerright = Widget()
g = Grid(2, 2)
g.setField(upperleft, 0, 0, (0, 0, 1, 1))
g.setField(upperright, 1, 0)
g.setField(lowerleft, 0, 1)
g.setField(lowerright, 1, 1, growx = 1, growy = 1)

screen.gridWrappedWindow(g, "Title Text")
f = Form()
f.add(g)
result = f.run()
screen.popWindow()
screen.finish()

Grid

The call to grid sets the size of the grid. The width comes
before the height, so in this case, the grid will be two widgets
wide and 2 widgets high.

After creating the grid, the widgets are added into the grid
using the setField method. The second and third parameters are
the location in the grid where the widget is to be placed.
Again, the horizontal parameter comes before the vertical
setting. Since python is zero based, the available positions
are 0 to horizontal size - 1.

snack will not complain if you put more than one widget into
the same location in the grid. However, it will put all of the
elements on top of each other, so you are unlikely to be pleased
with the result.

In the first call to setField, I have used the optional padding
argument. This is a tuple listing how much room is to be left
around the widget in the grid. The order of the widget is
(Left, Top, Right, Bottom).

The last call to setField has used the growx and growy
parameters. These are used when you have different sized
widgets. In order to make your window appear balanced, snack
will add extra space into a smaller widget if it can so that the size
of that widget will be grown to be the size of the widgets
around it.

You may create a grid, and then put that grid into another
grid's field. In this way you can create windows with different numbers
of fields in different rows or columns.

Before the grid can be displayed on the screen, you must call
screen.gridWrappedWindow(). This configures the grid and
readies it for display.

Form

The Form is the thing that holds all of the widgets and
displays them on the screen. It returns the widget that caused
the form to be exited. Once the form has returned, you must
call the screen.popWindow() method to remove the window from the
screen.

You must add each widget to the form. However, there is a
shortcut. If you have put all of your elements into a grid,
then you can just add the grid. This will recursively add each
element from the grid to the form.

reflow(text, width, flexDown = 5, flexUp = 5)

This is the only function in the snack library.
reflow takes some text and reformats it to fit into the
parameters given. flexDown and flexUp are
guidelines to the library as to how many lines it should try to
fit the text into while it is reconfiguring. reflow
takes line breaks literally, so if you are
going to use strings enclosed by """, then you should use line
continuation characters to keep each paragraph together.

reflowreturns a tuple of the new wrapped text, the
width of the text, and the actual height after adjustments.

Widget Classes

Here is a list of the different types of Widgets, and some of
the parameters to the init function, and their methods:

  • Button(text)

    Buttons are pretty simple. The text that you pass to the
    button is what will be displayed on the button. There isn't
    really a way to change the text later, so to do that, you
    would have to create a new button.

  • CompactButton(text)

    These are the same as buttons, but they don't have a nice
    border. Use them when you are cramped for space on the
    screen.

  • Checkbox(text, isOn = 0)

    This produces a box that can be turned on or off. It is
    independent of other checkboxes that you might have in the
    form. The text is displayed adjacent to the box.
    isOn is optional, and sets the initial value of the
    box.

    The Checkbox has the following methods:

    • value()

      Returns whether the box is on or not.

    • selected()

      Returns whether the box is currently selected or not.
      This is different from whether or not the box is on as
      returned by value().

    • setFlags(flag, sense)

      setFlags is used to change whether or not a
      user can actually select the checkbox. The possible
      values of sense are FLAGS_SET,
      FLAGS_RESET, and FLAGS_TOGGLE. FLAGS_SET
      sets a particular flag to true. FLAGS_RESET sets the
      flag to false. FLAGS_TOGGLE sets it to the opposite
      value of what it currently is.

      The only flag currently allowed is
      FLAG_DISABLED. This flag sets whether or not
      the checkbox can be selected, and thereby changed.

    • setValue()

      Sets the current value of the checkbox.

  • SingleRadioButton(text, group, isOn = 0)

    A Radio button is a widget, of which only one in a group can be
    selected. group is the group of radio buttons to
    which this button belongs. If there are no other buttons in
    the group yet, then pass in None. If you have other
    buttons, pass in one of the other buttons to associate the
    buttons together.

    It has the following methods:

    • selected()

      Returns whether this button is the one in the group
      that is selected.

  • Listbox(height, scroll = 0,
    returnExit = 0, width = 0, showCursor = 0)

    A list box offers a list of selection. Only one thing can
    be selected at a time. If you want to have multiple
    selections, use a CheckboxTree.

    height is how
    many lines there should be in the box. scroll
    determines whether or not a scroll bar is present.
    returnExit says that if the ENTER key is pressed
    while in the box, the form should be exited. width
    is the number of characters wide the Listbox should be.
    showCursor sets the status of the text cursor. In
    general, you want to have it off because having it on is
    fairly ugly.

    There is quite a bit you can do with a list box.

    • append(text, item)

      Adds an entry to the end of the Listbox. text
      is whatever text you want to be displayed
      in the list box. item is any object you would
      like. You can pass in an integer, a string, or anything
      at all.

    • insert(text, item, before)

      Inserts an entry before another entry in the Listbox.
      before is whatever object you passed in for
      another item in the Listbox (not the display text). If
      you pass in "None" for before, then this item will be
      inserted at the beginning of the list.

    • delete(item)

      Removes something from the Listbox. item is
      whatever you passed in to the listbox before when you
      added the element to the list.

    • replace(text, item)

      Replaces another element with this element. The
      element it replaces will be given by item. The new
      element in the list will still return the same item.

    • current()

      Returns the item corresponding to the current selection
      in the Listbox. It does not return the text.

    • setCurrent(item)

      Selects the entry in the Listbox given by
      item.

    • clear()

      Empties the Listbox.

  • Textbox(width, height, text, scroll = 0, wrap = 0)

    A Textbox displays some text. width,
    height, and text are fairly obvious. They
    set the size of the box and the text that should be
    displayed in it. scroll determines whether or not
    there is a vertical scrollbar on the box. There is no such
    thing as a horizontal scrollbar in snack, so you should
    probably use wrap to keep everything inside the
    box.

    Textbox offers only one method. setText(text) lets
    you change the text that will be displayed in the box.

  • TextboxReflowed(width, text, flexDown = 5, flexUp = 10,
    maxHeight = -1)

    This is a Textbox that uses reflow to
    configure the text that will be displayed.

  • Scale(width, total)

    Scales are different from other widgets in that they are
    intended to be updated on the fly. This means that you must
    use some other features of the Form and SnackScreen classes
    that are not required by other widgets.

    width is the character width of the scale on the screen.
    total is the amount that the scale should be out
    of.

    The Scale class has only one method:

    • set(amount)

      amount is a value between 0 and the
      total parameter used to set up the Scale.

    Here is an example of using a scale. Notice that instead
    of running the form, the draw method is used instead. Also,
    the screen must be refreshed every time that we want to see
    what has happened.

    screen = SnackScreen()
    g = Grid(1, 1)
    s = Scale(40, 1000)
    s.set(0)
    g.setField(s, 0, 0)
    screen.gridWrappedWindow(g, "Scale Example")
    f = Form()
    f.add(s)

    for i in range(1000):
    for j in range(10000):
    pass
    s.set(i)
    f.draw()
    screen.refresh()

    screen.finish()

    If you had other components on the form, and you wanted to
    get user input, then you would have to run the form at this
    point to get the result of the user's input. However, the
    scale would no longer be updated once the form was run.

  • Entry(width, text = "", hidden = 0, password = 0, scroll
    = 1, returnExit = 0)

    width is the size of the field where text is
    entered. text is the initial value of the entry.
    hidden hides the text from view. The user can't
    tell that anything is there, and when they edit, they can't
    tell what they are typing. password displays the
    value of the entry as a series of '*'. scroll
    allows the user to scroll horizontally if the text of the
    entry is too wide to fit in the space it has.
    returnExit indicates that the containing Form
    should exit when the ENTER key is pressed in the box.

    Entry boxes have the following methods:

    • value()

      This returns the text that is currently in the
      Entry.

    • set(text)

      Changes the text that is currently in the Entry.

    • setFlags(flag, sense)

      setFlags is used to change whether or not a
      user can actually select the Entry and change it's
      value. The possible
      values of sense are FLAGS_SET,
      FLAGS_RESET, and FLAGS_TOGGLE. FLAGS_SET
      sets a particular flag to true. FLAGS_RESET sets the
      flag to false. FLAGS_TOGGLE sets it to the opposite
      value of what it currently is.

      The only flag currently allowed is
      FLAG_DISABLED. This flag sets whether or not
      the Entry can be selected, and thereby changed.

Useful Combination Classes

There are several classes which have been defined for
convenience because they are so common.

  • RadioBar(screen, buttonlist)

    This class provides an easy way to set up a group of radio
    buttons, instead of doing everything yourself.

    buttonlist is a list of tuples containing
    information about each button to be contained in the group.
    Each tuple has three elements. The first is a title for the
    button. The second element is an object to return if the
    Radio button is currently selected. The third is either 0
    or 1. If 1, it indicates that this button is the default.

    RadioBar has the
    following methods:

    • add(title, value, default = None)

      This method creates a new Radio Button and adds it to the
      group. value is an object associated with the
      button that will be returned by getSelection

    • getSelection()

      Returns the value associated with the Radio button that
      is currently selected.

  • ButtonBar(screen, buttonlist)

    This class provides an easy way to set up a group of
    buttons without having to do everything yourself. The
    comments in snack.py suggest that when a ButtonBar is added
    to a grid, the growx parameter be set.

    buttonlist is a list of buttons that can take
    three different types of button descriptions in the
    list.

    1. If the element is a tuple consisting of three elements,
      then the elements are assumed to be the following:

      1. The text to display in the button.

      2. The value to return to the user when the
        buttonPressed method is called.

      3. A hotkey to be associated
        with the button.

    2. A tuple consisting of only two elements is the same as
      for three elements, but without a hotkey.

    3. A string is also acceptable. In this case, the
      text for the button is set to the string. The value for
      the button is set to the lower case value of the
      string.

    A ButtonBar has only one method:
    buttonPressed(result). result is the return
    value from calling Form.run(). If the Form exited because
    of a hotkey mapped to one of the buttons, or because one of
    the buttons was pressed, this function will return the value
    associated with that button. Otherwise, it will return
    None.

  • CheckboxTree(height, scroll = 0)

    This class creates a container for a tree of Checkboxes.
    An entry in the tree can either be a branch with checkboxes
    and branches under it, or it can be a checkbox.

    There are several methods associated with a CheckboxTree:

    • append(text, item = None, selected = 0)

      This method adds an element to the end of the top level
      of the CheckboxTree.

      As usual, text is the display text for this
      item. item is an object of your choice. It
      should be unique for any elements that will be accessed
      later by the class. If you don't supply an item, the
      text will be used. selected indicates whether
      the item is initially selected.

    • addItem(text, path, item = None, selected = 0)

      This method allows you to add an item to the
      CheckboxTree anywhere in the tree. The parameters are
      the same as for the append method, except for the
      path parameter.

      path is a tuple of integers which indicates to
      the CheckboxTree where this item should be placed in the
      tree. The last element of the tuple should be
      snackArgs['append'] to indicated that the tuple
      is terminated. Here is an example of adding several things to a
      CheckboxTree:

      tree = CheckboxTree(height = 5, scroll = 1)
      tree.addItem("First", (snackArgs['append'], ))
      tree.addItem("Second", (snackArgs['append'], ))
      tree.addItem("A", (0, snackArgs['append']))
      tree.addItem("B", (0, snackArgs['append']))
      tree.addItem("X", (0, 1, snackArgs['append']))

      This would produce a tree with containing five
      entries. Initially, only the items labeled
      First and Second would be displayed.
      Second would be a Checkbox, while First would
      be expandable. If you expanded
      First, you would see underneath it the
      A, and B items. A would be a
      checkbox, and B would be expandable. Finally, if you
      expanded B, you would see a Checkbox for
      X.

    • getCurrent()

      Returns the item that the cursor is currently
      highlighting.

    • getSelection()

      Returns a list of all items that are currently
      selected.

    • setEntry(item, text)

      Changes the text associated with this particular
      item.

    • setEntryValue(item, selected = 1)

      Set whether or not the entry associated with this item
      is selected.

    • getEntryValue(item)

      Return whether or not the entry associated with this
      item is selected.

Common Dialog Functions

There are three functions that can be used to display common
dialog boxes.

  • ListboxChoiceWindow(screen, title, text, items, buttons =
    ('Ok', 'Cancel'), width = 40, scroll = 0, height = -1,
    default = None, help = None)

    This function displays a dialog with some descriptive text,
    a listbox, and buttons at the bottom. It returns a tuple
    consisting of the button
    that was pressed to exit the dialog, and the entry in the
    list that was selected. It's parameters are:

    • screen

      A valid SnackScreen

    • title

      The title for the dialog box.

    • text

      Text that should be displayed at the top of the dialog
      box before the Listbox.

    • items

      This can be a list or tuple of things to display in the
      Listbox. Each item in the list should be either a
      string or a tuple. If it is a tuple, the first field is
      the text to display, and the second is an item
      associated with that text. If a string, then the text
      to display is set to the string, and the item returned
      will be the numeric position of the entry in the
      Listbox.

    • buttons

      The buttons parameter is passed directly through to a
      ButtonBar.

    • width

      This is used as the width of the text box at the top of
      the dialog.

    • scroll
    • height
    • default

      All of these items are given to the
      Listbox.

    • help

      This is the argument passed to the help callback
      function. See the section on help
      for more information.

  • ButtonChoiceWindow(screen, title, text, buttons = ['Ok',
    'Cancel'], width = 40, x = None, y = None, help = None)

    This function displays some text to the user and a set of
    buttons for them to press at the bottom of the dialog. It
    returns the button that was pressed.

    The parameters mean the following things:

    • screen

      A valid SnackScreen.

    • title

      The title for the dialog box.

    • text

      The text to be displayed in the dialog box.

    • buttons

      The buttons parameter is passed directly through to a
      ButtonBar.

    • width

      This is used as the width of the Textbox.

    • x, y

      These parameters are used to set the location of the Window on
      the screen. If they are not given, the window is centered,
      which is probably what you want.

    • help

      This is the argument passed to the help callback
      function. See the section on help
      for more information.

  • EntryWindow(screen, title, text, prompts, allowCancel =
    1, width = 40, entryWidth = 20, buttons = [ 'Ok', 'Cancel'
    ], help = None)

    This function displays a window with a set of Entries in it
    to be filled in by the user. It returns the button that was
    pressed to exit the dialog.

    The parameters mean the following things:

    • screen

      A valid SnackScreen.

    • title

      The title for the dialog box.

    • text

      The text to be displayed in the dialog box.

    • buttons

      The buttons parameter is passed directly through to a
      ButtonBar.

    • prompts

      This is a list of the prompts to be used as labels for
      each Entry. A string is used as the label for the
      Entry. On the other hand, if the prompt is a tuple,
      then the first element of the tuple is used as the
      tuple, and the second element is assumed to be an
      already existing Entry.

    • allowCancel

      This isn't actually used for anything in the version of
      snack that I worked with.

    • width

      This is used as the width of the Textbox.

    • entryWidth

      This value is used as the width for each Entry.

    • buttons

      As usual, this is passed through to a
      ButtonBar widget.

    • help

      This is the argument passed to the help callback
      function. See the section on help
      for more information.

help

There are two ways to provide help to the user.

The first method is
to display a single line of help on the bottom of the screen and
is quite easy to do. This is done using the
pushHelpLine and popHelpLine methods of the
SnackScreen class. These calls must be bracketing. That is,
each pushHelpLine must match with a call to
popHelpLine.

A default help line is displayed if none is supplied, or if you
try to set the help line to None. If you wish to
remove the help line entirely, push a string with a space
(" ").

The second method for providing help is to do something when
the user presses <F1>. An example action for <F1>
would be to bring up a dialog box with detailed explanations of
the screen, and instructions on how to fill out the dialog box.
To do this, you must create a function, and arrange for it to be
called when the user presses <F1>. The
helpCallback method of SnackScreen lets you set the
help function.

Your help callback function should take two parameters (in
addition to self). The first is the SnackScreen which is making
the call. The second is an object which is used to indicate
what screen the callback is coming from. Writing the actual
function is fairly simple. Use the object to look up the help
text that should be displayed in a dictionary and display it
Alternatively, you could just put everything into the main
object so that all the help callback has to do is display what
it is given.

The next thing to do is arrange for the help callback to be
called with the correct object at the right time. This is done
when you create your Form. The Form class takes an option help
parameter. It is this parameter that will be passed to the
callback.

Hotkeys

Hotkeys are useful for providing your users with a way to
navigate your dialog boxes without being forced to always tab to
a button. If you have several widgets in a form, tabbing to the
correct button quickly becomes tedious.

In general, <F1> is always defined as
help, while <F12> is defined as exit
the Form. The ButtonBar widget provides an easy way to use hot
keys. Passing in a third element in a tuple for a button causes
the hot key to be set to that element.

If you want to add a hot key to a Form, then you can use the
addHotKey method. This method will be called automatically for
any widgets with a hotkeys list as member of the class.

Available hot keys are defined in the hotkeys dictionary in the
snack.py module. You can see there that the correct way to use
a hot key is to pass in it's matching string.

If a hot key is pressed to exit the form, it will be the return
value of running the form. You can check this directly, or pass
it to your ButtonBar to find out the equivalent button.

It is actually possible to add your own hot keys to the hotkeys
dictionary.

import snack
ESC = '^['
snack.hotkeys[ESC] = ord(ESC)
snack.hotkeys[ord(ESC)] = ESC

In this example, ESC is set to the actual value of the escape
key. You can produce this by typing Ctrl-V ESC in vi, or Ctrl-Q
ESC in emacs. Note that the key is double mapped in the dictionary.


Jared Riley



Last modified: Mon Nov 11 16:36:10 EST 2002

原文地址:https://www.cnblogs.com/lexus/p/2368378.html