Source code for GLXCurses.MessageBar

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# It script it publish under GNU GENERAL PUBLIC LICENSE
# http://www.gnu.org/licenses/gpl-3.0.en.html
# Author: the Galaxie Curses Team, all rights reserved

import GLXCurses
import curses
import logging
import locale

locale.setlocale(locale.LC_ALL, "")
code = locale.getpreferredencoding()


[docs]class MessageBar(GLXCurses.Widget): """ A MessageBar is usually placed along the bottom of an Application. It may provide a regular commentary of the application's status (as is usually the case in a web browser, for example), or may be used to simply output a message when the status changes, (when an upload is complete in an FTP client, for example). Status bars in GLXCurses maintain a stack of messages. The message at the top of the each bar’s stack is the one that will currently be displayed. Any messages added to a StatusBar’s stack must specify a context id that is used to uniquely identify the source of a message. This context id can be generated by :func:`GLXCurses.StatusBar.get_context_id() <GLXCurses.StatusBar.StatusBar.get_context_id>`, given a message and the StatusBar that it will be added to. Note that messages are stored in a stack, and when choosing which message to display, the stack structure is adhered to, regardless of the context identifier of a message. One could say that a StatusBar maintains one stack of messages for display purposes, but allows multiple message producers to maintain sub-stacks of the messages they produced (via context ids). Status bars are created using :func:`GLXCurses.MessageBar.new() <GLXCurses.MessageBar.MessageBar.new>`. Messages are added to the bar’s stack with :func:`GLXCurses.MessageBar.push() <GLXCurses.MessageBar.MessageBar.push>`. The message at the top of the stack can be removed using :func:`GLXCurses.MessageBar.pop() <GLXCurses.MessageBar.MessageBar.pop>`. A message can be removed from anywhere in the stack if its message id was recorded at the time it was added. This is done using :func:`GLXCurses.MessageBar.remove() <GLXCurses.MessageBar.StatusBar.remove>`. """ def __init__(self): # Load heritage GLXCurses.Widget.__init__(self) # self.foreground_color_normal = self.style.get_attributes_rgb_color("text", "STATE_NORMAL") # self.background_color_normal = self.style.get_attributes_rgb_color("black", "STATE_NORMAL") # It's a GLXCurse Type self.glxc_type = "GLXCurses.MessageBar" self.name = "{0}{1}".format(self.__class__.__name__, self.id) # Widget Setting self.messagebar_stack = list() self.context_id_dict = dict()
[docs] def new(self): """ Creates a new :func:`GLXCurses.MessageBar <GLXCurses.MessageBar.MessageBar>` ready for messages. :return: the new MessageBar :rtype: GLXCurses.MessageBar """ self.__init__() return self
[docs] def get_context_id(self, context_description="Default"): """ Returns a new context identifier, given a description of the actual context. .. note: the description is not shown in the UI. :param context_description: textual description of what context the new message is being used in :type context_description: str :return: an context_id generate by Utils.new_id() :rtype: str :raises TypeError: When context_description is not a str """ # Try to exit as soon of possible if type(context_description) != str: raise TypeError('"context_description" must be a str or unicode type') # If we are here everything look ok if context_description not in self._get_context_id_list(): self._get_context_id_list()[context_description] = GLXCurses.new_id() logging.debug( "MessageBar CONTEXT CREATION: context_id={0} context_description={1}".format( self._get_context_id_list()[context_description], str(context_description), ) ) return self._get_context_id_list()[context_description]
[docs] def push(self, context_id, text): """ Push a new message onto the MessageBar's stack. :param context_id: a context identifier, as returned by MessageBar.get_context_id() :type context_id: str :param text: the message to add to the MessageBar :type text: str :return: a message identifier that can be used with MessageBar.remove(). :rtype: str """ # Try to exit as soon of possible if not GLXCurses.is_valid_id(context_id): raise TypeError( '"context_id" must be a str type as returned by MessageBar.get_context_id()' ) if type(text) != str: raise TypeError('"text" must be a str type') # If we are here everything look ok message_id = GLXCurses.new_id() message_info = dict() message_info["context_id"] = context_id message_info["message_id"] = message_id message_info["text"] = text self._get_messagebar_stack().append(message_info) self._emit_text_pushed(context_id, text) return message_id
[docs] def pop(self, context_id): """ Removes the first message in the MessageBar’s stack with the given context id. Note that this may not change the displayed message, if the message at the top of the stack has a different context id. :param context_id: a context identifier, as returned by MessageBar.get_context_id() :type context_id: str """ # Try to exit as soon of possible if not GLXCurses.is_valid_id(context_id): raise TypeError( '"context_id" must be a unicode type see MessageBar.get_context_id()' ) # If we are here everything look ok count = 0 last_found = None last_element = None for element in self._get_messagebar_stack(): if context_id == element["context_id"]: last_found = count last_element = element count += 1 if last_found is not None: self._get_messagebar_stack().pop(last_found) self._emit_text_popped(last_element["context_id"], last_element["text"])
[docs] def remove(self, context_id, message_id): """ Forces the removal of a message from a MessageBar’s stack. The exact **context_id** and **message_id** must be specified. :param context_id: a context identifier, as returned by MessageBar.get_context_id() :type context_id: str :param message_id: a message identifier, as returned by MessageBar.push() :type message_id: str """ # Try to exit as soon of possible if not GLXCurses.is_valid_id(context_id): raise TypeError( '"context_id" arguments must be unicode type as returned by MessageBar.get_context_id()' ) if not GLXCurses.is_valid_id(message_id): raise TypeError( '"message_id" arguments must be unicode type as returned by MessageBar.push()' ) # If we are here everything look ok count = 0 last_found = None last_element = None for element in self._get_messagebar_stack(): if ( context_id == element["context_id"] and message_id == element["message_id"] ): last_found = count last_element = element count += 1 if last_found is not None: logging.debug( "MessageBar REMOVE: index={0} context_id={1} message_id={2} text={3}".format( str(last_found), str(last_element["context_id"]), str(last_element["message_id"]), str(last_element["text"]), ) ) self._get_messagebar_stack().pop(last_found)
[docs] def remove_all(self, context_id): """ Forces the removal of all messages from a MessageBar's stack with the exact context_id . :param context_id: a context identifier, as returned by MessageBar.get_context_id() :type context_id: str """ # Try to exit as soon of possible if not GLXCurses.is_valid_id(context_id): raise TypeError( '"context_id" arguments must be unicode type as returned by MessageBar.get_context_id()' ) # If we are here everything look ok for element in self._get_messagebar_stack(): if context_id == element["context_id"]: self.remove(element["context_id"], element["message_id"])
# def _draw_background(self): # # self.subwin.addstr( # 0, # 0, # str(' ' * (self.width - 1)), # self.style.get_color_pair( # foreground=self.style.get_color_text('text', 'STATE_NORMAL'), # background=self.style.get_color_text('dark', 'STATE_NORMAL') # ) # ) # self.subwin.insstr( # 0, # self.width - 1, # str(' '), # self.style.get_color_pair( # foreground=self.style.get_color_text('text', 'STATE_NORMAL'), # background=self.style.get_color_text('dark', 'STATE_NORMAL') # ) # )
[docs] def draw(self): """ Place the status bar from the end of the stdscr by look if it have a tool bar before """ # Background for x_inc in range(self.x, self.width): try: self.stdscr.addstr( self.y, x_inc, str.encode(" ", encoding=code, errors="strict"), self.style.curses_color_pair_number(0, 0), ) except curses.error: # pragma: no cover pass # If it have something inside the StatusBar stack they display it but care about the display size if len(self._get_messagebar_stack()): for x_inc in range( self.x, len(str(self._get_messagebar_stack()[-1]["text"])) ): try: self.stdscr.addstr( self.y, x_inc, str.encode( self._get_messagebar_stack()[-1]["text"][x_inc], encoding=code, errors="strict", ), self.style.curses_color_pair_number(0, 0), ) except curses.error: # pragma: no cover pass
# Siganles def _emit_text_popped(self, context_id, text, user_data=None): """ Is emitted whenever a new message is popped off a StatusBar's stack. :param context_id: the context id of the relevant message/StatusBar :type context_id: str :param text: the message that was just popped :type text: str :param user_data: user __area_data set when the signal handler was connected. :type user_data: list """ if user_data is None: user_data = list() # Create a Dict with everything instance = { "class": self.__class__.__name__, "type": "text-popped", "id": self.id, "context_id": context_id, "text": text, "user_data": user_data, } # EVENT EMIT self.emit("SIGNALS", instance) def _emit_text_pushed(self, context_id, text, user_data=None): """ Is emitted whenever a new message is popped off a StatusBar's stack. :param context_id: the context id of the relevant message/StatusBar :type context_id: str :param text: the message that was just popped :type text: str :param user_data: user __area_data set when the signal handler was connected. :type user_data: list """ if user_data is None: user_data = list() # Create a Dict with everything instance = { "class": self.__class__.__name__, "type": "text-pushed", "id": self.id, "context_id": context_id, "text": text, "user_data": user_data, } # EVENT EMIT self.emit("SIGNALS", instance) # Internal Method's def _get_context_id_list(self): """ Return context_id_dict attribute :return: context_id_dict attribute :rtype: dict """ return self.context_id_dict def _get_messagebar_stack(self): """ Return messagebar_stack attribute :return: messagebar_stack attribute :rtype: list """ return self.messagebar_stack
[docs] def update_preferred_sizes(self): self.preferred_height = 1 self.preferred_width = self.width