Source code for pyfem.gui.app

# SPDX-License-Identifier: MIT
# Copyright (c) 2011–2026 Joris J.C. Remmers

import sys
#import subprocess
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton, QTextEdit, QFileDialog, QVBoxLayout, QWidget, QToolBar, QMessageBox, QStyle, QSplitter, QHBoxLayout, QTabWidget, QLabel, QComboBox
from PySide6.QtCore import QProcess, Qt, QThread, Signal, QObject
from PySide6.QtGui import QIcon, QAction, QKeySequence

from pyfem.io.InputReader   import InputRead
from pyfem.io.OutputManager import OutputManager
from pyfem.solvers.Solver   import Solver

#-------------------------------------------------------------------------------
#
#-------------------------------------------------------------------------------

[docs] class EmittingStream(QObject): text_written = Signal(str)
[docs] def write(self, text): self.text_written.emit(str(text))
[docs] def flush(self): pass
#------------------------------------------------------------------------------- # #-------------------------------------------------------------------------------
[docs] class WorkerThread(QThread): output_signal = Signal(str) def __init__(self, input_file): super().__init__() self.input_file = input_file
[docs] def run(self): self.props,self.globdat = InputRead( self.input_file ) self.solver = Solver ( self.props , self.globdat ) self.output = OutputManager ( self.props , self.globdat ) while self.globdat.active: self.solver.run( self.props , self.globdat ) self.output.run( self.props , self.globdat ) self.globdat.close()
#------------------------------------------------------------------------------- # #-------------------------------------------------------------------------------
[docs] class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("PyFEM") self.create_menu() self.create_toolbar() ''' self.layout = QVBoxLayout() ''' # Main container widget main_widget = QWidget() self.setCentralWidget(main_widget) # Main horizontal layout with a splitter main_splitter = QSplitter(Qt.Horizontal) main_layout = QHBoxLayout(main_widget) main_layout.addWidget(main_splitter) # Left panel with tabs tab_widget = QTabWidget() tab_widget.setTabPosition(QTabWidget.West) tab_widget.setFixedWidth(400) # Adding tabs to the tab widget tabss = ["Mesh","Elements","Solver","Output"] for tabs in tabss: tab = QWidget() tab_layout = QVBoxLayout() tab.setLayout(tab_layout) if tabs == "Solver": # Add a combo box for solver selection in the Solver tab solver_label = QLabel("Select a solver:") combo_box = QComboBox() combo_box.addItems(["SolverA", "SolverB", "SolverC"]) # Add the label and combo box to the tab layout tab_layout.addWidget(solver_label) tab_layout.addWidget(combo_box) tab_layout.setAlignment(Qt.AlignmentFlag.AlignTop) tab_layout.setSpacing(5) else: # Default content for other tabs tab_label = QLabel(f"Content for Tab {tabs}") tab_layout.addWidget(tab_label) #tab.setLayout(tab_layout) tab_widget.addTab(tab, f"{tabs}") main_splitter.addWidget(tab_widget) # Right side layout with a vertical splitter right_splitter = QSplitter(Qt.Vertical) main_splitter.addWidget(right_splitter) # Image display area image_area = QLabel("Image Area") image_area.setMinimumHeight(300) image_area.setMinimumWidth(900) image_area.setStyleSheet("background-color: white;") image_area.setAlignment(Qt.AlignCenter) right_splitter.addWidget(image_area) self.output_terminal = QTextEdit() self.output_terminal.setReadOnly(True) self.output_terminal.setMinimumHeight(200) self.output_terminal.setStyleSheet("background-color: black; color: white; font-family: 'Courier New', monospace;") right_splitter.addWidget(self.output_terminal) ''' container = QWidget() container.setLayout(self.layout) self.setCentralWidget(container) ''' self.input_file = None # Set initial sizes of the split panels #main_splitter.setSizes([300, 900]) #right_splitter.setSizes([600, 300]) self.emitting_stream = EmittingStream() self.emitting_stream.text_written.connect(self.handle_output) sys.stdout = self.emitting_stream sys.stderr = self.emitting_stream #------------------------------------------------------------------------------- # #-------------------------------------------------------------------------------
[docs] def create_menu(self): menu_bar = self.menuBar() file_menu = menu_bar.addMenu("File") edit_menu = menu_bar.addMenu("Edit") run_menu = menu_bar.addMenu("Run") about_menu = menu_bar.addMenu("About") new_action = QAction(self.style().standardIcon(QStyle.SP_FileIcon), 'New', self) new_action.setShortcut(QKeySequence("Ctrl+N")) new_action.triggered.connect(self.load_file) file_menu.addAction(new_action) open_action = QAction(self.style().standardIcon(QStyle.SP_DialogOpenButton), 'Open', self) open_action.setShortcut(QKeySequence("Ctrl+O")) open_action.triggered.connect(self.load_file) file_menu.addAction(open_action) save_action = QAction(self.style().standardIcon(QStyle.SP_DialogSaveButton), 'Save', self) save_action.setShortcut(QKeySequence("Ctrl+S")) save_action.triggered.connect(self.load_file) file_menu.addAction(save_action) exit_action = QAction(QIcon.fromTheme("application-exit"), "Exit", self) exit_action.triggered.connect(self.close) file_menu.addAction(exit_action) execute_action = QAction(self.style().standardIcon(QStyle.SP_MediaPlay), "Execute", self) execute_action.setShortcut(QKeySequence("Ctrl+R")) execute_action.triggered.connect(self.execute_script) run_menu.addAction(execute_action) abort_action = QAction(self.style().standardIcon(QStyle.SP_BrowserStop), "Abort", self) abort_action.setShortcut(QKeySequence("Ctrl+C")) abort_action.triggered.connect(self.execute_script) run_menu.addAction(abort_action) about_action = QAction("About", self) about_action.triggered.connect(self.show_about) about_menu.addAction(about_action)
#------------------------------------------------------------------------------- # #-------------------------------------------------------------------------------
[docs] def create_toolbar(self): toolbar = QToolBar("Main Toolbar") self.addToolBar(toolbar) new_action = QAction(self.style().standardIcon(QStyle.SP_FileIcon), 'New', self) new_action.triggered.connect(self.load_file) toolbar.addAction(new_action) open_action = QAction(self.style().standardIcon(QStyle.SP_DialogOpenButton), 'Open', self) open_action.triggered.connect(self.load_file) toolbar.addAction(open_action) save_action = QAction(self.style().standardIcon(QStyle.SP_DialogSaveButton), "Save", self) save_action.triggered.connect(self.execute_script) toolbar.addAction(save_action) execute_action = QAction(self.style().standardIcon(QStyle.SP_MediaPlay), "Execute", self) execute_action.triggered.connect(self.execute_script) toolbar.addAction(execute_action) abort_action = QAction(self.style().standardIcon(QStyle.SP_BrowserStop), "Abort", self) abort_action.triggered.connect(self.execute_script) toolbar.addAction(abort_action)
#------------------------------------------------------------------------------- # #-------------------------------------------------------------------------------
[docs] def load_file(self): file_dialog = QFileDialog() self.input_file, _ = file_dialog.getOpenFileName(self, caption = "Open Input File", dir = "examples", filter = "PyFEM Input Files (*.pro);;All Files (*.*)") if self.input_file: self.output_terminal.append(f"Loaded file: {self.input_file}")
#------------------------------------------------------------------------------- # #-------------------------------------------------------------------------------
[docs] def execute_script(self): if not self.input_file: self.output_terminal.append("Please load an input file first.") return self.worker = WorkerThread(self.input_file) self.worker.output_signal.connect(self.handle_output) self.worker.start()
#------------------------------------------------------------------------------- # #-------------------------------------------------------------------------------
[docs] def handle_output(self, text): self.output_terminal.append( text.rstrip() )
#------------------------------------------------------------------------------- # #-------------------------------------------------------------------------------
[docs] def update_output(self): output = self.process.readAllStandardOutput().data().decode() error = self.process.readAllStandardError().data().decode() self.output_terminal.append(output)
#------------------------------------------------------------------------------- # #-------------------------------------------------------------------------------
[docs] def show_about(self): QMessageBox.about(self, "About", "PyFEM gui\n\nDeveloped with PySide6.")
#------------------------------------------------------------------------------- # #-------------------------------------------------------------------------------
[docs] def main(): app = QApplication(sys.argv) window = MainWindow() #window.resize(800, 600) window.show() sys.exit(app.exec())