very first commit

This commit is contained in:
devfred78
2022-01-30 17:56:07 +01:00
commit 43d3ec25aa
34 changed files with 4841 additions and 0 deletions

152
.gitignore vendored Normal file
View File

@@ -0,0 +1,152 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintainted in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

21
LICENSE.md Normal file
View File

@@ -0,0 +1,21 @@
# MIT License
Copyright (c) 2022 [devfred78](https://github.com/devfred78)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

276
README.md Normal file
View File

@@ -0,0 +1,276 @@
# pygconsole
ANSI terminal emulation for Pygame.
## About the project
This package provides a way to emulate a terminal onto a Pygame display, compliant with a subset of the [ECMA-48 standard](https://www.ecma-international.org/publications-and-standards/standards/ecma-48/) (aka **ANSI escape codes**). Once correctly implemented, it can replace entirely `sys.stdout` if you desire, or at least be used with `print()` built-in function, as simply as displaying a string on a legacy terminal.
## Getting started
### Prerequisites
Of course, pygconsole cannot run without Python ! More precisely, it requires at least the 3.8 version of our beloved language.
pygconsole depends on the following packages. The installation of pygconsole should install automatically those packages if they are missing on your system. If it fails, you can install them individually:
* pygame: version 2.1.0 or above
```sh
pip install pygame
```
* colorlog: version 6.4.1 or above
```sh
pip install colorlog
```
### Installation
Install from PyPi with:
```sh
pip install pygconsole
```
## Usage
First, import the package with the following command:
```python
import pygconsole
```
pygconsole is used in close coordination with pygame. So you have to import and initialize pygame for instance with the following lines:
```python
import pygame
from pygame.locals import *
DISPLAY_SIZE = (1920,1080)
RESOURCE_DIR = os.path.join(os.path.dirname(__file__),"resources") # directory where graphical resources are stored
BACKGROUND_IMAGE = os.path.join(RESOURCE_DIR,"background.jpg") # Background image
pygame.init()
flags = FULLSCREEN|SCALED|DOUBLEBUF
screen_surface = pygame.display.set_mode(size=DISPLAY_SIZE,flags=flags)
background_surface = pygame.image.load(BACKGROUND_IMAGE)
```
Those lines initalize a full HD, fullscreen display, with a background image named `background.jpg`. For deeper information about pygame, read the [official site](https://www.pygame.org/docs/).
Initialize an I/O text, buffered stream with:
```python
iotextstreaam = pygconsole.io.TextIOConsoleWrapper(console_name="pygame_console", newline='\r\n', line_buffering=True)
```
Retrieve the console created "under the hoods" with this I/O stream:
```python
console = pygconsole.console.Console.get_console(name="pygame_console")
```
Finally, retrieve the pygame.Surface object used for displaying the console on screen:
```python
surface = console.surface
```
It is now possible to enter the display loop ! A possible example of display loop can be (take care to previously import the builtin module `sys` otherwise the `sys.exit()` command will raise an exception !) :
```python
while True:
# Events
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
sys.exit() # Exit when hitting the ESCAPE key
elif event.key == K_RETURN:
print("", file=iotextstreaam, flush=True) # New line
if event.type == TEXTINPUT: # When typing a key with a writable character...
print(event.text, end='', file=iotextstreaam, flush=True) # Display the character
# Background display
screen_surface.blit(background_surface,(0,0))
# Console display at coordinates (1000,620)
screen_surface.blit(console.surface,(1000,620))
# Screen rendering
pygame.display.flip()
```
If you wish to custom the console, you can initialize it independantly, and assign it to an I/O stream in a second step.
```python
my_console = pygconsole.console.get_console(name="custom_console",width=120,height=50)
iotextstreaam = pygconsole.io.TextIOConsoleWrapper(console_name="custom_console", newline='\r\n', line_buffering=True)
```
A console is identified by its name, 2 consoles with the same name are in reality the same instance of the Console class.
### Recognised ECMA-48 controls
Apart from recognising the main displayable unicode characters, pygconsole supports also the following non-displayable values in the byte stream sent to the console:
```
'\x0a' # LF (Line Field): move cursor to the corresponding character position of the following line
'\x0d' # CR (Carriage Return): move cursor to the first position of the current line
'\x1b' # ESC (Escape): start an escape sequence (see below)
```
Escape sequences supported by pygconsole take the form:
```
ESC [ <param> ; <param> ... <command>
```
Where `<param>` is an integer, and `<command>` is a single letter. Zero or more params are passed to a `<command>`. If no params are passed, it is generally synonymous with passing a single zero. No spaces exist in the sequence; they have been inserted here simply to read more easily.
The exhaustive list of escape sequences supported by pygconsole is the following:
```
# Erase in Display
ESC [ 0 J # clear from cursor to end of screen
ESC [ 1 J # clear from cursor to beginning of the screen
ESC [ 2 J # clear entire screen
ESC [ 3 J # clear entire screen and delete all lines saved in the scrollback buffer
# Screen scrolling
ESC [ n S # Scroll up of n lines. New lines are added at the bottom of the screen
ESC [ n T # Scroll down of n lines. New lines are added at the top of the screen
# general graphic rendition commands
ESC [ 0 m # default rendition, cancels the effect of any preceding occurrence of SGR sequence ("m" command)
ESC [ 1 m # bold characters
ESC [ 3 m # italicized characters
ESC [ 4 m # singly underlined characters
ESC [ 7 m # negative image, swaps foreground and background colours
ESC [ 22 m # normal intensity characters (not bold)
ESC [ 23 m # not italicized characters
ESC [ 24 m # not underlined characters
ESC [ 27 m # positive image, colours are no more reversed
# Foreground
ESC [ 30 m # black
ESC [ 31 m # red
ESC [ 32 m # green
ESC [ 33 m # yellow
ESC [ 34 m # blue
ESC [ 35 m # magenta
ESC [ 36 m # cyan
ESC [ 37 m # white
ESC [ 90 m # bright black (grey)
ESC [ 91 m # bright red
ESC [ 92 m # bright green
ESC [ 93 m # bright yellow
ESC [ 94 m # bright blue
ESC [ 95 m # bright magenta
ESC [ 96 m # bright cyan
ESC [ 97 m # bright white
# Background
ESC [ 40 m # black
ESC [ 41 m # red
ESC [ 42 m # green
ESC [ 43 m # yellow
ESC [ 44 m # blue
ESC [ 45 m # magenta
ESC [ 46 m # cyan
ESC [ 47 m # white
ESC [ 100 m # bright black (grey)
ESC [ 101 m # bright red
ESC [ 102 m # bright green
ESC [ 103 m # bright yellow
ESC [ 104 m # bright blue
ESC [ 105 m # bright magenta
ESC [ 106 m # bright cyan
ESC [ 107 m # bright white
```
For instance, to display "Hello world !" in red:
```python
print("\x1b[31mHello world !", file=iotextstreaam, flush=True)
```
Multiple numeric params to the 'm' command can be combined into a single sequence:
```
ESC [ 1 ; 92 ; 41 m # bold, bright green characters on red background
```
The colon character `:` is also recognised as a separator (like the semicolon `;`).
All other escape sequences of the form `ESC [ <param> ; <param> ... <command>`, and, more generally, all other ECMA-48 controls, are silently stripped from the output.
### API documentation
The API documentation is not yet implemented, sorry for the inconvenience :-(. Until the completion of this chapter, you can refer to the source files, they are self-documented enough to understand how the package proceeds.
The most two important classes are:
```python
pygconsole.io.TextIOConsoleWrapper()
```
A buffered text stream providing higher-level access to a BufferedIOConsole buffered binary stream. It inherits io.TextIOWrapper in the `ìo` Python built-in library. For further details about inherited elements, see <https://docs.python.org/3/library/io.html> . This class is generally used directly to write characters on the console, by objects using I/O streams (like `print()`).
```python
pygconsole.console.Console()
```
Console objects represent terminals that can be displayed onto any `pygame.Surface` object (in particular the display surface given by the `pygame.display.get_surface()` method).
Note that this class should never be instantiated directly, but always through the `get_console()` static method. Multiple calls to `get_console()` with the same name will always return a reference to the same Console object.
## Contributing
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.
If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement" or "bug", according to whether you want to share a proposal of a new function, or to record an anomaly.
Don't forget to give the project a star! Thanks again!
1. Fork the Project
2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
4. Push to the Branch (`git push origin feature/AmazingFeature`)
5. Open a Pull Request
## License
Distributed under the MIT License. See `LICENSE.md` file for more information.
## Acknowledgments
I would like greatfully to thank:
[DejaVU](https://dejavu-fonts.github.io/) authors for their astounding work on that famous font family I chose as the default font on pygconsole.
[Karl MPhotography](https://www.pexels.com/fr-fr/photo/paysage-nature-ciel-sable-8092914/) for the great picture I chose for the background of the testing set.
[Make a README](https://www.makeareadme.com/), [Sayan Mondal](https://medium.com/swlh/how-to-make-the-perfect-readme-md-on-github-92ed5771c061), [Hillary Nyakundi](https://www.freecodecamp.org/news/how-to-write-a-good-readme-file/) and [othneildrew](https://github.com/othneildrew/Best-README-Template) for providing very interesting materials to write good README files (far better than I can write by myself !).
[Choose an open source license](https://choosealicense.com/) for helping to choose the best suitable license for this project.
[pygame](https://www.pygame.org) for their wonderful library.
[colorama](https://github.com/tartley/colorama) for their inspiring description regarding ANSI escape sequences.
[python-colorlog](https://github.com/borntyping/python-colorlog) for adding joyful colors to the logging outputs.
[ECMA International](https://www.ecma-international.org/) for maintaining over years the [ECMA-48 standard](https://www.ecma-international.org/publications-and-standards/standards/ecma-48/)
[Semantic Versioning](https://semver.org/) for providing clear specifications for versioning projects.
[Real Python](https://realpython.com/) for contributing really increasing skills in Python for everyone, novices or veterans.
[GitHub](https://github.com/) for hosting this project, and helping to share it.
[Pypi](https://pypi.org/) for providing a very convenient way to share modules and package to the entire Python community.
And, of course, all the former, current and further contributors of this project !

4
pygconsole/__init__.py Normal file
View File

@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
__VERSION__ = "0.1.0"
RESOURCE_LOCATION = "resources"
import pygconsole.console, pygconsole.io

909
pygconsole/console.py Normal file
View File

@@ -0,0 +1,909 @@
# -*- coding: utf-8 -*-
"""
Module console : Terminal emulation onto a pygame display.
"""
# Standard modules
#------------------
import sys
import logging
import threading
from collections import namedtuple, deque
from dataclasses import dataclass
import io
import pkgutil
# Third party modules
#--------------------
import pygame
from pygame.locals import *
from colorlog import ColoredFormatter
# Internal modules
#-----------------
from pygconsole import RESOURCE_LOCATION
# Global constants
#-----------------
# namedtuples
#------------
Coordinates = namedtuple("Coordinates", "x y")
Colour = namedtuple("Colour", "red green blue alpha")
# Dataclasses
#------------
@dataclass
class CharArgs:
"""
Character caracteristics
"""
foreground_colour: Colour = Colour(255,255,255,255) # Font colour
background_colour: Colour = Colour(0,0,0,255) # Background colour
italic: bool = False # If True, character is italic (default is False)
bold: bool = False # If True, character is bold (default is False)
underline: bool = False # If True, character is underlined (default is False)
str: str = " " # Character representation
# Classes
#--------
class Console():
"""
Console objects represent terminals that can be displayed onto any `pygame.Surface` object (in particular the display surface given by the `pygame.display.get_surface()` method).
Note that this class should never be instantiated directly, but always through the `get_console()` static method. Multiple calls to `get_console()` with the same name will always return a reference to the same Console object.
Once instantiated, a typical cycling use-case of the object is:
- If necessary, modify attributes `italic`, `bold`, `underline`, `foreground_colour` or `background_colour` in accordance with the desired format to be rendered
- Call the `add_char()` method to add a string onto the console display
- Render the console screen onto the display by the use of the `surface` attribute in the Pygame display loop.
In addition to this cycle, several other methods can be called for specific commands to the console, like `clear()` or `scroll()`.
At last, a `logging.logger` object can be applied to facilitate status monitoring or fault investigation. In this case, the `create_log()` static method can be used to create a colorized, well-formed logger before calling `get_console()`. CAUTION: To prevent infinite loops, NEVER use the Console object to display its proper logger's outputs !
Attributes
----------
log : logging.logger object
logger used to track events that append when the instance is running (default is None).
name : str
the name of the console. Read-only attribute (default is the module name (eg: "pygconsole")).
surface : pygame.Surface object
represents the console rendering. Should be applied in the display loop. Read-only attribute.
foreground_colour : namedtuple(red,green,blue,alpha)
font colour that will be applied to the next printed character (default is `bright_white` (255,255,255,255)).
background_colour : namedtuple(red,green,blue,alpha)
background colour that will be applied to the next printed character (default is `black` (0,0,0,255)).
font_transparency : int
font transparency, using a value range of 0 (invisible) to 255 (opaque) inclusive (default 255).
background_transparency : int
Background transparency, using a value range of 0 (invisible) to 255 (opaque) inclusive (default 255).
italic : bool
gets or sets whether the font should be rendered in italics (default False).
bold : bool
gets or sets whether the font should be rendered in bold (default False).
underline : bool
gets or sets whether the font should be rendered with an underline (default False).
memory_size : int
amount of memorized characters (default 19200).
width : int
console width, in characters (default 80).
height : int
console height, in characters (default 24).
Static methods
--------------
get_console(name = __name__, width = 80, height = 24, font_size = 10, font_transparency = 255, background_transparency = 255, logger = None)
Return a Console instance.
create_log(name, level = logging.DEBUG, writestream = sys.stdout)
Return a logger with the specified name, level and output stream.
Methods
-------
add_char(char)
Display one or several characters on the console, at the current cursor position.
clear(mode = "all")
Clear full or part of screen.
scroll(nb_lines = 1, up = True)
Vertical scrolling of the display.
carriage_return()
Move cursor to the first position of the current line.
line_field()
Move cursor to the corresponding character position of the following line.
"""
#############################################################
# Class attributes
DEFAULT_NORMAL_FONT = RESOURCE_LOCATION + "/fonts/DejaVu/DejaVuSansMono.ttf"
DEFAULT_BOLD_FONT = RESOURCE_LOCATION + "/fonts/DejaVu/DejaVuSansMono-Bold.ttf"
DEFAULT_ITALIC_FONT = RESOURCE_LOCATION + "/fonts/DejaVu/DejaVuSansMono-Oblique.ttf"
DEFAULT_BOLDITALIC_FONT = RESOURCE_LOCATION + "/fonts/DejaVu/DejaVuSansMono-BoldOblique.ttf"
DEFAULT_LICENCE_FONT = RESOURCE_LOCATION + "/fonts/DejaVu/LICENSE"
DEFAULT_AUTHOR_FONT = RESOURCE_LOCATION + "/fonts/DejaVu/AUTHORS"
# Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc.
# https://dejavu-fonts.github.io/
DEFAULT_ALPHA = 255 # Default console transparency (0=invisible, 255=opaque)
DEFAULT_FOREGROUND_COLOUR = Colour(255,255,255,DEFAULT_ALPHA) # Default font colour
DEFAULT_BACKGROUND_COLOUR = Colour(0,0,0,DEFAULT_ALPHA) # Default background colour
DEFAULT_CHAR_MEMORY_SIZE = 80*24*10 # Default amount of memorized characters
STANDARD_COLOURS = {
'black': Colour(0,0,0,DEFAULT_ALPHA),
'red': Colour(170,0,0,DEFAULT_ALPHA),
'green': Colour(0,170,0,DEFAULT_ALPHA),
'yellow': Colour(170,85,0,DEFAULT_ALPHA),
'blue': Colour(0,0,170,DEFAULT_ALPHA),
'magenta': Colour(170,0,170,DEFAULT_ALPHA),
'cyan': Colour(0,170,170,DEFAULT_ALPHA),
'white': Colour(170,170,170,DEFAULT_ALPHA),
'bright_black': Colour(85,85,85,DEFAULT_ALPHA),
'bright_red': Colour(255,85,85,DEFAULT_ALPHA),
'bright_green': Colour(85,255,85,DEFAULT_ALPHA),
'bright_yellow': Colour(255,255,85,DEFAULT_ALPHA),
'bright_blue': Colour(85,85,255,DEFAULT_ALPHA),
'bright_magenta': Colour(255,85,255,DEFAULT_ALPHA),
'bright_cyan': Colour(85,255,255,DEFAULT_ALPHA),
'bright_white': Colour(255,255,255,DEFAULT_ALPHA)
}
_instances = dict()
#############################################################
# Static methods
@staticmethod
def get_console(name = __name__, width = 80, height = 24, font_size = 10, font_transparency = DEFAULT_ALPHA, background_transparency = DEFAULT_ALPHA, logger = None):
"""
Return a Console instance.
If a new name is given, a new instance is created. All calls to this method with a given name return the same Console instance. in this case, other parameters are ignored.
Parameters
----------
name : str, optional
The name of the console. Default is the module name (eg: "pygconsole")
width : int, optional
The width of the console, in characters (default is 80).
height : int, optional
The height of the console, in characters (default is 24).
font_size : int, optional
The height of the font, in pixels (default is 10).
font_transparency : int, optional
The transparency of the font, using a value range of 0 to 255 inclusive. 0 means invisible, 255 means fully opaque (default is 255).
background_transparency : int, optional
The transparency of the background, using a value range of 0 to 255 inclusive. 0 means invisible, 255 means fully opaque (default is 255).
logger : logging.Logger object, optional
The parent logger used to track events that append when the instance is running. Mainly for status monitoring or fault investigation purposes. If None (the default), no event is tracked.
"""
if name not in Console._instances:
with threading.Lock():
if name not in Console._instances:
if logger is None:
log = Console.create_log(name, writestream = None)
else:
log = logger.getChild(name)
Console(name,width,height,font_size,font_transparency,background_transparency,log)
log.info("------------------------------")
log.info(f"Creation of a new Console instance with the name '{name}'")
log.info("Characteristics:")
log.info(f"- dimensions (in chars) : {width}x{height}")
log.info(f"- font size : {font_size}")
log.info(f"- font transparency : {font_transparency}")
log.info(f"- background transparency : {background_transparency}")
log.info("------------------------------")
log.debug(f"List of Console instances: {Console._instances}")
else:
Console._instances[name].log.info(f"Usage of the already instanciated console '{name}'")
Console._instances[name].log.debug(f"List of Console instances: {Console._instances}")
else:
Console._instances[name].log.info(f"Usage of the already instanciated console '{name}'")
Console._instances[name].log.debug(f"List of Console instances: {Console._instances}")
return Console._instances[name]
@staticmethod
def create_log(name, level = logging.DEBUG, writestream = sys.stdout):
"""
Return a logger with the specified name, level and output stream.
This method wraps the logging.getLogger() function, adding colorization for ECMA-48 compliant terminals (almost all modern terminals are concerned). Hence, all calls to this method with a given name return the same logger instance.
Parameters
----------
name : str
The name of the logger
level : int, optional
The threshold of the logger. Logging messages which are less severe than level will be ignored; logging messages which have severity level or higher will be emitted. See https://docs.python.org/3/library/logging.html#levels for the possible values (default is logging.DEBUG)
writestream : writable text file-like object with buffering implementation, optional
Output stream where logging messages should be printed. For instance, all subclasses of io.TextIOBase should be available. Can also be None. In this case, no logging messages will be printed. (default is sys.stdout).
"""
# Colorized formatter
formatter = ColoredFormatter(
'%(log_color)s[%(asctime)s][%(levelname)s][%(name)s]:%(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
reset=True,
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'red,bg_white'
},
secondary_log_colors={},
style='%'
)
# Handler to specified output
if writestream is None:
handler = logging.NullHandler()
else:
handler = logging.StreamHandler(writestream)
handler.setLevel(level)
handler.setFormatter(formatter)
# logging
log = logging.getLogger(name)
log.setLevel(level)
log.addHandler(handler)
return log
#############################################################
# Initialization
def __init__(self, name = __name__, width = 80, height = 24, font_size = 10, font_transparency = DEFAULT_ALPHA, background_transparency = DEFAULT_ALPHA, logger = None):
"""
Parameters
----------
See get_console() method.
"""
if logger is None:
self.log = self.create_log(name, writestream = None)
else:
self.log = logger
if name not in Console._instances:
# New instance recording
Console._instances[name] = self
# console carateristics recording
self._width = width
self._height = height
self._font_size = font_size
self._name = name
self._font_transparency = font_transparency
self._background_transparency = background_transparency
# Console initialization
self._init_console()
def _init_console(self):
"""
Console initialization, with associated internal variables.
"""
# Pygame initialization
if not pygame.get_init(): pygame.init()
# Fonts initialization
self._init_font(self._font_size)
self._italic = False
self._bold = False
self._underline = False
# Default colours initialization
self._default_foreground_colour = Console.DEFAULT_FOREGROUND_COLOUR._replace(alpha = self.font_transparency)
self._default_background_colour = Console.DEFAULT_BACKGROUND_COLOUR._replace(alpha = self.background_transparency)
# Current colours initialization
self._current_foreground_colour = Console.DEFAULT_FOREGROUND_COLOUR._replace(alpha = self.font_transparency)
self._current_background_colour = Console.DEFAULT_BACKGROUND_COLOUR._replace(alpha = self.background_transparency)
# pygame.Surface objects initialization
char_width, char_height = self._normal_font.size(" ") # Works only with fixed-width fonts !
surf_width = char_width * self._width
surf_height = char_height * self._height
self._current_surface = pygame.Surface((surf_width, surf_height), flags=SRCALPHA)
with pygame.PixelArray(self._current_surface) as currentpxarray:
currentpxarray[:] = pygame.Color(0, 0, 0, 0) # invisible surface by default
self._previous_surface = self._current_surface.copy()
# Presentation stream initialization
self._char_memory_size = Console.DEFAULT_CHAR_MEMORY_SIZE
self._presentation_stream = deque([None] * self._width * self._height, self._char_memory_size)
# Other initializations
self._cursor = 0 # Cursor position in the presentation stream (ie: the index where the next character will be added)
self._start_window = 0 # First rendered character position in the presentation stream
self._end_window = self._width * self._height - 1 # Last rendered character position in the presentation stream
self._update_surface_lock = threading.Lock() # Protect the _current_surface object during its update
# Initial surfaces populating
self._render_all()
def _init_font(self, font_size):
"""
Fonts initialization.
Parameters
----------
font_size: int
size of initialized fonts
"""
# get binary resources
normal_font_bin = pkgutil.get_data(__name__, Console.DEFAULT_NORMAL_FONT)
bold_font_bin = pkgutil.get_data(__name__, Console.DEFAULT_BOLD_FONT)
italic_font_bin = pkgutil.get_data(__name__, Console.DEFAULT_ITALIC_FONT)
bolditalic_font_bin = pkgutil.get_data(__name__, Console.DEFAULT_BOLDITALIC_FONT)
# translate binaries into file-like objects
normal_font_file = io.BytesIO(normal_font_bin)
bold_font_file = io.BytesIO(bold_font_bin)
italic_font_file = io.BytesIO(italic_font_bin)
bolditalic_font_file = io.BytesIO(bolditalic_font_bin)
# Creation of pygame.font.Font objects
self._normal_font = pygame.font.Font(normal_font_file, font_size)
self._bold_font = pygame.font.Font(bold_font_file, font_size)
self._italic_font = pygame.font.Font(italic_font_file, font_size)
self._bolditalic_font = pygame.font.Font(bolditalic_font_file, font_size)
#############################################################
# Private attributes
def __repr__(self):
"""
"Official" String representation of the instance.
"""
return f"<Console '{self.name}' with dimensions {self.width}x{self.height}>"
def __str__(self):
"""
"Informal" String representation of the instance.
"""
return f"Console instance called '{self.name}'"
#############################################################
# Public attributes
@property
def name(self):
"""
Name of the instance. Read-only attribute.
"""
return self._name
@property
def surface(self):
"""
pygame.Surface object representing the console rendering. Should be applied in the display loop. Read-only attribute.
"""
if self._update_surface_lock.locked(): return self._previous_surface
else: return self._current_surface
@property
def foreground_colour(self):
"""
Font colour that will be applied to the next printed character.
The return value is a namedtuple Colour "red green blue alpha".
The value can be set using one of the following formats:
- namedtuple Colour
- tuple (red, green, blue, alpha) (each element is an int in the range of 0 to 255 inclusive)
- tuple (red, green, blue) (each element is an int in the range of 0 to 255 inclusive)
- string 'default': font default colour
- string among the following: 'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white', 'bright_black', 'bright_red', 'bright_green', 'bright_yellow', 'bright_blue', 'bright_magenta', 'bright_cyan', 'bright_white'
if none of these formats is used, the attribute remains unchanged.
"""
return self._current_foreground_colour
@foreground_colour.setter
def foreground_colour(self, value):
if isinstance(value, str):
if value in Console.STANDARD_COLOURS:
self._current_foreground_colour = Console.STANDARD_COLOURS[value]._replace(alpha = self.font_transparency)
self.log.debug(f"The font colour is set to {value}.")
elif value == 'default':
self._current_foreground_colour = self._default_foreground_colour._replace(alpha = self.font_transparency)
self.log.debug(f"The font colour is set to {value}.")
elif len(value) == 4:
if -1 < value[0] < 256 and -1 < value[1] < 256 and -1 < value[2] < 256 and -1 < value[3] < 256:
self._current_foreground_colour = Colour._make(value)
self.font_transparency = value[3]
self.log.debug(f"The font colour is set to {value}.")
else: self.log.warning(f"{value} cannot be applied to a foreground colour. The previous value is kept: {self._current_foreground_colour}")
elif len(value) == 3:
if -1 < value[0] < 256 and -1 < value[1] < 256 and -1 < value[2] < 256:
self._current_foreground_colour = Colour._make(list(value) + [self.font_transparency])
self.log.debug(f"The font colour is set to {value}.")
else: self.log.warning(f"{value} cannot be applied to a foreground colour. The previous value is kept: {self._current_foreground_colour}")
else: self.log.warning(f"{value} cannot be applied to a foreground colour. The previous value is kept: {self._current_foreground_colour}")
@property
def background_colour(self):
"""
Background colour that will be applied to the next printed character.
The return value is a namedtuple Colour "red green blue alpha".
The value can be set using one of the following formats:
- namedtuple Colour
- tuple (red, green, blue, alpha) (each element is an int in the range of 0 to 255 inclusive)
- tuple (red, green, blue) (each element is an int in the range of 0 to 255 inclusive)
- string 'default': background default colour
- string among the following: 'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white', 'bright_black', 'bright_red', 'bright_green', 'bright_yellow', 'bright_blue', 'bright_magenta', 'bright_cyan', 'bright_white'
if none of these formats is used, the attribute remains unchanged.
"""
return self._current_background_colour
@background_colour.setter
def background_colour(self, value):
if isinstance(value, str):
if value in Console.STANDARD_COLOURS:
self._current_background_colour = Console.STANDARD_COLOURS[value]._replace(alpha = self.background_transparency)
self.log.debug(f"The background colour is set to {value}.")
elif value == 'default':
self._current_background_colour = self._default_background_colour._replace(alpha = self.background_transparency)
self.log.debug(f"The background colour is set to {value}.")
elif len(value) == 4:
if -1 < value[0] < 256 and -1 < value[1] < 256 and -1 < value[2] < 256 and -1 < value[3] < 256:
self._current_background_colour = Colour._make(value)
self.background_transparency = value[3]
self.log.debug(f"The background colour is set to {value}.")
else: self.log.warning(f"{value} cannot be applied to a background colour. The previous value is kept: {self._current_background_colour}")
elif len(value) == 3:
if -1 < value[0] < 256 and -1 < value[1] < 256 and -1 < value[2] < 256:
self._current_background_colour = Colour._make(list(value) + [self.background_transparency])
self.log.debug(f"The background colour is set to {value}.")
else: self.log.warning(f"{value} cannot be applied to a background colour. The previous value is kept: {self._current_background_colour}")
else: self.log.warning(f"{value} cannot be applied to a background colour. The previous value is kept: {self._current_background_colour}")
@property
def font_transparency(self):
"""
Font transparency, using a value range of 0 (invisible) to 255 (opaque) inclusive.
"""
return self._font_transparency
@font_transparency.setter
def font_transparency(self, value):
try:
value_int = int(value)
except ValueError:
self.log.warning(f"{value} cannot be applied to a transparency value. The default value is kept: {self.Console.DEFAULT_ALPHA}")
value_int = Console.DEFAULT_ALPHA
finally:
self._font_transparency = value_int
self.log.debug(f"The font transparency is set to {value_int}.")
@property
def background_transparency(self):
"""
Background transparency, using a value range of 0 (invisible) to 255 (opaque) inclusive.
"""
return self._background_transparency
@background_transparency.setter
def background_transparency(self, value):
try:
value_int = int(value)
except ValueError:
self.log.warning(f"{value} cannot be applied to a transparency value. The default value is kept: {self.Console.DEFAULT_ALPHA}")
value_int = Console.DEFAULT_ALPHA
finally:
self._background_transparency = value_int
self.log.debug(f"The background transparency is set to {value_int}.")
@property
def italic(self):
"""
Gets or sets whether the font should be rendered in italics.
If the set value is not a boolean, the attribute remains unchanged.
"""
return self._italic
@italic.setter
def italic(self, value):
if isinstance(value, bool):
self._italic = value
else: self.log.warning(f"{value} cannot be applied to 'italic' attribute. The previous value is kept: {self._italic}")
@property
def bold(self):
"""
Gets or sets whether the font should be rendered in bold.
If the set value is not a boolean, the attribute remains unchanged.
"""
return self._bold
@bold.setter
def bold(self, value):
if isinstance(value, bool):
self._bold = value
else: self.log.warning(f"{value} cannot be applied to 'bold' attribute. The previous value is kept: {self._bold}")
@property
def underline(self):
"""
Gets or sets whether the font should be rendered with an underline.
If the set value is not a boolean, the attribute remains unchanged.
"""
return self._underline
@underline.setter
def underline(self, value):
if isinstance(value, bool):
self._underline = value
else: self.log.warning(f"{value} cannot be applied to 'underline' attribute. The previous value is kept: {self._underline}")
@property
def memory_size(self):
"""
Amount of memorized characters.
If modified, the console is re-initialized, and all memorized characters are lost. Its value must be higher or equal to the amount of displayable characters on the console surface. If not, the set value is ignored, and the attribute remains unchanged.
"""
return self._char_memory_size
@memory_size.setter
def memory_size(self, value):
if value >= self._width * self._height:
self._char_memory_size = value
# Console re-init
self._presentation_stream = deque([None] * self._width * self._height, self._char_memory_size)
self._cursor = 0
self._start_window = 0
self._end_window = self._width * self._height - 1
else: self.log.warning(f"Memory cannot be less than {self._width * self._height}. The previous value is kept: {self._char_memory_size}")
@property
def width(self):
"""
Console width, in characters.
If modified, the console is re-initialized, and all memorized characters are lost. If the set value is negative or null, it is ignored, and the attribute remains unchanged.
"""
return self._width
@width.setter
def width(self, value):
if value > 0:
self._width = value
# Console re-init
self._presentation_stream = deque([None] * self._width * self._height, self._char_memory_size)
self._cursor = 0
self._start_window = 0
self._end_window = self._width * self._height - 1
else: self.log.warning(f"Console width cannot be negative. The previous value is kept: {self._width}")
@property
def height(self):
"""
Console height, in characters.
If modified, the console is re-initialized, and all memorized characters are lost. If the set value is negative or null, it is ignored, and the attribute remains unchanged.
"""
return self._height
@height.setter
def height(self, value):
if value > 0:
self._height = value
# Console re-init
self._presentation_stream = deque([None] * self._width * self._height, self._char_memory_size)
self._cursor = 0
self._start_window = 0
self._end_window = self._width * self._height - 1
else: self.log.warning(f"Console height cannot be negative. The previous value is kept: {self._height}")
#############################################################
# Public methods
def add_char(self, char):
"""
Display one or several characters on the console, at the current cursor position.
The rendered surface is updated. The cursor moves to the position just after the last character printed. If `char` is empty, nothing is modified, and the cursor remains at the same place. All characters included in `char` share the same caracteristics (foregroung and background colours, italic, bold and underline) from the corresponding attributes.
Parameters
----------
char: string
Characters that will be displayed.
"""
if len(char) == 0:
self.log.debug("An empty string is displayed: nothing is done.")
elif len(char) == 1:
self.log.debug(f"Position: {self._cursor} - displayed character: '{char}'")
render_all = False
# Caracteristics of the character
char_args = CharArgs()
char_args.foreground_colour = self.foreground_colour
char_args.background_colour = self.background_colour
char_args.italic = self.italic
char_args.bold = self.bold
char_args.underline = self.underline
char_args.str = char
# Adding a new line in the presentation stream if needed
if self._cursor >= len(self._presentation_stream):
self._presentation_stream.extend([None] * self.width)
# rendered window move
self._start_window += self.width
self._end_window += self.width
# The complete rendered surface must be updated
render_all = True
# Adding character in the presentation stream
self._presentation_stream[self._cursor] = char_args
# Surface rendering
if render_all:
self._render_all()
else:
self._render_char(self._cursor)
# Increasing cursor position
self._cursor += 1
else: # More than 1 character to display
self.log.debug(f"Position: {self._cursor} - displayed string: '{char}'")
render_all = False
list_of_indexes = []
for single_char in char:
# Caracteristics of the character
char_args = CharArgs()
char_args.foreground_colour = self.foreground_colour
char_args.background_colour = self.background_colour
char_args.italic = self.italic
char_args.bold = self.bold
char_args.underline = self.underline
char_args.str = single_char
# Adding a new line in the presentation stream if needed
if self._cursor >= len(self._presentation_stream):
self._presentation_stream.extend([None] * self.width)
# rendered window move
self._start_window += self.width
self._end_window += self.width
# The complete rendered surface must be updated
render_all = True
# Adding character in the presentation stream
self._presentation_stream[self._cursor] = char_args
# Adding cursor position in the characters list to display
list_of_indexes.append(self._cursor)
# Increasing cursor position
self._cursor += 1
# Surface rendering
if render_all:
self._render_all()
else:
self._render_chars(list_of_indexes)
def clear(self, mode = "all"):
"""
Clear full or part of screen.
According to the value of `mode`, this function has one of the follwing behaviours:
"all" (the default): all character positions of the page are cleared, and the cursor is moved to the upper left position. All memorized characters are lost.
"before": the character positions from the beginning of the page up to the cursor are cleared.
"after": the cursor position and the character positions up to the end of the page are cleared.
"all_without_memory": all character positions of the page are cleared, but cursor position and memorized characters are preserved
Parameters
----------
mode: string
type of clearing
"""
if mode == "before":
self.log.debug("All characters before the cursor are cleared.")
for index_char in range(self._start_window,self._cursor):
self._presentation_stream[index_char] = None
elif mode == "after":
self.log.debug("All characters after the cursor are cleared.")
for index_char in range(self._cursor,self._end_window+1):
self._presentation_stream[index_char] = None
elif mode == "all_without_memory":
self.log.debug("All characters of the page are cleared, preserving memorized characters.")
for index_char in range(self._start_window,self._end_window+1):
self._presentation_stream[index_char] = None
else:
self.log.debug("Clear full screen and memory.")
# Console re-init
self._presentation_stream = deque([None] * self._width * self._height, self._char_memory_size)
self._cursor = 0
self._start_window = 0
self._end_window = self._width * self._height - 1
# Surface rendering
self._render_all()
def scroll(self, nb_lines = 1, up = True):
"""
Vertical scrolling of the display.
The rendered characters are moved down or up by `nb_lines`, depending on the value of `up`.
PARAMETERS
----------
nb_lines: int, optional
number of lines the display is moved of (default is 1).
up: bool, optional
direction of the move. `True` for moving up (the default), `False` for moving down.
"""
# Rendered window update
if up:
if nb_lines == 1:
self.log.debug(f"Display moving up of 1 line.")
else:
self.log.debug(f"Display moving up of {nb_lines} lines.")
if self._start_window - self._width * nb_lines < 0:
self._start_window = 0
else:
self._start_window -= self._width * nb_lines
self._end_window = self._start_window + self._width * self._height - 1
else:
if nb_lines == 1:
self.log.debug(f"Display moving down of 1 line.")
else:
self.log.debug(f"Display moving down of {nb_lines} lines.")
if self._end_window + self._width * nb_lines > len(self._presentation_stream):
self._end_window = len(self._presentation_stream) - 1
else:
self._end_window += self._width * nb_lines
self._start_window = self._end_window - self._width * self._height + 1
# Surface rendering
self._render_all()
def carriage_return(self):
"""
Carriage return.
The cursor is moved to the first position of the current line.
"""
self.log.debug(f"Carriage return at the line n° {self._cursor // self._width}")
self._cursor = (self._cursor // self._width) * self._width
def line_field(self):
"""
Line field.
The cursor is moved to the corresponding character position of the following line.
"""
self.log.debug(f"Cursor move to the line n° {self._cursor // self._width + 1}")
self._cursor += self._width
#############################################################
# Private methods
def _chartopixcoord(self, index_char = 0):
"""
Return coordinates of a specific character on the rendered surface.
This function returns a Coordinates namedtuple object, corresponding to the coordinates of the upper left pixel of the displayed character, on the rendered surface where the upper left pixel is (0,0).
Return `None`if the character is out of the rendered surface.
PARAMETERS
----------
index_char: int, optional
character index in the presentation stream (default is 0).
"""
if index_char < self._start_window: return None
if index_char > self._end_window: return None
# Calculing column and row
column = (index_char - self._start_window) % self._width
row = (index_char - self._start_window) // self._width
# Figuring out character dimensions
char_width, char_height = self._normal_font.size(" ") # Works only with fixed-width fonts !
# Calculing abscissa and ordinate
x = column * char_width
y = row * char_height
# Returning calculated coordinates
return Coordinates(x, y)
def _render_char(self, index):
"""
Update the rendered surface with a specified character.
The image of the character is drawn onto the rendered surface at the appropriate coordinates, and the rest of the surface remains unchanged.
PARAMETERS
----------
index: int
character index in the presentation stream.
"""
coord_char = self._chartopixcoord(index)
if self._presentation_stream[index] is None:
font_surf = self._normal_font.render(" ", True, self.foreground_colour, self.background_colour)
else:
if self._presentation_stream[index].bold and self._presentation_stream[index].italic:
local_font = self._bolditalic_font
elif self._presentation_stream[index].bold and not self._presentation_stream[index].italic:
local_font = self._bold_font
elif self._presentation_stream[index].italic and not self._presentation_stream[index].bold:
local_font = self._italic_font
else:
local_font = self._normal_font
local_font.underline = self._presentation_stream[index].underline
font_surf = local_font.render(self._presentation_stream[index].str, True, self._presentation_stream[index].foreground_colour, self._presentation_stream[index].background_colour)
with self._update_surface_lock:
self._current_surface.blit(font_surf,coord_char)
# self._current_surface = self._current_surface.convert_alpha()
self._previous_surface = self._current_surface.copy()
def _render_chars(self, list_of_indexes):
"""
Update the rendered surface with several specified characters.
The images of the characters are drawn onto the rendered surface at the appropriate coordinates, and the rest of the surface remains unchanged.
PARAMETERS
----------
list_of_indexes: iterable of integers
list of character indexes to be drawn onto the rendered surface
"""
surf_to_blit = []
for index in list_of_indexes:
coord_char = self._chartopixcoord(index)
if self._presentation_stream[index] is None:
font_surf = self._normal_font.render(" ", True, self.foreground_colour, self.background_colour)
else:
if self._presentation_stream[index].bold and self._presentation_stream[index].italic:
local_font = self._bolditalic_font
elif self._presentation_stream[index].bold and not self._presentation_stream[index].italic:
local_font = self._bold_font
elif self._presentation_stream[index].italic and not self._presentation_stream[index].bold:
local_font = self._italic_font
else:
local_font = self._normal_font
local_font.underline = self._presentation_stream[index].underline
font_surf = local_font.render(self._presentation_stream[index].str, True, self._presentation_stream[index].foreground_colour, self._presentation_stream[index].background_colour)
surf_to_blit.append((font_surf,coord_char))
with self._update_surface_lock:
self._current_surface.blits(surf_to_blit,doreturn=False)
# self._current_surface = self._current_surface.convert_alpha()
self._previous_surface = self._current_surface.copy()
def _render_all(self):
"""
Update the complete rendered surface.
"""
surf_to_blit = []
for index in range(self._start_window, self._end_window + 1):
coord_char = self._chartopixcoord(index)
if self._presentation_stream[index] is None:
font_surf = self._normal_font.render(" ", True, self.foreground_colour, self.background_colour)
else:
if self._presentation_stream[index].bold and self._presentation_stream[index].italic:
local_font = self._bolditalic_font
elif self._presentation_stream[index].bold and not self._presentation_stream[index].italic:
local_font = self._bold_font
elif self._presentation_stream[index].italic and not self._presentation_stream[index].bold:
local_font = self._italic_font
else:
local_font = self._normal_font
local_font.underline = self._presentation_stream[index].underline
font_surf = local_font.render(self._presentation_stream[index].str, True, self._presentation_stream[index].foreground_colour, self._presentation_stream[index].background_colour)
surf_to_blit.append((font_surf,coord_char))
with self._update_surface_lock:
self._current_surface.blits(surf_to_blit,doreturn=False)
# self._current_surface = self._current_surface.convert_alpha()
self._previous_surface = self._current_surface.copy()

493
pygconsole/io.py Normal file
View File

@@ -0,0 +1,493 @@
# -*- coding: utf-8 -*-
"""
Module io : I/O interfaces to the terminal emulation provided by the console module.
"""
# Standard modules
#------------------
import logging
import io
import codecs
# Third party modules
#--------------------
# Internal modules
#-----------------
from pygconsole.console import Console as Console
# Global constants
#-----------------
# namedtuples
#------------
# Dataclasses
#------------
# Classes
#--------
class RawIOConsole(io.RawIOBase):
"""
RawIOConsole class provides the lower-level binary access to an underlying Console object, without buffering. It inherits io.RawIOBase. For further details about inherited elements, see https://docs.python.org/3/library/io.html . In normal cases, RawIOConsole objects do not have to be used directly to write characters on the console (use TextIOConsoleWrapper objects instead).
Attributes
----------
console: pygconsole.console.Console object
Underlying Console object associated to this API.
log : logging.logger object
logger used to track events that append when the instance is running (default is None).
Inherited attributes remain unchanged.
Overriden methods
-----------------
isatty()
As the stream is not interactive, return always `False` in the current implementation of the RawIOConsole class.
readable()
As the stream cannot be read from, return always `False` in the current implementation of the RawIOConsole class.
seekable()
As the stream does not support random access, return always `False` in the current implementation of the RawIOConsole class.
writable()
As the stream supports writing, return always `True` in the current implementation of the RawIOConsole class.
write(bytes_str)
Write the given bytes-like object `bytes_str` to the underlying console, and return the number of bytes written.
Other inherited methods remain unchanged.
"""
#############################################################
# Class constants
# ECMA-48 standard implementation (aka 'ANSI escape codes')
# C0 control code set
CTRL_RANGE = range(0x1f+1) # Range of possible values for the C0 control byte
SUPPORTED_C0_CTRL = {
0x0a: 'LF', # LINE FIELD
0x0d: 'CR', # CARRIAGE RETURN
0x1b: 'ESC' # ESCAPE
}
# C1 control code set
Fe_RANGE = range(0x40,0x5f+1) # Range of possible values for the Fe byte (byte following ESC in a C1 control code)
SUPPORTED_C1_CTRL = {
0x5b: 'CSI' # '[' : CONTROL SEQUENCE INTRODUCER
}
# Control sequences
FINAL_RANGE = range(0x40,0x7e+1) # Range of possibles values for the final byte in control sequences
SUPPORTED_FINAL_CTRL = {
0x4a: 'ED', # 'J' : Erase in Display (Erase in Page in ECMA-48 standard): CSI n J (n=0:clear from cursor to end of screen,n=1:clear from cursor to beginning of the screen,n=2:clear entire screen,n=3:clear entire screen and delete all lines saved in the scrollback buffer (not part of the ECMA-48 standard, but introduced for xterm and supported now by all terminal applications))
0x53: 'SU', # 'S' : Scroll Up: CSI n S (n = nb of lines). New lines are added at the bottom.
0x54: 'SD', # 'T': Scroll Down CSI n T (n = nb of lines). New lines are added at the top.
0x6d: 'SGR' # 'm': Select Graphic Rendition: CSI n m (n = colour or style identifiers - see below)
}
# SGR parameters
SEPARATORS = ";:" # Possible separators between parameters
SUPPORTED_SGR_PARAMETERS = {
'0': 'reset', # default rendition, cancels the effect of any preceding occurrence of SGR
'1': 'bold', # bold characters
'3': 'italic', # italicized characters
'4': 'underline', # singly underlined characters
'7': 'negative', # negative image, swaps foreground and background colours
'22': 'not_bold', # normal intensity characters (not bold)
'23': 'not_italic', # not italicized characters
'24': 'not_underline', # not underlined characters
'27': 'positive' # positive image, colours are no more reversed
}
STANDARD_FOREGROUND_COLOURS = {
'30': 'black',
'31': 'red',
'32': 'green',
'33': 'yellow',
'34': 'blue',
'35': 'magenta',
'36': 'cyan',
'37': 'white',
'90': 'bright_black', # grey
'91': 'bright_red',
'92': 'bright_green',
'93': 'bright_yellow',
'94': 'bright_blue',
'95': 'bright_magenta',
'96': 'bright_cyan',
'97': 'bright_white'
}
STANDARD_BACKGROUND_COLOURS = {
'40': 'black',
'41': 'red',
'42': 'green',
'43': 'yellow',
'44': 'blue',
'45': 'magenta',
'46': 'cyan',
'47': 'white',
'100': 'bright_black', # grey
'101': 'bright_red',
'102': 'bright_green',
'103': 'bright_yellow',
'104': 'bright_blue',
'105': 'bright_magenta',
'106': 'bright_cyan',
'107': 'bright_white'
}
BRIGHT_SHIFT = 60 # shift between normal and bright colours
#############################################################
# Initialization
def __init__(self, console_name = "pygame_console", logger = None):
"""
Parameters
----------
console_name: str
Name of theuUnderlying pygconsole.console.Console object associated to this API (default is "pygame_console")
logger: logging.Logger object, optional
The parent logger used to track events that append when the instance is running. Mainly for status monitoring or fault investigation purposes. If None (the default), no event is tracked.
"""
if logger is None:
handler = logging.NullHandler()
self.log = logging.getLogger('RawIOConsole')
self.log.addHandler(handler)
else:
self.log = logger.getChild('RawIOConsole')
self._console = Console.get_console(console_name, logger = self.log)
self._byte_esc_seq = bytearray() # undecoded escape sequence
self._byte_displaying_char = bytearray() # undecoded displayable characters
#############################################################
# Public attributes
@property
def console(self):
"""
Underlying pygconsole.console.Console object associated to this API.
If the set value is not a Console object, the attribute remains silently unchanged.
"""
return self._console
@console.setter
def console(self, value):
if isinstance(value, Console):
self._console = value
else:
self.log.warning(f"{value} is not a Console object. The previous value is kept: {self.console}")
pass
#############################################################
# Public methods
def isatty(self):
"""
Return True if the stream is interactive (i.e., connected to a terminal/tty device).
"""
return False
def readable(self):
"""
Return True if the stream can be read from. If False, read() will raise OSError.
"""
return False
def seekable(self):
"""
Return True if the stream supports random access. If False, seek(), tell() and truncate() will raise OSError.
"""
return False
def writable(self):
"""
Return True if the stream supports writing. If False, write() and truncate() will raise OSError.
"""
return True
def write(self, bytestream):
"""
Write the given bytes-like object, bytestream, to the underlying console, and return the number of bytes written.
Only encoded utf-8 characters in bytestream are accepted. If a decoding error occurs, then only bytes before the undecoded byte are taken into account. The supported escape sequences are translated.
Parameters
----------
bytestream: bytes, bytearray or memoryview object
bytes sequence, that is, succession of positive integers less or equal to 255
"""
# bytestream translation into bytearray object
byte_sequence = bytearray(bytestream)
# utf-8 check
try:
codecs.decode(byte_sequence,encoding='utf-8',errors='strict')
except UnicodeDecodeError as err:
byte_sequence = byte_sequence[:err.start]
finally:
nb_decoded_bytes = len(byte_sequence)
# byte treatment
for byte in byte_sequence:
if len(self._byte_esc_seq) > 0: # proceeding escape sequence
self._byte_esc_seq.append(byte)
decoded_esc_seq = self._esc_seq_decode(self._byte_esc_seq)
if decoded_esc_seq == 'error':
self.log.warning(f"The following escape sequence encountered an error: {list(self._byte_esc_seq)}")
self._byte_esc_seq = bytearray()
elif decoded_esc_seq == 'successful':
self.log.debug(f"The follwing escape sequence was treated successfully: {list(self._byte_esc_seq)}")
self._byte_esc_seq = bytearray()
elif decoded_esc_seq == 'incomplete':
pass
elif byte in RawIOConsole.CTRL_RANGE: # Beginning of an escape sequence
if len(self._byte_displaying_char) > 0:
self.console.add_char(self._byte_displaying_char.decode(encoding='utf-8'))
self._byte_displaying_char = bytearray()
self._byte_esc_seq.append(byte)
# 1-byte escape sequence
decoded_esc_seq = self._esc_seq_decode(self._byte_esc_seq)
if decoded_esc_seq == 'error':
self.log.warning(f"The following escape sequence encountered an error: {list(self._byte_esc_seq)}")
self._byte_esc_seq = bytearray()
elif decoded_esc_seq == 'successful':
self.log.debug(f"The follwing escape sequence was treated successfully: {list(self._byte_esc_seq)}")
self._byte_esc_seq = bytearray()
elif decoded_esc_seq == 'incomplete':
pass
else: # Displayable character (or part of)
self._byte_displaying_char.append(byte)
# Final treatment of displayable characters
if len(self._byte_displaying_char) > 0:
self.console.add_char(self._byte_displaying_char.decode(encoding='utf-8'))
self._byte_displaying_char = bytearray()
return nb_decoded_bytes
#############################################################
# Private methods
def _esc_seq_decode(self, sequence):
"""
Decode an escape sequence conforming to ECMA-48 standard (ANSI escape codes).
Supported commands are sent to the console. Return 'successful' if the sequence is supported and successfully proceeded, 'error' if an error is encountered, or 'incomplete' if the sequence is recognized as the beginning of a supported sequence, but not yet complete.
Parameters
----------
sequence: bytearray
Escape sequence encoded using utf-8 codec
"""
if len(sequence) == 0:
return 'incomplete'
elif sequence[0] in RawIOConsole.CTRL_RANGE:
if sequence[0] in RawIOConsole.SUPPORTED_C0_CTRL:
if RawIOConsole.SUPPORTED_C0_CTRL[sequence[0]] == 'ESC': # Beginning of the escape sequence
if len(sequence) > 1:
if sequence[1] in RawIOConsole.Fe_RANGE:
if sequence[1] in RawIOConsole.SUPPORTED_C1_CTRL:
if RawIOConsole.SUPPORTED_C1_CTRL[sequence[1]] == 'CSI': # CONTROL SEQUENCE INTRODUCER
if len(sequence) > 2:
if sequence[-1] in RawIOConsole.FINAL_RANGE:
if sequence[-1] in RawIOConsole.SUPPORTED_FINAL_CTRL:
if RawIOConsole.SUPPORTED_FINAL_CTRL[sequence[-1]] == 'ED': # Erase in Display
try:
mode = int(sequence[2:len(sequence)-1].decode(encoding='utf-8'))
except ValueError:
mode = 3
finally:
if mode == 0:
self.console.clear("after")
return 'successful'
elif mode == 1:
self.console.clear("before")
return 'successful'
elif mode == 2:
self.console.clear("all_without_memory")
return 'successful'
elif mode == 3:
self.console.clear()
return 'successful'
else:
return 'error'
elif RawIOConsole.SUPPORTED_FINAL_CTRL[sequence[-1]] == 'SU': # Scroll Up
try:
nb_lines = int(sequence[2:len(sequence)-1].decode(encoding='utf-8'))
except ValueError:
nb_lines = 1
finally:
self.console.scroll(nb_lines, True)
return 'successful'
elif RawIOConsole.SUPPORTED_FINAL_CTRL[sequence[-1]] == 'SD': # Scroll Down
try:
nb_lines = int(sequence[2:len(sequence)-1].decode(encoding='utf-8'))
except ValueError:
nb_lines = 1
finally:
self.console.scroll(nb_lines, False)
return 'successful'
elif RawIOConsole.SUPPORTED_FINAL_CTRL[sequence[-1]] == 'SGR': # Select Graphic Rendition
# All separators are replaced by the same character
sep = RawIOConsole.SEPARATORS[0]
formatted_sequence = sequence[2:len(sequence)-1].decode(encoding='utf-8')
for any_sep in RawIOConsole.SEPARATORS:
formatted_sequence = formatted_sequence.replace(any_sep,sep)
# All parameters in a list
seq_list = formatted_sequence.split(sep)
# Empty list: equivalent to '0'
if len(seq_list) == 0: seq_list = ['0']
# Processing of each parameter
for param in seq_list:
if param in RawIOConsole.SUPPORTED_SGR_PARAMETERS:
if RawIOConsole.SUPPORTED_SGR_PARAMETERS[param] == 'reset':
self.console.bold = False
self.console.italic = False
self.console.underline = False
self.console.foreground_colour = 'default'
self.console.background_colour = 'default'
elif RawIOConsole.SUPPORTED_SGR_PARAMETERS[param] == 'bold':
self.console.bold = True
elif RawIOConsole.SUPPORTED_SGR_PARAMETERS[param] == 'italic':
self.console.italic = True
elif RawIOConsole.SUPPORTED_SGR_PARAMETERS[param] == 'underline':
self.console.underline = True
elif RawIOConsole.SUPPORTED_SGR_PARAMETERS[param] == 'negative':
temp_colour = self.console.foreground_colour
self.console.foreground_colour = self.console.background_colour
self.console.background_colour = temp_colour
elif RawIOConsole.SUPPORTED_SGR_PARAMETERS[param] == 'not_bold':
self.console.bold = False
elif RawIOConsole.SUPPORTED_SGR_PARAMETERS[param] == 'not_italic':
self.console.italic = False
elif RawIOConsole.SUPPORTED_SGR_PARAMETERS[param] == 'not_underline':
self.console.underline = False
elif RawIOConsole.SUPPORTED_SGR_PARAMETERS[param] == 'positive':
temp_colour = self.console.foreground_colour
self.console.foreground_colour = self.console.background_colour
self.console.background_colour = temp_colour
else: # SGR parameter supported but not (yet) treated
pass
elif param in RawIOConsole.STANDARD_FOREGROUND_COLOURS:
self.console.foreground_colour = RawIOConsole.STANDARD_FOREGROUND_COLOURS[param]
elif param in RawIOConsole.STANDARD_BACKGROUND_COLOURS:
self.console.background_colour = RawIOConsole.STANDARD_BACKGROUND_COLOURS[param]
else: # valid but not supported SGR parameter
pass
return 'successful'
else: # Final byte supported but not (yet) treated
return 'error'
else: # Valid but not supported final byte
return 'error'
else: # Invalid final byte
return 'incomplete'
else: # incomplete control sequence
return 'incomplete'
else: # C1 control byte supported but not (yet) treated
return 'error'
else: # Valid but not supported C1 control byte
return 'error'
else: # Escape sequence not recognized
return 'error'
else: # Incomplete escape sequence
return 'incomplete'
elif RawIOConsole.SUPPORTED_C0_CTRL[sequence[0]] == 'LF': # Line Field (cursor moves to the following line)
self.console.line_field()
return 'successful'
elif RawIOConsole.SUPPORTED_C0_CTRL[sequence[0]] == 'CR': # Carriage Return (cursor moves to the beginning of the current line)
self.console.carriage_return()
return 'successful'
else: # C0 control byte supported but not (yet) treated
return 'error'
else: # Valid bu not supported C0 control byte
return 'error'
else: # Invalid C0 control byte
return 'error'
class BufferedIOConsole(io.BufferedWriter):
"""
BufferedIOConsole class provides the mid-level binary access to an underlying Console object, with buffering. It inherits io.BufferedWriter. For further details about inherited elements, see https://docs.python.org/3/library/io.html . In normal cases (except if the direct usage of binary sequences is required), BufferedIOConsole objects do not have to be used directly to write characters on the console (use TextIOConsoleWrapper objects instead).
Attributes
----------
console: pygconsole.console.Console object
Underlying Console object associated to this API.
Methods
-------
All inherited methods remain unchanged. No new method added.
"""
#############################################################
# Initialization
def __init__(self, console_name = "pygame_console"):
"""
Parameters
----------
console_name: str
Name of theuUnderlying pygconsole.console.Console object associated to this API (default is "pygame_console")
"""
raw = RawIOConsole(console_name)
super().__init__(raw = raw)
#############################################################
# Public attributes
@property
def console(self):
"""
Underlying pygconsole.console.Console object associated to this API.
If the set value is not a Console object, the attribute remains silently unchanged.
"""
return self.raw.console
@console.setter
def console(self, value):
if isinstance(value, Console):
self.raw.console = value
else:
pass
class TextIOConsoleWrapper(io.TextIOWrapper):
"""
A buffered text stream providing higher-level access to a BufferedIOConsole buffered binary stream. It inherits io.TextIOWrapper. For further details about inherited elements, see https://docs.python.org/3/library/io.html . This class is generally used directly to write characters on the console, by objects using I/O streams (like `print()`).
Attributes
----------
All inherited attributes remain unchanged. No new attribute added.
Methods
-------
All inherited methods remain unchanged. No new method added.
"""
#############################################################
# Initialisation
def __init__(self, console_name = "pygame_console", errors=None, newline=None, line_buffering=False, write_through=False):
"""
Parameters
----------
console_name: str
Name of theuUnderlying pygconsole.console.Console object associated to this API (default is "pygame_console")
error: str
optional string that specifies how encoding and decoding errors are to be handled.
newline: str
controls how line endings are handled. It can be None, '', '\n', '\r', and '\r\n'.
line_buffering: bool
If True, flush() is implied when a call to write contains a newline character or a carriage return. Default is False.
write_through: bool
If True, calls to write() are guaranteed not to be buffered. Default is False.
"""
buffer = BufferedIOConsole(console_name)
# To be compliant with RawIOConsole, encoding must be utf-8.
encoding = 'utf-8'
super().__init__(buffer, encoding, errors, newline, line_buffering, write_through)

View File

@@ -0,0 +1,57 @@
abysta at yandex.ru
Adrian Schroeter
Aleksey Chalabyan
Andrey Valentinovich Panov
Ben Laenen
Besarion Gugushvili
Bhikkhu Pesala
Clayborne Arevalo
Dafydd Harries
Danilo Segan
Davide Viti
David Jez
David Lawrence Ramsey
Denis Jacquerye
Dwayne Bailey
Eugeniy Meshcheryakov
Frédéric Wang
Gee Fung Sit
Heikki Lindroos
James Cloos
James Crippen
John Karp
Keenan Pepper
Lars Næsbye Christensen
Lior Halphon
MaEr
Mashrab Kuvatov
Max Berger
Mederic Boquien
Michael Everson
MihailJP
Misu Moldovan
Nguyen Thai Ngoc Duy
Nicolas Mailhot
Norayr Chilingarian
Olleg Samoylov
Ognyan Kulev
Ondrej Koala Vacha
Peter Cernak
Remy Oudompheng
Roozbeh Pournader
Rouben Hakobian
Sahak Petrosyan
Sami Tarazi
Sander Vesik
Stepan Roh
Stephen Hartke
Steve Tinney
Tavmjong Bah
Thomas Henlich
Tim May
Valentin Stoykov
Vasek Stodulka
Wesley Transue
Yoshiki Ohshima
$Id$

Binary file not shown.

View File

@@ -0,0 +1,187 @@
Fonts are (c) Bitstream (see below). DejaVu changes are in public domain.
Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below)
Bitstream Vera Fonts Copyright
------------------------------
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is
a trademark of Bitstream, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of the fonts accompanying this license ("Fonts") and associated
documentation files (the "Font Software"), to reproduce and distribute the
Font Software, including without limitation the rights to use, copy, merge,
publish, distribute, and/or sell copies of the Font Software, and to permit
persons to whom the Font Software is furnished to do so, subject to the
following conditions:
The above copyright and trademark notices and this permission notice shall
be included in all copies of one or more of the Font Software typefaces.
The Font Software may be modified, altered, or added to, and in particular
the designs of glyphs or characters in the Fonts may be modified and
additional glyphs or characters may be added to the Fonts, only if the fonts
are renamed to names not containing either the words "Bitstream" or the word
"Vera".
This License becomes null and void to the extent applicable to Fonts or Font
Software that has been modified and is distributed under the "Bitstream
Vera" names.
The Font Software may be sold as part of a larger software package but no
copy of one or more of the Font Software typefaces may be sold by itself.
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING
ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE
FONT SOFTWARE.
Except as contained in this notice, the names of Gnome, the Gnome
Foundation, and Bitstream Inc., shall not be used in advertising or
otherwise to promote the sale, use or other dealings in this Font Software
without prior written authorization from the Gnome Foundation or Bitstream
Inc., respectively. For further information, contact: fonts at gnome dot
org.
Arev Fonts Copyright
------------------------------
Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining
a copy of the fonts accompanying this license ("Fonts") and
associated documentation files (the "Font Software"), to reproduce
and distribute the modifications to the Bitstream Vera Font Software,
including without limitation the rights to use, copy, merge, publish,
distribute, and/or sell copies of the Font Software, and to permit
persons to whom the Font Software is furnished to do so, subject to
the following conditions:
The above copyright and trademark notices and this permission notice
shall be included in all copies of one or more of the Font Software
typefaces.
The Font Software may be modified, altered, or added to, and in
particular the designs of glyphs or characters in the Fonts may be
modified and additional glyphs or characters may be added to the
Fonts, only if the fonts are renamed to names not containing either
the words "Tavmjong Bah" or the word "Arev".
This License becomes null and void to the extent applicable to Fonts
or Font Software that has been modified and is distributed under the
"Tavmjong Bah Arev" names.
The Font Software may be sold as part of a larger software package but
no copy of one or more of the Font Software typefaces may be sold by
itself.
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
Except as contained in this notice, the name of Tavmjong Bah shall not
be used in advertising or otherwise to promote the sale, use or other
dealings in this Font Software without prior written authorization
from Tavmjong Bah. For further information, contact: tavmjong @ free
. fr.
TeX Gyre DJV Math
-----------------
Fonts are (c) Bitstream (see below). DejaVu changes are in public domain.
Math extensions done by B. Jackowski, P. Strzelczyk and P. Pianowski
(on behalf of TeX users groups) are in public domain.
Letters imported from Euler Fraktur from AMSfonts are (c) American
Mathematical Society (see below).
Bitstream Vera Fonts Copyright
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera
is a trademark of Bitstream, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of the fonts accompanying this license (“Fonts”) and associated
documentation
files (the “Font Software”), to reproduce and distribute the Font Software,
including without limitation the rights to use, copy, merge, publish,
distribute,
and/or sell copies of the Font Software, and to permit persons to whom
the Font Software is furnished to do so, subject to the following
conditions:
The above copyright and trademark notices and this permission notice
shall be
included in all copies of one or more of the Font Software typefaces.
The Font Software may be modified, altered, or added to, and in particular
the designs of glyphs or characters in the Fonts may be modified and
additional
glyphs or characters may be added to the Fonts, only if the fonts are
renamed
to names not containing either the words “Bitstream” or the word “Vera”.
This License becomes null and void to the extent applicable to Fonts or
Font Software
that has been modified and is distributed under the “Bitstream Vera”
names.
The Font Software may be sold as part of a larger software package but
no copy
of one or more of the Font Software typefaces may be sold by itself.
THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
FOUNDATION
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL,
SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN
ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR
INABILITY TO USE
THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
Except as contained in this notice, the names of GNOME, the GNOME
Foundation,
and Bitstream Inc., shall not be used in advertising or otherwise to promote
the sale, use or other dealings in this Font Software without prior written
authorization from the GNOME Foundation or Bitstream Inc., respectively.
For further information, contact: fonts at gnome dot org.
AMSFonts (v. 2.2) copyright
The PostScript Type 1 implementation of the AMSFonts produced by and
previously distributed by Blue Sky Research and Y&Y, Inc. are now freely
available for general use. This has been accomplished through the
cooperation
of a consortium of scientific publishers with Blue Sky Research and Y&Y.
Members of this consortium include:
Elsevier Science IBM Corporation Society for Industrial and Applied
Mathematics (SIAM) Springer-Verlag American Mathematical Society (AMS)
In order to assure the authenticity of these fonts, copyright will be
held by
the American Mathematical Society. This is not meant to restrict in any way
the legitimate use of the fonts, such as (but not limited to) electronic
distribution of documents containing these fonts, inclusion of these fonts
into other public domain or commercial font collections or computer
applications, use of the outline data to create derivative fonts and/or
faces, etc. However, the AMS does require that the AMS copyright notice be
removed from any derivative versions of the fonts which have been altered in
any way. In addition, to ensure the fidelity of TeX documents using Computer
Modern fonts, Professor Donald Knuth, creator of the Computer Modern faces,
has requested that any alterations which yield different font metrics be
given a different name.
$Id$

3
pyproject.toml Normal file
View File

@@ -0,0 +1,3 @@
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

39
setup.cfg Normal file
View File

@@ -0,0 +1,39 @@
[metadata]
name = pygconsole
version = attr: pygconsole.__VERSION__
author = devfred78
author_email = devfred78@gmail.com
description = ANSI terminal emulation for Pygame
long_description = file: README.md
long_decription_content_type = text/markdown
url = https://github.com/devfred78/pygconsole
project_urls =
Bug Tracker = https://github.com/devfred78/pygconsole/issues
Sources = https://github.com/devfred78/pygconsole
classifiers =
Development Status :: 3 - Alpha
Intended Audience :: Developers
Programming Language :: Python :: 3
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
License :: OSI Approved :: MIT License
Operating System :: OS Independent
Topic :: Software Development :: Libraries :: Python Modules
Topic :: Terminals
[options]
package_dir =
pygconsole = pygconsole
packages = pygconsole
install_requires =
pygame >= 2.1.0
colorlog >= 6.4.1
python_requires = >= 3.8
[options.packages.find]
where = pygconsole
include = pygconsole
[options.package_data]
pygconsole = resources/fonts/DejaVu/*

View File

@@ -0,0 +1,7 @@
Background image
----------------
Licence: Free
https://www.pexels.com/fr-fr/photo/paysage-nature-ciel-sable-8092914/
Location: Oljato-Monument Valley, United States
Date: 13th May 2014
Photographer: Karl MPhotography

Binary file not shown.

After

Width:  |  Height:  |  Size: 469 KiB

140
tests/test0.py Normal file
View File

@@ -0,0 +1,140 @@
# -*- coding: utf-8 -*-
"""
'console' test set : tests regarding only the pygconsole.console submodule
Test 0 : import pygconsole module and display a blank console (with default parameters)
ESCAPE Key to exit.
"""
# Standard modules
#-----------------
import sys
import os
from collections import namedtuple
import logging
# Third party modules
#--------------------
import pygame
from pygame.locals import *
from colorlog import ColoredFormatter
# Internal modules
#-----------------
if __name__ == '__main__':
sys.path.append(os.path.join(os.path.dirname(__file__),'..'))
import pygconsole
else:
from .. import pygconsole
# namedtuples
#------------
Coordinates = namedtuple("Coordinates", "x y")
Colour = namedtuple("Colour", "red green blue alpha")
# Global constants
#-----------------
RESOURCE_DIR = os.path.join(os.path.dirname(__file__),"resources") # directory where graphical resources are stored
BACKGROUND_IMAGE = os.path.join(RESOURCE_DIR,"background.jpg") # Background image
FONT = None # Font displayed outside of the console (None = default pygame font)
DISPLAY_SIZE = Coordinates(1920,1080) # screen resolution
CONSOLE_COORDINATES = Coordinates(1000,620) # upper left corner coordinates of the console
FRAMERATE = 50 # Maximum number of displaying loops per second
FONT_SIZE = 40 # size of the font displayed on the screen, but out of the console
FONT_COLOUR = Colour(255,0,0,255) # Colour of the font displayed outside of the console
TEXT_COORDINATES = Coordinates(50,50) # Coordinates of the first line of the text displayed outside of the console
TEXT_LIST = [
"Test 0: display a blank console with default parameters.",
"ESCAPE key to exit."
]
# Dataclasses
#------------
# Classes
#--------
# Functions
#----------
# Main function
#--------------
def main():
""" Main program execution"""
# Logging initialization
formatter = ColoredFormatter(
'%(log_color)s[%(asctime)s][%(levelname)s][%(name)s]:%(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
reset=True,
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'red,bg_white'
},
secondary_log_colors={},
style='%'
)
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
handler.setFormatter(formatter)
log = logging.getLogger('display')
log.setLevel(logging.DEBUG)
log.addHandler(handler)
# screen initialization
pygame.init()
clock = pygame.time.Clock() # timer to control framerate
flags = FULLSCREEN|SCALED|DOUBLEBUF
screen_surface = pygame.display.set_mode(size=DISPLAY_SIZE,flags=flags)
background_surface = pygame.image.load(BACKGROUND_IMAGE)
# Font initialization
font = pygame.font.Font(FONT,FONT_SIZE)
line_space = font.get_linesize()
# console initialization
console = pygconsole.console.Console.get_console(logger = log)
# Displaying loop
while True:
clock.tick(FRAMERATE)
# Events
for event in pygame.event.get():
if event.type == QUIT: sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE: sys.exit()
# Background display
screen_surface.blit(background_surface,(0,0))
# Text display
text_line_coordinates = TEXT_COORDINATES
for text_line in TEXT_LIST:
text_surface = font.render(text_line,True,FONT_COLOUR)
screen_surface.blit(text_surface,text_line_coordinates)
text_line_coordinates = text_line_coordinates._replace(y=text_line_coordinates.y+line_space)
# Console display
screen_surface.blit(console.surface,CONSOLE_COORDINATES)
# Screen rendering
pygame.display.flip()
# Main program,
# running only if the module is NOT imported (but directly executed)
#-------------------------------------------------------------------
if __name__ == '__main__':
main()

142
tests/test1.py Normal file
View File

@@ -0,0 +1,142 @@
# -*- coding: utf-8 -*-
"""
'console' test set : tests regarding only the pygconsole.console submodule
Test 1 : import pygconsole module and display a console with `Hello world !` written (with default parameters)
ESCAPE Key to exit.
"""
# Standard modules
#-----------------
import sys
import os
from collections import namedtuple
import logging
# Third party modules
#--------------------
import pygame
from pygame.locals import *
from colorlog import ColoredFormatter
# Internal modules
#-----------------
if __name__ == '__main__':
sys.path.append(os.path.join(os.path.dirname(__file__),'..'))
import pygconsole
else:
from .. import pygconsole
# namedtuples
#------------
Coordinates = namedtuple("Coordinates", "x y")
Colour = namedtuple("Colour", "red green blue alpha")
# Global constants
#-----------------
RESOURCE_DIR = os.path.join(os.path.dirname(__file__),"resources") # directory where graphical resources are stored
BACKGROUND_IMAGE = os.path.join(RESOURCE_DIR,"background.jpg") # Background image
FONT = None # Font displayed outside of the console (None = default pygame font)
DISPLAY_SIZE = Coordinates(1920,1080) # screen resolution
CONSOLE_COORDINATES = Coordinates(1000,620) # upper left corner coordinates of the console
FRAMERATE = 50 # Maximum number of displaying loops per second
FONT_SIZE = 40 # size of the font displayed on the screen, but out of the console
FONT_COLOUR = Colour(255,0,0,255) # Colour of the font displayed outside of the console
TEXT_COORDINATES = Coordinates(50,50) # Coordinates of the first line of the text displayed outside of the console
TEXT_LIST = [
"Test 1: display a console with default parameters.",
"press 'A' to write `Hello world !` at the cursor position.",
"ESCAPE key to exit."
]
# Dataclasses
#------------
# Classes
#--------
# Functions
#----------
# Main function
#--------------
def main():
""" Main program execution"""
# Logging initialization
formatter = ColoredFormatter(
'%(log_color)s[%(asctime)s][%(levelname)s][%(name)s]:%(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
reset=True,
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'red,bg_white'
},
secondary_log_colors={},
style='%'
)
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
handler.setFormatter(formatter)
log = logging.getLogger('display')
log.setLevel(logging.DEBUG)
log.addHandler(handler)
# screen initialization
pygame.init()
clock = pygame.time.Clock() # timer to control framerate
flags = FULLSCREEN|SCALED|DOUBLEBUF
screen_surface = pygame.display.set_mode(size=DISPLAY_SIZE,flags=flags)
background_surface = pygame.image.load(BACKGROUND_IMAGE)
# Font initialization
font = pygame.font.Font(FONT,FONT_SIZE)
line_space = font.get_linesize()
# console initialization
console = pygconsole.console.Console.get_console(logger = log)
# Displaying loop
while True:
clock.tick(FRAMERATE)
# Events
for event in pygame.event.get():
if event.type == QUIT: sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE: sys.exit()
elif event.key == K_a: console.add_char("Hello world !")
# Background display
screen_surface.blit(background_surface,(0,0))
# Text display
text_line_coordinates = TEXT_COORDINATES
for text_line in TEXT_LIST:
text_surface = font.render(text_line,True,FONT_COLOUR)
screen_surface.blit(text_surface,text_line_coordinates)
text_line_coordinates = text_line_coordinates._replace(y=text_line_coordinates.y+line_space)
# Console display
screen_surface.blit(console.surface,CONSOLE_COORDINATES)
# Screen rendering
pygame.display.flip()
# Main program,
# running only if the module is NOT imported (but directly executed)
#-------------------------------------------------------------------
if __name__ == '__main__':
main()

161
tests/test10.py Normal file
View File

@@ -0,0 +1,161 @@
# -*- coding: utf-8 -*-
"""
'console' test set : tests regarding only the pygconsole.console submodule
Test 10 : Write a long text on the console with normal characters colorized randomly (foreground / background).
ESCAPE Key to exit.
"""
# Standard modules
#-----------------
import sys
import os
from collections import namedtuple
import logging
import random
# Third party modules
#--------------------
import pygame
from pygame.locals import *
from colorlog import ColoredFormatter
# Internal modules
#-----------------
if __name__ == '__main__':
sys.path.append(os.path.join(os.path.dirname(__file__),'..'))
import pygconsole
else:
from .. import pygconsole
# namedtuples
#------------
Coordinates = namedtuple("Coordinates", "x y")
Colour = namedtuple("Colour", "red green blue alpha")
# Global constants
#-----------------
RESOURCE_DIR = os.path.join(os.path.dirname(__file__),"resources") # directory where graphical resources are stored
BACKGROUND_IMAGE = os.path.join(RESOURCE_DIR,"background.jpg") # Background image
FONT = None # Font displayed outside of the console (None = default pygame font)
DISPLAY_SIZE = Coordinates(1920,1080) # screen resolution
CONSOLE_COORDINATES = Coordinates(1000,620) # upper left corner coordinates of the console
FRAMERATE = 50 # Maximum number of displaying loops per second
FONT_SIZE = 40 # size of the font displayed on the screen, but out of the console
FONT_COLOUR = Colour(255,0,0,255) # Colour of the font displayed outside of the console
TEXT_COORDINATES = Coordinates(50,50) # Coordinates of the first line of the text displayed outside of the console
TEXT_LIST = [
"Test 10 : Write a long text on the console with normal characters colorized randomly",
"(foreground / background).",
"Press 'A' to write the 'Lorem ipsum' text",
"ESCAPE key to exit."
]
LONG_TEXT = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vitae nunc dictum, sagittis elit venenatis, efficitur justo. Etiam suscipit, ipsum accumsan aliquam elementum, massa tellus pellentesque lacus, ut porttitor ligula tortor at urna. Duis eu felis non tortor bibendum ultrices. Aliquam tortor velit, suscipit faucibus nunc quis, blandit posuere leo. Donec dignissim aliquam lectus, vitae lacinia risus feugiat non. Curabitur dapibus, massa quis eleifend lobortis, nulla sem ullamcorper turpis, vel sodales risus orci non velit. Nam ac neque faucibus, consequat eros eu, viverra neque. Nulla vel blandit lorem. Nam interdum nisl non sem pretium dictum. Phasellus id sapien vitae ipsum efficitur fringilla. Quisque suscipit consequat erat quis commodo. Aenean tristique felis libero, et ultrices justo porttitor eget. Vivamus sit amet urna nibh. Ut ultrices nulla et dui eleifend, ut sagittis ipsum condimentum. Mauris nec dolor eget augue gravida lacinia. "
# Dataclasses
#------------
# Classes
#--------
# Functions
#----------
# Main function
#--------------
def main():
""" Main program execution"""
# random initialization
random.seed()
# Logging initialization
formatter = ColoredFormatter(
'%(log_color)s[%(asctime)s][%(levelname)s][%(name)s]:%(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
reset=True,
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'red,bg_white'
},
secondary_log_colors={},
style='%'
)
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
handler.setFormatter(formatter)
log = logging.getLogger('display')
log.setLevel(logging.DEBUG)
log.addHandler(handler)
# screen initialization
pygame.init()
clock = pygame.time.Clock() # timer to control framerate
flags = FULLSCREEN|SCALED|DOUBLEBUF
screen_surface = pygame.display.set_mode(size=DISPLAY_SIZE,flags=flags)
background_surface = pygame.image.load(BACKGROUND_IMAGE)
# Font initialization
font = pygame.font.Font(FONT,FONT_SIZE)
line_space = font.get_linesize()
# console initialization
console = pygconsole.console.Console.get_console(logger = log)
# Displaying loop
while True:
clock.tick(FRAMERATE)
# Events
for event in pygame.event.get():
if event.type == QUIT: sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE: sys.exit()
elif event.key == K_a:
for word in LONG_TEXT.split():
console.background_colour = random.choice(list(console.STANDARD_COLOURS.keys()))
console.foreground_colour = random.choice(list(console.STANDARD_COLOURS.keys()))
if console.background_colour == console.foreground_colour:
if console.background_colour in ('black','red','green','yellow','blue','magenta','cyan','white'):
console.foreground_colour = 'bright_white'
else:
console.foreground_colour = 'black'
console.add_char(word)
console.background_colour = 'black'
console.foreground_colour = 'bright_white'
console.add_char(' ')
# Background display
screen_surface.blit(background_surface,(0,0))
# Text display
text_line_coordinates = TEXT_COORDINATES
for text_line in TEXT_LIST:
text_surface = font.render(text_line,True,FONT_COLOUR)
screen_surface.blit(text_surface,text_line_coordinates)
text_line_coordinates = text_line_coordinates._replace(y=text_line_coordinates.y+line_space)
# Console display
screen_surface.blit(console.surface,CONSOLE_COORDINATES)
# Screen rendering
pygame.display.flip()
# Main program,
# running only if the module is NOT imported (but directly executed)
#-------------------------------------------------------------------
if __name__ == '__main__':
main()

147
tests/test100.py Normal file
View File

@@ -0,0 +1,147 @@
# -*- coding: utf-8 -*-
"""
'io' test set : tests regarding the pygconsole.io submodule
Test 100 : Print characters typed on the keyboard, on a console initialized with the default parameters.
ESCAPE Key to exit.
"""
# Standard modules
#-----------------
import sys
import os
from collections import namedtuple
import logging
# Third party modules
#--------------------
import pygame
from pygame.locals import *
from colorlog import ColoredFormatter
# Internal modules
#-----------------
if __name__ == '__main__':
sys.path.append(os.path.join(os.path.dirname(__file__),'..'))
import pygconsole
else:
from .. import pygconsole
# namedtuples
#------------
Coordinates = namedtuple("Coordinates", "x y")
Colour = namedtuple("Colour", "red green blue alpha")
# Global constants
#-----------------
RESOURCE_DIR = os.path.join(os.path.dirname(__file__),"resources") # directory where graphical resources are stored
BACKGROUND_IMAGE = os.path.join(RESOURCE_DIR,"background.jpg") # Background image
FONT = None # Font displayed outside of the console (None = default pygame font)
DISPLAY_SIZE = Coordinates(1920,1080) # screen resolution
CONSOLE_COORDINATES = Coordinates(1000,620) # upper left corner coordinates of the console
FRAMERATE = 50 # Maximum number of displaying loops per second
FONT_SIZE = 40 # size of the font displayed on the screen, but out of the console
FONT_COLOUR = Colour(255,0,0,255) # Colour of the font displayed outside of the console
TEXT_COORDINATES = Coordinates(50,50) # Coordinates of the first line of the text displayed outside of the console
TEXT_LIST = [
"Test 100: Print characters typed on the keyboard,",
"on a console initialized with the default parameters.",
"ESCAPE key to exit."
]
# Dataclasses
#------------
# Classes
#--------
# Functions
#----------
# Main function
#--------------
def main():
""" Main program execution"""
# Logging initialization
formatter = ColoredFormatter(
'%(log_color)s[%(asctime)s][%(levelname)s][%(name)s]:%(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
reset=True,
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'red,bg_white'
},
secondary_log_colors={},
style='%'
)
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
handler.setFormatter(formatter)
log = logging.getLogger('display')
log.setLevel(logging.DEBUG)
log.addHandler(handler)
# screen initialization
pygame.init()
clock = pygame.time.Clock() # timer to control framerate
flags = FULLSCREEN|SCALED|DOUBLEBUF
screen_surface = pygame.display.set_mode(size=DISPLAY_SIZE,flags=flags)
background_surface = pygame.image.load(BACKGROUND_IMAGE)
# Font initialization
font = pygame.font.Font(FONT,FONT_SIZE)
line_space = font.get_linesize()
# console initialization
console = pygconsole.console.Console.get_console(name="pygame_console",logger = log)
iotextstream = pygconsole.io.TextIOConsoleWrapper(console_name="pygame_console", newline='\r\n', line_buffering=True)
# Displaying loop
while True:
clock.tick(FRAMERATE)
# Events
for event in pygame.event.get():
if event.type == QUIT: sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
sys.exit()
elif event.key == K_RETURN:
print("", file=iotextstream, flush=True) # New line
if event.type == TEXTINPUT: # When typing a key with a writable character...
print(event.text, end='', file=iotextstream, flush=True) # Display the character
# Background display
screen_surface.blit(background_surface,(0,0))
# Text display
text_line_coordinates = TEXT_COORDINATES
for text_line in TEXT_LIST:
text_surface = font.render(text_line,True,FONT_COLOUR)
screen_surface.blit(text_surface,text_line_coordinates)
text_line_coordinates = text_line_coordinates._replace(y=text_line_coordinates.y+line_space)
# Console display
screen_surface.blit(console.surface,CONSOLE_COORDINATES)
# Screen rendering
pygame.display.flip()
# Main program,
# running only if the module is NOT imported (but directly executed)
#-------------------------------------------------------------------
if __name__ == '__main__':
main()

147
tests/test101.py Normal file
View File

@@ -0,0 +1,147 @@
# -*- coding: utf-8 -*-
"""
'io' test set : tests regarding the pygconsole.io submodule
Test 101 : Display a message in red coloured characters.
ESCAPE Key to exit.
"""
# Standard modules
#-----------------
import sys
import os
from collections import namedtuple
import logging
# Third party modules
#--------------------
import pygame
from pygame.locals import *
from colorlog import ColoredFormatter
# Internal modules
#-----------------
if __name__ == '__main__':
sys.path.append(os.path.join(os.path.dirname(__file__),'..'))
import pygconsole
else:
from .. import pygconsole
# namedtuples
#------------
Coordinates = namedtuple("Coordinates", "x y")
Colour = namedtuple("Colour", "red green blue alpha")
# Global constants
#-----------------
RESOURCE_DIR = os.path.join(os.path.dirname(__file__),"resources") # directory where graphical resources are stored
BACKGROUND_IMAGE = os.path.join(RESOURCE_DIR,"background.jpg") # Background image
FONT = None # Font displayed outside of the console (None = default pygame font)
DISPLAY_SIZE = Coordinates(1920,1080) # screen resolution
CONSOLE_COORDINATES = Coordinates(1000,620) # upper left corner coordinates of the console
FRAMERATE = 50 # Maximum number of displaying loops per second
FONT_SIZE = 40 # size of the font displayed on the screen, but out of the console
FONT_COLOUR = Colour(255,0,0,255) # Colour of the font displayed outside of the console
TEXT_COORDINATES = Coordinates(50,50) # Coordinates of the first line of the text displayed outside of the console
TEXT_LIST = [
"Test 101: Display a message in red coloured characters.",
"press 'A' to write `Hello world !` at the cursor position.",
"ESCAPE key to exit."
]
# Dataclasses
#------------
# Classes
#--------
# Functions
#----------
# Main function
#--------------
def main():
""" Main program execution"""
# Logging initialization
formatter = ColoredFormatter(
'%(log_color)s[%(asctime)s][%(levelname)s][%(name)s]:%(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
reset=True,
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'red,bg_white'
},
secondary_log_colors={},
style='%'
)
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
handler.setFormatter(formatter)
log = logging.getLogger('display')
log.setLevel(logging.DEBUG)
log.addHandler(handler)
# screen initialization
pygame.init()
clock = pygame.time.Clock() # timer to control framerate
flags = FULLSCREEN|SCALED|DOUBLEBUF
screen_surface = pygame.display.set_mode(size=DISPLAY_SIZE,flags=flags)
background_surface = pygame.image.load(BACKGROUND_IMAGE)
# Font initialization
font = pygame.font.Font(FONT,FONT_SIZE)
line_space = font.get_linesize()
# console initialization
console = pygconsole.console.Console.get_console(name="pygame_console",logger = log)
iotextstream = pygconsole.io.TextIOConsoleWrapper(console_name="pygame_console", newline='\r\n', line_buffering=True)
# Displaying loop
while True:
clock.tick(FRAMERATE)
# Events
for event in pygame.event.get():
if event.type == QUIT: sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
sys.exit()
elif event.key == K_RETURN:
print("", file=iotextstream, flush=True) # New line
elif event.key == K_a:
print("\x1b[31mHello world !", file=iotextstream, flush=True) # Print 'Hello World !' in red
# Background display
screen_surface.blit(background_surface,(0,0))
# Text display
text_line_coordinates = TEXT_COORDINATES
for text_line in TEXT_LIST:
text_surface = font.render(text_line,True,FONT_COLOUR)
screen_surface.blit(text_surface,text_line_coordinates)
text_line_coordinates = text_line_coordinates._replace(y=text_line_coordinates.y+line_space)
# Console display
screen_surface.blit(console.surface,CONSOLE_COORDINATES)
# Screen rendering
pygame.display.flip()
# Main program,
# running only if the module is NOT imported (but directly executed)
#-------------------------------------------------------------------
if __name__ == '__main__':
main()

155
tests/test102.py Normal file
View File

@@ -0,0 +1,155 @@
# -*- coding: utf-8 -*-
"""
'io' test set : tests regarding the pygconsole.io submodule
Test 102 : Display a message in all supported colours.
ESCAPE Key to exit.
"""
# Standard modules
#-----------------
import sys
import os
from collections import namedtuple
import logging
# Third party modules
#--------------------
import pygame
from pygame.locals import *
from colorlog import ColoredFormatter
# Internal modules
#-----------------
if __name__ == '__main__':
sys.path.append(os.path.join(os.path.dirname(__file__),'..'))
import pygconsole
else:
from .. import pygconsole
# namedtuples
#------------
Coordinates = namedtuple("Coordinates", "x y")
Colour = namedtuple("Colour", "red green blue alpha")
# Global constants
#-----------------
RESOURCE_DIR = os.path.join(os.path.dirname(__file__),"resources") # directory where graphical resources are stored
BACKGROUND_IMAGE = os.path.join(RESOURCE_DIR,"background.jpg") # Background image
FONT = None # Font displayed outside of the console (None = default pygame font)
DISPLAY_SIZE = Coordinates(1920,1080) # screen resolution
CONSOLE_COORDINATES = Coordinates(1000,620) # upper left corner coordinates of the console
FRAMERATE = 50 # Maximum number of displaying loops per second
FONT_SIZE = 40 # size of the font displayed on the screen, but out of the console
FONT_COLOUR = Colour(255,0,0,255) # Colour of the font displayed outside of the console
TEXT_COORDINATES = Coordinates(50,50) # Coordinates of the first line of the text displayed outside of the console
TEXT_LIST = [
"Test 102: Display a message in coloured characters.",
"press 'A' to write `Hello world !` at the cursor position,",
"with a colour changing cyclicly.",
"ESCAPE key to exit."
]
COLOUR_LIST = ['\x1b[30m','\x1b[31m','\x1b[32m','\x1b[33m','\x1b[34m','\x1b[35m','\x1b[36m','\x1b[37m',
'\x1b[90m','\x1b[91m','\x1b[92m','\x1b[93m','\x1b[94m','\x1b[95m','\x1b[96m','\x1b[97m']
# Dataclasses
#------------
# Classes
#--------
# Functions
#----------
# Main function
#--------------
def main():
""" Main program execution"""
# Logging initialization
formatter = ColoredFormatter(
'%(log_color)s[%(asctime)s][%(levelname)s][%(name)s]:%(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
reset=True,
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'red,bg_white'
},
secondary_log_colors={},
style='%'
)
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
handler.setFormatter(formatter)
log = logging.getLogger('display')
log.setLevel(logging.DEBUG)
log.addHandler(handler)
# screen initialization
pygame.init()
clock = pygame.time.Clock() # timer to control framerate
flags = FULLSCREEN|SCALED|DOUBLEBUF
screen_surface = pygame.display.set_mode(size=DISPLAY_SIZE,flags=flags)
background_surface = pygame.image.load(BACKGROUND_IMAGE)
# Font initialization
font = pygame.font.Font(FONT,FONT_SIZE)
line_space = font.get_linesize()
# console initialization
console = pygconsole.console.Console.get_console(name="pygame_console",logger = log)
iotextstream = pygconsole.io.TextIOConsoleWrapper(console_name="pygame_console", newline='\r\n', line_buffering=True)
coloured_index = 0
# Displaying loop
while True:
clock.tick(FRAMERATE)
# Events
for event in pygame.event.get():
if event.type == QUIT: sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
sys.exit()
elif event.key == K_RETURN:
print("", file=iotextstream, flush=True) # New line
elif event.key == K_a:
print(COLOUR_LIST[coloured_index] + "Hello world !", file=iotextstream, flush=True) # Print 'Hello World !' in colour
coloured_index += 1
if coloured_index >= len(COLOUR_LIST): coloured_index = 0
# Background display
screen_surface.blit(background_surface,(0,0))
# Text display
text_line_coordinates = TEXT_COORDINATES
for text_line in TEXT_LIST:
text_surface = font.render(text_line,True,FONT_COLOUR)
screen_surface.blit(text_surface,text_line_coordinates)
text_line_coordinates = text_line_coordinates._replace(y=text_line_coordinates.y+line_space)
# Console display
screen_surface.blit(console.surface,CONSOLE_COORDINATES)
# Screen rendering
pygame.display.flip()
# Main program,
# running only if the module is NOT imported (but directly executed)
#-------------------------------------------------------------------
if __name__ == '__main__':
main()

155
tests/test103.py Normal file
View File

@@ -0,0 +1,155 @@
# -*- coding: utf-8 -*-
"""
'io' test set : tests regarding the pygconsole.io submodule
Test 103 : Display a message with a background in all supported colours.
ESCAPE Key to exit.
"""
# Standard modules
#-----------------
import sys
import os
from collections import namedtuple
import logging
# Third party modules
#--------------------
import pygame
from pygame.locals import *
from colorlog import ColoredFormatter
# Internal modules
#-----------------
if __name__ == '__main__':
sys.path.append(os.path.join(os.path.dirname(__file__),'..'))
import pygconsole
else:
from .. import pygconsole
# namedtuples
#------------
Coordinates = namedtuple("Coordinates", "x y")
Colour = namedtuple("Colour", "red green blue alpha")
# Global constants
#-----------------
RESOURCE_DIR = os.path.join(os.path.dirname(__file__),"resources") # directory where graphical resources are stored
BACKGROUND_IMAGE = os.path.join(RESOURCE_DIR,"background.jpg") # Background image
FONT = None # Font displayed outside of the console (None = default pygame font)
DISPLAY_SIZE = Coordinates(1920,1080) # screen resolution
CONSOLE_COORDINATES = Coordinates(1000,620) # upper left corner coordinates of the console
FRAMERATE = 50 # Maximum number of displaying loops per second
FONT_SIZE = 40 # size of the font displayed on the screen, but out of the console
FONT_COLOUR = Colour(255,0,0,255) # Colour of the font displayed outside of the console
TEXT_COORDINATES = Coordinates(50,50) # Coordinates of the first line of the text displayed outside of the console
TEXT_LIST = [
"Test 103: Display a message in coloured background.",
"press 'A' to write `Hello world !` at the cursor position,",
"with a colour background changing cyclicly.",
"ESCAPE key to exit."
]
COLOUR_LIST = ['\x1b[40m','\x1b[41m','\x1b[42m','\x1b[43m','\x1b[44m','\x1b[45m','\x1b[46m','\x1b[47m',
'\x1b[100m','\x1b[101m','\x1b[102m','\x1b[103m','\x1b[104m','\x1b[105m','\x1b[106m','\x1b[107m']
# Dataclasses
#------------
# Classes
#--------
# Functions
#----------
# Main function
#--------------
def main():
""" Main program execution"""
# Logging initialization
formatter = ColoredFormatter(
'%(log_color)s[%(asctime)s][%(levelname)s][%(name)s]:%(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
reset=True,
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'red,bg_white'
},
secondary_log_colors={},
style='%'
)
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
handler.setFormatter(formatter)
log = logging.getLogger('display')
log.setLevel(logging.DEBUG)
log.addHandler(handler)
# screen initialization
pygame.init()
clock = pygame.time.Clock() # timer to control framerate
flags = FULLSCREEN|SCALED|DOUBLEBUF
screen_surface = pygame.display.set_mode(size=DISPLAY_SIZE,flags=flags)
background_surface = pygame.image.load(BACKGROUND_IMAGE)
# Font initialization
font = pygame.font.Font(FONT,FONT_SIZE)
line_space = font.get_linesize()
# console initialization
console = pygconsole.console.Console.get_console(name="pygame_console",logger = log)
iotextstream = pygconsole.io.TextIOConsoleWrapper(console_name="pygame_console", newline='\r\n', line_buffering=True)
coloured_index = 0
# Displaying loop
while True:
clock.tick(FRAMERATE)
# Events
for event in pygame.event.get():
if event.type == QUIT: sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
sys.exit()
elif event.key == K_RETURN:
print("", file=iotextstream, flush=True) # New line
elif event.key == K_a:
print(COLOUR_LIST[coloured_index] + "Hello world !", file=iotextstream, flush=True) # Print 'Hello World !' in colour
coloured_index += 1
if coloured_index >= len(COLOUR_LIST): coloured_index = 0
# Background display
screen_surface.blit(background_surface,(0,0))
# Text display
text_line_coordinates = TEXT_COORDINATES
for text_line in TEXT_LIST:
text_surface = font.render(text_line,True,FONT_COLOUR)
screen_surface.blit(text_surface,text_line_coordinates)
text_line_coordinates = text_line_coordinates._replace(y=text_line_coordinates.y+line_space)
# Console display
screen_surface.blit(console.surface,CONSOLE_COORDINATES)
# Screen rendering
pygame.display.flip()
# Main program,
# running only if the module is NOT imported (but directly executed)
#-------------------------------------------------------------------
if __name__ == '__main__':
main()

163
tests/test10_bis.py Normal file
View File

@@ -0,0 +1,163 @@
# -*- coding: utf-8 -*-
"""
'console' test set : tests regarding only the pygconsole.console submodule
Test 10_bis : Write a long text on the console with normal characters colorized randomly (foreground / background).
ESCAPE Key to exit.
** USE the pygconsole package installed with `pip` **
"""
# Standard modules
#-----------------
import sys
import os
from collections import namedtuple
import logging
import random
# Third party modules
#--------------------
import pygame
from pygame.locals import *
from colorlog import ColoredFormatter
# Internal modules
#-----------------
# if __name__ == '__main__':
# sys.path.append(os.path.join(os.path.dirname(__file__),'..'))
# import pygconsole
# else:
# from .. import pygconsole
import pygconsole
# namedtuples
#------------
Coordinates = namedtuple("Coordinates", "x y")
Colour = namedtuple("Colour", "red green blue alpha")
# Global constants
#-----------------
RESOURCE_DIR = os.path.join(os.path.dirname(__file__),"resources") # directory where graphical resources are stored
BACKGROUND_IMAGE = os.path.join(RESOURCE_DIR,"background.jpg") # Background image
FONT = None # Font displayed outside of the console (None = default pygame font)
DISPLAY_SIZE = Coordinates(1920,1080) # screen resolution
CONSOLE_COORDINATES = Coordinates(1000,620) # upper left corner coordinates of the console
FRAMERATE = 50 # Maximum number of displaying loops per second
FONT_SIZE = 40 # size of the font displayed on the screen, but out of the console
FONT_COLOUR = Colour(255,0,0,255) # Colour of the font displayed outside of the console
TEXT_COORDINATES = Coordinates(50,50) # Coordinates of the first line of the text displayed outside of the console
TEXT_LIST = [
"Test 10 : Write a long text on the console with normal characters colorized randomly",
"(foreground / background).",
"Press 'A' to write the 'Lorem ipsum' text",
"ESCAPE key to exit."
]
LONG_TEXT = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vitae nunc dictum, sagittis elit venenatis, efficitur justo. Etiam suscipit, ipsum accumsan aliquam elementum, massa tellus pellentesque lacus, ut porttitor ligula tortor at urna. Duis eu felis non tortor bibendum ultrices. Aliquam tortor velit, suscipit faucibus nunc quis, blandit posuere leo. Donec dignissim aliquam lectus, vitae lacinia risus feugiat non. Curabitur dapibus, massa quis eleifend lobortis, nulla sem ullamcorper turpis, vel sodales risus orci non velit. Nam ac neque faucibus, consequat eros eu, viverra neque. Nulla vel blandit lorem. Nam interdum nisl non sem pretium dictum. Phasellus id sapien vitae ipsum efficitur fringilla. Quisque suscipit consequat erat quis commodo. Aenean tristique felis libero, et ultrices justo porttitor eget. Vivamus sit amet urna nibh. Ut ultrices nulla et dui eleifend, ut sagittis ipsum condimentum. Mauris nec dolor eget augue gravida lacinia. "
# Dataclasses
#------------
# Classes
#--------
# Functions
#----------
# Main function
#--------------
def main():
""" Main program execution"""
# random initialization
random.seed()
# Logging initialization
formatter = ColoredFormatter(
'%(log_color)s[%(asctime)s][%(levelname)s][%(name)s]:%(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
reset=True,
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'red,bg_white'
},
secondary_log_colors={},
style='%'
)
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
handler.setFormatter(formatter)
log = logging.getLogger('display')
log.setLevel(logging.DEBUG)
log.addHandler(handler)
# screen initialization
pygame.init()
clock = pygame.time.Clock() # timer to control framerate
flags = FULLSCREEN|SCALED|DOUBLEBUF
screen_surface = pygame.display.set_mode(size=DISPLAY_SIZE,flags=flags)
background_surface = pygame.image.load(BACKGROUND_IMAGE)
# Font initialization
font = pygame.font.Font(FONT,FONT_SIZE)
line_space = font.get_linesize()
# console initialization
console = pygconsole.console.Console.get_console(logger = log)
# Displaying loop
while True:
clock.tick(FRAMERATE)
# Events
for event in pygame.event.get():
if event.type == QUIT: sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE: sys.exit()
elif event.key == K_a:
for word in LONG_TEXT.split():
console.background_colour = random.choice(list(console.STANDARD_COLOURS.keys()))
console.foreground_colour = random.choice(list(console.STANDARD_COLOURS.keys()))
if console.background_colour == console.foreground_colour:
if console.background_colour in ('black','red','green','yellow','blue','magenta','cyan','white'):
console.foreground_colour = 'bright_white'
else:
console.foreground_colour = 'black'
console.add_char(word)
console.background_colour = 'black'
console.foreground_colour = 'bright_white'
console.add_char(' ')
# Background display
screen_surface.blit(background_surface,(0,0))
# Text display
text_line_coordinates = TEXT_COORDINATES
for text_line in TEXT_LIST:
text_surface = font.render(text_line,True,FONT_COLOUR)
screen_surface.blit(text_surface,text_line_coordinates)
text_line_coordinates = text_line_coordinates._replace(y=text_line_coordinates.y+line_space)
# Console display
screen_surface.blit(console.surface,CONSOLE_COORDINATES)
# Screen rendering
pygame.display.flip()
# Main program,
# running only if the module is NOT imported (but directly executed)
#-------------------------------------------------------------------
if __name__ == '__main__':
main()

146
tests/test1_1.py Normal file
View File

@@ -0,0 +1,146 @@
# -*- coding: utf-8 -*-
"""
'console' test set : tests regarding only the pygconsole.console submodule
Test 1_1 : display a console with `Hello world !` written (with default parameters), and test the carriage return and the line field functions.
ESCAPE Key to exit.
"""
# Standard modules
#-----------------
import sys
import os
from collections import namedtuple
import logging
# Third party modules
#--------------------
import pygame
from pygame.locals import *
from colorlog import ColoredFormatter
# Internal modules
#-----------------
if __name__ == '__main__':
sys.path.append(os.path.join(os.path.dirname(__file__),'..'))
import pygconsole
else:
from .. import pygconsole
# namedtuples
#------------
Coordinates = namedtuple("Coordinates", "x y")
Colour = namedtuple("Colour", "red green blue alpha")
# Global constants
#-----------------
RESOURCE_DIR = os.path.join(os.path.dirname(__file__),"resources") # directory where graphical resources are stored
BACKGROUND_IMAGE = os.path.join(RESOURCE_DIR,"background.jpg") # Background image
FONT = None # Font displayed outside of the console (None = default pygame font)
DISPLAY_SIZE = Coordinates(1920,1080) # screen resolution
CONSOLE_COORDINATES = Coordinates(1000,620) # upper left corner coordinates of the console
FRAMERATE = 50 # Maximum number of displaying loops per second
FONT_SIZE = 40 # size of the font displayed on the screen, but out of the console
FONT_COLOUR = Colour(255,0,0,255) # Colour of the font displayed outside of the console
TEXT_COORDINATES = Coordinates(50,50) # Coordinates of the first line of the text displayed outside of the console
TEXT_LIST = [
"Test 1_1: display a console with default parameters.",
"press 'A' to write `Hello world !` at the cursor position.",
"press 'ENTER' to move the cursor to the first position of the next line.",
"ESCAPE key to exit."
]
# Dataclasses
#------------
# Classes
#--------
# Functions
#----------
# Main function
#--------------
def main():
""" Main program execution"""
# Logging initialization
formatter = ColoredFormatter(
'%(log_color)s[%(asctime)s][%(levelname)s][%(name)s]:%(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
reset=True,
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'red,bg_white'
},
secondary_log_colors={},
style='%'
)
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
handler.setFormatter(formatter)
log = logging.getLogger('display')
log.setLevel(logging.DEBUG)
log.addHandler(handler)
# screen initialization
pygame.init()
clock = pygame.time.Clock() # timer to control framerate
flags = FULLSCREEN|SCALED|DOUBLEBUF
screen_surface = pygame.display.set_mode(size=DISPLAY_SIZE,flags=flags)
background_surface = pygame.image.load(BACKGROUND_IMAGE)
# Font initialization
font = pygame.font.Font(FONT,FONT_SIZE)
line_space = font.get_linesize()
# console initialization
console = pygconsole.console.Console.get_console(logger = log)
# Displaying loop
while True:
clock.tick(FRAMERATE)
# Events
for event in pygame.event.get():
if event.type == QUIT: sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE: sys.exit()
elif event.key == K_a: console.add_char("Hello world !")
elif event.key == K_RETURN:
console.carriage_return()
console.line_field()
# Background display
screen_surface.blit(background_surface,(0,0))
# Text display
text_line_coordinates = TEXT_COORDINATES
for text_line in TEXT_LIST:
text_surface = font.render(text_line,True,FONT_COLOUR)
screen_surface.blit(text_surface,text_line_coordinates)
text_line_coordinates = text_line_coordinates._replace(y=text_line_coordinates.y+line_space)
# Console display
screen_surface.blit(console.surface,CONSOLE_COORDINATES)
# Screen rendering
pygame.display.flip()
# Main program,
# running only if the module is NOT imported (but directly executed)
#-------------------------------------------------------------------
if __name__ == '__main__':
main()

154
tests/test2.py Normal file
View File

@@ -0,0 +1,154 @@
# -*- coding: utf-8 -*-
"""
'console' test set : tests regarding only the pygconsole.console submodule
Test 2 : Write several 'Hello world !' on a console with the different standard colours.
ESCAPE Key to exit.
"""
# Standard modules
#-----------------
import sys
import os
from collections import namedtuple
import logging
# Third party modules
#--------------------
import pygame
from pygame.locals import *
from colorlog import ColoredFormatter
# Internal modules
#-----------------
if __name__ == '__main__':
sys.path.append(os.path.join(os.path.dirname(__file__),'..'))
import pygconsole
else:
from .. import pygconsole
# namedtuples
#------------
Coordinates = namedtuple("Coordinates", "x y")
Colour = namedtuple("Colour", "red green blue alpha")
# Global constants
#-----------------
RESOURCE_DIR = os.path.join(os.path.dirname(__file__),"resources") # directory where graphical resources are stored
BACKGROUND_IMAGE = os.path.join(RESOURCE_DIR,"background.jpg") # Background image
FONT = None # Font displayed outside of the console (None = default pygame font)
DISPLAY_SIZE = Coordinates(1920,1080) # screen resolution
CONSOLE_COORDINATES = Coordinates(1000,620) # upper left corner coordinates of the console
FRAMERATE = 50 # Maximum number of displaying loops per second
FONT_SIZE = 40 # size of the font displayed on the screen, but out of the console
FONT_COLOUR = Colour(255,0,0,255) # Colour of the font displayed outside of the console
TEXT_COORDINATES = Coordinates(50,50) # Coordinates of the first line of the text displayed outside of the console
TEXT_LIST = [
"Test 2: Write several `Hello world !` on a console with the 16 different standard colours.",
"Press 'A' to write the 16 lines at the cursor position.",
"ESCAPE key to exit."
]
# Dataclasses
#------------
# Classes
#--------
# Functions
#----------
# Main function
#--------------
def main():
""" Main program execution"""
# Logging initialization
formatter = ColoredFormatter(
'%(log_color)s[%(asctime)s][%(levelname)s][%(name)s]:%(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
reset=True,
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'red,bg_white'
},
secondary_log_colors={},
style='%'
)
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
handler.setFormatter(formatter)
log = logging.getLogger('display')
log.setLevel(logging.DEBUG)
log.addHandler(handler)
# screen initialization
pygame.init()
clock = pygame.time.Clock() # timer to control framerate
flags = FULLSCREEN|SCALED|DOUBLEBUF
screen_surface = pygame.display.set_mode(size=DISPLAY_SIZE,flags=flags)
background_surface = pygame.image.load(BACKGROUND_IMAGE)
# Font initialization
font = pygame.font.Font(FONT,FONT_SIZE)
line_space = font.get_linesize()
# console initialization
console = pygconsole.console.Console.get_console(logger = log)
# Displaying loop
while True:
clock.tick(FRAMERATE)
# Events
for event in pygame.event.get():
if event.type == QUIT: sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE: sys.exit()
elif event.key == K_a:
for font_colour in console.STANDARD_COLOURS:
console.foreground_colour = 'bright_white'
console.background_colour = 'black'
console.add_char(f"{font_colour}: ")
console.foreground_colour = font_colour
if font_colour == 'black':
console.background_colour = 'bright_white'
else:
console.background_colour = 'black'
console.add_char("Hello world !")
console.line_field()
console.carriage_return()
# Background display
screen_surface.blit(background_surface,(0,0))
# Text display
text_line_coordinates = TEXT_COORDINATES
for text_line in TEXT_LIST:
text_surface = font.render(text_line,True,FONT_COLOUR)
screen_surface.blit(text_surface,text_line_coordinates)
text_line_coordinates = text_line_coordinates._replace(y=text_line_coordinates.y+line_space)
# Console display
screen_surface.blit(console.surface,CONSOLE_COORDINATES)
# Screen rendering
pygame.display.flip()
# Main program,
# running only if the module is NOT imported (but directly executed)
#-------------------------------------------------------------------
if __name__ == '__main__':
main()

154
tests/test3.py Normal file
View File

@@ -0,0 +1,154 @@
# -*- coding: utf-8 -*-
"""
'console' test set : tests regarding only the pygconsole.console submodule
Test 3 : Write several 'Hello world !' on a console with a background of the different standard colours.
ESCAPE Key to exit.
"""
# Standard modules
#-----------------
import sys
import os
from collections import namedtuple
import logging
# Third party modules
#--------------------
import pygame
from pygame.locals import *
from colorlog import ColoredFormatter
# Internal modules
#-----------------
if __name__ == '__main__':
sys.path.append(os.path.join(os.path.dirname(__file__),'..'))
import pygconsole
else:
from .. import pygconsole
# namedtuples
#------------
Coordinates = namedtuple("Coordinates", "x y")
Colour = namedtuple("Colour", "red green blue alpha")
# Global constants
#-----------------
RESOURCE_DIR = os.path.join(os.path.dirname(__file__),"resources") # directory where graphical resources are stored
BACKGROUND_IMAGE = os.path.join(RESOURCE_DIR,"background.jpg") # Background image
FONT = None # Font displayed outside of the console (None = default pygame font)
DISPLAY_SIZE = Coordinates(1920,1080) # screen resolution
CONSOLE_COORDINATES = Coordinates(1000,620) # upper left corner coordinates of the console
FRAMERATE = 50 # Maximum number of displaying loops per second
FONT_SIZE = 40 # size of the font displayed on the screen, but out of the console
FONT_COLOUR = Colour(255,0,0,255) # Colour of the font displayed outside of the console
TEXT_COORDINATES = Coordinates(50,50) # Coordinates of the first line of the text displayed outside of the console
TEXT_LIST = [
"Test 3: Write several `Hello world !` on a console with a background of the 16 different standard colours.",
"Press 'A' to write the 16 lines at the cursor position.",
"ESCAPE key to exit."
]
# Dataclasses
#------------
# Classes
#--------
# Functions
#----------
# Main function
#--------------
def main():
""" Main program execution"""
# Logging initialization
formatter = ColoredFormatter(
'%(log_color)s[%(asctime)s][%(levelname)s][%(name)s]:%(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
reset=True,
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'red,bg_white'
},
secondary_log_colors={},
style='%'
)
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
handler.setFormatter(formatter)
log = logging.getLogger('display')
log.setLevel(logging.DEBUG)
log.addHandler(handler)
# screen initialization
pygame.init()
clock = pygame.time.Clock() # timer to control framerate
flags = FULLSCREEN|SCALED|DOUBLEBUF
screen_surface = pygame.display.set_mode(size=DISPLAY_SIZE,flags=flags)
background_surface = pygame.image.load(BACKGROUND_IMAGE)
# Font initialization
font = pygame.font.Font(FONT,FONT_SIZE)
line_space = font.get_linesize()
# console initialization
console = pygconsole.console.Console.get_console(logger = log)
# Displaying loop
while True:
clock.tick(FRAMERATE)
# Events
for event in pygame.event.get():
if event.type == QUIT: sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE: sys.exit()
elif event.key == K_a:
for background_colour in console.STANDARD_COLOURS:
console.foreground_colour = 'bright_white'
console.background_colour = 'black'
console.add_char(f"{background_colour}: ")
console.background_colour = background_colour
if background_colour == 'bright_white':
console.foreground_colour = 'black'
else:
console.foreground_colour = 'bright_white'
console.add_char("Hello world !")
console.line_field()
console.carriage_return()
# Background display
screen_surface.blit(background_surface,(0,0))
# Text display
text_line_coordinates = TEXT_COORDINATES
for text_line in TEXT_LIST:
text_surface = font.render(text_line,True,FONT_COLOUR)
screen_surface.blit(text_surface,text_line_coordinates)
text_line_coordinates = text_line_coordinates._replace(y=text_line_coordinates.y+line_space)
# Console display
screen_surface.blit(console.surface,CONSOLE_COORDINATES)
# Screen rendering
pygame.display.flip()
# Main program,
# running only if the module is NOT imported (but directly executed)
#-------------------------------------------------------------------
if __name__ == '__main__':
main()

144
tests/test4.py Normal file
View File

@@ -0,0 +1,144 @@
# -*- coding: utf-8 -*-
"""
'console' test set : tests regarding only the pygconsole.console submodule
Test 4 : Write a long text on the console with the default parameters.
ESCAPE Key to exit.
"""
# Standard modules
#-----------------
import sys
import os
from collections import namedtuple
import logging
# Third party modules
#--------------------
import pygame
from pygame.locals import *
from colorlog import ColoredFormatter
# Internal modules
#-----------------
if __name__ == '__main__':
sys.path.append(os.path.join(os.path.dirname(__file__),'..'))
import pygconsole
else:
from .. import pygconsole
# namedtuples
#------------
Coordinates = namedtuple("Coordinates", "x y")
Colour = namedtuple("Colour", "red green blue alpha")
# Global constants
#-----------------
RESOURCE_DIR = os.path.join(os.path.dirname(__file__),"resources") # directory where graphical resources are stored
BACKGROUND_IMAGE = os.path.join(RESOURCE_DIR,"background.jpg") # Background image
FONT = None # Font displayed outside of the console (None = default pygame font)
DISPLAY_SIZE = Coordinates(1920,1080) # screen resolution
CONSOLE_COORDINATES = Coordinates(1000,620) # upper left corner coordinates of the console
FRAMERATE = 50 # Maximum number of displaying loops per second
FONT_SIZE = 40 # size of the font displayed on the screen, but out of the console
FONT_COLOUR = Colour(255,0,0,255) # Colour of the font displayed outside of the console
TEXT_COORDINATES = Coordinates(50,50) # Coordinates of the first line of the text displayed outside of the console
TEXT_LIST = [
"Test 4: Write a long text on the console with the default parameters.",
"Press 'A' to write the 'Lorem ipsum' text",
"ESCAPE key to exit."
]
LONG_TEXT = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vitae nunc dictum, sagittis elit venenatis, efficitur justo. Etiam suscipit, ipsum accumsan aliquam elementum, massa tellus pellentesque lacus, ut porttitor ligula tortor at urna. Duis eu felis non tortor bibendum ultrices. Aliquam tortor velit, suscipit faucibus nunc quis, blandit posuere leo. Donec dignissim aliquam lectus, vitae lacinia risus feugiat non. Curabitur dapibus, massa quis eleifend lobortis, nulla sem ullamcorper turpis, vel sodales risus orci non velit. Nam ac neque faucibus, consequat eros eu, viverra neque. Nulla vel blandit lorem. Nam interdum nisl non sem pretium dictum. Phasellus id sapien vitae ipsum efficitur fringilla. Quisque suscipit consequat erat quis commodo. Aenean tristique felis libero, et ultrices justo porttitor eget. Vivamus sit amet urna nibh. Ut ultrices nulla et dui eleifend, ut sagittis ipsum condimentum. Mauris nec dolor eget augue gravida lacinia. "
# Dataclasses
#------------
# Classes
#--------
# Functions
#----------
# Main function
#--------------
def main():
""" Main program execution"""
# Logging initialization
formatter = ColoredFormatter(
'%(log_color)s[%(asctime)s][%(levelname)s][%(name)s]:%(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
reset=True,
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'red,bg_white'
},
secondary_log_colors={},
style='%'
)
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
handler.setFormatter(formatter)
log = logging.getLogger('display')
log.setLevel(logging.DEBUG)
log.addHandler(handler)
# screen initialization
pygame.init()
clock = pygame.time.Clock() # timer to control framerate
flags = FULLSCREEN|SCALED|DOUBLEBUF
screen_surface = pygame.display.set_mode(size=DISPLAY_SIZE,flags=flags)
background_surface = pygame.image.load(BACKGROUND_IMAGE)
# Font initialization
font = pygame.font.Font(FONT,FONT_SIZE)
line_space = font.get_linesize()
# console initialization
console = pygconsole.console.Console.get_console(logger = log)
# Displaying loop
while True:
clock.tick(FRAMERATE)
# Events
for event in pygame.event.get():
if event.type == QUIT: sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE: sys.exit()
elif event.key == K_a: console.add_char(LONG_TEXT)
# Background display
screen_surface.blit(background_surface,(0,0))
# Text display
text_line_coordinates = TEXT_COORDINATES
for text_line in TEXT_LIST:
text_surface = font.render(text_line,True,FONT_COLOUR)
screen_surface.blit(text_surface,text_line_coordinates)
text_line_coordinates = text_line_coordinates._replace(y=text_line_coordinates.y+line_space)
# Console display
screen_surface.blit(console.surface,CONSOLE_COORDINATES)
# Screen rendering
pygame.display.flip()
# Main program,
# running only if the module is NOT imported (but directly executed)
#-------------------------------------------------------------------
if __name__ == '__main__':
main()

146
tests/test4_1.py Normal file
View File

@@ -0,0 +1,146 @@
# -*- coding: utf-8 -*-
"""
'console' test set : tests regarding only the pygconsole.console submodule
Test 4_1 : Write a long text on the console with the default parameters, and test the clear function.
ESCAPE Key to exit.
"""
# Standard modules
#-----------------
import sys
import os
from collections import namedtuple
import logging
# Third party modules
#--------------------
import pygame
from pygame.locals import *
from colorlog import ColoredFormatter
# Internal modules
#-----------------
if __name__ == '__main__':
sys.path.append(os.path.join(os.path.dirname(__file__),'..'))
import pygconsole
else:
from .. import pygconsole
# namedtuples
#------------
Coordinates = namedtuple("Coordinates", "x y")
Colour = namedtuple("Colour", "red green blue alpha")
# Global constants
#-----------------
RESOURCE_DIR = os.path.join(os.path.dirname(__file__),"resources") # directory where graphical resources are stored
BACKGROUND_IMAGE = os.path.join(RESOURCE_DIR,"background.jpg") # Background image
FONT = None # Font displayed outside of the console (None = default pygame font)
DISPLAY_SIZE = Coordinates(1920,1080) # screen resolution
CONSOLE_COORDINATES = Coordinates(1000,620) # upper left corner coordinates of the console
FRAMERATE = 50 # Maximum number of displaying loops per second
FONT_SIZE = 40 # size of the font displayed on the screen, but out of the console
FONT_COLOUR = Colour(255,0,0,255) # Colour of the font displayed outside of the console
TEXT_COORDINATES = Coordinates(50,50) # Coordinates of the first line of the text displayed outside of the console
TEXT_LIST = [
"Test 4: Write a long text on the console with the default parameters.",
"Press 'A' to write the 'Lorem ipsum' text.",
"Press 'ENTER' to clear the console.",
"ESCAPE key to exit."
]
LONG_TEXT = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vitae nunc dictum, sagittis elit venenatis, efficitur justo. Etiam suscipit, ipsum accumsan aliquam elementum, massa tellus pellentesque lacus, ut porttitor ligula tortor at urna. Duis eu felis non tortor bibendum ultrices. Aliquam tortor velit, suscipit faucibus nunc quis, blandit posuere leo. Donec dignissim aliquam lectus, vitae lacinia risus feugiat non. Curabitur dapibus, massa quis eleifend lobortis, nulla sem ullamcorper turpis, vel sodales risus orci non velit. Nam ac neque faucibus, consequat eros eu, viverra neque. Nulla vel blandit lorem. Nam interdum nisl non sem pretium dictum. Phasellus id sapien vitae ipsum efficitur fringilla. Quisque suscipit consequat erat quis commodo. Aenean tristique felis libero, et ultrices justo porttitor eget. Vivamus sit amet urna nibh. Ut ultrices nulla et dui eleifend, ut sagittis ipsum condimentum. Mauris nec dolor eget augue gravida lacinia. "
# Dataclasses
#------------
# Classes
#--------
# Functions
#----------
# Main function
#--------------
def main():
""" Main program execution"""
# Logging initialization
formatter = ColoredFormatter(
'%(log_color)s[%(asctime)s][%(levelname)s][%(name)s]:%(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
reset=True,
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'red,bg_white'
},
secondary_log_colors={},
style='%'
)
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
handler.setFormatter(formatter)
log = logging.getLogger('display')
log.setLevel(logging.DEBUG)
log.addHandler(handler)
# screen initialization
pygame.init()
clock = pygame.time.Clock() # timer to control framerate
flags = FULLSCREEN|SCALED|DOUBLEBUF
screen_surface = pygame.display.set_mode(size=DISPLAY_SIZE,flags=flags)
background_surface = pygame.image.load(BACKGROUND_IMAGE)
# Font initialization
font = pygame.font.Font(FONT,FONT_SIZE)
line_space = font.get_linesize()
# console initialization
console = pygconsole.console.Console.get_console(logger = log)
# Displaying loop
while True:
clock.tick(FRAMERATE)
# Events
for event in pygame.event.get():
if event.type == QUIT: sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE: sys.exit()
elif event.key == K_a: console.add_char(LONG_TEXT)
elif event.key == K_RETURN: console.clear()
# Background display
screen_surface.blit(background_surface,(0,0))
# Text display
text_line_coordinates = TEXT_COORDINATES
for text_line in TEXT_LIST:
text_surface = font.render(text_line,True,FONT_COLOUR)
screen_surface.blit(text_surface,text_line_coordinates)
text_line_coordinates = text_line_coordinates._replace(y=text_line_coordinates.y+line_space)
# Console display
screen_surface.blit(console.surface,CONSOLE_COORDINATES)
# Screen rendering
pygame.display.flip()
# Main program,
# running only if the module is NOT imported (but directly executed)
#-------------------------------------------------------------------
if __name__ == '__main__':
main()

146
tests/test5.py Normal file
View File

@@ -0,0 +1,146 @@
# -*- coding: utf-8 -*-
"""
'console' test set : tests regarding only the pygconsole.console submodule
Test 5 : Write a long text on the console in bold characters.
ESCAPE Key to exit.
"""
# Standard modules
#-----------------
import sys
import os
from collections import namedtuple
import logging
# Third party modules
#--------------------
import pygame
from pygame.locals import *
from colorlog import ColoredFormatter
# Internal modules
#-----------------
if __name__ == '__main__':
sys.path.append(os.path.join(os.path.dirname(__file__),'..'))
import pygconsole
else:
from .. import pygconsole
# namedtuples
#------------
Coordinates = namedtuple("Coordinates", "x y")
Colour = namedtuple("Colour", "red green blue alpha")
# Global constants
#-----------------
RESOURCE_DIR = os.path.join(os.path.dirname(__file__),"resources") # directory where graphical resources are stored
BACKGROUND_IMAGE = os.path.join(RESOURCE_DIR,"background.jpg") # Background image
FONT = None # Font displayed outside of the console (None = default pygame font)
DISPLAY_SIZE = Coordinates(1920,1080) # screen resolution
CONSOLE_COORDINATES = Coordinates(1000,620) # upper left corner coordinates of the console
FRAMERATE = 50 # Maximum number of displaying loops per second
FONT_SIZE = 40 # size of the font displayed on the screen, but out of the console
FONT_COLOUR = Colour(255,0,0,255) # Colour of the font displayed outside of the console
TEXT_COORDINATES = Coordinates(50,50) # Coordinates of the first line of the text displayed outside of the console
TEXT_LIST = [
"Test 5: Write a long text on the console in bold characters.",
"Press 'A' to write the 'Lorem ipsum' text",
"ESCAPE key to exit."
]
LONG_TEXT = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vitae nunc dictum, sagittis elit venenatis, efficitur justo. Etiam suscipit, ipsum accumsan aliquam elementum, massa tellus pellentesque lacus, ut porttitor ligula tortor at urna. Duis eu felis non tortor bibendum ultrices. Aliquam tortor velit, suscipit faucibus nunc quis, blandit posuere leo. Donec dignissim aliquam lectus, vitae lacinia risus feugiat non. Curabitur dapibus, massa quis eleifend lobortis, nulla sem ullamcorper turpis, vel sodales risus orci non velit. Nam ac neque faucibus, consequat eros eu, viverra neque. Nulla vel blandit lorem. Nam interdum nisl non sem pretium dictum. Phasellus id sapien vitae ipsum efficitur fringilla. Quisque suscipit consequat erat quis commodo. Aenean tristique felis libero, et ultrices justo porttitor eget. Vivamus sit amet urna nibh. Ut ultrices nulla et dui eleifend, ut sagittis ipsum condimentum. Mauris nec dolor eget augue gravida lacinia. "
# Dataclasses
#------------
# Classes
#--------
# Functions
#----------
# Main function
#--------------
def main():
""" Main program execution"""
# Logging initialization
formatter = ColoredFormatter(
'%(log_color)s[%(asctime)s][%(levelname)s][%(name)s]:%(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
reset=True,
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'red,bg_white'
},
secondary_log_colors={},
style='%'
)
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
handler.setFormatter(formatter)
log = logging.getLogger('display')
log.setLevel(logging.DEBUG)
log.addHandler(handler)
# screen initialization
pygame.init()
clock = pygame.time.Clock() # timer to control framerate
flags = FULLSCREEN|SCALED|DOUBLEBUF
screen_surface = pygame.display.set_mode(size=DISPLAY_SIZE,flags=flags)
background_surface = pygame.image.load(BACKGROUND_IMAGE)
# Font initialization
font = pygame.font.Font(FONT,FONT_SIZE)
line_space = font.get_linesize()
# console initialization
console = pygconsole.console.Console.get_console(logger = log)
# Displaying loop
while True:
clock.tick(FRAMERATE)
# Events
for event in pygame.event.get():
if event.type == QUIT: sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE: sys.exit()
elif event.key == K_a:
console.bold = True
console.add_char(LONG_TEXT)
# Background display
screen_surface.blit(background_surface,(0,0))
# Text display
text_line_coordinates = TEXT_COORDINATES
for text_line in TEXT_LIST:
text_surface = font.render(text_line,True,FONT_COLOUR)
screen_surface.blit(text_surface,text_line_coordinates)
text_line_coordinates = text_line_coordinates._replace(y=text_line_coordinates.y+line_space)
# Console display
screen_surface.blit(console.surface,CONSOLE_COORDINATES)
# Screen rendering
pygame.display.flip()
# Main program,
# running only if the module is NOT imported (but directly executed)
#-------------------------------------------------------------------
if __name__ == '__main__':
main()

146
tests/test6.py Normal file
View File

@@ -0,0 +1,146 @@
# -*- coding: utf-8 -*-
"""
'console' test set : tests regarding only the pygconsole.console submodule
Test 6 : Write a long text on the console in underlined, normal characters.
ESCAPE Key to exit.
"""
# Standard modules
#-----------------
import sys
import os
from collections import namedtuple
import logging
# Third party modules
#--------------------
import pygame
from pygame.locals import *
from colorlog import ColoredFormatter
# Internal modules
#-----------------
if __name__ == '__main__':
sys.path.append(os.path.join(os.path.dirname(__file__),'..'))
import pygconsole
else:
from .. import pygconsole
# namedtuples
#------------
Coordinates = namedtuple("Coordinates", "x y")
Colour = namedtuple("Colour", "red green blue alpha")
# Global constants
#-----------------
RESOURCE_DIR = os.path.join(os.path.dirname(__file__),"resources") # directory where graphical resources are stored
BACKGROUND_IMAGE = os.path.join(RESOURCE_DIR,"background.jpg") # Background image
FONT = None # Font displayed outside of the console (None = default pygame font)
DISPLAY_SIZE = Coordinates(1920,1080) # screen resolution
CONSOLE_COORDINATES = Coordinates(1000,620) # upper left corner coordinates of the console
FRAMERATE = 50 # Maximum number of displaying loops per second
FONT_SIZE = 40 # size of the font displayed on the screen, but out of the console
FONT_COLOUR = Colour(255,0,0,255) # Colour of the font displayed outside of the console
TEXT_COORDINATES = Coordinates(50,50) # Coordinates of the first line of the text displayed outside of the console
TEXT_LIST = [
"Test 6: Write a long text on the console in underlined, normal characters.",
"Press 'A' to write the 'Lorem ipsum' text",
"ESCAPE key to exit."
]
LONG_TEXT = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vitae nunc dictum, sagittis elit venenatis, efficitur justo. Etiam suscipit, ipsum accumsan aliquam elementum, massa tellus pellentesque lacus, ut porttitor ligula tortor at urna. Duis eu felis non tortor bibendum ultrices. Aliquam tortor velit, suscipit faucibus nunc quis, blandit posuere leo. Donec dignissim aliquam lectus, vitae lacinia risus feugiat non. Curabitur dapibus, massa quis eleifend lobortis, nulla sem ullamcorper turpis, vel sodales risus orci non velit. Nam ac neque faucibus, consequat eros eu, viverra neque. Nulla vel blandit lorem. Nam interdum nisl non sem pretium dictum. Phasellus id sapien vitae ipsum efficitur fringilla. Quisque suscipit consequat erat quis commodo. Aenean tristique felis libero, et ultrices justo porttitor eget. Vivamus sit amet urna nibh. Ut ultrices nulla et dui eleifend, ut sagittis ipsum condimentum. Mauris nec dolor eget augue gravida lacinia. "
# Dataclasses
#------------
# Classes
#--------
# Functions
#----------
# Main function
#--------------
def main():
""" Main program execution"""
# Logging initialization
formatter = ColoredFormatter(
'%(log_color)s[%(asctime)s][%(levelname)s][%(name)s]:%(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
reset=True,
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'red,bg_white'
},
secondary_log_colors={},
style='%'
)
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
handler.setFormatter(formatter)
log = logging.getLogger('display')
log.setLevel(logging.DEBUG)
log.addHandler(handler)
# screen initialization
pygame.init()
clock = pygame.time.Clock() # timer to control framerate
flags = FULLSCREEN|SCALED|DOUBLEBUF
screen_surface = pygame.display.set_mode(size=DISPLAY_SIZE,flags=flags)
background_surface = pygame.image.load(BACKGROUND_IMAGE)
# Font initialization
font = pygame.font.Font(FONT,FONT_SIZE)
line_space = font.get_linesize()
# console initialization
console = pygconsole.console.Console.get_console(logger = log)
# Displaying loop
while True:
clock.tick(FRAMERATE)
# Events
for event in pygame.event.get():
if event.type == QUIT: sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE: sys.exit()
elif event.key == K_a:
console.underline = True
console.add_char(LONG_TEXT)
# Background display
screen_surface.blit(background_surface,(0,0))
# Text display
text_line_coordinates = TEXT_COORDINATES
for text_line in TEXT_LIST:
text_surface = font.render(text_line,True,FONT_COLOUR)
screen_surface.blit(text_surface,text_line_coordinates)
text_line_coordinates = text_line_coordinates._replace(y=text_line_coordinates.y+line_space)
# Console display
screen_surface.blit(console.surface,CONSOLE_COORDINATES)
# Screen rendering
pygame.display.flip()
# Main program,
# running only if the module is NOT imported (but directly executed)
#-------------------------------------------------------------------
if __name__ == '__main__':
main()

146
tests/test7.py Normal file
View File

@@ -0,0 +1,146 @@
# -*- coding: utf-8 -*-
"""
'console' test set : tests regarding only the pygconsole.console submodule
Test 7 : Write a long text on the console in italic characters.
ESCAPE Key to exit.
"""
# Standard modules
#-----------------
import sys
import os
from collections import namedtuple
import logging
# Third party modules
#--------------------
import pygame
from pygame.locals import *
from colorlog import ColoredFormatter
# Internal modules
#-----------------
if __name__ == '__main__':
sys.path.append(os.path.join(os.path.dirname(__file__),'..'))
import pygconsole
else:
from .. import pygconsole
# namedtuples
#------------
Coordinates = namedtuple("Coordinates", "x y")
Colour = namedtuple("Colour", "red green blue alpha")
# Global constants
#-----------------
RESOURCE_DIR = os.path.join(os.path.dirname(__file__),"resources") # directory where graphical resources are stored
BACKGROUND_IMAGE = os.path.join(RESOURCE_DIR,"background.jpg") # Background image
FONT = None # Font displayed outside of the console (None = default pygame font)
DISPLAY_SIZE = Coordinates(1920,1080) # screen resolution
CONSOLE_COORDINATES = Coordinates(1000,620) # upper left corner coordinates of the console
FRAMERATE = 50 # Maximum number of displaying loops per second
FONT_SIZE = 40 # size of the font displayed on the screen, but out of the console
FONT_COLOUR = Colour(255,0,0,255) # Colour of the font displayed outside of the console
TEXT_COORDINATES = Coordinates(50,50) # Coordinates of the first line of the text displayed outside of the console
TEXT_LIST = [
"Test 7: Write a long text on the console in italic characters.",
"Press 'A' to write the 'Lorem ipsum' text",
"ESCAPE key to exit."
]
LONG_TEXT = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vitae nunc dictum, sagittis elit venenatis, efficitur justo. Etiam suscipit, ipsum accumsan aliquam elementum, massa tellus pellentesque lacus, ut porttitor ligula tortor at urna. Duis eu felis non tortor bibendum ultrices. Aliquam tortor velit, suscipit faucibus nunc quis, blandit posuere leo. Donec dignissim aliquam lectus, vitae lacinia risus feugiat non. Curabitur dapibus, massa quis eleifend lobortis, nulla sem ullamcorper turpis, vel sodales risus orci non velit. Nam ac neque faucibus, consequat eros eu, viverra neque. Nulla vel blandit lorem. Nam interdum nisl non sem pretium dictum. Phasellus id sapien vitae ipsum efficitur fringilla. Quisque suscipit consequat erat quis commodo. Aenean tristique felis libero, et ultrices justo porttitor eget. Vivamus sit amet urna nibh. Ut ultrices nulla et dui eleifend, ut sagittis ipsum condimentum. Mauris nec dolor eget augue gravida lacinia. "
# Dataclasses
#------------
# Classes
#--------
# Functions
#----------
# Main function
#--------------
def main():
""" Main program execution"""
# Logging initialization
formatter = ColoredFormatter(
'%(log_color)s[%(asctime)s][%(levelname)s][%(name)s]:%(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
reset=True,
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'red,bg_white'
},
secondary_log_colors={},
style='%'
)
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
handler.setFormatter(formatter)
log = logging.getLogger('display')
log.setLevel(logging.DEBUG)
log.addHandler(handler)
# screen initialization
pygame.init()
clock = pygame.time.Clock() # timer to control framerate
flags = FULLSCREEN|SCALED|DOUBLEBUF
screen_surface = pygame.display.set_mode(size=DISPLAY_SIZE,flags=flags)
background_surface = pygame.image.load(BACKGROUND_IMAGE)
# Font initialization
font = pygame.font.Font(FONT,FONT_SIZE)
line_space = font.get_linesize()
# console initialization
console = pygconsole.console.Console.get_console(logger = log)
# Displaying loop
while True:
clock.tick(FRAMERATE)
# Events
for event in pygame.event.get():
if event.type == QUIT: sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE: sys.exit()
elif event.key == K_a:
console.italic = True
console.add_char(LONG_TEXT)
# Background display
screen_surface.blit(background_surface,(0,0))
# Text display
text_line_coordinates = TEXT_COORDINATES
for text_line in TEXT_LIST:
text_surface = font.render(text_line,True,FONT_COLOUR)
screen_surface.blit(text_surface,text_line_coordinates)
text_line_coordinates = text_line_coordinates._replace(y=text_line_coordinates.y+line_space)
# Console display
screen_surface.blit(console.surface,CONSOLE_COORDINATES)
# Screen rendering
pygame.display.flip()
# Main program,
# running only if the module is NOT imported (but directly executed)
#-------------------------------------------------------------------
if __name__ == '__main__':
main()

147
tests/test8.py Normal file
View File

@@ -0,0 +1,147 @@
# -*- coding: utf-8 -*-
"""
'console' test set : tests regarding only the pygconsole.console submodule
Test 8 : Write a long text on the console in bold, italic characters.
ESCAPE Key to exit.
"""
# Standard modules
#-----------------
import sys
import os
from collections import namedtuple
import logging
# Third party modules
#--------------------
import pygame
from pygame.locals import *
from colorlog import ColoredFormatter
# Internal modules
#-----------------
if __name__ == '__main__':
sys.path.append(os.path.join(os.path.dirname(__file__),'..'))
import pygconsole
else:
from .. import pygconsole
# namedtuples
#------------
Coordinates = namedtuple("Coordinates", "x y")
Colour = namedtuple("Colour", "red green blue alpha")
# Global constants
#-----------------
RESOURCE_DIR = os.path.join(os.path.dirname(__file__),"resources") # directory where graphical resources are stored
BACKGROUND_IMAGE = os.path.join(RESOURCE_DIR,"background.jpg") # Background image
FONT = None # Font displayed outside of the console (None = default pygame font)
DISPLAY_SIZE = Coordinates(1920,1080) # screen resolution
CONSOLE_COORDINATES = Coordinates(1000,620) # upper left corner coordinates of the console
FRAMERATE = 50 # Maximum number of displaying loops per second
FONT_SIZE = 40 # size of the font displayed on the screen, but out of the console
FONT_COLOUR = Colour(255,0,0,255) # Colour of the font displayed outside of the console
TEXT_COORDINATES = Coordinates(50,50) # Coordinates of the first line of the text displayed outside of the console
TEXT_LIST = [
"Test 8: Write a long text on the console in bold, italic characters.",
"Press 'A' to write the 'Lorem ipsum' text",
"ESCAPE key to exit."
]
LONG_TEXT = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vitae nunc dictum, sagittis elit venenatis, efficitur justo. Etiam suscipit, ipsum accumsan aliquam elementum, massa tellus pellentesque lacus, ut porttitor ligula tortor at urna. Duis eu felis non tortor bibendum ultrices. Aliquam tortor velit, suscipit faucibus nunc quis, blandit posuere leo. Donec dignissim aliquam lectus, vitae lacinia risus feugiat non. Curabitur dapibus, massa quis eleifend lobortis, nulla sem ullamcorper turpis, vel sodales risus orci non velit. Nam ac neque faucibus, consequat eros eu, viverra neque. Nulla vel blandit lorem. Nam interdum nisl non sem pretium dictum. Phasellus id sapien vitae ipsum efficitur fringilla. Quisque suscipit consequat erat quis commodo. Aenean tristique felis libero, et ultrices justo porttitor eget. Vivamus sit amet urna nibh. Ut ultrices nulla et dui eleifend, ut sagittis ipsum condimentum. Mauris nec dolor eget augue gravida lacinia. "
# Dataclasses
#------------
# Classes
#--------
# Functions
#----------
# Main function
#--------------
def main():
""" Main program execution"""
# Logging initialization
formatter = ColoredFormatter(
'%(log_color)s[%(asctime)s][%(levelname)s][%(name)s]:%(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
reset=True,
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'red,bg_white'
},
secondary_log_colors={},
style='%'
)
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
handler.setFormatter(formatter)
log = logging.getLogger('display')
log.setLevel(logging.DEBUG)
log.addHandler(handler)
# screen initialization
pygame.init()
clock = pygame.time.Clock() # timer to control framerate
flags = FULLSCREEN|SCALED|DOUBLEBUF
screen_surface = pygame.display.set_mode(size=DISPLAY_SIZE,flags=flags)
background_surface = pygame.image.load(BACKGROUND_IMAGE)
# Font initialization
font = pygame.font.Font(FONT,FONT_SIZE)
line_space = font.get_linesize()
# console initialization
console = pygconsole.console.Console.get_console(logger = log)
# Displaying loop
while True:
clock.tick(FRAMERATE)
# Events
for event in pygame.event.get():
if event.type == QUIT: sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE: sys.exit()
elif event.key == K_a:
console.bold = True
console.italic = True
console.add_char(LONG_TEXT)
# Background display
screen_surface.blit(background_surface,(0,0))
# Text display
text_line_coordinates = TEXT_COORDINATES
for text_line in TEXT_LIST:
text_surface = font.render(text_line,True,FONT_COLOUR)
screen_surface.blit(text_surface,text_line_coordinates)
text_line_coordinates = text_line_coordinates._replace(y=text_line_coordinates.y+line_space)
# Console display
screen_surface.blit(console.surface,CONSOLE_COORDINATES)
# Screen rendering
pygame.display.flip()
# Main program,
# running only if the module is NOT imported (but directly executed)
#-------------------------------------------------------------------
if __name__ == '__main__':
main()

154
tests/test9.py Normal file
View File

@@ -0,0 +1,154 @@
# -*- coding: utf-8 -*-
"""
'console' test set : tests regarding only the pygconsole.console submodule
Test 9 : Write a long text on the console in characters parametered randomly (bold / underline / italic), but with the default colours (foreground in bright white, background in black).
ESCAPE Key to exit.
"""
# Standard modules
#-----------------
import sys
import os
from collections import namedtuple
import logging
import random
# Third party modules
#--------------------
import pygame
from pygame.locals import *
from colorlog import ColoredFormatter
# Internal modules
#-----------------
if __name__ == '__main__':
sys.path.append(os.path.join(os.path.dirname(__file__),'..'))
import pygconsole
else:
from .. import pygconsole
# namedtuples
#------------
Coordinates = namedtuple("Coordinates", "x y")
Colour = namedtuple("Colour", "red green blue alpha")
# Global constants
#-----------------
RESOURCE_DIR = os.path.join(os.path.dirname(__file__),"resources") # directory where graphical resources are stored
BACKGROUND_IMAGE = os.path.join(RESOURCE_DIR,"background.jpg") # Background image
FONT = None # Font displayed outside of the console (None = default pygame font)
DISPLAY_SIZE = Coordinates(1920,1080) # screen resolution
CONSOLE_COORDINATES = Coordinates(1000,620) # upper left corner coordinates of the console
FRAMERATE = 50 # Maximum number of displaying loops per second
FONT_SIZE = 40 # size of the font displayed on the screen, but out of the console
FONT_COLOUR = Colour(255,0,0,255) # Colour of the font displayed outside of the console
TEXT_COORDINATES = Coordinates(50,50) # Coordinates of the first line of the text displayed outside of the console
TEXT_LIST = [
"Test 9 : Write a long text on the console in characters parametered randomly (bold / underline / italic), but with the default colours (foreground in bright white, background in black).",
"Press 'A' to write the 'Lorem ipsum' text",
"ESCAPE key to exit."
]
LONG_TEXT = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vitae nunc dictum, sagittis elit venenatis, efficitur justo. Etiam suscipit, ipsum accumsan aliquam elementum, massa tellus pellentesque lacus, ut porttitor ligula tortor at urna. Duis eu felis non tortor bibendum ultrices. Aliquam tortor velit, suscipit faucibus nunc quis, blandit posuere leo. Donec dignissim aliquam lectus, vitae lacinia risus feugiat non. Curabitur dapibus, massa quis eleifend lobortis, nulla sem ullamcorper turpis, vel sodales risus orci non velit. Nam ac neque faucibus, consequat eros eu, viverra neque. Nulla vel blandit lorem. Nam interdum nisl non sem pretium dictum. Phasellus id sapien vitae ipsum efficitur fringilla. Quisque suscipit consequat erat quis commodo. Aenean tristique felis libero, et ultrices justo porttitor eget. Vivamus sit amet urna nibh. Ut ultrices nulla et dui eleifend, ut sagittis ipsum condimentum. Mauris nec dolor eget augue gravida lacinia. "
# Dataclasses
#------------
# Classes
#--------
# Functions
#----------
# Main function
#--------------
def main():
""" Main program execution"""
# random initialization
random.seed()
# Logging initialization
formatter = ColoredFormatter(
'%(log_color)s[%(asctime)s][%(levelname)s][%(name)s]:%(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
reset=True,
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'red,bg_white'
},
secondary_log_colors={},
style='%'
)
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
handler.setFormatter(formatter)
log = logging.getLogger('display')
log.setLevel(logging.DEBUG)
log.addHandler(handler)
# screen initialization
pygame.init()
clock = pygame.time.Clock() # timer to control framerate
flags = FULLSCREEN|SCALED|DOUBLEBUF
screen_surface = pygame.display.set_mode(size=DISPLAY_SIZE,flags=flags)
background_surface = pygame.image.load(BACKGROUND_IMAGE)
# Font initialization
font = pygame.font.Font(FONT,FONT_SIZE)
line_space = font.get_linesize()
# console initialization
console = pygconsole.console.Console.get_console(logger = log)
# Displaying loop
while True:
clock.tick(FRAMERATE)
# Events
for event in pygame.event.get():
if event.type == QUIT: sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE: sys.exit()
elif event.key == K_a:
for word in LONG_TEXT.split():
console.bold = random.choice((True, False))
console.italic = random.choice((True, False))
console.underline = random.choice((True, False))
console.add_char(word)
console.add_char(' ')
# Background display
screen_surface.blit(background_surface,(0,0))
# Text display
text_line_coordinates = TEXT_COORDINATES
for text_line in TEXT_LIST:
text_surface = font.render(text_line,True,FONT_COLOUR)
screen_surface.blit(text_surface,text_line_coordinates)
text_line_coordinates = text_line_coordinates._replace(y=text_line_coordinates.y+line_space)
# Console display
screen_surface.blit(console.surface,CONSOLE_COORDINATES)
# Screen rendering
pygame.display.flip()
# Main program,
# running only if the module is NOT imported (but directly executed)
#-------------------------------------------------------------------
if __name__ == '__main__':
main()