bwk 1.2.0
A simple Python windowing kit module for use with Blessed terminal formatting.
License: BSD-2-Clause
Requires Python: <4.0,>=3.9

#  Available Character Sets

## ``` bwk.UTF```



 A struct-like class which contains Unicode characters commonly used in Text-Based User Interfaces (TUI's).

* ```line.solid.horizontal```: ```─```
* ```line.solid.vertical```: ```│```
* ```line.solid.dash_h```: ```┄```
* ```line.solid.dash_v```: ```┆```
* ```line.solid.tiny_dash_h```: ```┈```
* ```line.solid.tiny_dash_v```: ```┊```
* ```line.solid.split_h```: ```╌```
* ```line.solid.left_half_h```: ```╴```
* ```line.solid.right_half_h```: ```╶```
* ```line.solid.split_v```: ```╎```
* ```line.solid.top_half_v```: ```╵```
* ```line.solid.bottom_half_v```: ```╷```
* ```line.solid.corner.upper_left```: ```┌```
* ```line.solid.corner.upper_right```: ```┐```
* ```line.solid.corner.lower_left```: ```└```
* ```line.solid.corner.lower_right```: ```┘```
* ```line.solid.rounded_corner.upper_left```: ```╭```
* ```line.solid.rounded_corner.upper_right```: ```╮```
* ```line.solid.rounded_corner.lower_left```: ```╰```
* ```line.solid.rounded_corner.lower_right```: ```╯```
* ```line.solid.intersect.T_LEFT```: ```├```
* ```line.solid.intersect.T_RIGHT```: ```┤```
* ```line.solid.intersect.T_TOP```: ```┬```
* ```line.solid.intersect.T_BOTTOM```: ```┴```
* ```line.solid.intersect.CROSS```: ```┼```
* ```line.bold.horizontal```: ```━```
* ```line.bold.vertical```: ```┃```
* ```line.bold.dash_h```: ```┅```
* ```line.bold.dash_v```: ```┇```
* ```line.bold.tiny_dash_h```: ```┉```
* ```line.bold.tiny_dash_v```: ```┋```
* ```line.bold.split_h```: ```╍```
* ```line.bold.left_half_h```: ```╸```
* ```line.bold.right_half_h```: ```╺```
* ```line.bold.split_v```: ```╏```
* ```line.bold.top_half_v```: ```╹```
* ```line.bold.bottom_half_v```: ```╻```
* ```line.bold.corner.upper_left```: ```┏```
* ```line.bold.corner.upper_right```: ```┓```
* ```line.bold.corner.lower_left```: ```┗```
* ```line.bold.corner.lower_right```: ```┛```
* ```line.bold.intersect.T_LEFT```: ```┣```
* ```line.bold.intersect.T_RIGHT```: ```┫```
* ```line.bold.intersect.T_TOP```: ```┳```
* ```line.bold.intersect.T_BOTTOM```: ```┻```
* ```line.bold.intersect.CROSS```: ```╋```
* ```line.double.horizontal```: ```═```
* ```line.double.vertical```: ```║```
* ```line.double.corner.upper_left```: ```╔```
* ```line.double.corner.upper_right```: ```╗```
* ```line.double.corner.lower_left```: ```╚```
* ```line.double.corner.lower_right```: ```╝```
* ```line.double.intersect.T_LEFT```: ```╠```
* ```line.double.intersect.T_RIGHT```: ```╣```
* ```line.double.intersect.T_TOP```: ```╦```
* ```line.double.intersect.T_BOTTOM```: ```╩```
* ```line.double.intersect.CROSS```: ```╬```
* ```block.shade_light```: ```░```
* ```block.shade_med```: ```▒```
* ```block.shade_dark```: ```▓```
* ```block.v100```: ```█```
* ```block.h100```: ```█```
* ```block.full```: ```█```
* ```block.v13```: ```▁```
* ```block.v25```: ```▂```
* ```block.v38```: ```▃```
* ```block.v50```: ```▄```
* ```block.v63```: ```▅```
* ```block.v75```: ```▆```
* ```block.v88```: ```▇```
* ```block.h13```: ```▏```
* ```block.h25```: ```▎```
* ```block.h38```: ```▍```
* ```block.h50```: ```▌```
* ```block.h63```: ```▋```
* ```block.h75```: ```▊```
* ```block.h88```: ```▉```
* ```block.v13_upper```: ```▔```
* ```block.v50_upper```: ```▀```
* ```block.h13_right```: ```▕```
* ```block.h50_right```: ```▐```
* ```block.quad3```: ```▖```
* ```block.quad4```: ```▗```
* ```block.quad1```: ```▘```
* ```block.quad134```: ```▙```
* ```block.quad14```: ```▚```
* ```block.quad12```: ```▀```
* ```block.quad123```: ```▛```
* ```block.quad124```: ```▜```
* ```block.quad2```: ```▝```
* ```block.quad24```: ```▐```
* ```block.quad23```: ```▞```
* ```block.quad234```: ```▟```
* ```block.quad1234```: ```█```
* ```block.quad34```: ```▄```
* ```block.quad13```: ```▌```


#  Blessed Window Kit (bwk)


 A simple Python windowing kit module for use with _**Blessed**_ terminal formatting.

* Documentation:   [https:blessed.readthedocs.io/en/1.20.0/https:blessed.readthedocs.io/en/1.20.0/](https:blessed.readthedocs.io/en/1.20.0/)
* Source Code:     [https:github.com/jquast/blessedhttps:github.com/jquast/blessed](https:github.com/jquast/blessed)


- Use in your project by importing the module:
  - ```from bwk import Border, Window, echo, flush```


## ``` bwk```


**Constants:**

* ```DEFAULT_WINDOW_BORDER```: a ```borderchar```string (see ```Border```object) which uses the solid line Unicode characters


#### ``` def echo(str, end='', flush=False):```

**Parameters:**

* ```str```: the string to output
* ```end```: a suffix to append to the string
* ```flush```: whether or not to flush the buffer of the output stream



 A convenience method for working with _**Blessed**_ . This method will use the built-in Python ```print()```method but with the above parameters defaulted differently. It is intended to be used as a way to buffer output for the current outstream without flushing it.

#### ``` def flush():```


 A convenience method which flushes the current output stream. Equivalent to ``` print('', end='', flush=True) ``` .

#### ``` def window_shopper(termstuff, *args, **kwargs):```

**Parameters:**

* ```termstuff```: a function to execute within a _**Blessed**_ terminal context
* ```args```: arguments to be passed to ```termstuff```
* ```kwargs```: keyword arguments to be passed to ```termstuff```



 This method provides a default terminal context to preview window layouts. The terminal context provided is A fullscreen ```blessed.Terminal```with a hidden cursor. Pressing any key ends the terminal context.


 The ```termstuff```function must receive the ```Terminal```object as its first argument, it must include ```*args```and ```**kwargs```, and has no return value.

```
def mywindowfunc(term, *args, **kwargs):
   # do window layout here

```


**NOTE:**  The terminal context will automaticaly flush the stream, so you do not need to use the ```flush()```method, only ```echo()```.


## ``` class Border:```



 A class for defining the borders of a ```Window```object.

**Attributes:**

* ```upper_left_corner```: ( **default:** ```''```)
* ```top_border```: ( **default:** ```''```)
* ```upper_right_corner```: ( **default:** ```''```)
* ```right_border```: ( **default:** ```''```)
* ```lower_right_corner```: ( **default:** ```''```)
* ```bottom_border```: ( **default:** ```''```)
* ```lower_left_corner```: ( **default:** ```''```)
* ```left_border```: ( **default:** ```''```)


#### ``` def __init__(self, borderchars=''):```

**Parameters:**

* ```borderchars```: a string of characters to use for the border



 Creates a new ```Border```object. The ```borderchars```string must be exactly 8 characters long. If it is not, then no border is used. The characters form the corners and sides starting with the upper left corner and going clockwise.


 **Example:** ```borderchars='1=2:3+4|```'

```
1=====2
|     :
|     :
4+++++3

```



 You can also directly alter any of the border characters via the object's attributes (see **Attributes** above)

## ``` class Window:```



 A rectangular container for displaying content.

**Constants:**

* ```TITLE_ALIGNS```: A set of strings to identify the alignment of the window ```title```


**Attributes:**

* ```term```: the ```blessed.Terminal```object used for display
* ```x```: the column of the terminal that the upper left corner is placed
* ```y```: the row of the terminal that the upper left corner is placed
* ```height```: the total height of the window (including the borders)
* ```width```: the total width of the window (including borders)
* ```border```: a ```Border```object which defines the border display of the window
* ```title```: the title displayed at the top of the window
* ```title_align```: the alignment of the ```title```
* ```content```: a string of characters to display in the window


**NOTE:**  When a ```Window```is defined, there must be content added to it, either by setting the ```content```attribute directly, or by overriding the ```render_content()```method. The window is not buffered to the output stream unless the ```render()```method is called.


#### ``` def __init__(self, term, x, y, height=None, width=None, fore_color=None, bg_color=None, border=DEFAULT_WINDOW_BORDER, title='', title_align='center'):```

**Parameters:**

* ```term```: the ```blessed.Terminal```object used for display
* ```x```: the column of the terminal that the upper left corner is placed
* ```y```: the row of the terminal that the upper left corner is placed
* ```height```: the total height of the window (including the borders)
* ```width```: the total width of the window (including borders)
* ```fore_color```: _not yet implemented_
* ```bg_color```: _not yet implemented_
* ```border```: a ```borderchar```string or a ```Border```object
* ```title```: the title displayed at the top of the window
* ```title_align```: the alignment of the ```title```



 If ```height```or ```width```is not provided, then that dimension will stretch all the way to the edge of the terminal. If ```border```is set to ```None```, then no border will be drawn. The ```title```will not be displayed unless there is a ```border```with at least the ```top_border```attribute set. The ```title_align```string must be one of the following values: ```left```, ```center```, or ```right```.

#### ``` def render(self):```


 Echoes the window to the output stream buffer (via the ```echo()```function). The content of the window is limited by the ```height```and ```width```of the window (minus the height and width of the border). Any characters which are beyond the dimensions of the window will not be displayed.

#### ``` def render_content(self, max_width, max_height):```

**Parameters:**

* ```max_width```: the total width of the window (minus the width of the borders)
* ```max_height```: the total height of the window (minus the height of the borders)


**Returns:**  A string or list of strings which will fit in the dimensions of the window.



 This method is provided to be overriden, either by overwriting the instance attrbute ```render_content```with a different function, or by overriding this function in a subclass. By default, this method simply returns the window's ```content```string.


 This method is called by the ```render()```method. If the return value is a string, ```render()```will iterate over the each line (delimited by a ```n```). If the return value is a list of strings, ```render()```will iterate over the list.

#  Screen Tools (bwk.screen)


 An extension of the Blessed Window Kit (BWK) for quickly building Text-Based User Interface (TUI) applications.

- Use in your project by importing the module:
  - ```import bwk.screen```


## ``` class Screen:```



 A barebones implementation for rendering output to a terminal and processing input. This can be used with or without the BWK itself (see the ```ScreenManager```classes below).

**Attributes:**

* ```man```: a ```ScreenManager```which the sceen is associated to.
* ```name```: the name of the screen
* ```commands```: a dictionary of commands. Each value is a function which will execute when input matching its key is received by the screen.


#### ``` def __init__(self, manager, name, commands={}):```

**Parameters:**

* ```man```: a ```ScreenManager```which the sceen is associated to.
* ```name```: the name of the screen
* ```commands```: a dictionary of commands. Each value is a function which will execute when input matching its key is received by the screen.


#### ``` def set_commands(self):```


 Identify keystrokes which correlate with logic to trigger. This should be overridden in subclasses to set the keys used in that mode.


 For each keyboard key, set a string as the key in the ```self.commands```dictionary and the value as a method to call when the key is pressed. The method which is called is passed no arguments (except ```self```, if it is a class method).

```
self.commands['h'] = self.show_help

```



 If the key is a Unicode printable character, such as a letter, number, or punctuation, use the string representation of that key.

**NOTE:**  The Space key is a printable character ( ```'```' ) and should be indicated as such.


**NOTE:**  If an uppercase (or otherwised altered via the Shift key, such as the symbols on number keys) is used, this will require the Shift key to be held when pressing the key to get a ppropriate result. This is an easy way to incorporate modifier keys by using Shift as the modifier.



 If the key is a non-printable character (such as the Backspace, Enter, Esc, F# keys), the name of the constant as defined in the Blessed documentation is used. See the documentation for a list of supported names: [https:blessed.readthedocs.io/en/latest/keyboard.html#keycodeshttps:blessed.readthedocs.io/en/latest/keyboard.html#keycodes](https:blessed.readthedocs.io/en/latest/keyboard.html#keycodes)

**NOTE:**  Keys on the numpad are different keycodes, and as such, they can only be identified by the name of their constant. All numpad key constants start with ```Key_KP_```. For example, ```9```on the numpad would be ```KEY_KP_9```.




| Line | Key(s) |
|---|---|
| self.commands['h'] = self.show_help_ | ```H``` |
| self.commands['H'] = self.show_help_ | ```Shift```+ ```H``` |
| self.commands['KEY_ESCAPE'] = self.back | ```Esc``` |
| self.commands['9'] = self.options | ```9``` |
| self.commands['KEY_KP_9'] = self.options | ```9```on the numpad |
| self.commands[' '] = self.select_item_ | ```Space``` |
| self.commands['1'] = self.action_menu_ | ```1``` |
| self.commands['!'] = self.second_menu_ | ```Shift```+ ```1``` |


#### ``` def pre_process_input(self, userin):```

**Parameters:**

* ```userin```: the input received to the screen



 This method executes after a screen receives input, but before a matching command is executed.

#### ``` def post_process_input(self, userin):```

**Parameters:**

* ```userin```: the input received to the screen



 This method executes after the input has been processed by the screen (which may be a command, or an error).

#### ``` def process_input(self, userin):```

**Parameters:**

* ```userin```: the input received to the screen



 This is the main method which handles input to the screen. It executes the following steps in order:

* ```self.pre_process_input(userin)```
* if ```userin```matches a key in ```self.commands,```executes the corresponding function
* if ```userin```does not match a key in ```self.commands,```executes ```self.process_input_error(userin)```
* ```self.post_process_input(userin)```


#### ``` def process_input_error(self, userin):```

**Parameters:**

* ```userin```: the input received to the screen



 This method executes when an input does not match any key in ```self.commands```.

#### ``` def render(self):```


 This method will render the screen content to the terminal.

## ``` class GenericScreenManager:```



 A barebones implementation for running an application loop with ```Screen```objects.

**Attributes:**

* ```running```: a boolean indicating if the run loop should continue
* ```curr_screen```: the current ```Screen```object being used to render output and process input


#### ``` def __init__(self):```


 Initializes the manager.

#### ``` def pre_run(self):```


 This method executes before the run loop begins. Any necessary preparation before the application starts should be done here. By default it is empty and should be overridden as necessary.

#### ``` def post_run(self):```


 This method executes after the run loop ends. Any necessary cleanup after the application has ended should be done here. By default it is empty and should be overridden as necessary.

#### ``` def run(self):```


 This is the main entrypoint for the ```ScreenManager```. Execute this method to begin the application loop, including the ```pre_run()```and ```post_run()```methods.

#### ``` def run_loop(self):```


 The actual implementation of the application loop. It executes the follwoing setps in order:

* ```self.pre_render()```
* ```self.render()```
* ```self.pre_input()```
* ```self.get_user_input()```
* ```self.process_input(userin)```



 The above steps will continue to execute until ```self.running```is False or there is no ```self.curr_screen```set (meaning that there is no way to display output or process input).

#### ``` def pre_render(self):```


 This method executes at the beginning of each iteration of the run loop before the screen is rendered. By default it is empty and should be overridden as necessary.

#### ``` def render(self):```


 Renders output to the terminal. This is typically done via ```self.curr_screen.render()```.

#### ``` def pre_input(self):```


 This method executes after each iteration of the render in the run loop, but before user input is received. By default it is empty and should be overridden as necessary.

#### ``` def get_user_input(self):```


 Gets input from the user. This can be overriden for specific input types. By default, it uses Python's ```input()```method.

#### ``` def process_input(self, userin):```

**Parameters:**

* ```userin```: the input received from the user



 Processes the input received from the user. This is typically done via ```self.curr_screen.process_input(userin)```.

#### ``` def quit(self):```


 A convenience method for ending the application loop. By default, this simply sets ```self.running```to False.

#### ``` def handle_crash(self, exc):```

**Parameters:**

* ```exc```: the ```Exception```raised during the application loop



 In the event that an exception is not caught during the application loop, the manager will gracefully catch the exception and handle it here. After this method executes, the ```post_run()```method executes, to ensure that all necessary cleanup is still performed despite the program crashing.

## ``` class BwkScreenManager(GenericScreenManager):```



 A screen manager specifically for handling screens which utilize the Blessed Window Kit for rendering.

**Attributes:**

* ```running```: a boolean indicating if the run loop should continue
* ```curr_screen```: the current ```Screen```object being used to render output and process input
* ```term```: the ```blessed.Terminal```object used to render screens
* ```auto_flush```: if set to ```True```, a ```bwk.flush()```is called after the current screen is rendered


#### ``` def __init__(self, term=None, auto_flush=False):```

**Parameters:**

* ```term```: the ```blessed.Terminal```object used to render screens
* ```auto_flush```: if set to ```True```, a ```bwk.flush()```is called after the current screen is rendered



 Initializes the screen manager. If no ```term```is provided, a default ```Terminal()```is used.

#### ``` def run(self):```


 Overrides the original ```run()```with special context managers for BWK:

* ```term.fullscreen```
* ```term.cbreak```
* ```term.hidden_cursor```



 This ensures that the application behaves similarly to the ```window_shopper()```method provided by the BWK to simplify building and managing screens.

#### ``` def get_user_input(self):```


 Overrides this method to use the ```self.term.inkey()```method to get user input, rather than the default Python ```input()```method.

#### ``` def process_input(self, userin):```


 Overrides this method to ensure that proper key name is sent to the screen's ```process_input()```method.

#### ``` def render(self):```


 Renders output to the terminal. This is typically done via ```self.curr_screen.render()```. If ```self.auto_flush=True```, then a ```flush()```command is issued after the current screen is rendered.

#  Utilities ( bwk.utils )


 Helper methods and convenience functions for bwk.

#### ``` def get_dimensions(multiline_string):```


 Returns the width and height of a multiline string. This is helpful when trying to find the size to place it in a window or position it on the screen.

**Parameters:**

* ```multiline_string```: the string (which can be 1 or more lines) to get dimensions from


**Returns:**  A tuple of (width, height) in characters.



 The width returned is the maximum width found in the entire string. The height is simply the number of lines in the string.

#### ``` def margin(multiline_string, top=0, right=0, bottom=0, left=0, all=None):```


 Returns a new string with empty spaces added to it according to the margins provided.

**Parameters:**

* ```multiline_string```: the string to add the margins to
* ```top```: an integer indicating how many empty lines to add at the top
* ```right```: an integer indicating the number of empty spaces to add at the end of each line
* ```bottom```: an integer indicating how many empty lines to add at the bottom
* ```left```: an integer indicating the number of empty spaces to add at the beginning of each line
* ```all```: an integer which sets all 4 margins ( ```top```, ```right```, ```bottom```, ```left```) to the same value


**Returns:**  A new multiline string with the margins added to it.


**NOTE:**  The ```all```argument will override any individual margin values provided.


#### ``` def adjust_index(index, min=None, max=None, iterable=None, wrap=False):```


 Returns an index which is valid for the min and max provided.

**Parameters:**

* ```index```: the value to check and adjust
* ```min```: the minimum value allowed
* ```max```: the maximum value allowed
* ```iterable```: an object which has a ```__len__```attribute to inform the ```min```and ```max```values
* ```wrap```: a boolean indicating if the index should wrap around from max to min and vice versa


**Returns:**  A new index value adjusted by the provided data


**NOTE:**  The ```iterable```argument will override any ```min```or ```max```value provided.



 This method checks the provided ```index```value and returns a new value based on whether it resides within the ```min```and ```max```. If an ```iterable```is provided, the ```min```and ```max```values are disregarded and the size of the ```iterable```is used instead. The returned value will be the same ```index```value, unless the ```index```value provided does not fit within the ```min```and ```max```( or the min/max values of an ```iterable```).


 When ```wrap```is set to ```True```, the returned value will "wrap around" when the ```index```is outside the acceptable range. This means that an ```index```below the ```min```will be reset to the ```max```value, and an ```index```above the ```max```will be reset to the ```min```value. If ```wrap```is ```False```, an ```index```below the ```min```is reset to the ```min,```and an ```index```above the ```max```is reset to ```max```.