Advertisement
javajeff13

TUI

Jan 24th, 2022 (edited)
69
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.40 KB | None | 0 0
  1. import utils
  2. from math import ceil
  3. from pathlib import Path
  4. from asciimatics.screen import Screen
  5. from asciimatics.scene import Scene
  6. from asciimatics.widgets import Frame, TextBox, Layout, CheckBox, Button
  7. from asciimatics.exceptions import ResizeScreenError, StopApplication
  8. from asciimatics.event import KeyboardEvent
  9.  
  10. RESULTS_FILE = (
  11.     "/Users/jwr003/coding/pytest-fold/output_files_to_analyze/outputvvfold.ansi"
  12. )
  13. DEBUG = True
  14.  
  15.  
  16. class ResultsData:
  17.     """
  18.    Class to read in results from a 'pytest --fold' session (which inserts markers
  19.    around each failed test), and tokenize the results into individual sections for
  20.    display on the TUI
  21.    """
  22.  
  23.     def __init__(self, path: Path = RESULTS_FILE) -> None:
  24.         self.results_file = path
  25.         self.sections = []
  26.  
  27.     def _tokenize_results(self) -> None:
  28.         with open(self.results_file, "r") as results_file:
  29.             results_lines = results_file.readlines()
  30.         self.sections = utils.tokenize(results_lines)
  31.  
  32.     def get_results(self) -> list:
  33.         self._tokenize_results()
  34.         return self.sections
  35.  
  36.  
  37. class ResultsLayout(Layout):
  38.     """
  39.    This Layout handles both folded and unfolded results. There are two columns:
  40.    1) a checkbox (height:1) to fold/unfold the results textbox
  41.    2) a textbox (height:[1 | N]) to display data; height:1 => "folded" results,
  42.       height:N => "unfolded" results
  43.    """
  44.  
  45.     def __init__(
  46.         self,
  47.         screen: Screen,
  48.         folded: bool = True,
  49.         textboxheight: int = 4,
  50.         value: str = "No data!",
  51.     ) -> None:
  52.         super(ResultsLayout, self).__init__(columns=[4, screen.width - 6])
  53.         self.textboxheight = textboxheight
  54.         self.value = value
  55.         self.folded = folded
  56.  
  57.     def add_widgets(self) -> None:
  58.         cb = CheckBox(text="", on_change=self._toggle_checkbox)
  59.         self.add_widget(cb, column=0)
  60.         if not self.folded:
  61.             tb = TextBox(height=self.textboxheight, line_wrap=True, readonly=True)
  62.             tb.value = self.value
  63.             self.add_widget(tb, column=1)
  64.  
  65.     def _toggle_checkbox(self) -> None:
  66.         self.folded = not self.folded
  67.         self.clear_widgets()
  68.         self.add_widgets()
  69.         self._frame.fix()
  70.  
  71.  
  72. class QuitterLayout(Layout):
  73.     """
  74.    Layout class to quit the whole application
  75.    """
  76.  
  77.     def __init__(self, screen: Screen) -> None:
  78.         super(QuitterLayout, self).__init__(columns=[4, screen.width - 6])
  79.  
  80.     def add_widgets(self) -> None:
  81.         self.add_widget(Button(text="Quit", on_click=self._quit), 1)
  82.  
  83.     def _quit(self) -> None:
  84.         raise StopApplication("User requested exit by clicking 'Quit'")
  85.  
  86.  
  87. class ResultsFrame(Frame):
  88.     def __init__(self, screen: Screen) -> None:
  89.         super(ResultsFrame, self).__init__(
  90.             screen=screen, height=screen.height, width=screen.width
  91.         )
  92.  
  93.         # Snarf data from results file, tokenize, then add Layout for the resulting
  94.         # sections to the ResultsFrame
  95.         results_data = ResultsData()
  96.         sections = results_data.get_results()
  97.  
  98.         # First layout section: "header" info from Pytest
  99.         self.add_layout(
  100.             ResultsLayout(
  101.                 screen=screen,
  102.                 folded=False,
  103.                 textboxheight=ceil(len(sections[0]) / screen.width),
  104.                 # value="Come to the aid of the party.",
  105.                 value=sections[0],
  106.             )
  107.         )
  108.  
  109.         # Individual folded layouts, one per failure section from Pytest run
  110.         for _ in range(1, len(sections) - 1):
  111.             self.add_layout(
  112.                 ResultsLayout(
  113.                     screen=screen,
  114.                     folded=True,
  115.                     textboxheight=ceil(len(sections[_]) / screen.width),
  116.                     value=sections[_],
  117.                 )
  118.             )
  119.  
  120.         # Last layout sections: "summary" info from Pytest, plus the quit button
  121.         self.add_layout(
  122.             ResultsLayout(
  123.                 screen=screen,
  124.                 folded=False,
  125.                 textboxheight=ceil(len(sections[-1]) / screen.width),
  126.                 value=sections[-1],
  127.             )
  128.         )
  129.         self.add_layout(QuitterLayout(screen))
  130.  
  131.         # Add widgets to all layouts (needs to be done after layouts are added to frame)
  132.         for layout in self._layouts:
  133.             layout.add_widgets()
  134.  
  135.         # Set color theme; fix the layouts and calculate locations of all widgets
  136.         self.set_theme("monochrome")
  137.         self.fix()
  138.         print("Stub")
  139.  
  140.  
  141. def global_shortcuts(event):
  142.     # Event handler for global keys, used here to quit app with Ctrl keys
  143.     if isinstance(event, KeyboardEvent):
  144.         code = event.key_code
  145.         # Stop on ctrl+q or ctrl+x
  146.         if code in (17, 24):
  147.             raise StopApplication(f"User terminated app with {code}")
  148.  
  149.  
  150. def demo(screen: Screen, scene: Scene) -> None:
  151.     scenes = [Scene([ResultsFrame(screen)], duration=-1)]
  152.     screen.play(
  153.         scenes,
  154.         stop_on_resize=True,
  155.         start_scene=scenes[0],
  156.         allow_int=True,
  157.         unhandled_input=global_shortcuts,
  158.     )
  159.  
  160.  
  161. def main():
  162.     last_scene = None
  163.     while True:
  164.         try:
  165.             Screen.wrapper(demo, catch_interrupt=True, arguments=[last_scene])
  166.             quit()
  167.         except ResizeScreenError as e:
  168.             last_scene = e.scene
  169.  
  170.  
  171. if __name__ == "__main__":
  172.     main()
  173.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement