| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134 |
- """show_runnable_code_cell: a Jupyter notebook like code cell
- * Displays an editable code cell
- * A Run button
- * An indication (*) whether the code was modified since it was last run.
- * The result of the last run is displayed below the code cell
- (you can provide a custom renderer for the result)
- This is very much a work in progress.
- """
- import sys
- import io
- from typing import Any, Callable
- from imgui_bundle import immapp, imgui_md, imgui, imgui_ctx, hello_imgui, ImVec2
- import textwrap
- from dataclasses import dataclass
- class _NoResult:
- pass
- _NoResultValue = _NoResult()
- ResultRenderer = Callable[[Any], None]
- def _default_result_renderer(result: Any) -> None:
- as_string = str(result)
- md_string = "```\n" + as_string + "\n```"
- imgui_md.render(md_string)
- class _CaptureStdout(list[str]):
- def __enter__(self) -> "_CaptureStdout":
- self._stdout = sys.stdout
- sys.stdout = self._stringio = io.StringIO()
- return self
- def __exit__(self, *args: Any) -> None:
- self.extend(self._stringio.getvalue().splitlines())
- sys.stdout = self._stdout
- def _execute_and_capture_last_expr(code: str) -> Any | _NoResult:
- with _CaptureStdout() as output:
- code = textwrap.dedent(code).strip() # De-indent and strip code
- lines = code.splitlines()
- if not lines:
- return None
- last_line = lines[-1]
- try:
- exec(code, globals(), locals()) # Execute the entire code block
- except Exception as e:
- return f"Error:\n{e}"
- try:
- last_line_result = eval(last_line, globals(), locals()) # Evaluate the last line
- except Exception:
- last_line_result = _NoResult()
- return last_line_result
- @dataclass
- class RunnableCodeCellResult:
- code: str = ""
- last_run_code: str = ""
- was_last_edited_code_ran: bool = False # simply indicates if last_run_code == code
- result: Any = _NoResultValue
- was_just_run: bool = False
- def show_runnable_code_cell(label_id: str, code: str = "", result_renderer: ResultRenderer | None = None) -> RunnableCodeCellResult:
- statics = show_runnable_code_cell
- @dataclass
- class CodeAndResult:
- snippet_data: immapp.snippets.SnippetData
- result: Any
- last_ran_code: str
- if not hasattr(statics, "s_code_cells"):
- statics.s_code_cells: dict[str, CodeAndResult] = {} # type: ignore
- if label_id not in statics.s_code_cells:
- snippet_data = immapp.snippets.SnippetData()
- snippet_data.code = code
- snippet_data.height_in_lines = code.count("\n")
- snippet_data.palette = immapp.snippets.SnippetTheme.dark
- statics.s_code_cells[label_id] = CodeAndResult(snippet_data, _NoResult(), "")
- code_and_result = statics.s_code_cells[label_id]
- runnnable_code_cell_result = RunnableCodeCellResult()
- with imgui_ctx.push_id(label_id):
- original_cur_pos = imgui.get_cursor_screen_pos()
- visible_label = label_id if "##" not in label_id else label_id.split("##")[0]
- imgui.separator_text(visible_label)
- # Add "Run" button at the end of the separator line
- cur_pos_after_separator = imgui.get_cursor_screen_pos()
- x_end = imgui.get_item_rect_max().x
- x_end = x_end - hello_imgui.em_size(3.5)
- imgui.set_cursor_screen_pos(ImVec2(x_end, original_cur_pos.y))
- if imgui.button("Run"):
- code_and_result.result = _execute_and_capture_last_expr(code_and_result.snippet_data.code)
- code_and_result.last_ran_code = code_and_result.snippet_data.code
- runnnable_code_cell_result.was_just_run = True
- if code_and_result.last_ran_code != code_and_result.snippet_data.code:
- imgui.same_line()
- imgui.text("*")
- imgui.set_cursor_screen_pos(cur_pos_after_separator)
- immapp.snippets.show_editable_code_snippet(label_id, code_and_result.snippet_data)
- runnnable_code_cell_result.code = code_and_result.snippet_data.code
- runnnable_code_cell_result.last_run_code = code_and_result.last_ran_code
- runnnable_code_cell_result.result = code_and_result.result
- runnnable_code_cell_result.was_last_edited_code_ran = code_and_result.last_ran_code == code_and_result.snippet_data.code
- if not isinstance(code_and_result.result, _NoResult):
- if result_renderer is None:
- result_renderer = _default_result_renderer
- result_renderer(code_and_result.result)
- imgui.new_line()
- return runnnable_code_cell_result
|