feat(Anaconda): Local Repo
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build-3
|
||||
SPHINXPROJ = Simpleline
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = _build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
@@ -0,0 +1,22 @@
|
||||
.. _public_api_label:
|
||||
|
||||
Public API
|
||||
==========
|
||||
|
||||
API listed here is the public API. Developers shouldn't use anything which is not mentioned here!
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Contents:
|
||||
|
||||
App <api/app>
|
||||
UIScreen <api/screen>
|
||||
Screen Handling <api/screen_handling>
|
||||
Widgets <api/widgets>
|
||||
Containers <api/containers>
|
||||
Prompt <api/prompt>
|
||||
Advanced Widgets <api/adv_widgets>
|
||||
Event loops <api/event_loops>
|
||||
Signals <api/signals>
|
||||
Errors <api/errors>
|
||||
Advanced Input <api/advanced_input>
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
Advanced Widgets
|
||||
================
|
||||
|
||||
Advanced widgets are :class:`screens <simpleline.render.screen.UIScreen>` which can be used for a
|
||||
specific purpose. For example, reading input from the user and testing acceptance conditions on this
|
||||
input, or asking the user a yes/no question.
|
||||
|
||||
Advanced widget classes
|
||||
-----------------------
|
||||
|
||||
.. automodule:: simpleline.render.adv_widgets
|
||||
:members:
|
||||
:show-inheritance:
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
Advanced Input
|
||||
==============
|
||||
|
||||
.. automodule:: simpleline.input.input_handler
|
||||
|
||||
.. WARNING::
|
||||
This section contains advanced input techniques which may lead to buggy code in your
|
||||
program if they are not used correctly.
|
||||
|
||||
The default technique to get user input is already described in :ref:`UIScreen <uiscreen_label>`,
|
||||
and that should be the preferred way to obtain user input. However, if the default technique is
|
||||
not enough for your situation then read the text below to find out how to implement custom input.
|
||||
|
||||
Input handler classes
|
||||
---------------------
|
||||
Input handler classes are classes created to obtain user input. If required
|
||||
:class:`InputHandler` can block an application until user input is received.
|
||||
|
||||
This class can be instantiated everywhere in the code and used to ask for user input::
|
||||
|
||||
handler = InputHandler()
|
||||
handler.get_input("Shut up and give me your input:")
|
||||
handler.wait_for_input()
|
||||
if handler.input_successful:
|
||||
user_input = handler.value
|
||||
|
||||
The :meth:`InputHandler.wait_on_input` method will block code processing until user input is
|
||||
received. Input should always be checked before processing.
|
||||
|
||||
In case some other work needs to be done before user input is received, then pass
|
||||
a callback to the constructor of the :class:`InputHandler` class and do not use
|
||||
the :meth:`InputHandler.wait_on_input` method. However, the callback is using
|
||||
the :ref:`event loop<event_loops_label>` so it won't be called until event loop
|
||||
processing is active. If :ref:`concurrent input<concurrent_input_label>` is used then this
|
||||
callback might never get called!
|
||||
|
||||
If what a user types must not be displayed, then the :class:`PasswordInputHandler` class
|
||||
should be used. It shares most of its implementation with the :class:`InputHandler` but overrides
|
||||
how to obtain the code. For more info look at the :class:`PasswordInputHandler` class
|
||||
documentation.
|
||||
|
||||
.. _concurrent_input_label:
|
||||
|
||||
Concurrent input
|
||||
----------------
|
||||
|
||||
Concurrent input is something which should be avoided. It drags unexpected behavior into an
|
||||
application and is hard to debug. However, there could be an instance when user input is required
|
||||
immediately, even when an application is already waiting for other input.
|
||||
|
||||
By default, every attempt for concurrent input will raise an exception and kill the application to
|
||||
prevent unexpected behavior. In order to allow for concurrent input, the
|
||||
:attr:`InputHandler.skip_concurrency_check` property must be set. After this property is disabled
|
||||
for the :class:`InputHandler` instance, then the handler instance then it can support
|
||||
concurrent input.
|
||||
|
||||
The last registered concurrent input will result in dropping all other waiting inputs -- even other
|
||||
waiting inputs with :attr:`InputHandler.skip_concurrency_check` will be dropped. The dropped
|
||||
waiting inputs will get a failed input signal to unblock :meth:`InputHandler.wait_on_input`
|
||||
methods.
|
||||
|
||||
|
||||
Creating a custom InputHandler
|
||||
------------------------------
|
||||
|
||||
If the :class:`InputHandler` class or the :class:`PasswordInputHandler` class is not enough,
|
||||
developers can create their own handler. The structure of an input handler is based on two
|
||||
classes. First is a handler itself, and second is the request object it creates.
|
||||
|
||||
The handler class is used as an interface for the rest of the application. It also creates
|
||||
the requester instance. The requester object is a low-level implementation of obtaining user input.
|
||||
The requester object has to have the ``get_input`` method and should contain the ``text_prompt``
|
||||
method. The ``get_input`` method is called in a separate thread and should prompt the user for
|
||||
input. The ``text_prompt`` method is required mainly for concurrent input, and it returns
|
||||
a string representation of the prompt.
|
||||
|
||||
For more details, please look at the implementation of the :class:`InputHandler` class.
|
||||
|
||||
|
||||
InputHandler class
|
||||
------------------
|
||||
|
||||
.. autoclass:: InputHandler
|
||||
:members:
|
||||
|
||||
PasswordInputHandler class
|
||||
--------------------------
|
||||
|
||||
.. autoclass:: PasswordInputHandler
|
||||
:members:
|
||||
:inherited-members:
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
App
|
||||
===
|
||||
|
||||
.. automodule:: simpleline
|
||||
|
||||
The App class is the heart of Simpleline. It holds the :ref:`event loop <event_loops_label>`
|
||||
instance and scheduler which are used by Simpleline. Both can be replaced if needed in the
|
||||
initialization phase by calling the :meth:`App.initialize` method. However, by replacing the
|
||||
scheduler you are replacing most of the logic in Simpleline. Replacing the scheduler is not
|
||||
supported, since it is currently not a part of the public API.
|
||||
|
||||
All Simpleline applications must be run by the :meth:`App.run` method. This method will start
|
||||
the event loop instance created in the initialization process.
|
||||
|
||||
If a reaction on application quit is required, please set
|
||||
:meth:`set_quit_callback <event_loop.AbstractEventLoop.set_quit_callback>` in the used event loop.
|
||||
This can be done by::
|
||||
|
||||
loop = App.get_event_loop()
|
||||
loop.set_quit_callback(callback_function)
|
||||
|
||||
**Do not instantiate the** :class:`App` **class!** It is designed to be used in a purely static way.
|
||||
|
||||
|
||||
Application configuration
|
||||
-------------------------
|
||||
|
||||
Configuration of the application is saved in the
|
||||
:class:`GlobalConfiguration <global_configuration.GlobalConfiguration>` class. This class can be
|
||||
created before :meth:`App.initialize` is called and passed in as a
|
||||
parameter. This way the same configuration can be used between re-initialization of the
|
||||
application. The configuration can easily be changed, even while an application is running,
|
||||
by setting desired properties.
|
||||
|
||||
Look at the :class:`GlobalConfiguration <global_configuration.GlobalConfiguration>` to find out
|
||||
all the configuration possibilities.
|
||||
|
||||
App class
|
||||
---------
|
||||
|
||||
.. autoclass:: App
|
||||
:members:
|
||||
|
||||
GlobalConfiguration class
|
||||
-------------------------
|
||||
|
||||
.. autoclass:: simpleline.global_configuration.GlobalConfiguration
|
||||
:members:
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
.. _containers_label:
|
||||
|
||||
Containers
|
||||
==========
|
||||
|
||||
Containers are structures to hold widgets and handle automatic positioning. Containers are
|
||||
essentially widgets containing other widgets. Below is a collection of default containers which
|
||||
can position :ref:`widgets <widgets_label>`, but they can do more (e.g. handle user input).
|
||||
Recursive composition of containers is supported.
|
||||
|
||||
Customized containers can also be created. See the :ref:`creating_a_custom_container_label`
|
||||
section.
|
||||
|
||||
Container classes
|
||||
-----------------
|
||||
|
||||
.. automodule:: simpleline.render.containers
|
||||
:members:
|
||||
:inherited-members:
|
||||
:show-inheritance:
|
||||
|
||||
.. _creating_a_custom_container_label:
|
||||
|
||||
Creating a custom container
|
||||
---------------------------
|
||||
|
||||
If an existing container is missing a required feature, a new, customized container can be
|
||||
created based on the :class:`Container` class. Container creation is essentially the same as
|
||||
:ref:`custom widget creation <create_custom_widget_label>` because containers are based on widgets.
|
||||
The main difference is that containers are working with widgets added by a developer when using
|
||||
the container. As an example, every :class:`UIScreen <simpleline.render.screen.UIScreen>` has a
|
||||
:class:`WindowContainer`, and this is used as the main rendering point.
|
||||
|
||||
To create a customized container, the :meth:`Container.render` method should be overridden and
|
||||
the positioning of widgets and containers should be done here. It can even enhance these widgets,
|
||||
for example, by adding numbering (this is done in :class:`ListRowContainer` or
|
||||
:class:`ListColumnContainer`). The :meth:`Container.render` method should call the
|
||||
:meth:`Container.draw` method. The :meth:`Container.draw` method should be called for every
|
||||
widget placed. For a better understanding please refer to the existing implementation.
|
||||
|
||||
Base container class
|
||||
--------------------
|
||||
|
||||
.. autoclass:: Container
|
||||
:members:
|
||||
:inherited-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,39 @@
|
||||
Errors module
|
||||
=============
|
||||
|
||||
.. automodule:: simpleline.errors
|
||||
|
||||
Collection of generic exception classes used everywhere in the project. The most important one is
|
||||
:class:`SimplelineError`, which is the base exception for all the other exceptions used in
|
||||
the Simpleline project.
|
||||
|
||||
.. autoexception:: SimplelineError
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
Render exceptions
|
||||
-----------------
|
||||
|
||||
Exceptions used for rendering errors.
|
||||
|
||||
|
||||
.. automodule:: simpleline.render
|
||||
|
||||
.. autoexception:: RenderError
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
.. autoexception:: RenderUnexpectedError
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
Event loop exceptions
|
||||
---------------------
|
||||
|
||||
Exceptions used for errors in event loops.
|
||||
|
||||
.. automodule:: simpleline.event_loop
|
||||
|
||||
.. autoexception:: simpleline.event_loop.ExitMainLoop
|
||||
:members:
|
||||
:show-inheritance:
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
.. _event_loops_label:
|
||||
|
||||
Event Loops
|
||||
===========
|
||||
|
||||
.. currentmodule:: simpleline.event_loop
|
||||
|
||||
Event loops are the heart of Simpleline. Every event loop is based on the
|
||||
:class:`AbstractEventLoop`, and they all work with :ref:`signals <signals_label>`.
|
||||
A signal is a message passed to the loop containing some information. Signals are passed to
|
||||
an event loop by calling :meth:`AbstractEventLoop.enqueue_signal`. These signals are then
|
||||
processed by calling :meth:`AbstractEventLoop.process_signals`. This method can be called by
|
||||
an application developer manually or by the :meth:`AbstractEventLoop.run` method, which is
|
||||
called by :meth:`App.run() <simpleline.App.run>` to start the Simpleline-based application.
|
||||
When a signal is processed, all handlers attached to this signal are called. Signal handler
|
||||
assignment is done by the :meth:`AbstractEventLoop.register_signal_handler` method.
|
||||
|
||||
Event loops can also be started recursively by :meth:`AbstractEventLoop.execute_new_loop`.
|
||||
The old event loop is waiting for this event loop to stop. New loop execution is mandatory for
|
||||
modal screens to work, since they can't be interrupted by other screens. This new event
|
||||
loop is terminated by closing the last screen in the event loop or by calling
|
||||
the :meth:`AbstractEventLoop.close_loop` method.
|
||||
|
||||
The last event loop should be terminated by closing the last screen in the screen stack or by
|
||||
calling :meth:`AbstractEventLoop.close_loop`. In case a fatal error occurs the
|
||||
:meth:`AbstractEventLoop.force_quit` method can be used to immediately kill the loop.
|
||||
|
||||
If a reaction on quitting the application (closing the last event loop) is required, the quit
|
||||
callback can be used. The quit callback can be set by
|
||||
the :meth:`AbstractEventLoop.set_quit_callback` method.
|
||||
|
||||
The following event loops are supported by Simpleline, but you can also
|
||||
:ref:`Create_your_own_loop_label` :
|
||||
|
||||
* :ref:`MainLoop_label`
|
||||
* :ref:`GLib_Event_loop_label`
|
||||
|
||||
.. _MainLoop_label:
|
||||
|
||||
Main Loop
|
||||
---------
|
||||
|
||||
The main loop is the default event loop for Simpleline projects. The benefit of using
|
||||
:class:`MainLoop <main_loop.MainLoop>` is that it isn't necessary to have any dependencies on
|
||||
other libraries. It is a lightweight event loop implemented completely in Python.
|
||||
|
||||
.. autoclass:: simpleline.event_loop.main_loop.MainLoop
|
||||
:members:
|
||||
:inherited-members:
|
||||
:show-inheritance:
|
||||
|
||||
.. _GLib_Event_loop_label:
|
||||
|
||||
GLib Event loop
|
||||
---------------
|
||||
|
||||
The GLib event loop was added in order to utilize existing event loops used by other libraries,
|
||||
for example, DBus connections. Simpleline with this loop should have the same behavior as with
|
||||
the :ref:`MainLoop_label`.
|
||||
|
||||
To use this loop you need to set it via the :class:`App <simpleline.App>` class::
|
||||
|
||||
# Create Glib event loop.
|
||||
glib_loop = GLibEventLoop()
|
||||
|
||||
# Use glib event loop instead of the original one.
|
||||
# Everything else should behave the same as with the original Simpleline loop.
|
||||
App.initialize(event_loop=glib_loop)
|
||||
|
||||
The GLib loop can be accessed by the
|
||||
:attr:`GLibEventLoop.active_main_loop <simpleline.event_loop.glib_event_loop.GLibEventLoop.active_main_loop>`
|
||||
property, or by getting the default loop from GLib directly
|
||||
`GLib.MainLoop() <https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html>`_.
|
||||
|
||||
.. autoclass:: simpleline.event_loop.glib_event_loop.GLibEventLoop
|
||||
:members:
|
||||
:inherited-members:
|
||||
:show-inheritance:
|
||||
|
||||
.. _Create_your_own_loop_label:
|
||||
|
||||
Create your own loop
|
||||
--------------------
|
||||
|
||||
If new loop support is required, it should inherit from :class:`AbstractEventLoop` and
|
||||
implement the same behavior as the :ref:`MainLoop_label`. You can use existing tests from the
|
||||
event loops to start. If the new loop is stable enough, pull requests are always welcome at
|
||||
`Simpleline repository <https://github.com/rhinstaller/python-simpleline>`_.
|
||||
|
||||
.. autoclass:: simpleline.event_loop.AbstractEventLoop
|
||||
:members:
|
||||
:inherited-members:
|
||||
@@ -0,0 +1,23 @@
|
||||
.. _prompt_label:
|
||||
|
||||
Prompt
|
||||
======
|
||||
|
||||
.. automodule:: simpleline.render.prompt
|
||||
|
||||
Class for prompting a user for input. New user options can be added by :meth:`Prompt.add_option`,
|
||||
removed by :meth:`Prompt.remove_option` or updated by :meth:`Prompt.update_option`.
|
||||
A message for the user can also be set by the :meth:`Prompt.set_message` method.
|
||||
|
||||
This class is used in the :class:`UIScreen <simpleline.render.screen.UIScreen>` class. The default
|
||||
instance always handles *r* (refresh), *c* (continue) and *q* (quit) and is created in the
|
||||
:meth:`UIScreen.prompt() <simpleline.render.screen.UIScreen.prompt>` method. To create your own
|
||||
custom prompt please override
|
||||
the :meth:`UIScreen.prompt() <simpleline.render.screen.UIScreen.prompt>` method.
|
||||
|
||||
Prompt class
|
||||
------------
|
||||
|
||||
.. autoclass:: Prompt
|
||||
:members:
|
||||
:inherited-members:
|
||||
@@ -0,0 +1,126 @@
|
||||
.. _uiscreen_label:
|
||||
|
||||
UIScreen
|
||||
========
|
||||
|
||||
.. automodule:: simpleline.render.screen
|
||||
|
||||
The base class for creating a new screen. :class:`UIScreen` is used for any user interaction.
|
||||
:class:`UIScreen` uses :ref:`containers <containers_label>` and :ref:`widgets <widgets_label>`
|
||||
to present information to a user, and the :meth:`UIScreen.input` method to get input from a user
|
||||
to an application. Screens are pushed to the screen stack, which are then used to
|
||||
communicate with a user. See the :ref:`screen handling <screen_handling_label>` section to find
|
||||
out more.
|
||||
|
||||
The methods :meth:`UIScreen.redraw`, :meth:`UIScreen.close`, :meth:`UIScreen.emit` and
|
||||
:meth:`UIScreen.create_and_emit` are asynchronous. These methods will create a signal which
|
||||
is passed to the event loop for later processing.
|
||||
|
||||
**Beware**, methods :meth:`UIScreen.redraw` and :meth:`UIScreen.close` can lead to unexpected
|
||||
behavior when multiple instances of these signals are emitted (methods are called multiple times).
|
||||
This can even crash your application.
|
||||
|
||||
Lifecycle
|
||||
---------
|
||||
|
||||
Every :class:`UIScreen` has a distinct lifecycle: *uninitialized*, *initialized*, *draw*,
|
||||
*process input* and *closed*. If the screen has already been initialized
|
||||
(it was *drawn* to a monitor) and will be shown again, then the screen skips
|
||||
the *uninitialized* stage. The screen can cycle between *draw* and *process input* stages by
|
||||
calling the :meth:`UIScreen.redraw` method after processing user input.
|
||||
|
||||
In case :meth:`UIScreen.redraw` won't be called and no new screen is pushed, or this screen
|
||||
wasn't closed, then an application will stay in an infinite loop waiting for something to happen.
|
||||
This is correct behavior because there could be something in an event loop which will
|
||||
call :meth:`UIScreen.redraw` later.
|
||||
|
||||
Rendering widgets
|
||||
-----------------
|
||||
|
||||
The :meth:`UIScreen.refresh` method is the most important part of the :class:`UIScreen`.
|
||||
It contains preparations for rendering (creating widgets and adding them to containers).
|
||||
The :meth:`UIScreen.refresh` method will be called before anything is drawn on a monitor.
|
||||
|
||||
The :attr:`UIScreen.window` attribute, which is the
|
||||
:class:`WindowContainer <simpleline.render.containers.WindowContainer>` instance, contains
|
||||
all items (widgets, containers) which are to be rendered by the screen.
|
||||
A new :class:`WindowContainer <simpleline.render.containers.WindowContainer>` is created in the
|
||||
:meth:`UIScreen.refresh` method for every screen redraw. The :attr:`UIScreen.title` attribute is
|
||||
passed to the :attr:`WindowContainer.title <simpleline.render.containers.WindowContainer.title>`
|
||||
property, and a developer can add :ref:`widgets <widgets_label>` and other
|
||||
:ref:`containers <containers_label>` to present items to a user by calling
|
||||
:meth:`WindowContainer.add() <simpleline.render.containers.WindowContainer.add>` or
|
||||
:meth:`WindowContainer.add_with_separator() <simpleline.render.containers.WindowContainer.add_with_separator>`.
|
||||
Multiple items can be added by calling these methods repeatedly.
|
||||
|
||||
When everything is prepared properly in the :meth:`UIScreen.refresh` method, it needs to be drawn
|
||||
on the monitor for a user. This is handled by the :meth:`UIScreen.show_all` method. This method
|
||||
works automatically, but it could also be useful for a developer, especially when the screen will
|
||||
not process input. In this case, the :meth:`UIScreen.input` method is not called at all.
|
||||
Developers can override this method and call the parent class's :meth:`UIScreen.show_all`,
|
||||
which will handle drawing the screen and any additional processing.
|
||||
|
||||
Redrawing the screen can be invoked by the :meth:`UIScreen.redraw` method. However, this is
|
||||
not processed immediately. Instead it will be added to the event loop and processed later, when the
|
||||
loop is idle. Beware, after every :meth:`redraw <UIScreen.redraw>` call the input is processed
|
||||
if not disabled, so if multiple redraw signals are emitted, than the application will
|
||||
crash.
|
||||
|
||||
The :meth:`UIScreen.redraw` method is also invoked when a screen is
|
||||
:ref:`pushed <push_screen_label>` to the stack.
|
||||
|
||||
User input processing
|
||||
---------------------
|
||||
|
||||
If the screen shouldn't process user input, the :attr:`UIScreen.input_required` property needs
|
||||
to be set to `False`. `True` is the default value for this property.
|
||||
|
||||
After everything is printed to a monitor, the :class:`UIScreen` will wait for user input.
|
||||
For this purpose there is :meth:`UIScreen.input`, which is called when a user passes string input
|
||||
to a screen. The screen needs to react upon user input and return one of the options from the
|
||||
:class:`InputState` enum or the user input string.
|
||||
|
||||
To accept the user input, :attr:`InputState.PROCESSED`, :attr:`InputState.PROCESSED_AND_REDRAW` or
|
||||
:attr:`InputState.PROCESSED_AND_CLOSE` should be returned. Addition to accepting user input the
|
||||
:attr:`InputState.PROCESSED_AND_REDRAW` value will also redraw active screen and
|
||||
:attr:`InputState.PROCESSED_AND_CLOSE` will close active screen. However, if
|
||||
:attr:`InputState.PROCESSED` is used then the developer is responsible for not ending in frozen
|
||||
application. The :meth:`UIScreen.refresh` or the :meth:`UIScreen.close` methods must be called
|
||||
manually.
|
||||
|
||||
In case the user input is invalid, the :attr:`InputState.DISCARDED` value should be returned.
|
||||
This will reject the user input and wait for another attempt. The :class:`UIScreen.refresh`
|
||||
method will be called after 5 rejections and show the screen output again.
|
||||
|
||||
If the user input string is returned it will be checked for
|
||||
options of the :class:`Prompt <simpleline.render.prompt.Prompt>` instance which can either
|
||||
close the screen, refresh the screen (this will call :meth:`UIScreen.refresh`)
|
||||
or quit the application.
|
||||
|
||||
Closing screens
|
||||
---------------
|
||||
|
||||
There are several ways to close a screen. One is by calling :meth:`UIScreen.close` or
|
||||
by pressing *c* to continue (with the default
|
||||
:class:`Prompt <simpleline.render.prompt.Prompt>` class). When the screen is closed
|
||||
the next screen on the stack will be shown. A screen can also be
|
||||
:ref:`replaced <replace_screen_label>`. Then the original screen is removed from the stack
|
||||
without closing a screen. If a reaction on closing a screen is required then the
|
||||
:meth:`UIScreen.closed` callback should be overridden.
|
||||
|
||||
**Beware**, when calling :meth:`UIScreen.close` multiple times it will close multiple screens.
|
||||
This is because the close signal will always close the top screen on the screen stack.
|
||||
|
||||
UIScreen class
|
||||
--------------
|
||||
|
||||
.. autoclass:: UIScreen
|
||||
:members:
|
||||
:inherited-members:
|
||||
|
||||
InputState enum
|
||||
---------------
|
||||
|
||||
.. autoclass:: InputState
|
||||
:members:
|
||||
:undoc-members:
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
.. _screen_handling_label:
|
||||
|
||||
Screen Handling
|
||||
===============
|
||||
|
||||
.. automodule:: simpleline.render.screen_handler
|
||||
|
||||
The :class:`ScreenHandler` class is used to schedule a
|
||||
:class:`UIScreen <simpleline.render.screen.UIScreen>` to the screen stack.
|
||||
|
||||
Screen handling is an important part of using the Simpleline library, and it is recommended
|
||||
for a developer to get familiar with this principle. Screen handling is the gateway by which
|
||||
a developer manages the stack, by either adding or removing screens from it.
|
||||
There are many ways to add a screen to the stack. To remove a screen from the stack, the screen
|
||||
must be closed, or it can be replaced by another screen.
|
||||
To close a :class:`UIScreen <simpleline.render.screen.UIScreen>` a developer should call
|
||||
the :meth:`UIScreen.close() <simpleline.render.screen.UIScreen.close()>`
|
||||
method. A screen can also be closed when a user presses `c` (which can be disabled). This will
|
||||
close the screen automatically. When a screen is closed, the next screen on the top of
|
||||
the stack will be rendered. If the stack is empty then the application will close.
|
||||
|
||||
**Do not instantiate the** :class:`ScreenHandler` **class!** All methods in the
|
||||
:class:`ScreenHandler` class are class methods so the :class:`ScreenHandler` shouldn't be
|
||||
instantiated at all.
|
||||
|
||||
The following operations can be used to schedule a screen.
|
||||
|
||||
Schedule screen
|
||||
---------------
|
||||
|
||||
To schedule a screen use the :meth:`ScreenHandler.schedule_screen` method.
|
||||
Scheduling a screen should be used on the first screen in your application before starting the
|
||||
event loop. The screen is added to the bottom of the screen stack, and it will be visible as
|
||||
the last screen in a stack.
|
||||
|
||||
This is the only way to add a screen to the screen stack without emitting a redraw call.
|
||||
|
||||
.. _push_screen_label:
|
||||
|
||||
Push screen
|
||||
-----------
|
||||
|
||||
To push a screen to the stack use the :meth:`ScreenHandler.push_screen` method.
|
||||
Pushing a screen to the stack will place a screen on top of the stack, so it will be
|
||||
drawn on the next redraw call. The original screen will remain on the stack, so after the new
|
||||
screen is closed, the original screen will be rendered again. If you need to avoid this behavior
|
||||
please close the active screen before pushing a new screen to the stack.
|
||||
|
||||
The redraw signal will be emitted automatically when a screen is pushed.
|
||||
|
||||
.. _push_screen_modal_label:
|
||||
|
||||
Push screen as modal
|
||||
--------------------
|
||||
|
||||
To push a modal screen to the stack use the :meth:`ScreenHandler.push_screen_modal` method.
|
||||
This behaves the same as :ref:`push_screen_label` with one important difference. The pushed screen
|
||||
will act as a modal screen. A modal screen has its own event loop, so event processing of the old
|
||||
loop is blocked until the modal screen is closed. The code processing is also blocked by the
|
||||
:meth:`ScreenHandler.push_screen_modal` method call.
|
||||
|
||||
The :meth:`UIScreen.redraw() <simpleline.render.screen.UIScreen.redraw>` method may be
|
||||
required if the original screen was already drawn before invoking the
|
||||
:meth:`ScreenHandler.push_screen_method` method.
|
||||
|
||||
The redraw signal will be emitted automatically when a screen is pushed.
|
||||
|
||||
.. _replace_screen_label:
|
||||
|
||||
Replace screen
|
||||
--------------
|
||||
|
||||
Replace an existing screen with a new screen. This behaves like :ref:`push_screen_label` but it
|
||||
replaces the original screen, so there is no need to close the original screen.
|
||||
|
||||
The redraw signal will be emitted automatically when a screen is replaced.
|
||||
|
||||
ScreenHandler class
|
||||
-------------------
|
||||
|
||||
.. autoclass:: ScreenHandler
|
||||
:members:
|
||||
@@ -0,0 +1,22 @@
|
||||
.. _signals_label:
|
||||
|
||||
Signals
|
||||
=======
|
||||
|
||||
This is a collection of signals that can be used in the
|
||||
:class:`event loop <simpleline.event_loop.AbstractEventLoop>`.
|
||||
|
||||
.. automodule:: simpleline.event_loop.signals
|
||||
:members:
|
||||
:inherited-members:
|
||||
:show-inheritance:
|
||||
|
||||
Creating custom signals
|
||||
-----------------------
|
||||
|
||||
New signals can be created by subclassing an existing signal class or
|
||||
:class:`AbstractSignal <simpleline.event_loop.AbstractSignal>`
|
||||
|
||||
.. autoclass:: simpleline.event_loop.AbstractSignal
|
||||
:members:
|
||||
:inherited-members:
|
||||
@@ -0,0 +1,44 @@
|
||||
.. _widgets_label:
|
||||
|
||||
Widgets
|
||||
=======
|
||||
|
||||
.. currentmodule:: simpleline.render.widgets
|
||||
|
||||
Widgets are the basic units to render items on a
|
||||
:class:`screen <simpleline.render.screen.UIScreen>`. Widgets can wrap a common text
|
||||
(:class:`TextWidget`) or create empty lines (:class:`SeparatorWidget`). They can also be more
|
||||
complex structures (:class:`CheckboxWidget`). A new widget can also be created.
|
||||
See :ref:`create_custom_widget_label`.
|
||||
|
||||
These widgets should be used in :ref:`containers_label`.
|
||||
|
||||
Widgets classes
|
||||
---------------
|
||||
|
||||
.. automodule:: simpleline.render.widgets
|
||||
:members:
|
||||
:inherited-members:
|
||||
:show-inheritance:
|
||||
|
||||
.. _create_custom_widget_label:
|
||||
|
||||
Creating a custom widget
|
||||
------------------------
|
||||
|
||||
To create a custom widget you should subclass the :class:`Widget` class. The most important method
|
||||
for creating a customized widget is :meth:`Widget.render`. This method is responsible for
|
||||
presenting a user textual information.
|
||||
|
||||
If the new widget is composed of other widgets the :meth:`Widget.draw` method should be
|
||||
called on every widget used. The :meth:`Widget.write` method should be called if the input
|
||||
is a string. These calls can be repeated multiple times. For an example please look at the
|
||||
existing implementation.
|
||||
|
||||
Base Widget class
|
||||
-----------------
|
||||
|
||||
.. autoclass:: Widget
|
||||
:members:
|
||||
:inherited-members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,214 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Simpleline documentation build configuration file, created by
|
||||
# sphinx-quickstart on Fri Nov 3 11:00:41 2017.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
import os
|
||||
import sys
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
|
||||
sys.path.insert(0, os.path.abspath('..'))
|
||||
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#
|
||||
# needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = ['sphinx.ext.autodoc',
|
||||
'sphinx.ext.doctest',
|
||||
'sphinx.ext.intersphinx',
|
||||
'sphinx.ext.todo',
|
||||
'sphinx.ext.coverage',
|
||||
'sphinx.ext.imgmath']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
#
|
||||
# source_suffix = ['.rst', '.md']
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = 'Simpleline'
|
||||
copyright = '2017, Jiri Konecny' # pylint: disable=redefined-builtin
|
||||
author = 'Jiri Konecny'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
|
||||
|
||||
def get_version():
|
||||
"""Read version from ../python-simpleline.spec ."""
|
||||
import re
|
||||
version_re = re.compile(r"^Version: *([\d.]+)$")
|
||||
with open("../python-simpleline.spec", "r") as f:
|
||||
for line in f:
|
||||
m = version_re.match(line)
|
||||
if m:
|
||||
return m.group(1)
|
||||
|
||||
|
||||
# The short X.Y version.
|
||||
version = get_version()
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = version
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = None
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This patterns also effect to html_static_path and html_extra_path
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = True
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'alabaster'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
|
||||
# -- Options for HTMLHelp output ------------------------------------------
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'Simplelinedoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#
|
||||
# 'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
#
|
||||
# 'figure_align': 'htbp',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, 'Simpleline.tex', 'Simpleline Documentation',
|
||||
'Jiri Konecny', 'manual'),
|
||||
]
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
(master_doc, 'simpleline', 'Simpleline Documentation',
|
||||
[author], 1)
|
||||
]
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(master_doc, 'Simpleline', 'Simpleline Documentation',
|
||||
author, 'Simpleline', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
|
||||
|
||||
# -- Options for Epub output ----------------------------------------------
|
||||
|
||||
# Bibliographic Dublin Core info.
|
||||
epub_title = project
|
||||
epub_author = author
|
||||
epub_publisher = author
|
||||
epub_copyright = copyright
|
||||
|
||||
# The unique identifier of the text. This can be a ISBN number
|
||||
# or the project homepage.
|
||||
#
|
||||
# epub_identifier = ''
|
||||
|
||||
# A unique identification for the text.
|
||||
#
|
||||
# epub_uid = ''
|
||||
|
||||
# A list of files that should not be packed into the epub file.
|
||||
epub_exclude_files = ['search.html']
|
||||
|
||||
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
intersphinx_mapping = {'https://docs.python.org/3': None}
|
||||
|
||||
|
||||
# -- Mock missing stuff in readthedocs ------------------------------------
|
||||
|
||||
|
||||
class Mock(MagicMock):
|
||||
@classmethod
|
||||
def __getattr__(cls, name):
|
||||
return MagicMock()
|
||||
|
||||
|
||||
MOCK_MODULES = ['gi', 'gi.repository']
|
||||
sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES)
|
||||
@@ -0,0 +1,176 @@
|
||||
.. _guide_label:
|
||||
|
||||
Guide to Simpleline
|
||||
===================
|
||||
|
||||
Simpleline is a text user interface framework written completely in Python 3 with a possibility to
|
||||
have :ref:`non-python event loops <event_loops_label>`. With the exception of optional event loops,
|
||||
Simpleline has almost no dependency on external libraries.
|
||||
|
||||
This UI is simple and easy to use. It is designed to be used with line-based machines and tools
|
||||
(e.g. serial console) so that every new line is appended to the bottom of the screen.
|
||||
Printed lines are never rewritten!
|
||||
|
||||
Basic components
|
||||
----------------
|
||||
|
||||
For every application, the following parts are always required.
|
||||
|
||||
* The :class:`App <simpleline.App>` static class to initialize and run application.
|
||||
* The :class:`ScreenHandler <simpleline.render.screen_handler.ScreenHandler>` static class for
|
||||
scheduling screens.
|
||||
* The :class:`UIScreen <simpleline.render.screen.UIScreen>` based classes to create screens which
|
||||
will form the application.
|
||||
* :ref:`Widgets <widgets_label>` to show anything on the screens.
|
||||
* :ref:`Containers <containers_label>` to position widgets on the screen.
|
||||
|
||||
Look at the next section to see how everything fits together.
|
||||
|
||||
How to create a simple application
|
||||
----------------------------------
|
||||
|
||||
.. currentmodule:: simpleline.render.screen
|
||||
|
||||
Interaction with a user is necessary to have a useful UI framework. To show anything to a user the
|
||||
:class:`UIScreen` class must be used. So we will subclass this class to create our screen and set
|
||||
a title for it::
|
||||
|
||||
class DividerScreen(UIScreen):
|
||||
|
||||
def __init__(self):
|
||||
# Set title of the screen.
|
||||
super().__init__(title=u"Divider")
|
||||
self._message = 0
|
||||
|
||||
The ``self._message`` variable will be used later to show results to the user.
|
||||
|
||||
The screen's main purpose is to present content to a user. For this we need widgets and containers.
|
||||
|
||||
The :attr:`UIScreen.window` attribute is the most important part of the screen for rendering.
|
||||
It contains the :class:`WindowContainer <simpleline.render.containers.WindowContainer>`
|
||||
container which is created and filled up by the :meth:`UIScreen.refresh` method.
|
||||
Everything added to this container is printed to the monitor.
|
||||
We should override the :meth:`UIScreen.refresh` method and call the parent's version to
|
||||
prepare the container. Then we can add :ref:`widgets <widgets_label>` to the container.
|
||||
The screen will continue like this:
|
||||
|
||||
.. code-block:: python
|
||||
:dedent: 0
|
||||
|
||||
def refresh(self, args=None):
|
||||
# Fill the self.window attribute by the WindowContainer and set screen title as header.
|
||||
super().refresh()
|
||||
|
||||
widget = TextWidget("Result: " + str(self._message))
|
||||
self.window.add_with_separator(widget)
|
||||
|
||||
The :meth:`WindowContainer.add_with_separator() <simpleline.render.containers.WindowContainer.add_with_separator>`
|
||||
method will print a blank line after the
|
||||
:class:`TextWidget's <simpleline.render.widgets.TextWidget>` text.
|
||||
|
||||
Now the user has the header and result printed on the screen but it would be nice to give them a
|
||||
hint about how to use the Divider screen. The best part of the screen for this is the
|
||||
:class:`Prompt <simpleline.render.prompt.Prompt>`.
|
||||
The :class:`Prompt <simpleline.render.prompt.Prompt>` class is responsible for guiding the user
|
||||
by giving them a set of possible options to choose from.
|
||||
|
||||
We will set the message of the prompt inside of the :meth:`UIScreen.prompt` method. We also
|
||||
remove the default option to continue, because it functions the same as quitting when there is
|
||||
only one screen in the application.
|
||||
|
||||
.. code-block:: python
|
||||
:dedent: 0
|
||||
|
||||
def prompt(self, args=None):
|
||||
# Change user prompt
|
||||
prompt = super().prompt()
|
||||
|
||||
# Set message to the user prompt. Give a user hint how he/she may control our application.
|
||||
prompt.set_message("Pass numbers to divider in a format: 'num / num'")
|
||||
|
||||
# Remove continue option from the control. There is no need for that
|
||||
# when we have only one screen.
|
||||
prompt.remove_option('c')
|
||||
|
||||
return prompt
|
||||
|
||||
When we are able to present our content to a user, we want to have the possibility to process
|
||||
user input. For this purpose there is the :meth:`UIScreen.input` method. Input from a user is
|
||||
passed to this method, and the screen may process it, discard it or return it for further
|
||||
processing.
|
||||
|
||||
If input processing is not required and the screen should only be used for displaying
|
||||
information to a user, then the :attr:`UIScreen.input_required` property should be set to `False`.
|
||||
However, in this case you need to :meth:`close <UIScreen.close>`, :meth:`redraw <UIScreen.redraw>`
|
||||
or :ref:`push <screen_handling_label>` a new screen manually. This can be done, for example, in the
|
||||
:meth:`UIScreen.show_all` method.
|
||||
|
||||
.. code-block:: python
|
||||
:dedent: 0
|
||||
|
||||
def input(self, args, key):
|
||||
"""Process input from user and catch numbers with '/' symbol."""
|
||||
|
||||
# Test if user passed valid input for divider.
|
||||
# This will basically take number + number and nothing else and only positive numbers.
|
||||
groups = re.match(r'(\d+) *\/ *(\d+)$', key)
|
||||
if groups:
|
||||
num1 = int(groups[1])
|
||||
num2 = int(groups[2])
|
||||
|
||||
# Dividing by zero is not valid so we won't accept this input from the user. New
|
||||
# input is then required from the user.
|
||||
if num2 == 0:
|
||||
return InputState.DISCARDED
|
||||
|
||||
self._message = int(num1 / num2)
|
||||
|
||||
# Because this input is processed we need to show this screen (show the result)
|
||||
# again by returning PROCESSED_AND_REDRAW.
|
||||
# This will call the refresh method so our new result will be processed inside
|
||||
# of the refresh() method.
|
||||
return InputState.PROCESSED_AND_REDRAW
|
||||
else:
|
||||
# Not input for our screen, try other default inputs. This will result in the
|
||||
# same state as DISCARDED when no default option is used.
|
||||
return key
|
||||
|
||||
|
||||
.. py:currentmodule:: simpleline
|
||||
|
||||
Our screen is finished. Next, we need to use it in our application. To run an application the
|
||||
:class:`App` static class must be used.
|
||||
|
||||
This class will initialize an event loop and the scheduler by the :meth:`App.initialize` method.
|
||||
When the application is initialized, we need to pass our screen to the screen stack (you can
|
||||
pass multiple screens but we have only one here). To pass a screen to the screen stack we will use
|
||||
:class:`ScreenHandler <simpleline.render.screen_handler.ScreenHandler>`. For further explanation
|
||||
on screen scheduling, refer to the :ref:`screen_handling_label` section.
|
||||
|
||||
.. code-block:: python
|
||||
:dedent: 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Initialize application (create scheduler and event loop).
|
||||
App.initialize()
|
||||
|
||||
# Create our screen.
|
||||
screen = DividerScreen()
|
||||
|
||||
# Schedule screen to the screen scheduler.
|
||||
# This can be called only after App.initialize().
|
||||
ScreenHandler.schedule_screen(screen)
|
||||
|
||||
# Run the application. You must have some screen scheduled
|
||||
# otherwise it will end in an infinite loop.
|
||||
App.run()
|
||||
|
||||
Well done! You have your first application in Simpleline.
|
||||
|
||||
Further reading
|
||||
---------------
|
||||
|
||||
I would recommend everyone who wants to use Simpleline to look at the
|
||||
`examples <https://github.com/rhinstaller/python-simpleline/tree/master/examples>`_ which is one
|
||||
of the best sources of information. Another place to look is the :ref:`public_api_label`
|
||||
documentation section.
|
||||
@@ -0,0 +1,23 @@
|
||||
.. Simpleline documentation master file, created by
|
||||
sphinx-quickstart on Fri Nov 3 11:00:41 2017.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to Simpleline's documentation!
|
||||
======================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Contents:
|
||||
|
||||
introduction
|
||||
guide
|
||||
api
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
Introduction
|
||||
============
|
||||
|
||||
Simpleline is a text UI framework. Originally a part of the Anaconda installer project.
|
||||
|
||||
It is designed to be used with line-based machines and tools (e.g. serial console) so that
|
||||
every new line it appended to the bottom of the screen. Printed lines are never rewritten!
|
||||
|
||||
How to use
|
||||
----------
|
||||
|
||||
The best learning sources can be found in the
|
||||
`examples directory <https://github.com/rhinstaller/python-simpleline/tree/master/examples>`_ in
|
||||
the `GitHub repository <https://github.com/rhinstaller/python-simpleline>`_ and you can read the
|
||||
:ref:`guide_label` section of this documentation. However, some basic usage of Simpleline will be
|
||||
shown here too, to get an idea of how Simpleline works::
|
||||
|
||||
from simpleline import App
|
||||
from simpleline.render.screen import UIScreen
|
||||
from simpleline.render.screen_handler import ScreenHandler
|
||||
from simpleline.render.widgets import TextWidget
|
||||
|
||||
|
||||
# UIScreen is the main building item for Simpleline. Every screen
|
||||
# which will user see should be inherited from UIScreen.
|
||||
class HelloWorld(UIScreen):
|
||||
|
||||
def __init__(self):
|
||||
# Set title of the screen.
|
||||
super().__init__(title=u"Hello World")
|
||||
|
||||
def refresh(self, args=None):
|
||||
# Fill the self.window attribute by the WindowContainer and set screen title as header.
|
||||
super().refresh()
|
||||
widget = TextWidget("Body text")
|
||||
self.window.add_with_separator(widget)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Initialize application (create scheduler and event loop).
|
||||
App.initialize()
|
||||
|
||||
# Create our screen.
|
||||
screen = HelloWorld()
|
||||
|
||||
# Schedule screen to the screen scheduler.
|
||||
# This can be called only after App.initialize().
|
||||
ScreenHandler.schedule_screen(screen)
|
||||
|
||||
# Run the application. You must have some screen scheduled
|
||||
# otherwise it will end in an infinite loop.
|
||||
App.run()
|
||||
|
||||
The output from the simple *Hello World* example above::
|
||||
|
||||
$ ./run_example.sh 00_basic
|
||||
================================================================================
|
||||
================================================================================
|
||||
Hello World
|
||||
|
||||
Body text
|
||||
|
||||
Please make a selection from the above ['c' to continue, 'q' to quit, 'r' to
|
||||
refresh]:
|
||||
|
||||
If a user presses **r** and then **enter** to refresh, the same screen is printed again.
|
||||
This will be printed to a monitor::
|
||||
|
||||
$ ./run_example.sh 00_basic
|
||||
================================================================================
|
||||
================================================================================
|
||||
Hello World
|
||||
|
||||
Body text
|
||||
|
||||
Please make a selection from the above ['c' to continue, 'q' to quit, 'r' to
|
||||
refresh]: r
|
||||
================================================================================
|
||||
================================================================================
|
||||
Hello World
|
||||
|
||||
Body text
|
||||
|
||||
Please make a selection from the above ['c' to continue, 'q' to quit, 'r' to
|
||||
refresh]:
|
||||
|
||||
As you can see the whole screen is not rewritten -- only printed again on the bottom. This
|
||||
is the expected behavior so the actual screen is always at the bottom but you can see the whole
|
||||
history. This behavior makes working with line based machines and tools much easier.
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
This is a Python3-only project. This code should not be difficult to migrate to Python2. However,
|
||||
there is no need from the community, so it is only compatible with Python3 at the moment. No special
|
||||
libraries are required to use this library. If you want to use glib event loop instead of the
|
||||
original one you need to install glib and Python3 gobject introspection.
|
||||
|
||||
If you want to run tests (make ci), you need to install
|
||||
`Pocketlint <https://github.com/rhinstaller/pocketlint>`_ and
|
||||
`glib <https://developer.gnome.org/glib/>`_ with gobject introspection for
|
||||
`Python3 <https://docs.python.org/3/index.html>`_.
|
||||
@@ -0,0 +1,36 @@
|
||||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=.
|
||||
set BUILDDIR=_build
|
||||
set SPHINXPROJ=Simpleline
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.http://sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
||||
|
||||
:end
|
||||
popd
|
||||
Reference in New Issue
Block a user