Skip to content

Repository files navigation

ScenarioGUI: An open-source tool for an easy way to create graphical user interfaces

PyPI versionTestscodecovDownloadsDownloadsDocumentation Status

What is ScenarioGUI?

ScenarioGUI is a Python package that contains many functionalities to design your own Graphical User Interfaces (GUI). It should help to create and maintain scenario based GUIs.

Read The Docs

ScenarioGUI has an elaborate documentation were all the functionalities of the tool are explained, with examples. This can be found on ScenarioGUI.readthedocs.io.

Requirements

This code is tested with Python 3.10 until 3.12 and requires the following libraries (the versions mentioned are the ones with which the code is tested)

  • PySide6>=6.6.0
  • matplotlib>=3.5.2
  • numpy>=1.23.1
  • pandas>=1.4.3
  • black>=23.1.0

For the tests

  • pytest>=7.1.2
  • pytest-cov>=3.0.0
  • pytest-timeout>=2.1.0
  • pytest-qt>=4.1.0
  • keyboard>=0.13.5

Quick start

Installation

One can install ScenarioGUI by running Pip and running the command

pip install ScenarioGUI

Developers can clone this repository.

It is a good practise to use virtual environments (venv) when working on a (new) Python project so different Python and package versions don't conflict with eachother. For ScenarioGUI, Python 3.10 or higher is recommended. General information about Python virtual environments can be found here and in this article.

Get started with ScenarioGUI

The GUI can be customized using a gui_config.ini file. There the font and font size can be set. Furthermore, the gui name, icon, version, saving file extension can be set. Several colors like the DARK background and the LIGHT Foreground color can be set as well. A folder containing an icons folder can be given. This one needs to contain at least the icon contained under ScenarioGUI/icons.

[COLORS]
WHITE: rgb(255, 255, 255)
LIGHT: rgb(84, 188, 235)
LIGHT_SELECT: rgb(42, 126, 179)
DARK: rgb(0, 64, 122)
GREY: rgb(100, 100, 100)
WARNING: rgb(255, 200, 87)
BLACK: rgb(0, 0, 0)
[DEFAULT]
FILE_EXTENSION: scenario
GUI_NAME: Scenario GUI
ICON_NAME: icon.svg
PATH_2_ICONS: ./ScenarioGUI/
FONT_WINDOWS: Arial
FONT_MAC: Helvetica
FONT_SIZE_WINDOWS: 12
FONT_SIZE_MAC: 14

To create your own GUI part you can inherit from the GuiStructure provided by this lib and add more pages, categories and input field as you like.

fromScenarioGUIimportGuiStructurefromScenarioGUIimportelementsaselsfromtypingimportTYPE_CHECKINGifTYPE_CHECKING:
importPySide6.QtWidgetsasQtWfromexamples.translation_class_creation.translation_classimportTranslationsclassGUI(GuiStructure):
"""your own customized GUI"""def__init__(self, default_parent: QtW.QWidget, translations: Translations):
# first init the parent classuper().__init__(default_parent, translations)
# add a first page called "Inputs" and has a button name of "Input" and has an icon "Add.svg"self.page_inputs=els.Page(name="Inputs", button_name="Input", icon="Add.svg")
# Then several aims can be added to the page with different names and iconsself.aim_add=els.Aim(label="Adding", icon="Add", page=self.page_inputs)
self.aim_sub=els.Aim(label="Substract", icon="Delete", page=self.page_inputs)
self.aim_plot=els.Aim(label="Plot", icon="Parameters", page=self.page_inputs)
# this three aims can appear in one row by setting:self.page_inputs.aims_in_row=3# a category with the label "Inputs" can be added to the inputs page like:self.category_inputs=els.Category(label="Inputs", page=self.page_inputs)
# an integer box can be added with different options like this (some of these options are optional):self.int_a=els.IntBox(label="a", default_value=2, minimal_value=0, maximal_value=200, step=2, category=self.category_inputs)
# a float box can be added with different options like this (some of these options are optional):self.float_b=els.FloatBox(
label="b",
default_value=100,
minimal_value=0,
maximal_value=1000,
decimal_number=2,
step=0.5,
category=self.category_inputs,
)
# a button box can be added with different options like thisself.button_box=els.ButtonBox(label="a or b?", default_index=0, entries=["a", "b"], category=self.category_inputs)
# the button box can also be a list box for many optionsself.list_box=els.ListBox(label="a or b?", default_index=0, entries=["a", "b"], category=self.category_inputs)
# a filename box can be added with different options like thisfile="./example_data.csv"self.filename=els.FileNameBox(label="Filename", default_value=file, dialog_text="Hello", error_text="no file found", category=self.category_inputs)
# a function button can be implemented like this:self.function_button=els.FunctionButton(button_text="function", icon="Add", category=self.category_inputs)
# the function ("func") which will be called every time the button is clicked can be defined as follows:self.page_inputs.add_function_called_if_button_clicked(func)
# A Hint can be implemented (if warning is True the option is displayed in WARNING color) like:self.hint=els.Hint(hint="Very important hint", category=self.category_inputs, warning=False)
# The results page must be created like this:self.create_results_page()
# then a category for numerical results can be addedself.numerical_results=els.Category(page=self.page_result, label="Numerical results")
# A text result calling the get_results function from the ResultsClass and rounding it to 2 decimals can be set like this: self.result_text_add=els.ResultText("Result", category=self.numerical_results, prefix="Result: ", suffix="m")
self.result_text_add.text_to_be_shown("ResultsClass", "get_result")
self.result_text_add.function_to_convert_to_text(lambdax: round(x, 2))
# a results figure calling the create_plot function from ResultsClass which is returning a tuple of a plt.Figure and plt.Axes can be implemented # like this:self.figure_results=els.ResultFigure(label="Plot", page=self.page_result)
self.figure_results.fig_to_be_shown(class_name="ResultsClass", function_name="create_plot")
# this figure can then be linked to an option to display the legend like this:self.legend_figure_results=els.FigureOption(
category=self.figure_results, label="Legend on", param="legend", default=0, entries=["No", "Yes"], entries_values=[False, True]
)
# with this function the results options will be displayed if one of the aims is selectedself.aim_add.add_link_2_show(self.result_text_add)
self.aim_plot.add_link_2_show(self.figure_results)
# The settings page must be created like this:self.create_settings_page()
# This function needs to be called to update the page, category and option listsself.create_lists()
# links to next or previous pages can be set like this:self.page_inputs.set_next_page(self.page_result)
self.page_result.set_previous_page(self.page_inputs)
self.page_result.set_next_page(self.page_settings)
self.page_result.set_previous_page(self.page_result)

The ResultsClass needs to have a "_to_dict", "_from_dict" and all function defined in the ResulText and ResultFigure options. Furthermore, it needs to be creatable without any inputs.

fromcollections.abcimportCallableimportmatplotlib.pyplotaspltclassResultsClass:
"""Example results class"""def__init__(self, a: int=1, b: int=2):
self.a=aself.b=bself.result=Nonedefadding(self):
"""adding a and b"""self.result=self.a+self.bdefget_result(self) ->float:
"""returns the result"""returnself.resultdefcreate_plot(self, legend: bool=False) ->tuple[plt.Figure, plt.Axes]:
"""Creates a plot"""fig=plt.figure()
ax=fig.add_subplot(111)
# set axes labelsax.set_xlabel(r"Time (year)")
ax.set_ylabel(r"Temperature ($^\circ C$)")
ax.hlines(self.a, 0, self.b, colors="r", linestyles="dashed", label="line", lw=1)
iflegend:
ax.legend()
returnfig, axdef_to_dict(self) ->dict:
"""creates a dictionary from the class variables"""return {"a": self.a, "b": self.b, "result": self.result}
def_from_dict(self, dictionary: dict):
"""creates the class from a dictionary"""self.a=dictionary["a"]
self.b=dictionary["b"]
self.result=dictionary["result"]
defdata_2_results(data) ->tuple[ResultsClass, Callable[[], None]]:
"""casts the data in the Datastorage to the results class and the function which should be called"""result=ResultsClass(data.int_a, data.float_b)
returnresult, result.adding

The gui can then be start like this:

fromsysimportargv, exitassys_exitdefrun(path_list=None): # pragma: no coverimportPySide6.QtWidgetsasQtWfromScenarioGUI.global_settingsimportFILE_EXTENSIONfromScenarioGUIimportMainWindow# import your own Translation class a script to create one from a csv file is given as wellfromScenarioGUI.gui_classes.translation_classimportTranslations# init applicationapp=QtW.QApplication()
# init windowwindow=QtW.QMainWindow()
# init gui windowmain_window=MainWindow(window, app, GUI, Translations, result_creating_class=ResultsClass, data_2_results_function=data_2_results)
# load file if it is in path listifpath_listisnotNone:
main_window.filename= (
[pathforpathinpath_listifpath.endswith(f".{FILE_EXTENSION}")][0],
0,
)
main_window.fun_load_known_filename()
# show windowwindow.showMaximized()
# close appsys_exit(app.exec())
if__name__=="__main__": # pragma: no cover# pass system args like a file to readrun(argviflen(argv) >1elseNone)

A full list of functionalities is given below.

Functionalities

ScenarioGUI offers functionalities of value to all different disciplines which would like to create a GUI for different scenarios. These scenario can thern be easily compared. For more information about the functionalities of ScenarioGUI, please visit the ReadTheDocs.

License

ScenarioGUI is licensed under the terms of the 3-clause BSD-license. See ScenarioGUI license.

Contact ScenarioGUI

  • Do you want to contribute to ScenarioGUI?
  • Do you have a great idea for a new feature?
  • Do you have a specific remark/problem?

Please do contact us at blanke@fh-aachen.de.

About

An open-source tool for an easy way to create graphical user interfaces

Topics

Resources

Stars

4 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages