ChangeLog | 7 ++
MANIFEST.in | 6 ++
examples/appliance | 55 +++++-------------
examples/simple_app.py | 83 ++++++++++++++++++++++++++++
newt_syrup/__init__.py | 5 +
newt_syrup/application.py | 114 +++++++++++++++++++++++++++++++++++++++
newt_syrup/appscreen.py | 134 ++++++++++++++++++++++++++++++++++++++++++++++
newt_syrup/page.py | 105 ++++++++++++++++++++++++++++++++++++
setup.py | 2
9 files changed, 471 insertions(+), 40 deletions(-)
New commits:
commit 40e6e76dd37f48c413ceb7c5e9a1377454f69915
Author: Darryl L. Pierce <dpierce(a)redhat.com>
Date: Sat Apr 7 11:19:57 2012 -0400
Bumped the release to 0.2.0.
Added the new source files to the manifest.
diff --git a/MANIFEST.in b/MANIFEST.in
index 6f00212..0c6ba85 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -4,6 +4,12 @@ include COLORS
include COPYING
include newt_syrup/__init__.py
+include newt_syrup/application.py
+include newt_syrup/appscreen.py
+include newt_syrup/page.py
+
+# the old classes
+
include newt_syrup/configscreen.py
include newt_syrup/menuscreen.py
include newt_syrup/utils.py
diff --git a/setup.py b/setup.py
index af22130..595bf35 100755
--- a/setup.py
+++ b/setup.py
@@ -3,7 +3,7 @@
from setuptools import setup, find_packages
setup(name = "newt_syrup",
- version = "0.1.2",
+ version = "0.2.0",
packages = find_packages(),
author = "Darryl L. Pierce",
author_email = "dpierce(a)redhat.com",
commit d526eea95544db27eb9db0076cb888805969fe05
Author: Darryl L. Pierce <dpierce(a)redhat.com>
Date: Sat Apr 7 11:03:19 2012 -0400
Updated the appliance example to use the new framework.
It now uses the Application and Page classes to create the screen and
interact with the user.
diff --git a/ChangeLog b/ChangeLog
index 4c7c973..1cf50f1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -4,6 +4,7 @@ Change Log:
- Created the Applicatoin class.
- Created the ApplicationScreen class.
- Created the Page class.
+ - Updated the appliance example to use the new framework.
* Version 0.1.2:
- Added the utils module.
diff --git a/examples/appliance b/examples/appliance
index d4c94fd..7dd8e7e 100755
--- a/examples/appliance
+++ b/examples/appliance
@@ -18,45 +18,15 @@
# MA 02110-1301, USA. A copy of the GNU General Public License is
# also available at
http://www.gnu.org/copyleft/gpl.html.
-from newt_syrup.configscreen import *
+import newt_syrup
-MAIN_PAGE = 1
+from snack import Entry, Grid, Label, RadioBar
-class ApplianceConfigScreen(ConfigScreen):
- """
- This example presents an application that looks like
- an appliance more than an application.
- """
+class ConfigPage(newt_syrup.Page):
+ def __init__(self, app):
+ newt_syrup.Page.__init__(self, app, "Simple Kiosk Application")
- def __init__(self):
- ConfigScreen.__init__(self, "Example Appliance")
-
- def get_elements_for_page(self, screen, page):
- return self.get_elements_for_main_page(screen)
-
- def use_full_screen(self):
- return True
-
- def use_compact_buttons(self):
- return True
-
- def page_has_next(self, page):
- return (page < len(self.get_page_list()))
-
- def get_page_list(self):
- return ["Configure root password",
- "Configure security mode",
- " ",
- "Restart system",
- "Backup system components",
- " ",
- "Configure keyboard",
- "View help information",
- "View system logs",
- " ",
- "Quit"]
-
- def get_elements_for_main_page(self, screen):
+ def get_elements(self, screen):
elements = Grid(2, 5)
labels = [("This is button 1.", 1, True),
("This is button 2.", 2, False),
@@ -69,8 +39,17 @@ class ApplianceConfigScreen(ConfigScreen):
elements.setField(Entry(30), 1, 0, anchorLeft = 1)
elements.setField(Label("Some text:"), 0, 1, anchorRight = 1)
elements.setField(buttons, 1, 1, anchorLeft = 1)
- return [Label("This Is A Cool Appliance."), elements]
+ self.page_elements = [Label("This Is A Cool Appliance."),
elements]
+
+ return self.page_elements
+
+class ApplianceApp(newt_syrup.Application):
+ def __init__(self):
+ newt_syrup.Application.__init__(self, "Simple Appliance",
+ fullscreen = True,
+ compact_buttons = True)
+ self.current_page = ConfigPage(self)
if __name__ == "__main__":
- screen = ApplianceConfigScreen()
+ screen = ApplianceApp()
screen.start()
commit 698873f9d2bf5f80cef8f01ca0a18de989b2fd47
Author: Darryl L. Pierce <dpierce(a)redhat.com>
Date: Fri Apr 6 10:32:29 2012 -0400
Created a new application model.
The new model consists of an Application class that creates a full
screen ApplicationScreen instance. Page objects carry the fields for
each page of the application.
Also added a simple_app.py example for how to use this new model.
diff --git a/ChangeLog b/ChangeLog
index 84826d9..4c7c973 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,10 @@
Change Log:
+ * Version 0.2.0:
+ - Created a new model for writing applications.
+ - Created the Applicatoin class.
+ - Created the ApplicationScreen class.
+ - Created the Page class.
+
* Version 0.1.2:
- Added the utils module.
- Back is only shown if the current page explicitly sets one.
diff --git a/examples/simple_app.py b/examples/simple_app.py
new file mode 100755
index 0000000..e5aeba6
--- /dev/null
+++ b/examples/simple_app.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python
+# simple_app.py - Copyright (C) 2012 Red Hat, Inc.
+# Written by Darryl L. Pierce <dpierce(a)redhat.com>
+#
+# 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; version 2 of the License.
+#
+# 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. A copy of the GNU General Public License is
+# also available at
http://www.gnu.org/copyleft/gpl.html.
+
+import newt_syrup
+from snack import Label, RadioBar
+
+class GreetingPage(newt_syrup.Page):
+ '''
+ Shows a simple greeting.
+ '''
+ def __init__(self, app):
+ newt_syrup.Page.__init__(self, app, "How Are You Doing?", next=True)
+ self.response = None
+
+ def get_elements(self, screen):
+ if self.page_elements == None:
+ self.response = RadioBar(screen, [("Good", 1, True),
+ ("Bad", 2, False)])
+
+ self.page_elements = [Label("How do you feel?"),
+ self.response]
+ return self.page_elements
+
+ def get_happy(self):
+ return (self.response.getSelection() == 1)
+
+class HappyPage(newt_syrup.Page):
+ '''
+ Happy response.
+ '''
+ def __init__(self, app):
+ newt_syrup.Page.__init__(self, app, "You're Happy",
+ finish = True, cancel = False)
+ self.page_elements = [Label("Yay! I'm glad you're happy!")]
+
+class SadPage(newt_syrup.Page):
+ '''
+ Sad response.
+ '''
+ def __init__(self, app):
+ newt_syrup.Page.__init__(self, app, "You're Sad",
+ finish = True, cancel = False)
+ self.page_elements = [Label("Danga! Hope you feel better soon...")]
+
+class SimpleApp(newt_syrup.Application):
+ '''
+ SimpleApp provides a simple example for creating an application using
+ the Newt Syrup framework.
+ '''
+ def __init__(self):
+ newt_syrup.Application.__init__(self, "Simple Syrup App")
+ self.pages = [GreetingPage(self), HappyPage(self), SadPage(self)]
+ self.current_page = self.pages[0]
+
+ def next_page(self):
+ if self.pages[0].get_happy():
+ self.current_page = self.pages[1]
+ else:
+ self.current_page = self.pages[2]
+
+ def previous_page(self):
+ self.current_page = self.pages[0]
+
+
+if __name__ == "__main__":
+ app = SimpleApp()
+ app.start();
diff --git a/newt_syrup/__init__.py b/newt_syrup/__init__.py
index c3960c1..891d5e9 100644
--- a/newt_syrup/__init__.py
+++ b/newt_syrup/__init__.py
@@ -16,4 +16,7 @@
# MA 02110-1301, USA. A copy of the GNU General Public License is
# also available at
http://www.gnu.org/copyleft/gpl.html.
-__all__ = ["configscreen"]
+from application import Application
+from appscreen import ApplicationScreen
+from page import Page
+
diff --git a/newt_syrup/application.py b/newt_syrup/application.py
new file mode 100644
index 0000000..4d11598
--- /dev/null
+++ b/newt_syrup/application.py
@@ -0,0 +1,114 @@
+# application.py - Copyright (C) 2012 Red Hat, Inc.
+# Written by Darryl L. Pierce <dpierce(a)redhat.com>
+#
+# 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; version 2 of the License.
+#
+# 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. A copy of the GNU General Public License is
+# also available at
http://www.gnu.org/copyleft/gpl.html.
+
+from appscreen import ApplicationScreen
+
+class Application:
+ '''
+ Application is the launching point for a Syrup-based app. All such
+ apps should extend this type and override the appropriate methods
+ to define the initial screen and any command line argument processing
+ necessary.
+ '''
+
+ def __init__(self, title, fullscreen = False, compact_buttons = False):
+ '''
+ Creates a new application instance.
+
+ Keyword arguments:
+ title -- the application's title
+ fullscreen -- sets whether the application will be fullscreen (def. False)
+ compact_buttons -- use compact buttons (def. False)
+ '''
+ self.title=title
+ self.screen = None
+ self.finished = False
+ self.cancelled = False
+ self.fullscreen = fullscreen
+ self.compact_buttons = compact_buttons
+
+ def get_title(self):
+ '''
+ Returns the title for the application.
+ '''
+ return self.title
+
+ def get_fullscreen(self):
+ '''
+ Returns whether the application will be fullscreen.
+ '''
+ return self.fullscreen
+
+ def get_compact_buttons(self):
+ '''
+ Returns whether to use compact buttons on pages.
+ '''
+ return self.compact_buttons
+
+ def create_app_screen(self):
+ '''
+ Override this method in order to use an application-specific
+ instance of ApplicationScreen.
+ '''
+ return ApplicationScreen(self)
+
+ def get_current_page(self):
+ '''
+ Returns the current page.
+ '''
+ return self.current_page
+
+ def next_page(self):
+ '''
+ Invoked to advance the application by one page.
+
+ Child classes must override this method to define the
+ next page based on the application's context.
+ '''
+ pass
+
+ def previous_page(self):
+ '''
+ Invoked to move the application back by one page.
+
+ Child classes must override this method to define the
+ previous page based on the application's context.
+ '''
+ pass
+
+ def start(self):
+ '''
+ Starts the application. Creates the initial background screen and
+ then shows the first page.
+ '''
+ self.app_screen = self.create_app_screen()
+ while self.finished == False:
+ self.app_screen.show_current_page()
+
+ def set_finished(self, finished):
+ '''
+ Sets the finished flag for the application.
+ '''
+ self.finished = finished
+
+ def set_cancelled(self, cancelled):
+ '''
+ Sets the cancelled flag for the application.
+ '''
+ self.cancelled = cancelled
+
diff --git a/newt_syrup/appscreen.py b/newt_syrup/appscreen.py
new file mode 100644
index 0000000..11813b3
--- /dev/null
+++ b/newt_syrup/appscreen.py
@@ -0,0 +1,134 @@
+# appscreen.py - Copyright (C) 2012 Red Hat, Inc.
+# Written by Darryl L. Pierce <dpierce(a)redhat.com>
+#
+# 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; version 2 of the License.
+#
+# 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. A copy of the GNU General Public License is
+# also available at
http://www.gnu.org/copyleft/gpl.html.
+
+from snack import ButtonBar
+from snack import Grid
+from snack import GridForm
+from snack import Label
+from snack import SnackScreen
+
+BACK_BUTTON = "back"
+NEXT_BUTTON = "next"
+CANCEL_BUTTON = "cancel"
+FINISH_BUTTON = "finish"
+
+class ApplicationScreen:
+ '''
+ The ApplicationScreen is the background window displayed by an
+ application. Only one instance of this class is presented.
+
+ An application can override this screen by extending this class
+ and then overriding the Application.create_app_screen() method.
+ '''
+
+ def __init__(self, app):
+ self.app = app
+ self.screen_width = 0
+ self.screen_height = 0
+
+ def show_current_page(self):
+ '''
+ Shows the current application page.
+ '''
+ screen = SnackScreen()
+ page = self.app.get_current_page()
+
+ gridform = GridForm(screen, page.get_title(), 4, 1)
+
+ button_bar = self.get_button_bar(screen, page)
+ content = self.get_content(screen, button_bar, page)
+
+ # if we're full screen then move to full height
+ if self.app.get_fullscreen():
+ fullheight = screen.height
+ gridform.add(Label(""), 0, 0,
+ padding = (0, (fullheight / 2) - 4,
+ 0, (fullheight / 2) - 4),
+ growy = 1)
+ gridform.add(Label(" "), 2, 0, anchorTop = 1)
+ gridform.add(content, 3, 0, anchorTop = 1)
+
+ # run the page
+ topx = topy = None
+ if self.app.get_fullscreen():
+ topx = topy = 2
+ result = gridform.runOnce(topx, topy)
+ pressed = button_bar.buttonPressed(result)
+
+ screen.popWindow()
+ screen.finish()
+
+ if pressed == FINISH_BUTTON or pressed == CANCEL_BUTTON:
+ self.app.set_finished(True)
+ if pressed == CANCEL_BUTTON:
+ self.app.set_cancelled(True)
+ elif pressed == BACK_BUTTON:
+ self.app.previous_page()
+ elif pressed == NEXT_BUTTON:
+ self.app.next_page()
+ else:
+ # TODO: in future have applicaton-specific button handling
+ raise Exception("Unsupported button: %s" % pressed)
+
+ def get_content(self, screen, button_bar, page):
+ '''
+ Renders the current page.
+ '''
+ elements = page.get_elements(screen)
+ if elements == None:
+ raise AttributeError("No page elements defined.")
+ content = Grid(1, len(elements) + 2)
+ current_element = 0
+ for element in elements:
+ content.setField(element, 0, current_element, anchorLeft = 1)
+ current_element += 1
+
+ # if we're going to be fullscreen then page it out
+ if self.app.get_fullscreen():
+ fullwidth = screen.width
+ content.setField(Label(""), 0, len(elements),
+ padding = ((fullwidth / 2) - 4, 2,
+ (fullwidth / 2) - 4, 2),
+ growx = 1)
+
+ # add the button bar
+ content.setField(button_bar,
+ 0, page.get_element_count() + 1,
+ anchorLeft = 1, growx = 0)
+
+ return content
+
+ def get_button_bar(self, screen, page):
+ '''
+ Adds the buttons that the page requires to the form.
+ '''
+ buttons = []
+ if page.has_previous_button():
+ buttons.append(["Back", BACK_BUTTON, "F11"])
+ if page.has_next_button():
+ buttons.append(["Next", NEXT_BUTTON, "F12"])
+ if page.has_finish_button():
+ buttons.append(["Finish", FINISH_BUTTON, "F10"])
+ if page.has_cancel_button():
+ buttons.append(["Cancel", CANCEL_BUTTON, "ESC"])
+
+ compact = 0
+ if self.app.get_compact_buttons():
+ compact = 1
+
+ return ButtonBar(screen, buttons, compact = compact)
diff --git a/newt_syrup/page.py b/newt_syrup/page.py
new file mode 100644
index 0000000..d4c8f39
--- /dev/null
+++ b/newt_syrup/page.py
@@ -0,0 +1,105 @@
+# page.py - Copyright (C) 2012 Red Hat, Inc.
+# Written by Darryl L. Pierce <dpierce(a)redhat.com>
+#
+# 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; version 2 of the License.
+#
+# 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. A copy of the GNU General Public License is
+# also available at
http://www.gnu.org/copyleft/gpl.html.
+
+class Page:
+ '''
+ A Page represents the set of fields for a single page of an application,
+ along with hooks for validating the data presented and also for
+ navigating to other pages based on that data.
+ '''
+
+ def __init__(self, app, title, next = False, prev = False, finish = False, cancel =
True):
+ '''
+ Creates the page and gives it the specified title.
+
+ Children should assign the list of page elements at this time
+ as well.
+
+ Keyword arguments:
+ app -- the Application object
+ title -- the page's title
+ next -- True if there is a next page (def. False)
+ prev -- True if there is a previous page (def.False)
+ finish -- True if this page can finish the app (def. False)
+ cancel -- True if this page can cancel the app (def. True)
+ '''
+ self.app = app
+ self.title = title
+ self.next = next
+ self.prev = prev
+ self.finish = finish
+ self.cancel = cancel
+ self.page_elements = None
+
+ def get_title(self):
+ '''
+ Returns the title for this page.
+ '''
+ return self.title
+
+ def get_elements(self, screen):
+ '''
+ Returns the elements for this page as an array of Newt widgets.
+
+ If the fields in a page may change between displays (fields are
+ hidden or revealed based on previous pages) then you should
+ override this method and recreate the array each time it's
+ requested.
+
+ NOTE: The self.page_elements array should be updated even when the
+ list of elements is defined dynamically.
+ '''
+ return self.page_elements
+
+ def get_element_count(self):
+ '''
+ Returns the number of elements on the page current.
+ '''
+ return len(self.page_elements)
+
+ def get_valid(self):
+ '''
+ Called after the user has selected to navigate forward in the
+ page flow. It returns True if the page content is valid.
+ '''
+ return True
+
+ def has_next_button(self):
+ '''
+ Returns True if there is a page after the current page.
+ '''
+ return self.next
+
+ def has_previous_button(self):
+ '''
+ Returns True if there is a page before the current page.
+ '''
+ return self.prev
+
+ def has_finish_button(self):
+ '''
+ Returns True if the application can finish on this page.
+ '''
+ return self.finish
+
+ def has_cancel_button(self):
+ '''
+ Returns True if the application can be cancelled on this page.
+ '''
+ return self.cancel
+