diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index d4a30778..824c79ab 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -30,7 +30,7 @@ jobs: name: "Code style" runs-on: ubuntu-latest steps: - - uses: ansys/actions/code-style@v4 + - uses: ansys/actions/code-style@v5 with: python-version: ${{ env.MAIN_PYTHON_VERSION }} use-python-cache: false @@ -39,7 +39,7 @@ jobs: name: "Documentation style" runs-on: ubuntu-latest steps: - - uses: ansys/actions/doc-style@v4 + - uses: ansys/actions/doc-style@v5 with: token: ${{ secrets.GITHUB_TOKEN }} @@ -48,7 +48,7 @@ jobs: runs-on: ubuntu-latest needs: doc-style steps: - - uses: ansys/actions/doc-build@v4 + - uses: ansys/actions/doc-build@v5 with: python-version: ${{ env.MAIN_PYTHON_VERSION }} check-links: false @@ -63,7 +63,7 @@ jobs: runs-on: ${{ matrix.os }} needs: [ code-style ] steps: - - uses: ansys/actions/build-wheelhouse@v4 + - uses: ansys/actions/build-wheelhouse@v5 with: library-name: ${{ env.LIBRARY_NAME }} operating-system: ${{ matrix.os }} @@ -81,7 +81,7 @@ jobs: - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} @@ -103,13 +103,13 @@ jobs: timeout-minutes: 5 run: | .\.venv\Scripts\Activate.ps1 - pytest -v -m aedt_common --cov + pytest -v -m backend\tests_common_api --cov - - name: AEDT Test - timeout-minutes: 5 - run: | - .\.venv\Scripts\Activate.ps1 - pytest -v -m aedt --cov --cov-append +# - name: AEDT Test +# timeout-minutes: 5 +# run: | +# .\.venv\Scripts\Activate.ps1 +# pytest -v -m aedt --cov --cov-append - name: "Combine coverage files" run: | @@ -119,7 +119,7 @@ jobs: - name: "Upload coverage results" if: ${{ matrix.python-version == env.MAIN_PYTHON_VERSION }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: path: .cov/total-html name: html-total-coverage @@ -137,7 +137,7 @@ jobs: runs-on: ubuntu-latest needs: [ doc-build, tests_windows_aedt ] steps: - - uses: ansys/actions/build-library@v4 + - uses: ansys/actions/build-library@v5 with: library-name: ${{ env.LIBRARY_NAME }} python-version: ${{ env.MAIN_PYTHON_VERSION }} @@ -150,7 +150,7 @@ jobs: steps: - name: "Release to the private PyPI" - uses: ansys/actions/release-pypi-private@v4 + uses: ansys/actions/release-pypi-private@v5 with: library-name: ${{ env.LIBRARY_NAME }} twine-username: "__token__" @@ -164,7 +164,7 @@ jobs: # twine-token: ${{ secrets.PYPI_TOKEN }} - name: "Release to GitHub" - uses: ansys/actions/release-github@v4 + uses: ansys/actions/release-github@v5 with: library-name: ${{ env.LIBRARY_NAME }} @@ -174,7 +174,7 @@ jobs: needs: build-library if: github.event_name == 'push' steps: - - uses: ansys/actions/doc-deploy-dev@v4 + - uses: ansys/actions/doc-deploy-dev@v5 with: cname: ${{ env.DOCUMENTATION_CNAME }} token: ${{ secrets.GITHUB_TOKEN }} @@ -185,7 +185,7 @@ jobs: needs: release if: github.event_name == 'push' && contains(github.ref, 'refs/tags') steps: - - uses: ansys/actions/doc-deploy-stable@v4 + - uses: ansys/actions/doc-deploy-stable@v5 with: cname: ${{ env.DOCUMENTATION_CNAME }} token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml index 5d562835..dc0b97a1 100644 --- a/.github/workflows/label.yml +++ b/.github/workflows/label.yml @@ -70,7 +70,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Suggest to add labels - uses: peter-evans/create-or-update-comment@v3 + uses: peter-evans/create-or-update-comment@v4 # Execute only when no labels have been applied to the pull request if: toJSON(github.event.pull_request.labels.*.name) == '{}' with: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e6b2e803..940a8c31 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,8 +1,8 @@ exclude: | (?x)( - src/ansys/aedt/toolkits/common/ui/.* | + src/ansys/aedt/toolkits/common/ui/utils/ui_templates/.* | tests/input_data/.* | - examples/input_files/.* + examples/.* ) repos: diff --git a/doc/source/Contributing.rst b/doc/source/Contributing.rst index fc8a685b..35591bd5 100644 --- a/doc/source/Contributing.rst +++ b/doc/source/Contributing.rst @@ -27,7 +27,7 @@ to create issues to report bugs and request new features. View documentation ------------------- -Documentation for the latest stable release is hosted at `Motor Segmentation Toolkit documentation `_. +Documentation for the latest stable release is hosted at `Common PyAEDT Toolkit documentation `_. In the upper right corner of the documentation’s title bar, there is an option for switching from viewing the documentation for the latest stable release to viewing the documentation for the development version @@ -37,7 +37,7 @@ Adhere to code style -------------------- The Motor Segmentation Toolkit is compliant with `PyAnsys code style `_. It uses the tool -`pre-commit `_ to check the code style. +`pre-commit `_ to select the code style. You can install and activate this tool with these commands: diff --git a/doc/source/index.rst b/doc/source/index.rst index 38afd643..38a7bf51 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -1,5 +1,5 @@ -Motor Segmentation Toolkit documentation |version| -================================================== +Common PyAEDT Toolkit documentation |version| +============================================= .. Simply reuse the root readme diff --git a/examples/backend/api_aedt_connect_session_example.py b/examples/backend/api_aedt_connect_session_example.py index cca2f734..8d362552 100644 --- a/examples/backend/api_aedt_connect_session_example.py +++ b/examples/backend/api_aedt_connect_session_example.py @@ -15,12 +15,14 @@ sessions = toolkit.aedt_sessions() # Find if it is COM or GRPC -if sessions[0][1] == -1: +first_key, first_value = next(iter(sessions.items())) + +if first_value == -1: use_grpc = False - selected_process = sessions[0][0] + selected_process = first_key else: use_grpc = True - selected_process = sessions[0][1] + selected_process = first_value # Set properties new_properties = {"selected_process": selected_process, "use_grpc": use_grpc} diff --git a/examples/toolkit/pyaedt_toolkit/backend/api.py b/examples/toolkit/pyaedt_toolkit/backend/api.py index fbf3d587..9ec95d69 100644 --- a/examples/toolkit/pyaedt_toolkit/backend/api.py +++ b/examples/toolkit/pyaedt_toolkit/backend/api.py @@ -29,6 +29,7 @@ class ToolkitBackend(AEDTCommon): def __init__(self): """Initialize the ``Toolkit`` class.""" AEDTCommon.__init__(self, properties) + self.properties = properties self.multiplier = 1.0 def create_geometry(self): @@ -58,15 +59,19 @@ def create_geometry(self): self.connect_design() if self.aedtapp: - multiplier = properties.multiplier - geometry = properties.geometry + multiplier = self.properties.multiplier + geometry = self.properties.geometry self.multiplier = multiplier + prim = None if geometry == "Box": - self.draw_box() + prim = self.draw_box() elif geometry == "Sphere": - self.draw_sphere() + prim = self.draw_sphere() + if not prim: + logger.error("Primitive not created") + return False self.release_aedt(False, False) - return True + return prim.name logger.error("Design not connected") return False diff --git a/examples/toolkit/pyaedt_toolkit/backend/rest_api.py b/examples/toolkit/pyaedt_toolkit/backend/run_backend.py similarity index 82% rename from examples/toolkit/pyaedt_toolkit/backend/rest_api.py rename to examples/toolkit/pyaedt_toolkit/backend/run_backend.py index 72409e4a..d65c75ce 100644 --- a/examples/toolkit/pyaedt_toolkit/backend/rest_api.py +++ b/examples/toolkit/pyaedt_toolkit/backend/run_backend.py @@ -1,19 +1,17 @@ -from api import ToolkitBackend from ansys.aedt.toolkits.common.backend.multithreading_server import MultithreadingServer from ansys.aedt.toolkits.common.backend.rest_api import app from ansys.aedt.toolkits.common.backend.rest_api import jsonify from ansys.aedt.toolkits.common.backend.rest_api import logger - -toolkit_api = ToolkitBackend() +from ansys.aedt.toolkits.common.backend.rest_api import toolkit_api @app.route("/create_geometry", methods=["POST"]) -def create_geometry_call(): - logger.info("[POST] /create_geometry (create a box or sphere in HFSS)") +def create_geometry(): + logger.info("[POST] /create_geometry (create a box or sphere in HFSS).") response = toolkit_api.create_geometry() if response: - return jsonify("Geometry created"), 200 + return response, 200 else: return jsonify("Geometry not created"), 500 diff --git a/examples/toolkit/pyaedt_toolkit/run_toolkit.py b/examples/toolkit/pyaedt_toolkit/run_toolkit.py index 68cfa41d..7ec59d4f 100644 --- a/examples/toolkit/pyaedt_toolkit/run_toolkit.py +++ b/examples/toolkit/pyaedt_toolkit/run_toolkit.py @@ -14,8 +14,8 @@ import backend # Define global variables or constants -BACKEND_FILE = os.path.join(backend.__path__[0], "rest_api.py") -FRONTEND_FILE = os.path.join(ui.__path__[0], "run.py") +BACKEND_FILE = os.path.join(backend.__path__[0], "run_backend.py") +FRONTEND_FILE = os.path.join(ui.__path__[0], "run_frontend.py") IS_LINUX = os.name == "posix" URL = properties.backend_url PORT = properties.backend_port diff --git a/examples/toolkit/pyaedt_toolkit/ui/actions.py b/examples/toolkit/pyaedt_toolkit/ui/actions.py index dc77ef43..4cbd2f36 100644 --- a/examples/toolkit/pyaedt_toolkit/ui/actions.py +++ b/examples/toolkit/pyaedt_toolkit/ui/actions.py @@ -16,12 +16,13 @@ def create_geometry_toolkit(self, project_selected=None, design_selected=None, g if project_selected == "No Project": project_selected = generate_unique_project_name() project_selected = self.get_project_name(project_selected) + be_properties["active_project"] = project_selected for project in be_properties["project_list"]: if self.get_project_name(project) == project_selected: be_properties["active_project"] = project if project_selected in list(be_properties["design_list"].keys()): - designs = be_properties["design_lis"][project_selected] + designs = be_properties["design_list"][project_selected] for design in designs: if design_selected == design: be_properties["active_design"] = design @@ -30,15 +31,18 @@ def create_geometry_toolkit(self, project_selected=None, design_selected=None, g else: project_selected = generate_unique_project_name() project_selected = self.get_project_name(project_selected) + be_properties["active_project"] = project_selected # Multiplier and geometry be_properties["multiplier"] = float(multiplier) be_properties["geometry"] = geometry - be_properties["active_project"] = project_selected + self.set_properties(be_properties) response = requests.post(self.url + "/create_geometry") + self.properties.primitives_created.append(response.text) + if response.ok: msg = "Geometry created." logger.info(msg) diff --git a/examples/toolkit/pyaedt_toolkit/ui/frontend_properties.json b/examples/toolkit/pyaedt_toolkit/ui/frontend_properties.json index 2dbf5175..6d138f19 100644 --- a/examples/toolkit/pyaedt_toolkit/ui/frontend_properties.json +++ b/examples/toolkit/pyaedt_toolkit/ui/frontend_properties.json @@ -10,7 +10,7 @@ "copyright" : "By: slopez", "year" : 2023, "theme" : "ansys.json", - "high_resolution": false, + "high_resolution": true, "add_left_menus" : [ { "btn_icon": "icon_home.svg", diff --git a/examples/toolkit/pyaedt_toolkit/ui/run.py b/examples/toolkit/pyaedt_toolkit/ui/run_frontend.py similarity index 100% rename from examples/toolkit/pyaedt_toolkit/ui/run.py rename to examples/toolkit/pyaedt_toolkit/ui/run_frontend.py diff --git a/examples/toolkit/pyaedt_toolkit/ui/windows/create_geometry/geometry_menu.py b/examples/toolkit/pyaedt_toolkit/ui/windows/create_geometry/geometry_menu.py index d7fa5af4..547d4c1b 100644 --- a/examples/toolkit/pyaedt_toolkit/ui/windows/create_geometry/geometry_menu.py +++ b/examples/toolkit/pyaedt_toolkit/ui/windows/create_geometry/geometry_menu.py @@ -19,6 +19,10 @@ def __init__(self, app, selected_project, selected_design, geometry, multiplier) self.multiplier = multiplier def run(self): + self.app.ui.progress.progress = 50 + import time + + time.sleep(1) success = self.app.create_geometry_toolkit( self.selected_project, self.selected_design, self.geometry, self.multiplier ) @@ -162,6 +166,9 @@ def geometry_button_clicked(self): ) self.geometry_thread.finished_signal.connect(self.geometry_created_finished) + msg = "Creating geometry." + self.ui.logger.log(msg) + self.geometry_thread.start() else: @@ -180,4 +187,4 @@ def geometry_created_finished(self, success): self.ui.logger.log(msg) else: msg = f"Failed backend call: {self.app.url}" - self.ui.logger.error(msg) + self.ui.logger.log(msg) diff --git a/examples/toolkit/pyaedt_toolkit/ui/windows/create_geometry/geometry_page.py b/examples/toolkit/pyaedt_toolkit/ui/windows/create_geometry/geometry_page.py index adba3454..07dd6ded 100644 --- a/examples/toolkit/pyaedt_toolkit/ui/windows/create_geometry/geometry_page.py +++ b/examples/toolkit/pyaedt_toolkit/ui/windows/create_geometry/geometry_page.py @@ -8,21 +8,50 @@ ## WARNING! All changes made in this file will be lost when recompiling UI file! ################################################################################ -from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, - QMetaObject, QObject, QPoint, QRect, - QSize, QTime, QUrl, Qt) -from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, - QFont, QFontDatabase, QGradient, QIcon, - QImage, QKeySequence, QLinearGradient, QPainter, - QPalette, QPixmap, QRadialGradient, QTransform) -from PySide6.QtWidgets import (QApplication, QComboBox, QFrame, QHBoxLayout, - QLabel, QLineEdit, QSizePolicy, QSpacerItem, - QVBoxLayout, QWidget) +from PySide6.QtCore import QCoreApplication +from PySide6.QtCore import QDate +from PySide6.QtCore import QDateTime +from PySide6.QtCore import QLocale +from PySide6.QtCore import QMetaObject +from PySide6.QtCore import QObject +from PySide6.QtCore import QPoint +from PySide6.QtCore import QRect +from PySide6.QtCore import QSize +from PySide6.QtCore import QTime +from PySide6.QtCore import QUrl +from PySide6.QtCore import Qt +from PySide6.QtGui import QBrush +from PySide6.QtGui import QColor +from PySide6.QtGui import QConicalGradient +from PySide6.QtGui import QCursor +from PySide6.QtGui import QFont +from PySide6.QtGui import QFontDatabase +from PySide6.QtGui import QGradient +from PySide6.QtGui import QIcon +from PySide6.QtGui import QImage +from PySide6.QtGui import QKeySequence +from PySide6.QtGui import QLinearGradient +from PySide6.QtGui import QPainter +from PySide6.QtGui import QPalette +from PySide6.QtGui import QPixmap +from PySide6.QtGui import QRadialGradient +from PySide6.QtGui import QTransform +from PySide6.QtWidgets import QApplication +from PySide6.QtWidgets import QComboBox +from PySide6.QtWidgets import QFrame +from PySide6.QtWidgets import QHBoxLayout +from PySide6.QtWidgets import QLabel +from PySide6.QtWidgets import QLineEdit +from PySide6.QtWidgets import QSizePolicy +from PySide6.QtWidgets import QSpacerItem +from PySide6.QtWidgets import QVBoxLayout +from PySide6.QtWidgets import QWidget + class Ui_Geometry(object): def setupUi(self, Geometry): if not Geometry.objectName(): - Geometry.setObjectName(u"Geometry") + Geometry.setObjectName("Geometry") Geometry.resize(605, 442) sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) @@ -31,9 +60,9 @@ def setupUi(self, Geometry): Geometry.setSizePolicy(sizePolicy) Geometry.setMinimumSize(QSize(0, 0)) self.verticalLayout_2 = QVBoxLayout(Geometry) - self.verticalLayout_2.setObjectName(u"verticalLayout_2") + self.verticalLayout_2.setObjectName("verticalLayout_2") self.dimension_multiplier_label = QLabel(Geometry) - self.dimension_multiplier_label.setObjectName(u"dimension_multiplier_label") + self.dimension_multiplier_label.setObjectName("dimension_multiplier_label") sizePolicy1 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed) sizePolicy1.setHorizontalStretch(0) sizePolicy1.setVerticalStretch(0) @@ -48,7 +77,7 @@ def setupUi(self, Geometry): self.verticalLayout_2.addWidget(self.dimension_multiplier_label) self.line = QFrame(Geometry) - self.line.setObjectName(u"line") + self.line.setObjectName("line") sizePolicy1.setHeightForWidth(self.line.sizePolicy().hasHeightForWidth()) self.line.setSizePolicy(sizePolicy1) palette = QPalette() @@ -85,9 +114,9 @@ def setupUi(self, Geometry): palette.setBrush(QPalette.Active, QPalette.ToolTipText, brush) brush8 = QBrush(QColor(0, 0, 0, 127)) brush8.setStyle(Qt.SolidPattern) -#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) + # if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) palette.setBrush(QPalette.Active, QPalette.PlaceholderText, brush8) -#endif + # endif palette.setBrush(QPalette.Active, QPalette.Accent, brush6) palette.setBrush(QPalette.Inactive, QPalette.WindowText, brush) palette.setBrush(QPalette.Inactive, QPalette.Button, brush1) @@ -104,9 +133,9 @@ def setupUi(self, Geometry): palette.setBrush(QPalette.Inactive, QPalette.AlternateBase, brush2) palette.setBrush(QPalette.Inactive, QPalette.ToolTipBase, brush7) palette.setBrush(QPalette.Inactive, QPalette.ToolTipText, brush) -#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) + # if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) palette.setBrush(QPalette.Inactive, QPalette.PlaceholderText, brush8) -#endif + # endif palette.setBrush(QPalette.Inactive, QPalette.Accent, brush6) palette.setBrush(QPalette.Disabled, QPalette.WindowText, brush4) palette.setBrush(QPalette.Disabled, QPalette.Button, brush1) @@ -125,9 +154,9 @@ def setupUi(self, Geometry): palette.setBrush(QPalette.Disabled, QPalette.ToolTipText, brush) brush9 = QBrush(QColor(127, 0, 0, 127)) brush9.setStyle(Qt.SolidPattern) -#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) + # if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) palette.setBrush(QPalette.Disabled, QPalette.PlaceholderText, brush9) -#endif + # endif brush10 = QBrush(QColor(255, 76, 76, 255)) brush10.setStyle(Qt.SolidPattern) palette.setBrush(QPalette.Disabled, QPalette.Accent, brush10) @@ -140,10 +169,10 @@ def setupUi(self, Geometry): self.verticalLayout_2.addWidget(self.line) self.horizontalLayout = QHBoxLayout() - self.horizontalLayout.setObjectName(u"horizontalLayout") + self.horizontalLayout.setObjectName("horizontalLayout") self.horizontalLayout.setContentsMargins(-1, 15, -1, 15) self.multiplier_label = QLabel(Geometry) - self.multiplier_label.setObjectName(u"multiplier_label") + self.multiplier_label.setObjectName("multiplier_label") sizePolicy.setHeightForWidth(self.multiplier_label.sizePolicy().hasHeightForWidth()) self.multiplier_label.setSizePolicy(sizePolicy) self.multiplier_label.setFrameShape(QFrame.StyledPanel) @@ -154,14 +183,14 @@ def setupUi(self, Geometry): self.horizontalLayout.addWidget(self.multiplier_label) self.line_4 = QFrame(Geometry) - self.line_4.setObjectName(u"line_4") + self.line_4.setObjectName("line_4") self.line_4.setFrameShape(QFrame.VLine) self.line_4.setFrameShadow(QFrame.Sunken) self.horizontalLayout.addWidget(self.line_4) self.multiplier = QLineEdit(Geometry) - self.multiplier.setObjectName(u"multiplier") + self.multiplier.setObjectName("multiplier") sizePolicy2 = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) sizePolicy2.setHorizontalStretch(0) sizePolicy2.setVerticalStretch(0) @@ -170,18 +199,17 @@ def setupUi(self, Geometry): self.horizontalLayout.addWidget(self.multiplier) - self.verticalLayout_2.addLayout(self.horizontalLayout) self.select_geometry_label = QLabel(Geometry) - self.select_geometry_label.setObjectName(u"select_geometry_label") + self.select_geometry_label.setObjectName("select_geometry_label") self.select_geometry_label.setFont(font) self.select_geometry_label.setAlignment(Qt.AlignCenter) self.verticalLayout_2.addWidget(self.select_geometry_label) self.line_2 = QFrame(Geometry) - self.line_2.setObjectName(u"line_2") + self.line_2.setObjectName("line_2") sizePolicy1.setHeightForWidth(self.line_2.sizePolicy().hasHeightForWidth()) self.line_2.setSizePolicy(sizePolicy1) palette1 = QPalette() @@ -200,9 +228,9 @@ def setupUi(self, Geometry): palette1.setBrush(QPalette.Active, QPalette.AlternateBase, brush2) palette1.setBrush(QPalette.Active, QPalette.ToolTipBase, brush7) palette1.setBrush(QPalette.Active, QPalette.ToolTipText, brush) -#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) + # if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) palette1.setBrush(QPalette.Active, QPalette.PlaceholderText, brush8) -#endif + # endif palette1.setBrush(QPalette.Active, QPalette.Accent, brush6) palette1.setBrush(QPalette.Inactive, QPalette.WindowText, brush) palette1.setBrush(QPalette.Inactive, QPalette.Button, brush1) @@ -219,9 +247,9 @@ def setupUi(self, Geometry): palette1.setBrush(QPalette.Inactive, QPalette.AlternateBase, brush2) palette1.setBrush(QPalette.Inactive, QPalette.ToolTipBase, brush7) palette1.setBrush(QPalette.Inactive, QPalette.ToolTipText, brush) -#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) + # if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) palette1.setBrush(QPalette.Inactive, QPalette.PlaceholderText, brush8) -#endif + # endif palette1.setBrush(QPalette.Inactive, QPalette.Accent, brush6) palette1.setBrush(QPalette.Disabled, QPalette.WindowText, brush4) palette1.setBrush(QPalette.Disabled, QPalette.Button, brush1) @@ -238,9 +266,9 @@ def setupUi(self, Geometry): palette1.setBrush(QPalette.Disabled, QPalette.AlternateBase, brush1) palette1.setBrush(QPalette.Disabled, QPalette.ToolTipBase, brush7) palette1.setBrush(QPalette.Disabled, QPalette.ToolTipText, brush) -#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) + # if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) palette1.setBrush(QPalette.Disabled, QPalette.PlaceholderText, brush9) -#endif + # endif palette1.setBrush(QPalette.Disabled, QPalette.Accent, brush10) self.line_2.setPalette(palette1) font1 = QFont() @@ -254,16 +282,16 @@ def setupUi(self, Geometry): self.verticalLayout_2.addWidget(self.line_2) self.horizontalLayout_2 = QHBoxLayout() - self.horizontalLayout_2.setObjectName(u"horizontalLayout_2") + self.horizontalLayout_2.setObjectName("horizontalLayout_2") self.horizontalLayout_2.setContentsMargins(0, 15, 0, 15) self.geometry_label = QLabel(Geometry) - self.geometry_label.setObjectName(u"geometry_label") + self.geometry_label.setObjectName("geometry_label") self.geometry_label.setFrameShape(QFrame.StyledPanel) self.horizontalLayout_2.addWidget(self.geometry_label) self.line_3 = QFrame(Geometry) - self.line_3.setObjectName(u"line_3") + self.line_3.setObjectName("line_3") self.line_3.setFrameShape(QFrame.VLine) self.line_3.setFrameShadow(QFrame.Sunken) @@ -272,15 +300,14 @@ def setupUi(self, Geometry): self.geometry_combo = QComboBox(Geometry) self.geometry_combo.addItem("") self.geometry_combo.addItem("") - self.geometry_combo.setObjectName(u"geometry_combo") + self.geometry_combo.setObjectName("geometry_combo") self.horizontalLayout_2.addWidget(self.geometry_combo) - self.verticalLayout_2.addLayout(self.horizontalLayout_2) self.geometry_plot_layout = QVBoxLayout() - self.geometry_plot_layout.setObjectName(u"geometry_plot_layout") + self.geometry_plot_layout.setObjectName("geometry_plot_layout") self.geometry_plot_layout.setContentsMargins(-1, 0, -1, -1) self.verticalLayout_2.addLayout(self.geometry_plot_layout) @@ -290,25 +317,24 @@ def setupUi(self, Geometry): self.verticalLayout_2.addItem(self.verticalSpacer) self.button_layout = QVBoxLayout() - self.button_layout.setObjectName(u"button_layout") + self.button_layout.setObjectName("button_layout") self.verticalLayout_2.addLayout(self.button_layout) - self.retranslateUi(Geometry) QMetaObject.connectSlotsByName(Geometry) + # setupUi def retranslateUi(self, Geometry): - Geometry.setWindowTitle(QCoreApplication.translate("Geometry", u"Form", None)) - self.dimension_multiplier_label.setText(QCoreApplication.translate("Geometry", u"Dimension multiplier", None)) - self.multiplier_label.setText(QCoreApplication.translate("Geometry", u"Value", None)) - self.multiplier.setText(QCoreApplication.translate("Geometry", u"1", None)) - self.select_geometry_label.setText(QCoreApplication.translate("Geometry", u"Select geometry", None)) - self.geometry_label.setText(QCoreApplication.translate("Geometry", u"Geometry", None)) - self.geometry_combo.setItemText(0, QCoreApplication.translate("Geometry", u"Box", None)) - self.geometry_combo.setItemText(1, QCoreApplication.translate("Geometry", u"Sphere", None)) + Geometry.setWindowTitle(QCoreApplication.translate("Geometry", "Form", None)) + self.dimension_multiplier_label.setText(QCoreApplication.translate("Geometry", "Dimension multiplier", None)) + self.multiplier_label.setText(QCoreApplication.translate("Geometry", "Value", None)) + self.multiplier.setText(QCoreApplication.translate("Geometry", "1", None)) + self.select_geometry_label.setText(QCoreApplication.translate("Geometry", "Select geometry", None)) + self.geometry_label.setText(QCoreApplication.translate("Geometry", "Geometry", None)) + self.geometry_combo.setItemText(0, QCoreApplication.translate("Geometry", "Box", None)) + self.geometry_combo.setItemText(1, QCoreApplication.translate("Geometry", "Sphere", None)) # retranslateUi - diff --git a/examples/ui/widget_example/widget_usage.py b/examples/ui/widget_example/widget_usage.py index 84062e83..10f8cc96 100644 --- a/examples/ui/widget_example/widget_usage.py +++ b/examples/ui/widget_example/widget_usage.py @@ -1,9 +1,11 @@ -from PySide6.QtWidgets import * -from PySide6.QtCore import * -from ansys.aedt.toolkits.common.ui.utils.widgets import * from random import randint import sys +from PySide6.QtCore import * +from PySide6.QtWidgets import * + +from ansys.aedt.toolkits.common.ui.utils.widgets import * + class ApplicationWindow(QMainWindow): def __init__(self): @@ -13,13 +15,9 @@ def __init__(self): self.py_window.setFixedSize(400, 400) # Instantiate the widgets - self.progress_bar = PyProgress(progress=0, - progress_color="#FFB71B", - background_color="#313131", - width=10) + self.progress_bar = PyProgress(progress=0, progress_color="#FFB71B", background_color="#313131", width=10) - self.icon_but = PyIconButton('icon_signal.svg', - tooltip_text="Example", is_active=True) + self.icon_but = PyIconButton("icon_signal.svg", tooltip_text="Example", is_active=True) # Set window layout @@ -31,7 +29,7 @@ def __init__(self): column_group1.setLayout(layout1) # Instantiate the widgets for the right column - self.icon = PyIcon('icon_signal.svg', "#FF0000") + self.icon = PyIcon("icon_signal.svg", "#FF0000") self.label = PyLabel("hola", font_size=15) self.divider = PyDiv(color="#FF0000", height=50, width=1) self.my_edit = PyLineEdit(place_holder_text="here your text") @@ -46,13 +44,18 @@ def __init__(self): self.py_window.layout.setAlignment(Qt.AlignCenter) self.logger = PyLogger() - self.combo_box = PyComboBox(text_list=['Option 1', 'Option 2', 'Option 3']) + self.combo_box = PyComboBox(text_list=["Option 1", "Option 2", "Option 3"]) self.credits = PyCredits(text="© 2023 MyApp Co.", version="1.2.3") - self.left_column = PyLeftColumn(text_title="Left Column", - text_title_size=8, - icon_close_path='icon_close.svg') - self.my_button = PyPushButton(text="My Button", radius=5, color="#000", bg_color="#fff", bg_color_hover="#eee", - bg_color_pressed="#ddd", font_size=10) + self.left_column = PyLeftColumn(text_title="Left Column", text_title_size=8, icon_close_path="icon_close.svg") + self.my_button = PyPushButton( + text="My Button", + radius=5, + color="#000", + bg_color="#fff", + bg_color_hover="#eee", + bg_color_pressed="#ddd", + font_size=10, + ) self.toggle = PyToggle(50, "#777", "#DDD", "#00BCFF", QEasingCurve.OutBounce) # Create a GroupBox for column 3 @@ -67,15 +70,11 @@ def __init__(self): column_group3.setLayout(layout3) # Title - self.title = PyTitleBar(parent=self, - app_parent=self, - logo_width=50) + self.title = PyTitleBar(parent=self, app_parent=self, logo_width=50) title_layout = QVBoxLayout() title_layout.addWidget(self.title) - self.left_menu = PyLeftMenu(parent=self, - app_parent=self, - icon_path='icon_signal.svg') + self.left_menu = PyLeftMenu(parent=self, app_parent=self, icon_path="icon_signal.svg") self.left_menu.setMinimumWidth(150) # Create a GroupBox for the PyWindow widget @@ -101,12 +100,12 @@ def __init__(self): btn_color="blue", btn_color_hover="darkblue", btn_color_pressed="lightblue", - icon_path='icon_signal.svg', + icon_path="icon_signal.svg", icon_color="black", icon_color_hover="darkgray", icon_color_pressed="lightgray", context_color="gray", - radius=10 + radius=10, ) main_layout.addWidget(self.right_column) main_layout.addWidget(window_group) diff --git a/pyproject.toml b/pyproject.toml index 33fe3559..a3a84128 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,15 +32,16 @@ dependencies = [ "qdarkstyle", "flask", "pydantic", + "jsonschema" ] [project.optional-dependencies] tests = [ - "pytest==7.4.3", + "pytest==8.0.0", "pytest-cov==4.1.0", ] doc = [ - "ansys-sphinx-theme==0.12.5", + "ansys-sphinx-theme==0.13.1", "numpydoc==1.6.0", "recommonmark==0.7.1", "sphinx==7.2.6", @@ -48,7 +49,7 @@ doc = [ "enum-tools[sphinx]==0.11.0", "sphinx-autoapi==3.0.0", "sphinx-autobuild==2021.3.14", - "sphinx-autodoc-typehints==1.25.2", + "sphinx-autodoc-typehints==1.25.3", "sphinx-gallery==0.15.0", "sphinx-notfound-page==1.0.0", "nbsphinx==0.9.3", diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index 0c30f9ee..00000000 --- a/pytest.ini +++ /dev/null @@ -1,5 +0,0 @@ -[pytest] -markers = - common: mark a test as common functionality - aedt: mark a test as aedt functionality - edb: mark a test as edb functionality diff --git a/src/ansys/aedt/toolkits/common/backend/api.py b/src/ansys/aedt/toolkits/common/backend/api.py index 3453204d..3a1c3c44 100644 --- a/src/ansys/aedt/toolkits/common/backend/api.py +++ b/src/ansys/aedt/toolkits/common/backend/api.py @@ -8,9 +8,9 @@ from typing import Optional from typing import Tuple -import psutil import pyaedt from pyaedt import Desktop +from pyaedt.generic.general_methods import active_sessions from pyaedt.misc import list_installed_ansysem from pydantic import ValidationError @@ -145,8 +145,8 @@ def set_properties(self, data: Dict[str, Any]): logger.debug(msg) return updated, msg - def launch_thread(self, process): - self.thread_manager.launch_thread(process) + def launch_thread(self, process) -> ThreadManager: + return self.thread_manager.launch_thread(process) def get_thread_status(self) -> ToolkitThreadStatus: """Get the toolkit thread status. @@ -176,7 +176,7 @@ def get_thread_status(self) -> ToolkitThreadStatus: return res @staticmethod - def installed_aedt_version(): + def installed_aedt_version() -> List: """ Get the installed AEDT versions. @@ -202,7 +202,7 @@ def installed_aedt_version(): logger.debug(str(installed_versions)) return installed_versions - def aedt_sessions(self): + def aedt_sessions(self) -> Dict[int, int]: """Get information for the active AEDT sessions. Returns @@ -215,30 +215,48 @@ def aedt_sessions(self): >>> from ansys.aedt.toolkits.common.backend.api import Common >>> toolkit_api = Common() >>> toolkit_api.aedt_sessions() - [[pid1, grpc_port1], [pid2, grpc_port2]] + {pid1: grpc_port1, pid2: grpc_port2} """ - res = [] + + res = {} if not self.properties.is_toolkit_busy and self.properties.aedt_version: - keys = ["ansysedt.exe"] - version = self.properties.aedt_version - if version and "." in version: - version = version[-4:].replace(".", "") - if version < "222": # pragma: no cover - version = version[:2] + "." + version[2] - for process in filter(lambda p: p.name() in keys, psutil.process_iter()): - cmd = process.cmdline() - if version in cmd[0]: - try: - grpc_index = cmd.index("-grpcsrv") + 1 - port = int(cmd[grpc_index]) - except (ValueError, IndexError): - port = -1 - res.append([process.pid, port]) + res = active_sessions( + version=self.properties.aedt_version, student_version=False, non_graphical=self.properties.non_graphical + ) + if res: # pragma: no cover logger.debug(f"Active AEDT sessions: {res}.") - else: # pragma: no cover + else: logger.debug("No active sessions.") return res + def wait_to_be_idle(self, timeout: Optional[int] = 60) -> bool: + """Wait for the thread to be idle and ready to accept new task. + + Parameters + ---------- + timeout : int, optional + Time out in seconds. The default is 60 seconds. + + Examples + -------- + >>> import time + >>> from ansys.aedt.toolkits.common.backend.api import AEDTCommon + >>> toolkit_api = AEDTCommon() + >>> toolkit_api.launch_aedt() + >>> toolkit_api.wait_to_be_idle() + >>> toolkit_api.get_design_names() + """ + time.sleep(1) + status = self.get_thread_status() + cont = 0 + while status == ToolkitThreadStatus.BUSY: + time.sleep(1) + cont += 1 + status = self.get_thread_status() + if timeout and cont == timeout: # pragma: no cover + return False + return True + class AEDTCommon(Common): """Provides common functions to control AEDT. @@ -295,7 +313,7 @@ def is_aedt_connected(self) -> Tuple[bool, str]: logger.debug(msg) return connected, msg - def launch_aedt(self): + def launch_aedt(self) -> bool: """Launch AEDT. This method is launched in a thread if grpc is enabled. AEDT is released once it is opened. @@ -469,13 +487,13 @@ def connect_design(self, app_name: Optional[str] = None): aedt_app = pyaedt.Hfss if design_name != "No Design": project_name = self.get_project_name(project_name) + active_design = design_name if design_name in self.properties.design_list[project_name]: self.aedtapp = self.desktop[[project_name, design_name]] - if self.aedtapp: - active_design = self.aedtapp.design_name - else: # pragma: no cover + if not self.aedtapp: # pragma: no cover logger.error("Wrong active project and design.") return False + active_design = self.aedtapp.design_name elif app_name in list(NAME_TO_AEDT_APP.keys()): design_name = pyaedt.generate_unique_name(app_name) aedt_app = getattr(pyaedt, NAME_TO_AEDT_APP[app_name]) @@ -701,34 +719,6 @@ def get_design_names(self) -> List[str]: return design_list - def wait_to_be_idle(self, timeout=60): - """Wait for the thread to be idle and ready to accept new task. - - Parameters - ---------- - timeout : int, optional - Time out in seconds. The default is 60 seconds. - - Examples - -------- - >>> import time - >>> from ansys.aedt.toolkits.common.backend.api import AEDTCommon - >>> toolkit_api = AEDTCommon() - >>> toolkit_api.launch_aedt() - >>> toolkit_api.wait_to_be_idle() - >>> toolkit_api.get_design_names() - """ - time.sleep(1) - status = self.get_thread_status() - cont = 0 - while status == ToolkitThreadStatus.BUSY: - time.sleep(1) - cont += 1 - status = self.get_thread_status() - if timeout and cont == timeout: - return False - return True - def _save_project_info(self): # Save project and design info new_properties = {} diff --git a/src/ansys/aedt/toolkits/common/backend/rest_api.py b/src/ansys/aedt/toolkits/common/backend/rest_api.py index 07efbd91..5a42b370 100644 --- a/src/ansys/aedt/toolkits/common/backend/rest_api.py +++ b/src/ansys/aedt/toolkits/common/backend/rest_api.py @@ -1,27 +1,29 @@ +import json + from flask import Flask from flask import jsonify from flask import request -from ansys.aedt.toolkits.common.backend.api import AEDTCommon from ansys.aedt.toolkits.common.backend.api import ToolkitThreadStatus from ansys.aedt.toolkits.common.backend.logger_handler import logger -toolkit_api = AEDTCommon() +try: + from api import ToolkitBackend -app = Flask(__name__) + toolkit_api = ToolkitBackend() +except ImportError: + from ansys.aedt.toolkits.common.backend.api import AEDTCommon + toolkit_api = AEDTCommon() -# Generic services +app = Flask(__name__) @app.route("/health", methods=["GET"]) def get_health(): logger.info("[GET] /health (check if the server is healthy)") - desktop_connected, msg = toolkit_api.is_aedt_connected() - if desktop_connected: - return jsonify(msg), 200 - else: - return jsonify(msg), 200 + _, msg = toolkit_api.is_aedt_connected() + return jsonify(msg), 200 @app.route("/status", methods=["GET"]) @@ -33,6 +35,25 @@ def get_status(): return jsonify(status.value), 500 +@app.route("/wait_thread", methods=["GET"]) +def wait_thread(): + logger.info("[GET] /wait_thread (wait until the thread is idle).") + + body = request.data + + if not body: + msg = "Body is empty." + logger.error(msg) + return jsonify(msg), 500 + + response = toolkit_api.wait_to_be_idle(int(body.decode())) + + if response: + return jsonify("AEDT properties loaded"), 200 + else: + return jsonify("Fail to launch to AEDT"), 500 + + @app.route("/properties", methods=["GET"]) def get_properties(): logger.info("[GET] /properties (get toolkit properties).") @@ -51,26 +72,24 @@ def set_properties(): @app.route("/installed_versions", methods=["GET"]) -def installed_aedt_version_call(): +def installed_aedt_version(): logger.info("[GET] /version (get the version)") return jsonify(toolkit_api.installed_aedt_version()), 200 @app.route("/aedt_sessions", methods=["GET"]) -def aedt_sessions_call(): - logger.info("[GET] /aedt_sessions (aedt sessions for specific version)") - +def aedt_sessions(): + logger.info("[GET] /aedt_sessions (aedt sessions for specific version).") response = toolkit_api.aedt_sessions() - - if isinstance(response, list): + if isinstance(response, dict): return jsonify(response), 200 else: return jsonify(response), 500 @app.route("/launch_aedt", methods=["POST"]) -def launch_aedt_call(): - logger.info("[POST] /launch_aedt (launch or connect AEDT)") +def launch_aedt(): + logger.info("[POST] /launch_aedt (launch or connect AEDT).") response = toolkit_api.launch_thread(toolkit_api.launch_aedt) if response: @@ -80,16 +99,18 @@ def launch_aedt_call(): @app.route("/open_project", methods=["POST"]) -def open_project_call(): - logger.info("[POST] /open_project (open AEDT project)") +def open_project(): + logger.info("[POST] /open_project (open AEDT project).") body = request.data if not body: - msg = "body is empty!" + msg = "Body is empty." logger.error(msg) - return jsonify("body is empty!"), 500 + return jsonify(msg), 500 - response = toolkit_api.open_project(body.decode()) + data = body.decode("utf-8") + project_path = json.loads(data) + response = toolkit_api.open_project(project_path) if response: return jsonify("Project opened"), 200 @@ -98,17 +119,17 @@ def open_project_call(): @app.route("/close_aedt", methods=["POST"]) -def close_aedt_call(): - logger.info("[POST] /close_aedt (close AEDT)") +def close_aedt(): + logger.info("[POST] /close_aedt (close AEDT).") body = request.json aedt_keys = ["close_projects", "close_on_exit"] if not body: - msg = "body is empty!" + msg = "Body is empty." logger.error(msg) return jsonify(msg), 500 elif not isinstance(body, dict) or not all(item in body for item in set(aedt_keys)): - msg = "body not correct" + msg = "Body not correct." logger.error(msg) return jsonify(msg), 500 @@ -123,15 +144,15 @@ def close_aedt_call(): @app.route("/connect_design", methods=["POST"]) -def connect_design_call(): - logger.info("[POST] /connect_design (connect or create a design)") +def connect_design(): + logger.info("[POST] /connect_design (connect or create a design).") body = request.json if not body: - msg = "body is empty!" + msg = "Body is empty." logger.error(msg) - return jsonify("body is empty!"), 500 + return jsonify(msg), 500 response = toolkit_api.connect_design(body["aedtapp"]) @@ -142,15 +163,15 @@ def connect_design_call(): @app.route("/save_project", methods=["POST"]) -def save_project_call(): - logger.info("[POST] /save_project (Save AEDT project)") +def save_project(): + logger.info("[POST] /save_project (Save AEDT project).") body = request.json if not body: - msg = "body is empty!" + msg = "Body is empty." logger.error(msg) - return jsonify("body is empty!"), 500 + return jsonify(msg), 500 response = toolkit_api.save_project(body) @@ -160,12 +181,10 @@ def save_project_call(): return jsonify(response), 500 -@app.route("/get_design_names", methods=["GET"]) -def get_design_names_call(): - logger.info("[GET] /get_design_names (aedt designs for specific project)") - +@app.route("/design_names", methods=["GET"]) +def get_design_names(): + logger.info("[GET] /design_names (aedt designs for specific project).") response = toolkit_api.get_design_names() - if isinstance(response, list): return jsonify(response), 200 else: diff --git a/src/ansys/aedt/toolkits/common/ui/actions_generic.py b/src/ansys/aedt/toolkits/common/ui/actions_generic.py index e7ee9fa5..ff0a4307 100644 --- a/src/ansys/aedt/toolkits/common/ui/actions_generic.py +++ b/src/ansys/aedt/toolkits/common/ui/actions_generic.py @@ -120,10 +120,11 @@ def launch_aedt(self, selected_version, selected_process, non_graphical=False): be_properties["aedt_version"] = selected_version be_properties["non_graphical"] = non_graphical if selected_process != "New Session": + be_properties["non_graphical"] = False text_splitted = selected_process.split(" ") - if len(text_splitted) == 5: + if len(text_splitted) == 4: be_properties["use_grpc"] = True - be_properties["selected_process"] = int(text_splitted[4]) + be_properties["selected_process"] = int(text_splitted[3]) else: be_properties["use_grpc"] = False be_properties["selected_process"] = int(text_splitted[1]) diff --git a/src/ansys/aedt/toolkits/common/ui/common_windows/home_menu.py b/src/ansys/aedt/toolkits/common/ui/common_windows/home_menu.py index a53d2ba0..dae63f69 100644 --- a/src/ansys/aedt/toolkits/common/ui/common_windows/home_menu.py +++ b/src/ansys/aedt/toolkits/common/ui/common_windows/home_menu.py @@ -1,4 +1,6 @@ -from PySide6.QtWidgets import * +from PySide6.QtWidgets import QFrame +from PySide6.QtWidgets import QSizePolicy +from PySide6.QtWidgets import QSpacerItem class HomeMenu(object): @@ -30,7 +32,7 @@ def setup(self): height=40, width=[100, 50, 100], label=["AEDT Mode", "File Mode"], - font_size=12 + font_size=12, ) self.ui.left_column.menus.file_mode_select_row = row_returns[0] @@ -53,11 +55,13 @@ def setup(self): self.ui.left_column.menus.home_vertical_layout.addItem(spacer) # Browse row - row_returns = self.ui.add_icon_button(self.ui.left_column.menus.home_vertical_layout, - icon=self.ui.images_load.icon_path("icon_folder_open.svg"), - height=40, - width=[40, 160], - text='Browse...') + row_returns = self.ui.add_icon_button( + self.ui.left_column.menus.home_vertical_layout, + icon=self.ui.images_load.icon_path("icon_folder_open.svg"), + height=40, + width=[40, 160], + text="Browse...", + ) self.ui.left_column.menus.browse_file_group = row_returns[0] self.browse = row_returns[1] @@ -68,11 +72,13 @@ def setup(self): self.browse.clicked.connect(lambda: self.browse_file()) # Project row - row_returns = self.ui.add_combobox(self.ui.left_column.menus.home_vertical_layout, - height=40, - width=[75, 135], - label="Project", - combobox_list=["No Project"]) + row_returns = self.ui.add_combobox( + self.ui.left_column.menus.home_vertical_layout, + height=40, + width=[75, 135], + label="Project", + combobox_list=["No Project"], + ) self.ui.left_column.menus.browse_project_group = row_returns[0] self.project = row_returns[1] @@ -81,18 +87,19 @@ def setup(self): self.project_combobox.setEnabled(False) # Design row - row_returns = self.ui.add_combobox(self.ui.left_column.menus.home_vertical_layout, - height=40, - width=[75, 135], - label="Design", - combobox_list=["No Design"]) + row_returns = self.ui.add_combobox( + self.ui.left_column.menus.home_vertical_layout, + height=40, + width=[75, 135], + label="Design", + combobox_list=["No Design"], + ) self.ui.left_column.menus.browse_design_group = row_returns[0] self.design = row_returns[1] self.design_combobox = row_returns[2] def mode_changed(self): - if self.mode == "file_based": self.mode = "aedt_based" self.project.setVisible(True) @@ -109,19 +116,23 @@ def mode_changed(self): self.design_combobox.setVisible(False) self.file.setVisible(True) self.browse.setVisible(True) - self.ui.logger.log(f'Setting Mode to {self.mode}') + self.ui.logger.log(f"Setting Mode to {self.mode}") def browse_file(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog if self.mode == "file_based": - file, _ = QFileDialog.getOpenFileName(self.ui.app, "QFileDialog.getOpenFileName()", "", - "All Files (*)", - options=options) + file, _ = QFileDialog.getOpenFileName( + self.ui.app, "QFileDialog.getOpenFileName()", "", "All Files (*)", options=options + ) else: - file, _ = QFileDialog.getOpenFileName(self.ui.app, "QFileDialog.getOpenFileName()", "", - "Ansys Electronics Desktop Project Files (*.aedt *.aedtz)", - options=options) + file, _ = QFileDialog.getOpenFileName( + self.ui.app, + "QFileDialog.getOpenFileName()", + "", + "Ansys Electronics Desktop Project Files (*.aedt *.aedtz)", + options=options, + ) if file != "": self.file.setText(file) diff --git a/src/ansys/aedt/toolkits/common/ui/common_windows/main_window.py b/src/ansys/aedt/toolkits/common/ui/common_windows/main_window.py index 291a6614..e801ba94 100644 --- a/src/ansys/aedt/toolkits/common/ui/common_windows/main_window.py +++ b/src/ansys/aedt/toolkits/common/ui/common_windows/main_window.py @@ -1,6 +1,6 @@ -from PySide6.QtCore import * -from PySide6.QtSvgWidgets import * -from PySide6.QtWidgets import * +from PySide6.QtCore import Qt +from PySide6.QtSvgWidgets import QSvgWidget +from PySide6.QtWidgets import QLabel from ansys.aedt.toolkits.common.ui.models import general_settings diff --git a/src/ansys/aedt/toolkits/common/ui/common_windows/settings_column.py b/src/ansys/aedt/toolkits/common/ui/common_windows/settings_column.py index ccb99de9..709cc9d3 100644 --- a/src/ansys/aedt/toolkits/common/ui/common_windows/settings_column.py +++ b/src/ansys/aedt/toolkits/common/ui/common_windows/settings_column.py @@ -1,10 +1,12 @@ -from PySide6.QtCore import QObject, QThread, QTimer -from PySide6.QtWidgets import * import os +from PySide6.QtCore import QObject +from PySide6.QtCore import QThread +from PySide6.QtCore import QTimer +from PySide6.QtWidgets import QFileDialog -class SettingsMenu(QObject): +class SettingsMenu(QObject): def __init__(self, main_window): super(SettingsMenu, self).__init__() self.main_window = main_window @@ -64,7 +66,8 @@ def setup(self): height=40, width=[75, 135], label="AEDT Session", - combobox_list=[]) + combobox_list=[], + ) self.ui.right_column.menus.browse_aedt_session = row_returns[0] self.aedt_session_label = row_returns[1] @@ -79,7 +82,7 @@ def setup(self): height=40, width=[100, 50, 100], label=["Graphical", "Non-graphical"], - font_size=12 + font_size=12, ) self.ui.left_column.menus.non_graphical_select_row = row_returns[0] @@ -93,11 +96,13 @@ def setup(self): ) # Browse row - row_returns = self.ui.add_icon_button(self.ui.right_column.menus.settings_vertical_layout, - icon=self.ui.images_load.icon_path("icon_folder_open.svg"), - height=40, - width=[40, 160], - text='Browse...') + row_returns = self.ui.add_icon_button( + self.ui.right_column.menus.settings_vertical_layout, + icon=self.ui.images_load.icon_path("icon_folder_open.svg"), + height=40, + width=[40, 160], + text="Browse...", + ) self.ui.right_column.menus.browse_file_group = row_returns[0] self.browse = row_returns[1] @@ -116,7 +121,8 @@ def setup(self): num_buttons=1, height=40, width=[200], - text=["Connect to AEDT"]) + text=["Connect to AEDT"], + ) self.ui.right_column.menus.connect_aedt_layout = row_returns[0] self.connect_aedt = row_returns[1] @@ -132,11 +138,11 @@ def process_id(self): self.aedt_session.addItem("New Session") if self.aedt_version.currentText() and self.aedt_version.currentText() != "AEDT not installed": sessions = self.app.find_process_ids(self.aedt_version.currentText()) - for session in sessions: - if session[1] == -1: - self.aedt_session.addItem("Process {}".format(session[0], session[1])) + for pid in sessions: + if sessions[pid] == -1: + self.aedt_session.addItem("Process {}".format(pid)) else: - self.aedt_session.addItem("Process {} on Grpc {}".format(session[0], session[1])) + self.aedt_session.addItem("Grpc on port {}".format(sessions[pid])) def launch_aedt(self): selected_session = self.aedt_session.currentText() @@ -148,9 +154,9 @@ def launch_aedt(self): self.aedt_thread = QThread() - self.aedt_thread.started.connect(lambda: self.app.launch_aedt(selected_version, - selected_session, - non_graphical)) + self.aedt_thread.started.connect( + lambda: self.app.launch_aedt(selected_version, selected_session, non_graphical) + ) self.check_status_timer.start() @@ -191,8 +197,12 @@ def browse_file(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog - file, _ = QFileDialog.getOpenFileName(self.ui.app, "QFileDialog.getOpenFileName()", "", - "Ansys Electronics Desktop Project Files (*.aedt *.aedtz)", - options=options) + file, _ = QFileDialog.getOpenFileName( + self.ui.app, + "QFileDialog.getOpenFileName()", + "", + "Ansys Electronics Desktop Project Files (*.aedt *.aedtz)", + options=options, + ) if file != "": self.file.setText(file) diff --git a/src/ansys/aedt/toolkits/common/ui/main_window/main_window_layout.py b/src/ansys/aedt/toolkits/common/ui/main_window/main_window_layout.py index 27da0f87..951f762a 100644 --- a/src/ansys/aedt/toolkits/common/ui/main_window/main_window_layout.py +++ b/src/ansys/aedt/toolkits/common/ui/main_window/main_window_layout.py @@ -1,6 +1,10 @@ from PySide6 import QtCore from PySide6 import QtGui -from PySide6.QtWidgets import * +from PySide6.QtWidgets import QFrame +from PySide6.QtWidgets import QHBoxLayout +from PySide6.QtWidgets import QSizePolicy +from PySide6.QtWidgets import QVBoxLayout +from PySide6.QtWidgets import QWidget from ansys.aedt.toolkits.common.ui.models import general_settings from ansys.aedt.toolkits.common.ui.utils.images.load_images import LoadImages diff --git a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_combo_box/py_combo_box.py b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_combo_box/py_combo_box.py index ccc10476..944eeb5c 100644 --- a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_combo_box/py_combo_box.py +++ b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_combo_box/py_combo_box.py @@ -1,6 +1,5 @@ -from PySide6.QtWidgets import * -from PySide6.QtGui import * -from PySide6.QtCore import * +from PySide6.QtCore import Qt +from PySide6.QtWidgets import QComboBox from ansys.aedt.toolkits.common.ui.utils.widgets.py_combo_box.styles import Styles @@ -44,13 +43,16 @@ class PyComboBox(QComboBox): ... window = MyApp() ... sys.exit(app.exec()) """ - def __init__(self, - text_list, - radius=5, - color="#000000", - bg_color="#FFFFFF", - bg_color_hover="#FFFFFF", - bg_color_pressed="#FFFFFF"): + + def __init__( + self, + text_list, + radius=5, + color="#000000", + bg_color="#FFFFFF", + bg_color_hover="#FFFFFF", + bg_color_pressed="#FFFFFF", + ): super().__init__() self.addItems(text_list) self.setCursor(Qt.PointingHandCursor) @@ -60,6 +62,6 @@ def __init__(self, _radius=radius, _bg_color=bg_color, _bg_color_hover=bg_color_hover, - _bg_color_pressed=bg_color_pressed + _bg_color_pressed=bg_color_pressed, ) self.setStyleSheet(custom_style) diff --git a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_combo_box/styles.py b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_combo_box/styles.py index 6c345260..93b7f196 100644 --- a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_combo_box/styles.py +++ b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_combo_box/styles.py @@ -1,35 +1,32 @@ class Styles(object): - bg_style = ''' + bg_style = """ QComboBox {{ border: none; padding: 10px; color: {_color}; - border-radius: {_radius}; + border-radius: {_radius}; background-color: {_bg_color}; selection-background-color: red; }} QComboBox:editable {{ background: black; }} - QComboBox:!editable, QComboBox::drop-down:editable {{ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #E1E1E1, stop: 0.4 #DDDDDD, stop: 0.5 #D8D8D8, stop: 1.0 #D3D3D3); }} - /* QComboBox gets the "on" state when the popup is open */ QComboBox:!editable:on, QComboBox::drop-down:editable:on {{ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #D3D3D3, stop: 0.4 #D8D8D8, stop: 0.5 #DDDDDD, stop: 1.0 #E1E1E1); }} - QComboBox:on {{ /* shift the text when the popup opens */ padding-top: 3px; padding-left: 4px; }} - QComboBox:drop-down {{ + QComboBox:drop-down {{ subcontrol-origin: padding; subcontrol-position: top right; width: 15px; @@ -67,4 +64,4 @@ class Styles(object): selection-color: transparent; selection-background-color: transparent; }} - ''' + """ diff --git a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_credits/py_credits.py b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_credits/py_credits.py index b01fb137..072b0057 100644 --- a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_credits/py_credits.py +++ b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_credits/py_credits.py @@ -1,5 +1,10 @@ -from PySide6.QtWidgets import * -from PySide6.QtGui import * +from PySide6.QtCore import Qt +from PySide6.QtWidgets import QFrame +from PySide6.QtWidgets import QHBoxLayout +from PySide6.QtWidgets import QLabel +from PySide6.QtWidgets import QSizePolicy +from PySide6.QtWidgets import QSpacerItem +from PySide6.QtWidgets import QWidget class PyCredits(QWidget): @@ -47,15 +52,15 @@ class PyCredits(QWidget): """ def __init__( - self, - text="© 2023 MyApp Co.", - version="0.0.1", - bg="#FFFFFF", - font_family="Segoe UI", - text_size=9, - text_description_color="#00000", - radius=8, - padding=10 + self, + text="© 2023 MyApp Co.", + version="0.0.1", + bg="#FFFFFF", + font_family="Segoe UI", + text_size=9, + text_description_color="#00000", + radius=8, + padding=10, ): super().__init__() diff --git a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_div/py_div.py b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_div/py_div.py index e0506359..3a818594 100644 --- a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_div/py_div.py +++ b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_div/py_div.py @@ -1,4 +1,6 @@ -from PySide6.QtWidgets import * +from PySide6.QtWidgets import QFrame +from PySide6.QtWidgets import QHBoxLayout +from PySide6.QtWidgets import QWidget class PyDiv(QWidget): @@ -34,10 +36,8 @@ class PyDiv(QWidget): >>> window.show() >>> app.exec() """ - def __init__(self, - color="#000000", - height=0, - width=20): + + def __init__(self, color="#000000", height=0, width=20): super().__init__() self.layout = QHBoxLayout(self) diff --git a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_icon/py_icon.py b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_icon/py_icon.py index a18cec9c..b4e40d3a 100644 --- a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_icon/py_icon.py +++ b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_icon/py_icon.py @@ -1,6 +1,9 @@ -from PySide6.QtWidgets import QWidget, QVBoxLayout, QLabel from PySide6.QtCore import Qt -from PySide6.QtGui import QPixmap, QPainter +from PySide6.QtGui import QPainter +from PySide6.QtGui import QPixmap +from PySide6.QtWidgets import QLabel +from PySide6.QtWidgets import QVBoxLayout +from PySide6.QtWidgets import QWidget class PyIcon(QWidget): @@ -37,11 +40,7 @@ class PyIcon(QWidget): >>> app.exec() """ - def __init__( - self, - icon_path, - icon_color="#000000" - ): + def __init__(self, icon_path, icon_color="#000000"): super().__init__() self._icon_path = icon_path diff --git a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_icon_button/py_icon_button.py b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_icon_button/py_icon_button.py index 6a0d8f28..e3b205a3 100644 --- a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_icon_button/py_icon_button.py +++ b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_icon_button/py_icon_button.py @@ -1,6 +1,13 @@ -from PySide6.QtWidgets import QPushButton, QLabel, QGraphicsDropShadowEffect -from PySide6.QtGui import QPainter, QColor, QPixmap, QBrush -from PySide6.QtCore import QEvent, QRect, QPoint, Qt +from PySide6.QtCore import QEvent +from PySide6.QtCore import QRect +from PySide6.QtCore import Qt +from PySide6.QtGui import QBrush +from PySide6.QtGui import QColor +from PySide6.QtGui import QPainter +from PySide6.QtGui import QPixmap +from PySide6.QtWidgets import QGraphicsDropShadowEffect +from PySide6.QtWidgets import QLabel +from PySide6.QtWidgets import QPushButton class PyIconButton(QPushButton): @@ -72,26 +79,27 @@ class PyIconButton(QPushButton): >>> window.show() >>> app.exec() """ + def __init__( - self, - icon_path=None, - tooltip_text="", - btn_id=None, - width=30, - height=30, - radius=8, - bg_color="#343b48", - bg_color_hover="#3c4454", - bg_color_pressed="#2c313c", - icon_color="#c3ccdf", - icon_color_hover="#dce1ec", - icon_color_pressed="#edf0f5", - icon_color_active="#f5f6f9", - dark_one="#1b1e23", - text_foreground="#8a95aa", - context_color="#568af2", - top_margin=40, - is_active=False + self, + icon_path=None, + tooltip_text="", + btn_id=None, + width=30, + height=30, + radius=8, + bg_color="#343b48", + bg_color_hover="#3c4454", + bg_color_pressed="#2c313c", + icon_color="#c3ccdf", + icon_color_hover="#dce1ec", + icon_color_pressed="#edf0f5", + icon_color_active="#f5f6f9", + dark_one="#1b1e23", + text_foreground="#8a95aa", + context_color="#568af2", + top_margin=40, + is_active=False, ): super().__init__() @@ -118,11 +126,7 @@ def __init__( # TOOLTIP if tooltip_text: self._tooltip_text = tooltip_text - self._tooltip = Tooltip( - tooltip_text, - dark_one, - text_foreground - ) + self._tooltip = Tooltip(tooltip_text, dark_one, text_foreground) self._tooltip.hide() def set_active(self, is_active): @@ -149,11 +153,7 @@ def paintEvent(self, event): rect = QRect(0, 0, self.width(), self.height()) paint.setPen(Qt.NoPen) paint.setBrush(brush) - paint.drawRoundedRect( - rect, - self._set_border_radius, - self._set_border_radius - ) + paint.drawRoundedRect(rect, self._set_border_radius, self._set_border_radius) # DRAW ICONS self.icon_paint(paint, self._set_icon_path, rect) @@ -204,11 +204,7 @@ def icon_paint(self, qp, image, rect): painter.fillRect(icon.rect(), self._icon_color_active) else: painter.fillRect(icon.rect(), self._set_icon_color) - qp.drawPixmap( - (rect.width() - icon.width()) / 2, - (rect.height() - icon.height()) / 2, - icon - ) + qp.drawPixmap((rect.width() - icon.width()) / 2, (rect.height() - icon.height()) / 2, icon) painter.end() def set_icon(self, icon_path): @@ -217,9 +213,9 @@ def set_icon(self, icon_path): class Tooltip(QLabel): - style_tooltip = """ - QLabel {{ - background-color: {_dark_one}; + style_tooltip = """ + QLabel {{ + background-color: {_dark_one}; color: {_text_foreground}; padding-left: 10px; padding-right: 10px; @@ -229,19 +225,11 @@ class Tooltip(QLabel): }} """ - def __init__( - self, - tooltip, - dark_one, - text_foreground - ): + def __init__(self, tooltip, dark_one, text_foreground): QLabel.__init__(self) - style = self.style_tooltip.format( - _dark_one=dark_one, - _text_foreground=text_foreground - ) - self.setObjectName(u"label_tooltip") + style = self.style_tooltip.format(_dark_one=dark_one, _text_foreground=text_foreground) + self.setObjectName("label_tooltip") self.setStyleSheet(style) self.setMinimumHeight(34) self.setText(tooltip) diff --git a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_label/py_label.py b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_label/py_label.py index d71b8107..b6213b80 100644 --- a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_label/py_label.py +++ b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_label/py_label.py @@ -19,11 +19,7 @@ class PyLabel(QLabel): Weight for the font, by default is 'bold'. """ - def __init__(self, - text="", - color="#000000", - font_size=8, - font_weight='bold'): + def __init__(self, text="", color="#000000", font_size=8, font_weight="bold"): super().__init__() text and self.setText(text) self.apply_stylesheet(color, font_size, font_weight) diff --git a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_left_column/py_left_button.py b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_left_column/py_left_button.py index c294b915..6f4419e5 100644 --- a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_left_column/py_left_button.py +++ b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_left_column/py_left_button.py @@ -1,6 +1,11 @@ -from PySide6.QtWidgets import * -from PySide6.QtCore import * -from PySide6.QtGui import * +from PySide6.QtCore import QEvent +from PySide6.QtCore import QRect +from PySide6.QtCore import Qt +from PySide6.QtGui import QBrush +from PySide6.QtGui import QColor +from PySide6.QtGui import QPainter +from PySide6.QtGui import QPixmap +from PySide6.QtWidgets import QPushButton class PyLeftButton(QPushButton): @@ -43,24 +48,25 @@ class PyLeftButton(QPushButton): Whether the button is active. """ + def __init__( - self, - btn_id=None, - width=30, - height=30, - radius=8, - bg_color="#343b48", - bg_color_hover="#3c4454", - bg_color_pressed="#2c313c", - icon_color="#c3ccdf", - icon_color_hover="#dce1ec", - icon_color_pressed="#edf0f5", - icon_color_active="#f5f6f9", - icon_path="no_icon.svg", - dark_one="#1b1e23", - context_color="#568af2", - text_foreground="#8a95aa", - is_active=False + self, + btn_id=None, + width=30, + height=30, + radius=8, + bg_color="#343b48", + bg_color_hover="#3c4454", + bg_color_pressed="#2c313c", + icon_color="#c3ccdf", + icon_color_hover="#dce1ec", + icon_color_pressed="#edf0f5", + icon_color_active="#f5f6f9", + icon_path="no_icon.svg", + dark_one="#1b1e23", + context_color="#568af2", + text_foreground="#8a95aa", + is_active=False, ): super().__init__() @@ -106,11 +112,7 @@ def paintEvent(self, event): rect = QRect(0, 0, self.width(), self.height()) paint.setPen(Qt.NoPen) paint.setBrush(brush) - paint.drawRoundedRect( - rect, - self._set_border_radius, - self._set_border_radius - ) + paint.drawRoundedRect(rect, self._set_border_radius, self._set_border_radius) self.icon_paint(paint, self._set_icon_path, rect) @@ -162,11 +164,7 @@ def icon_paint(self, qp, image, rect): painter.fillRect(icon.rect(), self._context_color) else: painter.fillRect(icon.rect(), self._set_icon_color) - qp.drawPixmap( - (rect.width() - icon.width()) / 2, - (rect.height() - icon.height()) / 2, - icon - ) + qp.drawPixmap((rect.width() - icon.width()) / 2, (rect.height() - icon.height()) / 2, icon) painter.end() def set_icon(self, icon_path): diff --git a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_left_column/py_left_column.py b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_left_column/py_left_column.py index af00db55..25a5e441 100644 --- a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_left_column/py_left_column.py +++ b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_left_column/py_left_column.py @@ -1,9 +1,14 @@ -from PySide6.QtWidgets import * -from PySide6.QtCore import * +from PySide6.QtCore import Qt +from PySide6.QtCore import Signal +from PySide6.QtWidgets import QFrame +from PySide6.QtWidgets import QHBoxLayout +from PySide6.QtWidgets import QLabel +from PySide6.QtWidgets import QVBoxLayout +from PySide6.QtWidgets import QWidget -from ansys.aedt.toolkits.common.ui.utils.widgets.py_left_column.py_left_button import PyLeftButton -from ansys.aedt.toolkits.common.ui.utils.widgets.py_icon.py_icon import PyIcon from ansys.aedt.toolkits.common.ui.utils.ui_templates.columns.ui_left_column import Ui_LeftColumn +from ansys.aedt.toolkits.common.ui.utils.widgets.py_icon.py_icon import PyIcon +from ansys.aedt.toolkits.common.ui.utils.widgets.py_left_column.py_left_button import PyLeftButton class PyLeftColumn(QWidget): @@ -11,22 +16,22 @@ class PyLeftColumn(QWidget): released = Signal(object, name="left_column_released") def __init__( - self, - text_title="Title", - text_title_size=10, - text_title_color="#343b48", - dark_one="#1b1e23", - bg_color="#343b48", - btn_color="#c3ccdf", - btn_color_hover="#3c4454", - btn_color_pressed="#2c313c", - icon_path="no_icon.svg", - icon_color="#343b48", - icon_color_hover="#dce1ec", - icon_color_pressed="#edf0f5", - context_color="#dce1ec", - icon_close_path="no_icon.svg", - radius=8 + self, + text_title="Title", + text_title_size=10, + text_title_color="#343b48", + dark_one="#1b1e23", + bg_color="#343b48", + btn_color="#c3ccdf", + btn_color_hover="#3c4454", + btn_color_pressed="#2c313c", + icon_path="no_icon.svg", + icon_color="#343b48", + icon_color_hover="#dce1ec", + icon_color_pressed="#edf0f5", + context_color="#dce1ec", + icon_close_path="no_icon.svg", + radius=8, ): super().__init__() self._text_title = text_title @@ -73,12 +78,14 @@ def setup_ui(self): self.title_bg_frame = QFrame() self.title_bg_frame.setObjectName("title_bg_frame") - self.title_bg_frame.setStyleSheet(f''' + self.title_bg_frame.setStyleSheet( + f""" #title_bg_frame {{ background-color: {self._bg_color}; border-radius: {self._radius}px; }} - ''') + """ + ) self.title_bg_layout = QHBoxLayout(self.title_bg_frame) self.title_bg_layout.setContentsMargins(5, 5, 5, 5) @@ -95,14 +102,16 @@ def setup_ui(self): self.title_label = QLabel(self._text_title) self.title_label.setObjectName("title_label") - self.title_label.setStyleSheet(f''' + self.title_label.setStyleSheet( + f""" #title_label {{ font-size: {self._text_title_size}pt; color: {self._text_title_color}; padding-bottom: 2px; background: none; }} - ''') + """ + ) self.btn_frame = QFrame() self.btn_frame.setFixedSize(30, 30) diff --git a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_left_menu/py_left_menu.py b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_left_menu/py_left_menu.py index 9aea786b..75c122ae 100644 --- a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_left_menu/py_left_menu.py +++ b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_left_menu/py_left_menu.py @@ -1,10 +1,15 @@ -from PySide6.QtWidgets import * -from PySide6.QtCore import * - -from ansys.aedt.toolkits.common.ui.utils.widgets.py_left_menu.py_left_menu_button import PyLeftMenuButton -from ansys.aedt.toolkits.common.ui.utils.widgets.py_div.py_div import PyDiv +from PySide6.QtCore import QEasingCurve +from PySide6.QtCore import QPropertyAnimation +from PySide6.QtCore import Qt +from PySide6.QtCore import Signal +from PySide6.QtWidgets import QFrame +from PySide6.QtWidgets import QPushButton +from PySide6.QtWidgets import QVBoxLayout +from PySide6.QtWidgets import QWidget from ansys.aedt.toolkits.common.ui.utils.images.load_images import LoadImages +from ansys.aedt.toolkits.common.ui.utils.widgets.py_div.py_div import PyDiv +from ansys.aedt.toolkits.common.ui.utils.widgets.py_left_menu.py_left_menu_button import PyLeftMenuButton class PyLeftMenu(QWidget): @@ -12,27 +17,27 @@ class PyLeftMenu(QWidget): released = Signal(object) def __init__( - self, - parent=None, - app_parent=None, - dark_one="#1b1e23", - dark_three="#21252d", - dark_four="#272c36", - bg_one="#2c313c", - icon_color="#c3ccdf", - icon_color_hover="#dce1ec", - icon_color_pressed="#edf0f5", - icon_color_active="#f5f6f9", - context_color="#568af2", - text_foreground="#8a95aa", - text_active="#dce1ec", - radius=8, - minimum_width=50, - maximum_width=240, - icon_path="icon_menu.svg", - icon_path_close="icon_menu_close.svg", - toggle_text="Hide Menu", - toggle_tooltip="Show menu" + self, + parent=None, + app_parent=None, + dark_one="#1b1e23", + dark_three="#21252d", + dark_four="#272c36", + bg_one="#2c313c", + icon_color="#c3ccdf", + icon_color_hover="#dce1ec", + icon_color_pressed="#edf0f5", + icon_color_active="#f5f6f9", + context_color="#568af2", + text_foreground="#8a95aa", + text_active="#dce1ec", + radius=8, + minimum_width=50, + maximum_width=240, + icon_path="icon_menu.svg", + icon_path_close="icon_menu_close.svg", + toggle_text="Hide Menu", + toggle_tooltip="Show menu", ): super().__init__(parent) self._images_load = LoadImages() @@ -75,12 +80,11 @@ def __init__( context_color=self._context_color, text_foreground=self._text_foreground, text_active=self._text_active, - icon_path=icon_path + icon_path=icon_path, ) self.toggle_button.clicked.connect(self.toggle_animation) self.div_top = PyDiv(dark_four) - self.top_layout.addWidget(self.toggle_button) self.top_layout.addWidget(self.div_top) @@ -91,12 +95,12 @@ def __init__( def add_menus(self, parameters): if parameters != None: for parameter in parameters: - _btn_icon = parameter['btn_icon'] - _btn_id = parameter['btn_id'] - _btn_text = parameter['btn_text'] - _btn_tooltip = parameter['btn_tooltip'] - _show_top = parameter['show_top'] - _is_active = parameter['is_active'] + _btn_icon = parameter["btn_icon"] + _btn_id = parameter["btn_id"] + _btn_text = parameter["btn_text"] + _btn_tooltip = parameter["btn_tooltip"] + _show_top = parameter["show_top"] + _is_active = parameter["is_active"] self.menu = PyLeftMenuButton( self._app_parent, @@ -115,7 +119,7 @@ def add_menus(self, parameters): text_foreground=self._text_foreground, text_active=self._text_active, icon_path=_btn_icon, - is_active=_is_active + is_active=_is_active, ) self.menu.clicked.connect(self.btn_clicked) self.menu.released.connect(self.btn_released) diff --git a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_left_menu/py_left_menu_button.py b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_left_menu/py_left_menu_button.py index b69d8171..1e3f7adf 100644 --- a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_left_menu/py_left_menu_button.py +++ b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_left_menu/py_left_menu_button.py @@ -1,34 +1,43 @@ -from PySide6.QtWidgets import * -from PySide6.QtCore import * -from PySide6.QtGui import * +import os -from ansys.aedt.toolkits.common.ui.utils.images.load_images import * +from PySide6.QtCore import QEvent +from PySide6.QtCore import QPoint +from PySide6.QtCore import QRect +from PySide6.QtCore import Qt +from PySide6.QtGui import QColor +from PySide6.QtGui import QPainter +from PySide6.QtGui import QPixmap +from PySide6.QtWidgets import QGraphicsDropShadowEffect +from PySide6.QtWidgets import QLabel +from PySide6.QtWidgets import QPushButton + +from ansys.aedt.toolkits.common.ui.utils.images.load_images import LoadImages class PyLeftMenuButton(QPushButton): def __init__( - self, - app_parent, - text, - btn_id=None, - tooltip_text="", - margin=4, - dark_one="#1b1e23", - dark_three="#21252d", - dark_four="#272c36", - bg_one="#2c313c", - icon_color="#c3ccdf", - icon_color_hover="#dce1ec", - icon_color_pressed="#edf0f5", - icon_color_active="#f5f6f9", - context_color="#568af2", - text_foreground="#8a95aa", - text_active="#dce1ec", - icon_path="icon_add_user.svg", - icon_active_menu="active_menu.svg", - is_active=False, - is_active_tab=False, - is_toggle_active=False + self, + app_parent, + text, + btn_id=None, + tooltip_text="", + margin=4, + dark_one="#1b1e23", + dark_three="#21252d", + dark_four="#272c36", + bg_one="#2c313c", + icon_color="#c3ccdf", + icon_color_hover="#dce1ec", + icon_color_pressed="#edf0f5", + icon_color_active="#f5f6f9", + context_color="#568af2", + text_foreground="#8a95aa", + text_active="#dce1ec", + icon_path="icon_add_user.svg", + icon_active_menu="active_menu.svg", + is_active=False, + is_active_tab=False, + is_toggle_active=False, ): super().__init__() self._images_load = LoadImages() @@ -61,13 +70,7 @@ def __init__( self._is_toggle_active = is_toggle_active self._tooltip_text = tooltip_text - self.tooltip = _ToolTip( - app_parent, - tooltip_text, - dark_one, - context_color, - text_foreground - ) + self.tooltip = _ToolTip(app_parent, tooltip_text, dark_one, context_color, text_foreground) self.tooltip.hide() def paintEvent(self, event): @@ -185,11 +188,7 @@ def icon_paint(self, qp, image, rect, color): painter = QPainter(icon) painter.setCompositionMode(QPainter.CompositionMode_SourceIn) painter.fillRect(icon.rect(), color) - qp.drawPixmap( - (rect.width() - icon.width()) / 2, - (rect.height() - icon.height()) / 2, - icon - ) + qp.drawPixmap((rect.width() - icon.width()) / 2, (rect.height() - icon.height()) / 2, icon) painter.end() def icon_active(self, qp, image, width): @@ -247,7 +246,7 @@ def move_tooltip(self): # GET MAIN WINDOW PARENT gp = self.mapToGlobal(QPoint(0, 0)) - # SET WIDGET TO GET POSTION + # SET WIDGET TO GET POSITION # Return absolute position of widget inside app pos = self._parent.mapFromGlobal(gp) @@ -263,9 +262,9 @@ def move_tooltip(self): class _ToolTip(QLabel): # TOOLTIP / LABEL StyleSheet - style_tooltip = """ - QLabel {{ - background-color: {_dark_one}; + style_tooltip = """ + QLabel {{ + background-color: {_dark_one}; color: {_text_foreground}; padding-left: 10px; padding-right: 10px; @@ -276,23 +275,14 @@ class _ToolTip(QLabel): }} """ - def __init__( - self, - parent, - tooltip, - dark_one, - context_color, - text_foreground - ): + def __init__(self, parent, tooltip, dark_one, context_color, text_foreground): QLabel.__init__(self) # LABEL SETUP style = self.style_tooltip.format( - _dark_one=dark_one, - _context_color=context_color, - _text_foreground=text_foreground + _dark_one=dark_one, _context_color=context_color, _text_foreground=text_foreground ) - self.setObjectName(u"label_tooltip") + self.setObjectName("label_tooltip") self.setStyleSheet(style) self.setMinimumHeight(34) self.setParent(parent) diff --git a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_line_edit/py_line_edit.py b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_line_edit/py_line_edit.py index 71280b7d..c1472669 100644 --- a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_line_edit/py_line_edit.py +++ b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_line_edit/py_line_edit.py @@ -1,19 +1,20 @@ -from PySide6.QtWidgets import * +from PySide6.QtWidgets import QLineEdit + from ansys.aedt.toolkits.common.ui.utils.widgets.py_line_edit.styles import Styles class PyLineEdit(QLineEdit): def __init__( - self, - text="", - place_holder_text="", - radius=8, - border_size=2, - color="#FFF", - selection_color="#FFF", - bg_color="#333", - bg_color_active="#222", - context_color="#00ABE8" + self, + text="", + place_holder_text="", + radius=8, + border_size=2, + color="#FFF", + selection_color="#FFF", + bg_color="#333", + bg_color_active="#222", + context_color="#00ABE8", ): super().__init__() @@ -22,27 +23,9 @@ def __init__( if place_holder_text: self.setPlaceholderText(place_holder_text) - self.set_stylesheet( - radius, - border_size, - color, - selection_color, - bg_color, - bg_color_active, - context_color - ) - - def set_stylesheet( - self, - radius, - border_size, - color, - selection_color, - bg_color, - bg_color_active, - context_color - ): + self.set_stylesheet(radius, border_size, color, selection_color, bg_color, bg_color_active, context_color) + def set_stylesheet(self, radius, border_size, color, selection_color, bg_color, bg_color_active, context_color): style_format = Styles.style.format( _radius=radius, _border_size=border_size, @@ -50,6 +33,6 @@ def set_stylesheet( _selection_color=selection_color, _bg_color=bg_color, _bg_color_active=bg_color_active, - _context_color=context_color + _context_color=context_color, ) self.setStyleSheet(style_format) diff --git a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_line_edit/styles.py b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_line_edit/styles.py index 7013a3a1..a2dcf0a5 100644 --- a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_line_edit/styles.py +++ b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_line_edit/styles.py @@ -1,5 +1,5 @@ class Styles(object): - style = ''' + style = """ QLineEdit {{ background-color: {_bg_color}; border-radius: {_radius}px; @@ -14,4 +14,4 @@ class Styles(object): border: {_border_size}px solid {_context_color}; background-color: {_bg_color_active}; }} - ''' + """ diff --git a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_logger/py_logger.py b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_logger/py_logger.py index b8f09549..370088cd 100644 --- a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_logger/py_logger.py +++ b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_logger/py_logger.py @@ -1,5 +1,5 @@ -from PySide6.QtWidgets import * from PySide6.QtGui import QFont +from PySide6.QtWidgets import QTextEdit class PyLogger(QTextEdit): @@ -22,16 +22,12 @@ class PyLogger(QTextEdit): Logger height. The default is ``10``. """ - def __init__(self, - text_color="#f5f6f9", - background_color="#000000", - font_size=10, - font_family="Segoe UI", - height=50 - ): + def __init__( + self, text_color="#f5f6f9", background_color="#000000", font_size=10, font_family="Segoe UI", height=50 + ): super().__init__() self.setReadOnly(True) - font = QFont(font_family, font_size), + font = (QFont(font_family, font_size),) self.setFont(font) self.setStyleSheet(f"background-color: {background_color}; color: {text_color}") self.setFixedHeight(height) diff --git a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_progress/py_progress.py b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_progress/py_progress.py index 6e877a8a..d7e6411f 100644 --- a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_progress/py_progress.py +++ b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_progress/py_progress.py @@ -1,6 +1,12 @@ -from PySide6.QtWidgets import * -from PySide6.QtGui import * -from PySide6.QtCore import * +from PySide6.QtCore import QRect +from PySide6.QtCore import QRectF +from PySide6.QtCore import Qt +from PySide6.QtGui import QColor +from PySide6.QtGui import QFont +from PySide6.QtGui import QPainter +from PySide6.QtGui import QPen +from PySide6.QtWidgets import QSizePolicy +from PySide6.QtWidgets import QWidget class PyProgress(QWidget): @@ -48,15 +54,16 @@ class PyProgress(QWidget): ... sys.exit(app.exec()) """ - def __init__(self, - progress=0, - progress_color="#ff79c6", - background_color="#151617", - text_color="#FFFFFF", - font_size=10, - font_family="Segoe UI", - width=10, - ): + def __init__( + self, + progress=0, + progress_color="#ff79c6", + background_color="#151617", + text_color="#FFFFFF", + font_size=10, + font_family="Segoe UI", + width=10, + ): super().__init__() self._progress = progress self._backgroundColor = QColor(background_color) @@ -93,7 +100,7 @@ def paintEvent(self, e): pen = QPen(self._progressColor, self._progressWidth) paint.setPen(pen) rect = QRectF(-radius, -radius, 2 * radius, 2 * radius) - paint.drawArc(rect, 90 * 16, - self._progress / 100.0 * 360 * 16) + paint.drawArc(rect, 90 * 16, -self._progress / 100.0 * 360 * 16) # draw progress text font = QFont(self._font_family, self._font_size) diff --git a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_push_button/py_push_button.py b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_push_button/py_push_button.py index 5b03ca79..19f805cc 100644 --- a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_push_button/py_push_button.py +++ b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_push_button/py_push_button.py @@ -1,20 +1,20 @@ -from PySide6.QtWidgets import * -from PySide6.QtGui import * -from PySide6.QtCore import * +from PySide6.QtCore import Qt +from PySide6.QtWidgets import QPushButton + from ansys.aedt.toolkits.common.ui.utils.widgets.py_push_button.styles import Styles class PyPushButton(QPushButton): def __init__( - self, - text, - radius, - color, - bg_color, - bg_color_hover, - bg_color_pressed, - font_size, - parent=None, + self, + text, + radius, + color, + bg_color, + bg_color_hover, + bg_color_pressed, + font_size, + parent=None, ): super().__init__() diff --git a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_push_button/styles.py b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_push_button/styles.py index 021eb478..8dbe5137 100644 --- a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_push_button/styles.py +++ b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_push_button/styles.py @@ -1,18 +1,18 @@ class Styles(object): - style = ''' + style = """ QPushButton {{ border: none; padding-left: 10px; padding-right: 5px; color: {_color}; - border-radius: {_radius}; + border-radius: {_radius}; background-color: {_bg_color}; font-size: {_font_size}pt; }} QPushButton:hover {{ background-color: {_bg_color_hover}; }} - QPushButton:pressed {{ + QPushButton:pressed {{ background-color: {_bg_color_pressed}; }} - ''' + """ diff --git a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_right_column/py_right_column.py b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_right_column/py_right_column.py index 71f090a9..09c2ff21 100644 --- a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_right_column/py_right_column.py +++ b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_right_column/py_right_column.py @@ -1,8 +1,13 @@ -from PySide6.QtWidgets import * -from PySide6.QtCore import * +from PySide6.QtCore import Qt +from PySide6.QtCore import Signal +from PySide6.QtWidgets import QFrame +from PySide6.QtWidgets import QHBoxLayout +from PySide6.QtWidgets import QLabel +from PySide6.QtWidgets import QVBoxLayout +from PySide6.QtWidgets import QWidget -from ansys.aedt.toolkits.common.ui.utils.widgets.py_icon.py_icon import PyIcon from ansys.aedt.toolkits.common.ui.utils.ui_templates.columns.ui_right_column import Ui_RightColumn +from ansys.aedt.toolkits.common.ui.utils.widgets.py_icon.py_icon import PyIcon class PyRightColumn(QWidget): @@ -10,21 +15,21 @@ class PyRightColumn(QWidget): released = Signal(object, name="right_column_released") def __init__( - self, - text_title, - text_title_size, - text_title_color, - dark_one, - bg_color, - btn_color, - btn_color_hover, - btn_color_pressed, - icon_path, - icon_color, - icon_color_hover, - icon_color_pressed, - context_color, - radius=8 + self, + text_title, + text_title_size, + text_title_color, + dark_one, + bg_color, + btn_color, + btn_color_hover, + btn_color_pressed, + icon_path, + icon_color, + icon_color_hover, + icon_color_pressed, + context_color, + radius=8, ): super().__init__() @@ -58,18 +63,19 @@ def setup_ui(self): self.title_frame.setMaximumHeight(47) self.title_frame.setMinimumHeight(47) - self.title_base_layout = QVBoxLayout(self.title_frame) self.title_base_layout.setContentsMargins(5, 3, 5, 3) self.title_bg_frame = QFrame() self.title_bg_frame.setObjectName("title_bg_frame") - self.title_bg_frame.setStyleSheet(f''' + self.title_bg_frame.setStyleSheet( + f""" #title_bg_frame {{ background-color: {self._bg_color}; border-radius: {self._radius}px; }} - ''') + """ + ) self.title_bg_layout = QHBoxLayout(self.title_bg_frame) self.title_bg_layout.setContentsMargins(5, 5, 5, 5) @@ -86,14 +92,16 @@ def setup_ui(self): self.title_label = QLabel(self._text_title) self.title_label.setObjectName("title_label") - self.title_label.setStyleSheet(f''' + self.title_label.setStyleSheet( + f""" #title_label {{ font-size: {self._text_title_size}pt; color: {self._text_title_color}; padding-bottom: 2px; background: none; }} - ''') + """ + ) self.title_bg_layout.addWidget(self.icon_frame) self.title_bg_layout.addWidget(self.title_label) diff --git a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_slider/py_slider.py b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_slider/py_slider.py index daaa2c0e..2bf3bb6e 100644 --- a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_slider/py_slider.py +++ b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_slider/py_slider.py @@ -1,22 +1,22 @@ -from PySide6.QtWidgets import * +from PySide6.QtWidgets import QSlider from ansys.aedt.toolkits.common.ui.utils.widgets.py_slider.styles import Styles class PySlider(QSlider): def __init__( - self, - margin=0, - bg_size=20, - bg_radius=10, - bg_color="#1b1e23", - bg_color_hover="#1e2229", - handle_margin=2, - handle_size=16, - handle_radius=8, - handle_color="#568af2", - handle_color_hover="#6c99f4", - handle_color_pressed="#3f6fd1" + self, + margin=0, + bg_size=20, + bg_radius=10, + bg_color="#1b1e23", + bg_color_hover="#1e2229", + handle_margin=2, + handle_size=16, + handle_radius=8, + handle_color="#568af2", + handle_color_hover="#6c99f4", + handle_color_pressed="#3f6fd1", ): super(PySlider, self).__init__() @@ -31,7 +31,7 @@ def __init__( _handle_radius=handle_radius, _handle_color=handle_color, _handle_color_hover=handle_color_hover, - _handle_color_pressed=handle_color_pressed + _handle_color_pressed=handle_color_pressed, ) self.setStyleSheet(adjust_style) diff --git a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_slider/styles.py b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_slider/styles.py index 7fd6695e..ebfd862c 100644 --- a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_slider/styles.py +++ b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_slider/styles.py @@ -5,8 +5,8 @@ class Styles(object): QSlider::groove:horizontal {{ border-radius: {_bg_radius}px; height: {_bg_size}px; - margin: 0px; - background-color: {_bg_color}; + margin: 0px; + background-color: {_bg_color}; }} QSlider::groove:horizontal:hover {{ background-color: {_bg_color_hover}; }} QSlider::handle:horizontal {{ @@ -14,7 +14,7 @@ class Styles(object): height: {_handle_size}px; width: {_handle_size}px; margin: {_handle_margin}px; - border-radius: {_handle_radius}px; + border-radius: {_handle_radius}px; background-color: {_handle_color}; }} QSlider::handle:horizontal:hover {{ background-color: {_handle_color_hover}; }} @@ -25,15 +25,15 @@ class Styles(object): border-radius: {_bg_radius}px; width: {_bg_size}px; margin: 0px; - background-color: {_bg_color}; + background-color: {_bg_color}; }} QSlider::groove:vertical:hover {{ background-color: {_bg_color_hover}; }} QSlider::handle:vertical {{ - border: none; + border: none; height: {_handle_size}px; width: {_handle_size}px; margin: {_handle_margin}px; - border-radius: {_handle_radius}px; + border-radius: {_handle_radius}px; background-color: {_handle_color}; }} QSlider::handle:vertical:hover {{ background-color: {_handle_color_hover}; }} diff --git a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_title_bar/py_title_bar.py b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_title_bar/py_title_bar.py index a280c3e7..4c72b63e 100644 --- a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_title_bar/py_title_bar.py +++ b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_title_bar/py_title_bar.py @@ -1,10 +1,14 @@ -from PySide6.QtCore import Signal, QSize -from PySide6.QtWidgets import * -from PySide6.QtGui import * -from PySide6.QtSvgWidgets import * +from PySide6.QtCore import QSize +from PySide6.QtCore import Qt +from PySide6.QtCore import Signal +from PySide6.QtSvgWidgets import QSvgWidget +from PySide6.QtWidgets import QFrame +from PySide6.QtWidgets import QHBoxLayout +from PySide6.QtWidgets import QLabel +from PySide6.QtWidgets import QVBoxLayout +from PySide6.QtWidgets import QWidget from ansys.aedt.toolkits.common.ui.utils.images.load_images import LoadImages - from ansys.aedt.toolkits.common.ui.utils.widgets.py_div.py_div import PyDiv from .py_title_button import PyTitleButton @@ -18,26 +22,26 @@ class PyTitleBar(QWidget): released = Signal(object) def __init__( - self, - parent, - app_parent, - logo_image="ANSS_BIG.D.svg", - logo_width=10, - dark_one="#1b1e23", - bg_color="#343b48", - div_color="#3c4454", - btn_bg_color="#343b48", - btn_bg_color_hover="#3c4454", - btn_bg_color_pressed="#2c313c", - icon_color="#c3ccdf", - icon_color_hover="#dce1ec", - icon_color_pressed="#edf0f5", - icon_color_active="#f5f6f9", - context_color="#6c99f4", - text_foreground="#8a95aa", - radius=8, - font_family="Segoe UI", - title_size=10, + self, + parent, + app_parent, + logo_image="ANSS_BIG.D.svg", + logo_width=10, + dark_one="#1b1e23", + bg_color="#343b48", + div_color="#3c4454", + btn_bg_color="#343b48", + btn_bg_color_hover="#3c4454", + btn_bg_color_pressed="#2c313c", + icon_color="#c3ccdf", + icon_color_hover="#dce1ec", + icon_color_pressed="#edf0f5", + icon_color_active="#f5f6f9", + context_color="#6c99f4", + text_foreground="#8a95aa", + radius=8, + font_family="Segoe UI", + title_size=10, ): super().__init__(parent) @@ -100,10 +104,10 @@ def moveWindow(event): def add_menus(self, parameters): if parameters is not None and len(parameters) > 0: for parameter in parameters: - _btn_icon = self._images_load.icon_path(parameter['btn_icon']) - _btn_id = parameter['btn_id'] - _btn_tooltip = parameter['btn_tooltip'] - _is_active = parameter['is_active'] + _btn_icon = self._images_load.icon_path(parameter["btn_icon"]) + _btn_id = parameter["btn_id"] + _btn_tooltip = parameter["btn_tooltip"] + _is_active = parameter["is_active"] self.menu = PyTitleButton( self._parent, @@ -121,7 +125,7 @@ def add_menus(self, parameters): context_color=self._context_color, text_foreground=self._text_foreground, icon_path=_btn_icon, - is_active=_is_active + is_active=_is_active, ) self.menu.clicked.connect(self.btn_clicked) self.menu.released.connect(self.btn_released) @@ -145,15 +149,11 @@ def change_ui(): if _is_maximized: self._parent.ui.main_window_layout.setContentsMargins(0, 0, 0, 0) self._parent.ui.main_window.set_stylesheet(border_radius=0, border_size=0) - self.maximize_restore_button.set_icon( - self._images_load.icon_path("icon_restore.svg") - ) + self.maximize_restore_button.set_icon(self._images_load.icon_path("icon_restore.svg")) else: self._parent.ui.main_window_layout.setContentsMargins(10, 10, 10, 10) self._parent.ui.main_window.set_stylesheet(border_radius=10, border_size=2) - self.maximize_restore_button.set_icon( - self._images_load.icon_path("icon_maximize.svg") - ) + self.maximize_restore_button.set_icon(self._images_load.icon_path("icon_maximize.svg")) # CHECK EVENT if self._parent.isMaximized(): @@ -210,7 +210,7 @@ def setup_ui(self): context_color=self._context_color, text_foreground=self._text_foreground, radius=6, - icon_path=self._images_load.icon_path("icon_minimize.svg") + icon_path=self._images_load.icon_path("icon_minimize.svg"), ) self.maximize_restore_button = PyTitleButton( @@ -228,7 +228,7 @@ def setup_ui(self): context_color=self._context_color, text_foreground=self._text_foreground, radius=6, - icon_path=self._images_load.icon_path("icon_maximize.svg") + icon_path=self._images_load.icon_path("icon_maximize.svg"), ) self.close_button = PyTitleButton( @@ -246,7 +246,7 @@ def setup_ui(self): context_color=self._context_color, text_foreground=self._text_foreground, radius=6, - icon_path=self._images_load.icon_path("icon_close.svg") + icon_path=self._images_load.icon_path("icon_close.svg"), ) self.title_bar_layout.addWidget(self.bg) diff --git a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_title_bar/py_title_button.py b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_title_bar/py_title_button.py index f819f2d6..46366f9d 100644 --- a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_title_bar/py_title_button.py +++ b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_title_bar/py_title_button.py @@ -1,30 +1,38 @@ -from PySide6.QtWidgets import * -from PySide6.QtGui import * -from PySide6.QtCore import * +from PySide6.QtCore import QEvent +from PySide6.QtCore import QPoint +from PySide6.QtCore import QRect +from PySide6.QtCore import Qt +from PySide6.QtGui import QBrush +from PySide6.QtGui import QColor +from PySide6.QtGui import QPainter +from PySide6.QtGui import QPixmap +from PySide6.QtWidgets import QGraphicsDropShadowEffect +from PySide6.QtWidgets import QLabel +from PySide6.QtWidgets import QPushButton class PyTitleButton(QPushButton): def __init__( - self, - parent, - app_parent=None, - tooltip_text="", - btn_id=None, - width=30, - height=30, - radius=8, - bg_color="#343b48", - bg_color_hover="#3c4454", - bg_color_pressed="#2c313c", - icon_color="#c3ccdf", - icon_color_hover="#dce1ec", - icon_color_pressed="#edf0f5", - icon_color_active="#f5f6f9", - icon_path="no_icon.svg", - dark_one="#1b1e23", - context_color="#568af2", - text_foreground="#8a95aa", - is_active=False + self, + parent, + app_parent=None, + tooltip_text="", + btn_id=None, + width=30, + height=30, + radius=8, + bg_color="#343b48", + bg_color_hover="#3c4454", + bg_color_pressed="#2c313c", + icon_color="#c3ccdf", + icon_color_hover="#dce1ec", + icon_color_pressed="#edf0f5", + icon_color_active="#f5f6f9", + icon_path="no_icon.svg", + dark_one="#1b1e23", + context_color="#568af2", + text_foreground="#8a95aa", + is_active=False, ): super().__init__() @@ -55,13 +63,7 @@ def __init__( # TOOLTIP self._tooltip_text = tooltip_text - self._tooltip = _ToolTip( - app_parent, - tooltip_text, - dark_one, - context_color, - text_foreground - ) + self._tooltip = _ToolTip(app_parent, tooltip_text, dark_one, context_color, text_foreground) self._tooltip.hide() # SET ACTIVE MENU @@ -95,11 +97,7 @@ def paintEvent(self, event): rect = QRect(0, 0, self.width(), self.height()) paint.setPen(Qt.NoPen) paint.setBrush(brush) - paint.drawRoundedRect( - rect, - self._set_border_radius, - self._set_border_radius - ) + paint.drawRoundedRect(rect, self._set_border_radius, self._set_border_radius) # DRAW ICONS self.icon_paint(paint, self._set_icon_path, rect) @@ -174,11 +172,7 @@ def icon_paint(self, qp, image, rect): painter.fillRect(icon.rect(), self._icon_color_active) else: painter.fillRect(icon.rect(), self._set_icon_color) - qp.drawPixmap( - (rect.width() - icon.width()) / 2, - (rect.height() - icon.height()) / 2, - icon - ) + qp.drawPixmap((rect.width() - icon.width()) / 2, (rect.height() - icon.height()) / 2, icon) painter.end() # SET ICON @@ -193,7 +187,7 @@ def move_tooltip(self): # GET MAIN WINDOW PARENT gp = self.mapToGlobal(QPoint(0, 0)) - # SET WIDGET TO GET POSTION + # SET WIDGET TO GET POSITION # Return absolute position of widget inside app pos = self._parent.mapFromGlobal(gp) @@ -211,9 +205,9 @@ def move_tooltip(self): # /////////////////////////////////////////////////////////////// class _ToolTip(QLabel): # TOOLTIP / LABEL StyleSheet - style_tooltip = """ - QLabel {{ - background-color: {_dark_one}; + style_tooltip = """ + QLabel {{ + background-color: {_dark_one}; color: {_text_foreground}; padding-left: 10px; padding-right: 10px; @@ -224,23 +218,14 @@ class _ToolTip(QLabel): }} """ - def __init__( - self, - parent, - tooltip, - dark_one, - context_color, - text_foreground - ): + def __init__(self, parent, tooltip, dark_one, context_color, text_foreground): QLabel.__init__(self) # LABEL SETUP style = self.style_tooltip.format( - _dark_one=dark_one, - _context_color=context_color, - _text_foreground=text_foreground + _dark_one=dark_one, _context_color=context_color, _text_foreground=text_foreground ) - self.setObjectName(u"label_tooltip") + self.setObjectName("label_tooltip") self.setStyleSheet(style) self.setMinimumHeight(34) self.setParent(parent) diff --git a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_toggle/py_toggle.py b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_toggle/py_toggle.py index 940bf7d6..c12780ef 100644 --- a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_toggle/py_toggle.py +++ b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_toggle/py_toggle.py @@ -1,16 +1,23 @@ -from PySide6.QtWidgets import * -from PySide6.QtGui import * -from PySide6.QtCore import * +from PySide6.QtCore import Property +from PySide6.QtCore import QEasingCurve +from PySide6.QtCore import QPoint +from PySide6.QtCore import QPropertyAnimation +from PySide6.QtCore import QRect +from PySide6.QtCore import Qt +from PySide6.QtGui import QColor +from PySide6.QtGui import QFont +from PySide6.QtGui import QPainter +from PySide6.QtWidgets import QCheckBox class PyToggle(QCheckBox): def __init__( - self, - width=50, - bg_color="#777", - circle_color="#DDD", - active_color="#00BCFF", - animation_curve=QEasingCurve.OutBounce + self, + width=50, + bg_color="#777", + circle_color="#DDD", + active_color="#00BCFF", + animation_curve=QEasingCurve.OutBounce, ): QCheckBox.__init__(self) self.setFixedSize(width, 28) diff --git a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_window/py_window.py b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_window/py_window.py index a3fdf4ac..b62b1cfb 100644 --- a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_window/py_window.py +++ b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_window/py_window.py @@ -1,13 +1,25 @@ -from PySide6.QtWidgets import * -from PySide6.QtGui import * +from PySide6.QtGui import QColor +from PySide6.QtWidgets import QFrame +from PySide6.QtWidgets import QGraphicsDropShadowEffect +from PySide6.QtWidgets import QHBoxLayout from ansys.aedt.toolkits.common.ui.utils.widgets.py_window.styles import Styles class PyWindow(QFrame): - def __init__(self, parent, margin=0, spacing=2, - bg_color="#2c313c", text_color="#fff", text_font="9pt 'Segoe UI'", - border_radius=10, border_size=2, border_color="#343b48", enable_shadow=True): + def __init__( + self, + parent, + margin=0, + spacing=2, + bg_color="#2c313c", + text_color="#fff", + text_font="9pt 'Segoe UI'", + border_radius=10, + border_size=2, + border_color="#343b48", + enable_shadow=True, + ): super().__init__(parent) self.parent = parent self.margin = margin @@ -32,8 +44,9 @@ def __init__(self, parent, margin=0, spacing=2, if enable_shadow: self._addDropShadow() - def set_stylesheet(self, bg_color=None, border_radius=None, border_size=None, - border_color=None, text_color=None, text_font=None): + def set_stylesheet( + self, bg_color=None, border_radius=None, border_size=None, border_color=None, text_color=None, text_font=None + ): # Use provided if not None else get the instance attribute style_args = { "_bg_color": bg_color or self.bg_color, @@ -41,7 +54,7 @@ def set_stylesheet(self, bg_color=None, border_radius=None, border_size=None, "_border_size": border_size or self.border_size, "_border_color": border_color or self.border_color, "_text_color": text_color or self.text_color, - "_text_font": text_font or self.text_font + "_text_font": text_font or self.text_font, } self.setStyleSheet(Styles.bg_style.format(**style_args)) diff --git a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_window/styles.py b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_window/styles.py index f368a731..81c1a98d 100644 --- a/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_window/styles.py +++ b/src/ansys/aedt/toolkits/common/ui/utils/widgets/py_window/styles.py @@ -5,8 +5,8 @@ class Styles(object): border-radius: {_border_radius}; border: {_border_size}px solid {_border_color}; }} - QFrame {{ + QFrame {{ color: {_text_color}; font: {_text_font}; }} - """ \ No newline at end of file + """ diff --git a/src/ansys/aedt/toolkits/common/ui/utils/windows/common_window_utils.py b/src/ansys/aedt/toolkits/common/ui/utils/windows/common_window_utils.py index 9c450c7d..330c70c9 100644 --- a/src/ansys/aedt/toolkits/common/ui/utils/windows/common_window_utils.py +++ b/src/ansys/aedt/toolkits/common/ui/utils/windows/common_window_utils.py @@ -1,8 +1,19 @@ -from PySide6.QtCore import * -from PySide6.QtWidgets import * +from PySide6.QtCore import QEasingCurve +from PySide6.QtCore import QParallelAnimationGroup +from PySide6.QtCore import QPropertyAnimation +from PySide6.QtCore import Qt +from PySide6.QtWidgets import QFrame +from PySide6.QtWidgets import QHBoxLayout +from PySide6.QtWidgets import QSizePolicy +from PySide6.QtWidgets import QSpacerItem from ansys.aedt.toolkits.common.ui.models import general_settings -from ansys.aedt.toolkits.common.ui.utils.widgets import * +from ansys.aedt.toolkits.common.ui.utils.widgets import PyComboBox +from ansys.aedt.toolkits.common.ui.utils.widgets import PyIconButton +from ansys.aedt.toolkits.common.ui.utils.widgets import PyLabel +from ansys.aedt.toolkits.common.ui.utils.widgets import PyLineEdit +from ansys.aedt.toolkits.common.ui.utils.widgets import PyPushButton +from ansys.aedt.toolkits.common.ui.utils.widgets import PyToggle class CommonWindowUtils(object): diff --git a/tests/backend/tests_aedt_api/__init__.py b/tests/backend/tests_aedt_api/__init__.py new file mode 100644 index 00000000..51dee7ba --- /dev/null +++ b/tests/backend/tests_aedt_api/__init__.py @@ -0,0 +1,2 @@ +"""Test API +""" diff --git a/tests/backend/tests_aedt_api/backend_properties.json b/tests/backend/tests_aedt_api/backend_properties.json new file mode 100644 index 00000000..30e83719 --- /dev/null +++ b/tests/backend/tests_aedt_api/backend_properties.json @@ -0,0 +1,7 @@ +{ + "debug" : true, + "log_file" : "test_aedt_api.log", + "desktop_version": "2023.2", + "non_graphical": true, + "use_grpc": true +} \ No newline at end of file diff --git a/tests/backend/tests_api/conftest.py b/tests/backend/tests_aedt_api/conftest.py similarity index 77% rename from tests/backend/tests_api/conftest.py rename to tests/backend/tests_aedt_api/conftest.py index aa860d4e..04236dc4 100644 --- a/tests/backend/tests_api/conftest.py +++ b/tests/backend/tests_aedt_api/conftest.py @@ -1,10 +1,10 @@ """ -AEDT Common Test Configuration Module ------------------------------------- +API Test Configuration Module +----------------------------- Description =========== -This module contains the configuration and fixture for the pytest-based tests for AEDT. +This module contains the configuration and fixture for the pytest-based tests for the API. The default configuration can be changed by placing a file called local_config.json in the same directory as this module. An example of the contents of local_config.json @@ -28,10 +28,7 @@ from pyaedt import settings from pyaedt.generic.filesystem import Scratch import pytest -from tests_api.models import properties - -# Constants -PROJECT_NAME = "Test" +from tests_aedt_api.models import properties config = { "desktop_version": properties.aedt_version, @@ -55,11 +52,6 @@ if not properties.use_grpc and properties.non_graphical: com_non_graphical = True -# The import should be here to have the updated properties -from ansys.aedt.toolkits.common.backend.api import AEDTCommon -from ansys.aedt.toolkits.common.backend.api import Common -from ansys.aedt.toolkits.common.backend.api import EDBCommon - settings.enable_error_handler = False settings.enable_desktop_logs = False settings.use_grpc_api = config.get("use_grpc", True) @@ -72,10 +64,6 @@ aedt_project = os.path.join(input_data_dir, "input_data", "Test.aedt") aedt_scratch = shutil.copy(aedt_project, local_scratch.path) -edb_scratch = shutil.copytree( - os.path.join(input_data_dir, "input_data", "edb_test.aedb"), os.path.join(local_scratch.path, "edb_test.aedb") -) - logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) log_file = os.path.join(local_scratch.path, "pytest_api.log") @@ -91,18 +79,10 @@ logger.addHandler(console_handler) -@pytest.fixture(scope="session") -def common(request): - logger.info("Common API initialization") - common_api = Common(properties) - yield common_api - # Check if any test has failed - if request.session.testsfailed == 0: - cleanup_process() - - @pytest.fixture(scope="session") def aedt_common(request): + from ansys.aedt.toolkits.common.backend.api import AEDTCommon + logger.info("AEDTCommon API initialization") aedt_common = AEDTCommon(properties) if com_non_graphical: @@ -112,21 +92,13 @@ def aedt_common(request): aedt_common.launch_thread(aedt_common.launch_aedt) is_aedt_launched = aedt_common.wait_to_be_idle() if is_aedt_launched: + aedt_common.open_project(aedt_scratch) yield aedt_common aedt_common.release_aedt(True, True) else: logger.error("AEDT is not launched") - # Check if any test has failed - if request.session.testsfailed == 0: - cleanup_process() - - -@pytest.fixture(scope="session") -def edb_common(request): - logger.info("EDBCommon API initialization") - edb_common = EDBCommon(properties) - yield edb_common + aedt_common.release_aedt(True, True) # Check if any test has failed if request.session.testsfailed == 0: cleanup_process() @@ -161,11 +133,6 @@ def aedt_example(): return aedt_scratch -@pytest.fixture(scope="session") -def edb_example(): - return edb_scratch - - def skip_test(skip=False): skip_flag = False if com_non_graphical or skip: diff --git a/tests/backend/tests_api/models.py b/tests/backend/tests_aedt_api/models.py similarity index 100% rename from tests/backend/tests_api/models.py rename to tests/backend/tests_aedt_api/models.py diff --git a/tests/backend/tests_api/test_aedt.py b/tests/backend/tests_aedt_api/test_aedt.py similarity index 94% rename from tests/backend/tests_api/test_aedt.py rename to tests/backend/tests_aedt_api/test_aedt.py index a9a5f3b0..a10cf4ee 100644 --- a/tests/backend/tests_api/test_aedt.py +++ b/tests/backend/tests_aedt_api/test_aedt.py @@ -1,10 +1,9 @@ import os import pytest -from tests_api.conftest import skip_test +from tests_aedt_api.conftest import skip_test -@pytest.mark.aedt class TestAEDT: """AEDTCommon unit tests.""" @@ -37,7 +36,6 @@ def test_02_open_project(self, aedt_common, assert_handler, aedt_example): if skip_test(): pytest.skip() - assert aedt_common.open_project(aedt_example) assert not aedt_common.open_project(aedt_example) def test_03_save_project(self, aedt_common, assert_handler, aedt_example): diff --git a/tests/backend/tests_api/__init__.py b/tests/backend/tests_api/__init__.py deleted file mode 100644 index 02f9f5fe..00000000 --- a/tests/backend/tests_api/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -"""Test AEDT Module -""" diff --git a/tests/backend/tests_common_api/__init__.py b/tests/backend/tests_common_api/__init__.py new file mode 100644 index 00000000..51dee7ba --- /dev/null +++ b/tests/backend/tests_common_api/__init__.py @@ -0,0 +1,2 @@ +"""Test API +""" diff --git a/tests/backend/tests_api/backend_properties.json b/tests/backend/tests_common_api/backend_properties.json similarity index 64% rename from tests/backend/tests_api/backend_properties.json rename to tests/backend/tests_common_api/backend_properties.json index 0cd67366..bb1af541 100644 --- a/tests/backend/tests_api/backend_properties.json +++ b/tests/backend/tests_common_api/backend_properties.json @@ -1,6 +1,6 @@ { - "debug" : false, - "log_file" : "test_api.log", + "debug" : true, + "log_file" : "test_common_api.log", "desktop_version": "2023.2", "non_graphical": true, "use_grpc": true, diff --git a/tests/backend/tests_common_api/conftest.py b/tests/backend/tests_common_api/conftest.py new file mode 100644 index 00000000..3e84bfba --- /dev/null +++ b/tests/backend/tests_common_api/conftest.py @@ -0,0 +1,96 @@ +""" +API Test Configuration Module +----------------------------- + +Description +=========== +This module contains the configuration and fixture for the pytest-based tests for the API. + +The default configuration can be changed by placing a file called local_config.json in the same +directory as this module. An example of the contents of local_config.json +{ + "desktop_version": "2023.2", + "non_graphical": false, + "use_grpc": true +} + +You can enable the API log file in the backend_properties.json. + +""" + +import logging +import os +import shutil +import tempfile + +from pyaedt.generic.filesystem import Scratch +import pytest +from tests_common_api.models import properties + +scratch_path = tempfile.gettempdir() +local_scratch = Scratch(scratch_path) + +logger = logging.getLogger(__name__) +logger.setLevel(logging.DEBUG) +log_file = os.path.join(local_scratch.path, "pytest_api.log") +file_handler = logging.FileHandler(log_file) +formatter = logging.Formatter("%(levelname)s - %(message)s") +file_handler.setFormatter(formatter) +logger.addHandler(file_handler) + +logging.StreamHandler() +formatter = logging.Formatter("%(levelname)s - %(message)s") +console_handler = logging.StreamHandler() +console_handler.setFormatter(formatter) +logger.addHandler(console_handler) + + +@pytest.fixture(scope="session") +def common(request): + from ansys.aedt.toolkits.common.backend.api import Common + + logger.info("Common API initialization") + common_api = Common(properties) + yield common_api + # Check if any test has failed + if request.session.testsfailed == 0: + cleanup_process() + + +failed_tests = set() + + +def pytest_runtest_makereport(item, call): + if call.excinfo is not None and call.excinfo.typename == "AssertionError": + failed_tests.add(item) + + +@pytest.fixture(scope="session") +def assert_handler(request): + def finalizer(): + # Code to run after the test + logger.info("Test Teardown") + # Check if any test has failed during the session + if request.session.testsfailed != 0: + # Additional code to run when an assert fails + for failed_test in failed_tests: + logger.error(f"FAILED: {failed_test.name}") + + request.addfinalizer(finalizer) + + return assert_handler + + +def skip_test(skip=False): + skip_flag = False + if com_non_graphical or skip: + skip_flag = True + return skip_flag + + +def cleanup_process(): + """Cleanup process after the test is completed.""" + for handler in logger.handlers: + if isinstance(handler, logging.FileHandler): + handler.close() + shutil.rmtree(local_scratch.path, ignore_errors=True) diff --git a/tests/backend/tests_common_api/models.py b/tests/backend/tests_common_api/models.py new file mode 100644 index 00000000..648fbbeb --- /dev/null +++ b/tests/backend/tests_common_api/models.py @@ -0,0 +1,37 @@ +import json +import os + +from pydantic import BaseModel + +from ansys.aedt.toolkits.common.backend.models import CommonProperties +from ansys.aedt.toolkits.common.backend.models import common_properties + + +class BackendProperties(BaseModel): + """Store toolkit properties.""" + + new_property: bool = True + + +class Properties(BackendProperties, CommonProperties, validate_assignment=True): + """Store all properties.""" + + +backend_properties = {} +if os.path.expanduser(os.path.join(os.path.dirname(__file__), "backend_properties.json")): + with open(os.path.join(os.path.dirname(__file__), "backend_properties.json")) as file_handler: + backend_properties = json.load(file_handler) + +toolkit_property = {} +if backend_properties: + for backend_key in backend_properties: + if hasattr(common_properties, backend_key): + setattr(common_properties, backend_key, backend_properties[backend_key]) + else: + toolkit_property[backend_key] = backend_properties[backend_key] + +new_common_properties = {} +for common_key in common_properties: + new_common_properties[common_key[0]] = common_key[1] + +properties = Properties(**toolkit_property, **new_common_properties) diff --git a/tests/backend/tests_api/test_common.py b/tests/backend/tests_common_api/test_common.py similarity index 92% rename from tests/backend/tests_api/test_common.py rename to tests/backend/tests_common_api/test_common.py index 50c632f8..07b26d6d 100644 --- a/tests/backend/tests_api/test_common.py +++ b/tests/backend/tests_common_api/test_common.py @@ -1,7 +1,3 @@ -import pytest - - -@pytest.mark.common class TestCommon: """Common unit tests.""" @@ -46,3 +42,5 @@ def test_01_set_properties(self, common, assert_handler): def test_02_installed_aedt_version(self, common, assert_handler): """Installed AEDT version.""" + installed_versions = common.installed_aedt_version() + assert isinstance(installed_versions, list) diff --git a/tests/backend/tests_edb_api/__init__.py b/tests/backend/tests_edb_api/__init__.py new file mode 100644 index 00000000..51dee7ba --- /dev/null +++ b/tests/backend/tests_edb_api/__init__.py @@ -0,0 +1,2 @@ +"""Test API +""" diff --git a/tests/backend/tests_edb_api/backend_properties.json b/tests/backend/tests_edb_api/backend_properties.json new file mode 100644 index 00000000..2264fb90 --- /dev/null +++ b/tests/backend/tests_edb_api/backend_properties.json @@ -0,0 +1,5 @@ +{ + "debug" : true, + "log_file" : "test_edb_api.log", + "desktop_version": "2023.2" +} \ No newline at end of file diff --git a/tests/backend/tests_edb_api/conftest.py b/tests/backend/tests_edb_api/conftest.py new file mode 100644 index 00000000..9b53b152 --- /dev/null +++ b/tests/backend/tests_edb_api/conftest.py @@ -0,0 +1,125 @@ +""" +API Test Configuration Module +----------------------------- + +Description +=========== +This module contains the configuration and fixture for the pytest-based tests for the API. + +The default configuration can be changed by placing a file called local_config.json in the same +directory as this module. An example of the contents of local_config.json +{ + "desktop_version": "2023.2" +} + +You can enable the API log file in the backend_properties.json. + +""" + +import json +import logging +import os +import pathlib +import shutil +import tempfile + +from pyaedt import settings +from pyaedt.generic.filesystem import Scratch +import pytest +from tests_edb_api.models import properties + +config = {"desktop_version": properties.aedt_version} + +# Check for the local config file, override defaults if found +local_path = os.path.dirname(os.path.realpath(__file__)) +local_config_file = os.path.join(local_path, "local_config.json") +if os.path.exists(local_config_file): + with open(local_config_file) as f: + local_config = json.load(f) + config.update(local_config) + +properties.aedt_version = config["desktop_version"] + +settings.enable_error_handler = False +settings.enable_desktop_logs = False + +scratch_path = tempfile.gettempdir() +local_scratch = Scratch(scratch_path) + +input_data_dir = pathlib.Path(__file__).parent.parent.parent +aedt_project = os.path.join(input_data_dir, "input_data", "Test.aedt") +aedt_scratch = shutil.copy(aedt_project, local_scratch.path) + +edb_scratch = shutil.copytree( + os.path.join(input_data_dir, "input_data", "edb_test.aedb"), os.path.join(local_scratch.path, "edb_test.aedb") +) + +logger = logging.getLogger(__name__) +logger.setLevel(logging.DEBUG) +log_file = os.path.join(local_scratch.path, "pytest_api.log") +file_handler = logging.FileHandler(log_file) +formatter = logging.Formatter("%(levelname)s - %(message)s") +file_handler.setFormatter(formatter) +logger.addHandler(file_handler) + +logging.StreamHandler() +formatter = logging.Formatter("%(levelname)s - %(message)s") +console_handler = logging.StreamHandler() +console_handler.setFormatter(formatter) +logger.addHandler(console_handler) + + +@pytest.fixture(scope="session") +def edb_common(request): + from ansys.aedt.toolkits.common.backend.api import EDBCommon + + logger.info("EDBCommon API initialization") + edb_common = EDBCommon(properties) + yield edb_common + # Check if any test has failed + if request.session.testsfailed == 0: + cleanup_process() + + +failed_tests = set() + + +def pytest_runtest_makereport(item, call): + if call.excinfo is not None and call.excinfo.typename == "AssertionError": + failed_tests.add(item) + + +@pytest.fixture(scope="session") +def assert_handler(request): + def finalizer(): + # Code to run after the test + logger.info("Test Teardown") + # Check if any test has failed during the session + if request.session.testsfailed != 0: + # Additional code to run when an assert fails + for failed_test in failed_tests: + logger.error(f"FAILED: {failed_test.name}") + + request.addfinalizer(finalizer) + + return assert_handler + + +@pytest.fixture(scope="session") +def edb_example(): + return edb_scratch + + +def skip_test(skip=False): + skip_flag = False + if com_non_graphical or skip: + skip_flag = True + return skip_flag + + +def cleanup_process(): + """Cleanup process after the test is completed.""" + for handler in logger.handlers: + if isinstance(handler, logging.FileHandler): + handler.close() + shutil.rmtree(local_scratch.path, ignore_errors=True) diff --git a/tests/backend/tests_edb_api/models.py b/tests/backend/tests_edb_api/models.py new file mode 100644 index 00000000..648fbbeb --- /dev/null +++ b/tests/backend/tests_edb_api/models.py @@ -0,0 +1,37 @@ +import json +import os + +from pydantic import BaseModel + +from ansys.aedt.toolkits.common.backend.models import CommonProperties +from ansys.aedt.toolkits.common.backend.models import common_properties + + +class BackendProperties(BaseModel): + """Store toolkit properties.""" + + new_property: bool = True + + +class Properties(BackendProperties, CommonProperties, validate_assignment=True): + """Store all properties.""" + + +backend_properties = {} +if os.path.expanduser(os.path.join(os.path.dirname(__file__), "backend_properties.json")): + with open(os.path.join(os.path.dirname(__file__), "backend_properties.json")) as file_handler: + backend_properties = json.load(file_handler) + +toolkit_property = {} +if backend_properties: + for backend_key in backend_properties: + if hasattr(common_properties, backend_key): + setattr(common_properties, backend_key, backend_properties[backend_key]) + else: + toolkit_property[backend_key] = backend_properties[backend_key] + +new_common_properties = {} +for common_key in common_properties: + new_common_properties[common_key[0]] = common_key[1] + +properties = Properties(**toolkit_property, **new_common_properties) diff --git a/tests/backend/tests_api/test_edb.py b/tests/backend/tests_edb_api/test_edb.py similarity index 96% rename from tests/backend/tests_api/test_edb.py rename to tests/backend/tests_edb_api/test_edb.py index a8a43603..88a96f20 100644 --- a/tests/backend/tests_api/test_edb.py +++ b/tests/backend/tests_edb_api/test_edb.py @@ -1,9 +1,6 @@ import os.path -import pytest - -@pytest.mark.edb class TestEDB: """EDBCommon unit tests.""" diff --git a/tests/backend/tests_rest_api/__init__.py b/tests/backend/tests_rest_api/__init__.py new file mode 100644 index 00000000..bec76572 --- /dev/null +++ b/tests/backend/tests_rest_api/__init__.py @@ -0,0 +1,2 @@ +"""Test REST API +""" diff --git a/tests/backend/tests_rest_api/conftest.py b/tests/backend/tests_rest_api/conftest.py new file mode 100644 index 00000000..093a977f --- /dev/null +++ b/tests/backend/tests_rest_api/conftest.py @@ -0,0 +1,135 @@ +""" +REST API Test Configuration Module +---------------------------------- + +Description +=========== +This module contains the configuration and fixture for the pytest-based tests for the REST API. + +The default configuration can be changed by placing a file called local_config.json in the same +directory as this module. An example of the contents of local_config.json +{ + "desktop_version": "2023.2", + "non_graphical": false, + "use_grpc": true +} + +You can enable the API log file in the backend_properties.json. + +""" + +import json +import logging +import os +import pathlib +import shutil +import tempfile + +from pyaedt import settings +from pyaedt.generic.filesystem import Scratch +import pytest + +config = {"desktop_version": "2023.2", "non_graphical": True, "use_grpc": True, "debug": False} + +# Check for the local config file, override defaults if found +local_path = os.path.dirname(os.path.realpath(__file__)) +local_config_file = os.path.join(local_path, "local_config.json") +if os.path.exists(local_config_file): + with open(local_config_file) as f: + local_config = json.load(f) + config.update(local_config) + +# The import should be here to have the updated properties +from ansys.aedt.toolkits.common.backend.rest_api import app + +settings.enable_error_handler = False +settings.enable_desktop_logs = False +settings.use_grpc_api = config.get("use_grpc", True) +settings.non_graphical = config["non_graphical"] + +scratch_path = tempfile.gettempdir() +local_scratch = Scratch(scratch_path) + +input_data_dir = pathlib.Path(__file__).parent.parent.parent +aedt_project = os.path.join(input_data_dir, "input_data", "Test.aedt") +aedt_scratch = shutil.copy(aedt_project, local_scratch.path) + +edb_scratch = shutil.copytree( + os.path.join(input_data_dir, "input_data", "edb_test.aedb"), os.path.join(local_scratch.path, "edb_test.aedb") +) + +# Logger +logger = logging.getLogger(__name__) +logger.setLevel(logging.DEBUG) +log_file = os.path.join(local_scratch.path, "pytest_rest_api.log") +file_handler = logging.FileHandler(log_file) +formatter = logging.Formatter("%(levelname)s - %(message)s") +file_handler.setFormatter(formatter) +logger.addHandler(file_handler) + +logging.StreamHandler() +formatter = logging.Formatter("%(levelname)s - %(message)s") +console_handler = logging.StreamHandler() +console_handler.setFormatter(formatter) +logger.addHandler(console_handler) + + +@pytest.fixture(scope="session") +def client(request): + logger.info("Client initialization") + with app.test_client() as client: + properties = { + "aedt_version": config["desktop_version"], + "non_graphical": config["non_graphical"], + "use_grpc": config.get("use_grpc", True), + "debug": config["debug"], + } + client.put("/properties", json=properties) + + client.post("/launch_aedt") + response = client.get("/wait_thread", json=60) + client.post("/open_project", json=aedt_scratch) + assert response.status_code == 200 + yield client + close_properties = {"close_projects": True, "close_on_exit": True} + client.post("/close_aedt", json=close_properties) + + if request.session.testsfailed == 0: + cleanup_process() + + +failed_tests = set() + + +def pytest_runtest_makereport(item, call): + if call.excinfo is not None and call.excinfo.typename == "AssertionError": + failed_tests.add(item) + + +@pytest.fixture(scope="session") +def aedt_example(): + return aedt_scratch + + +@pytest.fixture(scope="session") +def assert_handler(request): + def finalizer(): + # Code to run after the test + logger.info("Test Teardown") + # Check if any test has failed during the session + if request.session.testsfailed != 0: + # Additional code to run when an assert fails + for failed_test in failed_tests: + logger.error(f"FAILED: {failed_test.name}") + + request.addfinalizer(finalizer) + + return assert_handler + + +def cleanup_process(): + """Cleanup process after the test is completed.""" + for handler in logger.handlers: + if isinstance(handler, logging.FileHandler): + handler.close() + shutil.rmtree(local_scratch.path, ignore_errors=True) diff --git a/tests/backend/tests_rest_api/test_rest_api.py b/tests/backend/tests_rest_api/test_rest_api.py new file mode 100644 index 00000000..df9a2425 --- /dev/null +++ b/tests/backend/tests_rest_api/test_rest_api.py @@ -0,0 +1,60 @@ +import json +import os + +from ansys.aedt.toolkits.common.backend.api import ToolkitThreadStatus + + +class TestRESTAPI: + def test_00_get_status(self, client, assert_handler): + response = client.get("/status") + assert response.status_code == 200 + data = json.loads(response.data.decode("utf-8")) + assert data == ToolkitThreadStatus.IDLE.value + + def test_01_health(self, client, assert_handler): + response = client.get("/health") + assert response.status_code == 200 + + def test_02_get_properties(self, client, assert_handler): + response = client.get("/properties") + data = json.loads(response.data.decode("utf-8")) + assert response.status_code == 200 + assert data.get("log_file") + + def test_03_set_properties(self, client, assert_handler): + new_properties = { + "port": "5002", + } + response = client.put("/properties", json=new_properties) + assert response.status_code == 200 + response = client.get("/properties") + data = json.loads(response.data.decode("utf-8")) + assert data.get("port") == 5002 + + def test_04_installed_versions(self, client, assert_handler): + response = client.get("/installed_versions") + assert response.status_code == 200 + data = json.loads(response.data.decode("utf-8")) + assert isinstance(data, list) + + def test_05_aedt_sessions(self, client, assert_handler): + response = client.get("/aedt_sessions") + assert response.status_code == 200 + data = json.loads(response.data.decode("utf-8")) + assert isinstance(data, dict) + + def test_06_connect_design(self, client, assert_handler): + response = client.post("/connect_design", json={"aedtapp": "Icepak"}) + assert response.status_code == 200 + + def test_07_save_project(self, client, assert_handler, aedt_example): + new_project = os.path.join(os.path.dirname(aedt_example), "New.aedt") + response = client.post("/save_project", json=new_project) + assert response.status_code == 200 + assert os.path.exists(new_project) + + def test_08_get_design_names(self, client, assert_handler): + response = client.get("/design_names") + assert response.status_code == 200 + data = json.loads(response.data.decode("utf-8")) + assert isinstance(data, list)