from __future__ import annotations
from .pane import Pane
from .colum import Column
from ..can import Message, MessageType, MessageTable
import curses
[docs]class MessagePane(Pane):
"""
A derivative of Pane customized specifically to list miscellaneous CAN
messages stored in a MessageTable
:param name: The name of the pane (to be printed in the top left)
:type name: str
:param cols: A dictionary describing the pane layout. The key is the Pane
collumn name, the value is a tuple containing the Message attribute to
map the collumn to, and the max collumn width respectively.
:type cols: dict
:param selected: An indicator that the current Pane is selected
:type selected: bool
:param table: The message table
:type table: MessageTable
"""
def __init__(self: MessagePane,
cols: [Column],
types: [MessageType],
name: str = '',
parent: any = None,
height: int = 1,
width: int = 1,
y: int = 0,
x: int = 0,
message_table: MessageTable = MessageTable()):
super().__init__(parent=(parent or curses.newpad(0, 0)),
height=height,
width=width,
y=y,
x=x)
# Pane details
self._name = name
self.cols = cols
self.types = types
self.__top = 0
self.__top_max = 0
self.__header_style = curses.color_pair(4)
self.table = message_table
# Cursor stuff
self.cursor = 0
self.cursor_min = 0
self.cursor_max = self.d_height - 10
[docs] def clear_messages(self: MessagePane) -> None:
"""
Clears the message table for the pane.
"""
self.table.clear()
[docs] def resize(self: MessagePane, height: int, width: int) -> None:
"""
A wrapper for `Pane.resize()`. This intercepts a call for a resize
in order to upate MessagePane-specific details that change on a resize
event. The parent `resize()` gets called first and then MessagePane's
details are updated.
:param height: New virtual height
:type height: int
:param width: New virtual width
:type width: int
"""
super().resize(height, width)
p_height = self.d_height - 3
table_size = len(self.table.filter(self.types))
occluded = table_size - self.__top - self.d_height + 3
self.cursor_max = table_size if table_size < p_height else p_height
self.__top_max = occluded if occluded > 0 else 0
@property
def scroll_limit_y(self: MessagePane) -> int:
"""
The maximim rows the pad is allowed to shift by when scrolling
"""
return self.d_height - 2
@property
def scroll_limit_x(self: MessagePane) -> int:
"""
The maximim columns the pad is allowed to shift by when scrolling
"""
max_length = sum(list(map(lambda x: x.length, self.cols)))
occluded = max_length - self.d_width + 7
return occluded if(occluded > 0) else 0
def __draw_header(self: Pane) -> None:
"""
Draw the table header at the top of the Pane
This uses the `cols` dictionary to determine what to write
"""
self.add_line(f'{self._name}:'
f' ({len(self.table.filter(self.types))} messages)',
y=0,
x=1,
highlight=self.selected)
self._pad.move(1, 1)
for col in self.cols:
self.add_line(col.header,
highlight=True,
color=curses.color_pair(4))
[docs] def draw(self: MessagePane) -> None:
"""
Draw all records from the MessageTable to the Pane
"""
super().draw()
self.resize(self.v_height, self.v_width)
# Get the messages to be displayed based on scroll positioning,
# and adjust column widths accordingly
draw_messages = self.table.filter(self.types, self.__top, self.__top + self.d_height - 3)
self.__check_col_widths(draw_messages)
# Draw the header and messages
self.__draw_header()
for i, message in enumerate(draw_messages):
self._pad.move(2 + i, 1)
for col in self.cols:
self.add_line(col.format(message),
highlight=((self.cursor == i) and self.selected))
# Refresh the Pane and end the draw cycle
super().refresh()
def __check_col_widths(self: MessagePane, messages: [Message]) -> None:
"""
Check the width of the message in Pane column.
:param messages: The list of the messages
:type messages: list
"""
for col in self.cols:
for message in messages:
if(col.update_length(message)):
self._pad.clear()