1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
|
###############################################################################
#
# file: __init__.py
#
# Purpose: refer to module documentation for details
#
# Note: This file is part of Termsaver application, and should not be used
# or executed separately.
#
###############################################################################
#
# Copyright 2012 Termsaver
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
###############################################################################
"""
This module holds base classes that are used by all screens within termsaver
application. Each individual "screen" represents a unique screensaver that can
be triggered by termsaver.
The base classes available in this package are:
* `ScreenBase`: the most basic screen class, which will handle simple
interaction with the terminal.
* `filereader.FileReaderBase`: handles screens that require recursive
directory scanning for files to be printed out
* `urlfetcher.UrlFetcherBase`: handles screens that require Internet
connectivity.
* `urlfetcher.SimpleUrlFetcherBase`: similar as `UrlFetcherBase`,
with simpler options (to avoid overhead to build your own argument
parsing, and usage documentation)
* `rssfeed.RSSFeedScreenBase`: handles RSS parsing from Internet.
* `rssfeed.SimpleRSSFeedScreenBase`: similar as `RSSFeedScreenBase`,
with simpler options (to avoid overhead to build your own argument
parsing, and usage documentation)
Build your own screen
=====================
It is very simple to inherit from these base classes and create your own
screen. See some of the examples implemented here already. Basically, you will
need to:
* define a name and description for your screen (class instantiation)
and keep them as short as possible (avoid too much typing)
* if applicable, define your command-line usage guidelines and options
(see `cli_opts`), if appropriate and override `_parse_args` method.
Create your help/usage text by overriding the `_usage_options_example`
method.
* build your action by overriding the `_run_cycle` method, if applicable
(the base class will be triggered by the `autorun` method that loops
indefinitely or until there is a keyboard interruption (ctrl+C).
Before you start, though, I strongly advise you to check out the code here
thoroughly, to avoid reinventing the wheel in parts that are already covered.
Additionally, consistency is important, so try to keep the same concept of how
things are done here... Well, if you have better idea, I am very opened to
adapt (but then instead of making a mess, we would change it all to be still
consistent).
"""
#
# Python built-in modules
#
import os
import getopt
import sys
#
# Internal modules
#
from termsaverlib import common, constants, exception
from termsaverlib.screen.helper import ScreenHelperBase
from termsaverlib.i18n import _
class ScreenBase(ScreenHelperBase):
"""
This is the main screen that all screens must inherit in order to be part
of the screensaver list, accessible with termsaver command-line options.
When inheriting this to your own screen, remember to override the
following methods:
* `_run_cycle`: define here the algorithm to display a text-based
look-alike screensaver. See other classes for example on how to
use this.
* `_usage_options_example`: print out here the options and examples
on how to use your screen. See other classes for examples on how
to use this.
* `_parse_args`: from a properly parsed (using getopt) argument list,
customize the configuration of your screen accordingly
That's all you need to do!
Additionally, you can also call the following helper methods:
* `screen_exit`: if by any reason you need to close the application
(remember, in most cases, you can just rely on throwing exceptions
that are understood by termsaver application, available in
`termsaverlib.exception` module)
* `log` : if you need to write anything on screen before or after a
screen cycle, you can do it in style by calling this method, which
will inform the screen as a prefix to the message being displayed
on screen.
You can also use the following optional property:
* `cleanup_per_cycle`: Defines if the screen should be cleaned up for
every rotation cycle (new file).
IMPORTANT:
All other methods are not to be tempered with!
"""
name = ''
"""
Defines the name of the screen.
"""
description = ''
"""
Defines the description (short) of the screen.
"""
cli_opts = {}
"""
Defines the getopt format command-line options of the screen. It should be
an object in the following structure:
cli_opts = {
'opts': 'h',
'long_opts': ['help',],
}
"""
cleanup_per_cycle = False
"""
Defines if the screen should be cleaned up for every rotation cycle
(new file).
"""
def __init__(self, name, description, cli_opts):
"""
The basic constructor of this class. You need to inform basic
information about your screen:
* `name`: describes the name of the screen (try to keep it short,
and/or abbreviated, as much as possible)
* `description`: a brief (very brief) description of what the screen
does (if you need to write more documentation about
it, you can rely on man docs for that)
* `cli_opts`: the command line options that will be available for
your screen (use getopt formatting)
"""
self.name = name
self.description = description
self.cli_opts = cli_opts
def autorun(self, args, loop=True):
"""
The accessible method for dynamically running a screen.
This method will basically parse the arguments, prepare them with
the method `_parse_args` that is inherited in sub-classes, and with
the property `cli_opts` that holds the formatting of the arguments.
Once all is ready to go, this will call the `_run_cycle` method, which
is filled in the sub-classes with the algorithms to display text on
screen to behave as a screensaver.
The arguments of this method are:
* args: (MANDATORY) the arguments passed when termsaver is executed
from command-line. See `termsaver` script for details.
* loop: (OPTIONAL) defines if termsaver should be executing on an
infinite looping (goes on until the keyboard interrupt
(Ctrl+C) is pressed), or not. This is up to the screen
action (or end-user through configuable setting) to decide.
"""
# prepare values and validate
if not args:
args = ''
if not self.cli_opts \
or 'opts' not in self.cli_opts.keys() \
or not self.cli_opts['opts']:
self.cli_opts['opts'] = ''
if not self.cli_opts['long_opts']:
self.cli_opts['long_opts'] = []
else:
if not type(self.cli_opts['long_opts']) is list or \
[type(i) == str for i in self.cli_opts['long_opts']] \
!= [True for __ in range(len(self.cli_opts['long_opts']))]:
#
# Don't worry too much about errors here. This is supposed to
# help developers while programming screens for this app.
#
raise Exception("Value of 'long_opts' in cli_opts dict MUST "\
"be a list of strings.")
try:
self._parse_args(getopt.getopt(args, self.cli_opts['opts'],
self.cli_opts['long_opts']))
except getopt.GetoptError, e:
raise exception.InvalidOptionException("", str(e))
# execute the cycle
self.clear_screen()
while(loop):
self._run_cycle()
# Clear screen if appropriate
if self.cleanup_per_cycle:
self.clear_screen()
def _run_cycle(self):
"""
Executes a cycle of this screen. This base class actually does not hold
any special actions to begin with, but executing it from inheriting
classes is also a good practice, to allow future implementations that
must be taken from a base class.
"""
pass
@staticmethod
def usage_header():
"""
Simply prints a header information, used with the `usage` method.
See also `usage` method for details.
"""
print """%(app_title)s v.%(app_version)s - %(app_description)s.
""" % {
'app_title': constants.App.TITLE,
'app_version': constants.App.VERSION,
'app_description': constants.App.DESCRIPTION,
}
@staticmethod
def usage_footer():
"""
Simply prints a footer information, used with the `usage` method.
See also `usage` method for details.
"""
print """--
See more information about this project at:
%(url)s
Report bugs to authors at:
%(source_url)s
""" % {
'url': constants.App.URL,
'source_url': constants.App.SOURCE_URL,
}
def _usage_options_example(self):
"""
Describe here the options and examples of your screen.
See some examples of already implemented base screens so you can
write similar stuff on your own, and keep consistency.
"""
pass
def usage(self):
"""
Defines the usage information that is presented when a user hits the
help option.You should not directly override this method, instead, just
override the protected method `_usage_options_example`, created for
this purpose. All other stuff will be defined by the `usage_header` and
`usage_footer` methods.
"""
# header
self.usage_header()
print _("""Screen: %(screen)s
Description: %(description)s
Usage: %(app_name)s %(screen)s [options]""") % {
'app_name': constants.App.NAME,
'screen': self.name,
'description': self.description,
}
# any additional info in between (see other classes for reference)
self._usage_options_example()
#footer
self.usage_footer()
def _parse_args(self, prepared_args):
"""
(protected) MUST be overriden in inheriting classes, to deal with
special arguments that will customize values for them.
"""
pass
def screen_exit(self, error=0):
"""
Exits the screen (and finishes the application) with a specific error.
If none is informed, it exits as successful (error 0).
"""
sys.exit(error)
def log(self, text):
"""
Prints a log message on screen in the format:
%(app_name)s.%(screen)s: %(message)s
"""
print "%s.%s: %s" % (constants.App.NAME, self.name, text)
|