Customisation
This section discusses customising the program to suit your own needs. This includes creating your own scripts.
Custom scripts can be created by anyone and used by inserting that script in the corresponding folder and restarting the program.
Creating Custom Scripts
Custom scripts can be created for all tabs, but have two minimum requirements for functionality. A name from the
function get_name()
and a GUI structure from the function gui_elements()
.
This allows the program to switch tab elements dynamically as you load alternative scripts.
See examples below:
def gui_elements():
gui_structure = {
'type': 'QVBoxLayout', # Top-level layout
'items': [
{
'type': 'QHBoxLayout', # Nested layout
'items': [
{
'type': 'QLabel',
'text': 'FEM File Directory:'
},
{
'type': 'QLineEdit',
'placeholder': 'FEM File Directory Path'
'obname': 'fem_file_dir',
},
{
'type': 'QPushButton',
'text': 'Browse',
'slots': {'clicked': update_label}
}
]
},
{
'type': 'QHBoxLayout', # Nested layout
'items': [
{
'type': 'QLabel',
'text': 'Filename:'
},
{
'type': 'QLineEdit',
'placeholder': 'FEM File Name',
'obname': 'fem_file_name',
'slots': {
'valueChanged': ('id', var_ins, lambda value: on_value_changed(value, var_ins, 'id'))
}
}
]
},
{
'type': 'QHBoxLayout', # Nested layout
'items': [
{
'type': 'QLabel',
'text': 'Force (N):'
},
{
'type':'QDoubleSpinBox',
'min': 0,
'max': 100000,
'value': var_ins.F,
'step': 1,
'dp': 2,
'slots': {
'valueChanged': ('F', var_ins, lambda value: on_value_changed(value, var_ins, 'F'))
}
}
]
},
{
'type': 'QPushButton',
'text': 'FEM Analysis',
'slots': {'clicked': update_label}
}
]
}
return gui_structure
Linking UI Elements to Functions
To link UI elements to functions, you can use the slots
key in the gui_elements()
function. This key is a dictionary
of the signal and the function to call when the signal is emitted. Standard signals include clicked
, valueChanged
.
Clicked Signal
The clicked
signal is emitted when the button is clicked. This can be linked to a function to perform an action.
Value Changed Signal
The valueChanged
signal is emitted when the value of the element changes. This can be linked to a function to update
a variable or perform an action.
{
'type':'QDoubleSpinBox',
'min': 0,
'max': 100000,
'value': var_ins.F,
'step': 1,
'dp': 2,
'slots': {
'valueChanged': ('F', var_ins, lambda value: on_value_changed(value, var_ins, 'F'))
}
}
Custom Clicked Functions
Some custom functions are defined to make certain actions easier like browsing for a file.
def browse_file_path(text, var_target, var_key, file_types, line_edit_name, parent_widget):
"""
Browse for a file path and update the variable and widget
:param text: The dialog caption
:param var_target: The target object containing the variable
:param var_key: The key of the variable to update
:param file_types: The file types to filter
:param line_edit_name: The object name of the QLineEdit widget
:param parent_widget: The parent widget containing the QLineEdit widget
"""
file_name, _ = QFileDialog.getOpenFileName(caption=text, filter=file_types)
if file_name:
try:
setattr(var_target, var_key, file_name)
except Exception as e:
print(f"Error setting value: {e}")
update_line_edit(file_name, line_edit_name, parent_widget)
Parent Widget and Object Name
The parent_widget
and line_edit_name
are used to update the widget with the new value. This is done by finding the
widget by name in the parent widget and updating the text.
def update_line_edit(text, line_edit_name, parent_widget):
"""
Update the text of a QLineEdit widget
:param text: The new text to set
:param line_edit_name: The object name of the QLineEdit widget
:param parent_widget: The parent widget containing the QLineEdit widget
"""
line_edit = parent_widget.findChild(QLineEdit, line_edit_name) # Find the QLineEdit widget
if isinstance(line_edit, QLineEdit):
try:
line_edit.setText(text)
except Exception as e:
print(f"Error setting text: {e}")
else:
print(f"Expected a QLineEdit instance, got {type(line_edit)} instead.")
It is important to note that the parent_widget
is the top-level widget containing the widget you want to update. This
is often the QDialog
or QMainWindow
instance. Make sure you are passing the correct parent widget. As using
self
in the function will generally not work, QCoreApplication.instance().activeWindow()
will only work when the
window is focused.
The line_edit_name
is the object name of the widget you want to update. This is set in the obname
key in the
gui_funcs.py
file in core_libs.
Custom GUI Build Function
The create_gui_element()
function is a factory function to create GUI elements based on the provided info.
This function is used to create the GUI elements from the gui_elements()
function. It is a switch statement that
creates the appropriate widget based on the type provided.
It is not exhaustive and can be expanded to include more widget types.
def create_gui_element(element_info, signal_slots=None):
"""
Factory function to create GUI elements based on the provided info.
"""
element_type = element_info.get('type')
match element_type:
case 'QLabel':
return QLabel(element_info.get('text', ''))
case 'QPushButton':
button = QPushButton(element_info.get('text', ''))
if signal_slots and 'clicked' in signal_slots:
button.clicked.connect(signal_slots['clicked'])
return button
case 'QLineEdit':
widget = QLineEdit()
widget.setObjectName(element_info.get('obname', ''))
widget.setPlaceholderText(element_info.get('placeholder', ''))
widget.setText(element_info.get('text', ''))
if signal_slots and 'valueChanged' in signal_slots:
var_name, var_inst, slot_func = signal_slots['valueChanged']
if hasattr(var_inst, var_name):
widget.setText(getattr(var_inst, var_name))
widget.textChanged.connect(lambda value, vn=var_name, vi=var_inst: on_value_changed(value, vi, vn))
return widget
case 'QDoubleSpinBox':
widget = QDoubleSpinBox()
widget.setRange(element_info.get('min', 0.00), element_info.get('max', 100000.00))
widget.setValue(element_info.get('value', 0.00))
widget.setSingleStep(element_info.get('step', 1.00))
widget.setDecimals(element_info.get('dp', 2))
if signal_slots and 'valueChanged' in signal_slots:
var_name, var_inst, slot_func = signal_slots['valueChanged']
widget.valueChanged.connect(lambda value, vn=var_name, vi=var_inst: on_value_changed(value, vi, vn))
return widget
case 'QSpinBox':
widget = QSpinBox()
widget.setRange(element_info.get('min', 0), element_info.get('max', 100000))
widget.setValue(element_info.get('value', 0))
widget.setSingleStep(element_info.get('step', 1))
if signal_slots and 'valueChanged' in signal_slots:
var_name, var_inst, slot_func = signal_slots['valueChanged']
widget.valueChanged.connect(lambda value, vn=var_name, vi=var_inst: on_value_changed(value, vi, vn))
return widget
case 'QComboBox':
widget = QComboBox()
widget.setObjectName(element_info.get('obname', ''))
widget.addItem(element_info.get('placeholder', ''))
if signal_slots and 'valueChanged' in signal_slots:
var_name, var_inst, slot_func = signal_slots['valueChanged']
widget.currentIndexChanged.connect(lambda value, vn=var_name, vi=var_inst: on_value_changed(value, vi, vn))
return widget
case _:
return None