Created
September 2, 2025 15:40
-
-
Save sjgallagher2/039baa89648dc57e3c318d8cbc4a7fc2 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import sys | |
| import os | |
| import time | |
| os.environ["QT_API"] = "pyside6" | |
| from qtpy import QtWidgets,QtCore | |
| from pyvistaqt import QtInteractor, MainWindow | |
| from pathlib import Path | |
| class MyMainWindow(MainWindow): | |
| def __init__(self, parent=None, show=True): | |
| QtWidgets.QMainWindow.__init__(self, parent) | |
| self.fname = '' | |
| self.fs_watcher = QtCore.QFileSystemWatcher([self.fname]) | |
| self._cached_geom_lines = '' | |
| # create the frame | |
| self.frame = QtWidgets.QFrame() | |
| vlayout = QtWidgets.QVBoxLayout() | |
| # add the pyvista interactor object | |
| self.plotter = QtInteractor(self.frame) | |
| vlayout.addWidget(self.plotter.interactor) | |
| self.signal_close.connect(self.plotter.close) | |
| self.frame.setLayout(vlayout) | |
| self.setCentralWidget(self.frame) | |
| # simple menu to demo functions | |
| main_menu = self.menuBar() | |
| file_menu = main_menu.addMenu('File') | |
| open_button = QtWidgets.QAction('Open', self) | |
| open_button.setShortcut('Ctrl+O') | |
| open_button.triggered.connect(self.file_open) | |
| file_menu.addAction(open_button) | |
| exit_button = QtWidgets.QAction('Exit', self) | |
| exit_button.setShortcut('Ctrl+Q') | |
| exit_button.triggered.connect(self.close) | |
| file_menu.addAction(exit_button) | |
| mesh_menu = main_menu.addMenu('Geometry') | |
| self.recompile_action = QtWidgets.QAction('Recompile', self) | |
| self.recompile_action.triggered.connect(self.recompile) | |
| mesh_menu.addAction(self.recompile_action) | |
| self.fs_watcher.fileChanged.connect(self.recompile) | |
| if show: | |
| self.show() | |
| self.update_geom_lines() | |
| self.execute_file() | |
| self.cam_pos_saved = self.plotter.camera.position | |
| self.cam_foc_saved = self.plotter.camera.focal_point | |
| self.cam_up_saved = self.plotter.camera.up | |
| self.last_update_time = time.time() | |
| def file_open(self): | |
| fname_sel = QtWidgets.QFileDialog.getOpenFileName(self, "Open Image", str(Path.home()), "Python Files (*.py)") | |
| fname_sel = fname_sel[0] | |
| if fname_sel != '': | |
| self.fname = fname_sel | |
| self.fs_watcher.removePaths(self.fs_watcher.files()) | |
| self.fs_watcher.addPath(self.fname) | |
| self.recompile() | |
| def recompile(self): | |
| # Recompile, with debounce for editors that delete and resave a file | |
| if self.fname == '': | |
| return | |
| current_time = time.time() | |
| if current_time - self.last_update_time > 1: | |
| did_update = self.update_geom_lines() | |
| if did_update: | |
| # Save camera state | |
| self.cam_pos_saved = self.plotter.camera.position | |
| self.cam_foc_saved = self.plotter.camera.focal_point | |
| self.cam_up_saved = self.plotter.camera.up | |
| # Clear | |
| self.plotter.clear_actors() | |
| # Run geometry scripts | |
| self.execute_file() | |
| # Restore camera state | |
| self.plotter.camera.position = self.cam_pos_saved | |
| self.plotter.camera.focal_point = self.cam_foc_saved | |
| self.plotter.camera.up = self.cam_up_saved | |
| self.last_update_time = current_time | |
| def update_geom_lines(self) -> bool: | |
| """Read file and update cached geometry text, return True if updated, False if no change""" | |
| if self.fname == '': | |
| return False | |
| with open(self.fname, 'r') as f: | |
| lines = f.readlines() | |
| # Look for matching lines | |
| try: | |
| end_idx = lines.index('# %% END LAYOUT\n') | |
| except ValueError: | |
| print("Error: Could not find %% END LAYOUT comment.") | |
| end_idx = -1 | |
| if end_idx != -1: | |
| geom_lines = lines[:end_idx] | |
| geom_lines = [gl for gl in geom_lines if gl[0] != '#'] | |
| geom_lines = ''.join(geom_lines) | |
| # Make sure __EMERGE_INTERACTIVE_PLOTTER appears | |
| if geom_lines.find('__EMERGE_INTERACTIVE_PLOTTER') != -1: | |
| geom_lines = geom_lines.replace('__EMERGE_INTERACTIVE_PLOTTER','self.plotter') | |
| if geom_lines != self._cached_geom_lines: | |
| self._cached_geom_lines = geom_lines | |
| return True | |
| else: | |
| return False | |
| def execute_file(self): | |
| exec(self._cached_geom_lines) | |
| if __name__ == '__main__': | |
| app = QtWidgets.QApplication(sys.argv) | |
| window = MyMainWindow() | |
| sys.exit(app.exec_()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment