Skip to content

Instantly share code, notes, and snippets.

@sjgallagher2
Created September 2, 2025 15:40
Show Gist options
  • Select an option

  • Save sjgallagher2/039baa89648dc57e3c318d8cbc4a7fc2 to your computer and use it in GitHub Desktop.

Select an option

Save sjgallagher2/039baa89648dc57e3c318d8cbc4a7fc2 to your computer and use it in GitHub Desktop.
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