Note
Go to the end to download the full example code.
EMIT: Classify interference type GUI#
This example uses a GUI to open an AEDT project with an EMIT design and analyze the results to classify the worst-case interference.
Perform required imports#
Perform required imports.
import sys
from pyaedt.emit_core.emit_constants import InterfererType, ResultType, TxRxMode
from pyaedt import Emit
from pyaedt import get_pyaedt_app
import pyaedt
import os
import subprocess
import pyaedt.generic.constants as consts
# Check that emit is a compatible version
aedt_version = "2024.1"
if aedt_version < "2023.2":
print("Must have v2023.2 or later")
sys.exit()
# Check to see which Python libraries have been installed
reqs = subprocess.check_output([sys.executable, '-m', 'pip', 'freeze'])
installed_packages = [r.decode().split('==')[0] for r in reqs.split()]
# Install required packages if they are not installed
def install(package):
subprocess.check_call([sys.executable, '-m', 'pip', 'install', package])
# Install required libraries for GUI and Excel exporting (internet connection needed)
required_packages = ['PySide6', 'openpyxl']
for package in required_packages:
if package not in installed_packages:
install(package)
# Import PySide6 and openpyxl libraries
from PySide6 import QtWidgets, QtUiTools, QtGui, QtCore
from openpyxl.styles import PatternFill
import openpyxl
# Uncomment if there are Qt plugin errors
# import PySide6
# dirname = os.path.dirname(PySide6.__file__)
# plugin_path = os.path.join(dirname, 'plugins', 'platforms')
# os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = plugin_path
# Launch EMIT
non_graphical = False
new_thread = True
desktop = pyaedt.launch_desktop(aedt_version, non_graphical, new_thread)
# Add emitapi to system path
emit_path = os.path.join(desktop.install_path, "Delcross")
sys.path.insert(0,emit_path)
import EmitApiPython
api = EmitApiPython.EmitApi()
# Define .ui file for GUI
ui_file = pyaedt.downloads.download_file("emit", "interference_gui.ui")
Ui_MainWindow, _ = QtUiTools.loadUiType(ui_file)
class DoubleDelegate(QtWidgets.QStyledItemDelegate):
def __init__(self, decimals, values, max_power, min_power):
super().__init__()
self.decimals = decimals
self.values = values
self.max_power = max_power
self.min_power = min_power
def createEditor(self, parent, option, index):
editor = super().createEditor(parent, option, index)
if isinstance(editor, QtWidgets.QLineEdit):
validator = QtGui.QDoubleValidator(parent)
num_rows = len(self.values)
cur_row = index.row()
if cur_row == 0:
min_val = self.values[1]
max_val = self.max_power
elif cur_row == num_rows - 1:
min_val = self.min_power
max_val = self.values[cur_row-1]
else:
min_val = self.values[cur_row + 1]
max_val = self.values[cur_row - 1]
validator.setRange(min_val, max_val, self.decimals)
validator.setNotation(QtGui.QDoubleValidator.Notation.StandardNotation)
editor.setValidator(validator)
return editor
def update_values(self, values):
self.values = values
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.emitapp = None
self.populating_dropdown = False
self.setupUi(self)
self.setup_widgets()
###############################################################################
# Setup widgets
# ~~~~~~~~~~~~~
# Define all widgets from the UI file, connect the widgets to functions, define
# table colors, and format table settings.
def setup_widgets(self):
# Widget definitions for file selection/tab management
self.file_select_btn = self.findChild(QtWidgets.QToolButton, "file_select_btn")
self.file_path_box = self.findChild(QtWidgets.QLineEdit, "file_path_box")
self.design_name_dropdown = self.findChild(QtWidgets.QComboBox, "design_name_dropdown")
self.design_name_dropdown.setEnabled(True)
self.tab_widget = self.findChild(QtWidgets.QTabWidget, "tab_widget")
# Widget definitions for protection level classification
self.protection_results_btn = self.findChild(QtWidgets.QPushButton, "protection_results_btn")
self.protection_matrix = self.findChild(QtWidgets.QTableWidget, "protection_matrix")
self.protection_legend_table = self.findChild(QtWidgets.QTableWidget, "protection_legend_table")
self.damage_check = self.findChild(QtWidgets.QCheckBox, "damage_check")
self.overload_check = self.findChild(QtWidgets.QCheckBox, "overload_check")
self.intermodulation_check = self.findChild(QtWidgets.QCheckBox, "intermodulation_check")
self.desensitization_check = self.findChild(QtWidgets.QCheckBox, "desensitization_check")
self.protection_export_btn = self.findChild(QtWidgets.QPushButton, "protection_export_btn")
self.radio_specific_levels = self.findChild(QtWidgets.QCheckBox, "radio_specific_levels")
self.radio_dropdown = self.findChild(QtWidgets.QComboBox, "radio_dropdown")
self.protection_save_img_btn = self.findChild(QtWidgets.QPushButton, 'protection_save_img_btn')
# warning label
self.warning_label = self.findChild(QtWidgets.QLabel, "warnings")
myFont = QtGui.QFont()
myFont.setBold(True)
self.warning_label.setFont(myFont)
self.warning_label.setHidden(True)
self.design_name_dropdown.currentIndexChanged.connect(self.design_dropdown_changed)
# Setup for protection level buttons and table
self.protection_results_btn.setEnabled(False)
self.protection_export_btn.setEnabled(False)
self.protection_save_img_btn.setEnabled(False)
self.file_select_btn.clicked.connect(self.open_file_dialog)
self.protection_export_btn.clicked.connect(self.save_results_excel)
self.protection_results_btn.clicked.connect(self.protection_results)
self.protection_legend_table.resizeRowsToContents()
self.protection_legend_table.resizeColumnsToContents()
self.damage_check.stateChanged.connect(self.protection_results)
self.overload_check.stateChanged.connect(self.protection_results)
self.intermodulation_check.stateChanged.connect(self.protection_results)
self.desensitization_check.stateChanged.connect(self.protection_results)
self.protection_legend_table.setEditTriggers(QtWidgets.QTableWidget.DoubleClicked)
self.global_protection_level = True
self.protection_levels = {}
values = [float(self.protection_legend_table.item(row, 0).text()) for row in range(self.protection_legend_table.rowCount())]
self.protection_levels['Global'] = values
self.changing = False
self.radio_dropdown.currentIndexChanged.connect(self.radio_dropdown_changed)
self.protection_legend_table.itemChanged.connect(self.table_changed)
self.protection_save_img_btn.clicked.connect(self.save_image)
# Widget definitions for interference type
self.interference_results_btn = self.findChild(QtWidgets.QPushButton, "interference_results_btn")
self.interference_matrix = self.findChild(QtWidgets.QTableWidget, "interference_matrix")
self.interference_legend_table = self.findChild(QtWidgets.QTableWidget, "interference_legend_table")
# set the items read only
for i in range(0, self.interference_legend_table.rowCount()):
item = self.interference_legend_table.item(i, 0)
item.setFlags(QtCore.Qt.ItemIsEnabled)
self.interference_legend_table.setItem(i, 0, item)
self.in_in_check = self.findChild(QtWidgets.QCheckBox, "in_in_check")
self.in_out_check = self.findChild(QtWidgets.QCheckBox, "in_out_check")
self.out_in_check = self.findChild(QtWidgets.QCheckBox, "out_in_check")
self.out_out_check = self.findChild(QtWidgets.QCheckBox, "out_out_check")
self.interference_export_btn = self.findChild(QtWidgets.QPushButton, "interference_export_btn")
self.interference_save_img_btn = self.findChild(QtWidgets.QPushButton, 'interference_save_img_btn')
# Setup for interference type buttons and table
self.interference_results_btn.setEnabled(False)
self.interference_export_btn.setEnabled(False)
self.interference_save_img_btn.setEnabled(False)
self.interference_export_btn.clicked.connect(self.save_results_excel)
self.interference_results_btn.clicked.connect(self.interference_results)
self.interference_legend_table.resizeRowsToContents()
self.interference_legend_table.resizeColumnsToContents()
self.in_in_check.stateChanged.connect(self.interference_results)
self.in_out_check.stateChanged.connect(self.interference_results)
self.out_in_check.stateChanged.connect(self.interference_results)
self.out_out_check.stateChanged.connect(self.interference_results)
self.radio_specific_levels.stateChanged.connect(self.radio_specific)
self.interference_save_img_btn.clicked.connect(self.save_image)
# Color definition dictionary and previous project/design names
self.color_dict = {"green": [QtGui.QColor(125, 115, 202),'#7d73ca'],
"yellow":[QtGui.QColor(211, 89, 162), '#d359a2'],
"orange": [QtGui.QColor(255, 99, 97), '#ff6361'],
"red": [QtGui.QColor(255, 166, 0), '#ffa600'],
"white": [QtGui.QColor("white"),'#ffffff']}
self.previous_design = ''
self.previous_project = ''
# Set the legend tables to strech resize mode
header = self.protection_legend_table.horizontalHeader()
v_header = self.protection_legend_table.verticalHeader()
header.setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeMode.Stretch)
v_header.setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeMode.Stretch)
v_header.setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeMode.Stretch)
v_header.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeMode.Stretch)
v_header.setSectionResizeMode(3, QtWidgets.QHeaderView.ResizeMode.Stretch)
header = self.interference_legend_table.horizontalHeader()
v_header = self.interference_legend_table.verticalHeader()
header.setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeMode.Stretch)
v_header.setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeMode.Stretch)
v_header.setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeMode.Stretch)
v_header.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeMode.Stretch)
v_header.setSectionResizeMode(3, QtWidgets.QHeaderView.ResizeMode.Stretch)
# Input validation for protection level legend table
self.delegate = DoubleDelegate(decimals=2, values=values,
max_power=1000, min_power=-200)
self.protection_legend_table.setItemDelegateForColumn(0, self.delegate)
self.open_file_dialog()
###############################################################################
# Open file dialog and select project
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Open the file dialog for project selection and populate the design dropdown
# with all EMIT designs in the project.
def open_file_dialog(self):
fname, _filter = QtWidgets.QFileDialog.getOpenFileName(self, "Select EMIT Project", "", "Ansys Electronics Desktop Files (*.aedt)", )
if fname:
self.file_path_box.setText(fname)
# Close previous project and open specified one
if self.emitapp is not None:
self.emitapp.close_project()
self.emitapp = None
desktop_proj = desktop.load_project(self.file_path_box.text())
# check for an empty project (i.e. no designs)
if isinstance(desktop_proj, bool):
self.file_path_box.setText("")
msg = QtWidgets.QMessageBox()
msg.setWindowTitle("Error: Project missing designs.")
msg.setText(
"The selected project has no designs. Projects must have at least "
"one EMIT design. See AEDT log for more information.")
x = msg.exec()
return
# Check if project is already open
if desktop_proj.lock_file == None:
msg = QtWidgets.QMessageBox()
msg.setWindowTitle("Error: Project already open")
msg.setText("Project is locked. Close or remove the lock before proceeding. See AEDT log for more information.")
x = msg.exec()
return
# Populate design dropdown with all design names
designs = desktop_proj.design_list
emit_designs = []
self.populating_dropdown = True
self.design_name_dropdown.clear()
self.populating_dropdown = False
for d in designs:
design_type = desktop.design_type(desktop_proj.project_name, d)
if design_type == "EMIT":
emit_designs.append(d)
# add warning if no EMIT design
# Note: this should never happen since loading a project without an EMIT design
# should add a blank EMIT design
self.warning_label.setHidden(True)
if len(emit_designs) == 0:
self.warning_label.setText("Warning: The project must contain at least one EMIT design.")
self.warning_label.setHidden(False)
return
self.populating_dropdown = True
self.design_name_dropdown.addItems(emit_designs)
self.populating_dropdown = False
self.emitapp = get_pyaedt_app(desktop_proj.project_name, emit_designs[0])
self.design_name_dropdown.setCurrentIndex(0)
# check for at least 2 radios
radios = self.emitapp.modeler.components.get_radios()
self.warning_label.setHidden(True)
if len(radios) < 2:
self.warning_label.setText("Warning: The selected design must contain at least two radios.")
self.warning_label.setHidden(False)
if self.radio_specific_levels.isEnabled():
self.radio_specific_levels.setChecked(False)
self.radio_dropdown.clear()
self.radio_dropdown.setEnabled(False)
self.protection_levels = {}
values = [float(self.protection_legend_table.item(row, 0).text()) for row in range(self.protection_legend_table.rowCount())]
self.protection_levels['Global'] = values
self.radio_specific_levels.setEnabled(True)
self.protection_results_btn.setEnabled(True)
self.interference_results_btn.setEnabled(True)
###############################################################################
# Change design selection
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Refresh the warning messages when the selected design changes
def design_dropdown_changed(self):
if self.populating_dropdown:
# don't load design's on initial project load
return
design_name = self.design_name_dropdown.currentText()
self.emitapp = get_pyaedt_app(self.emitapp.project_name, design_name)
# check for at least 2 radios
radios = self.emitapp.modeler.components.get_radios()
self.warning_label.setHidden(True)
if len(radios) < 2:
self.warning_label.setText("Warning: The selected design must contain at least two radios.")
self.warning_label.setHidden(False)
# clear the table if the design is changed
self.clear_table()
###############################################################################
# Enable radio specific protection levels
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Activate radio selection dropdown and initialize dictionary to store protection levels
# when the radio-specific level dropdown is checked.
def radio_specific(self):
self.radio_dropdown.setEnabled(self.radio_specific_levels.isChecked())
self.radio_dropdown.clear()
if self.radio_dropdown.isEnabled():
self.emitapp.set_active_design(self.design_name_dropdown.currentText())
radios = self.emitapp.modeler.components.get_radios()
values = [float(self.protection_legend_table.item(row, 0).text()) for row in range(self.protection_legend_table.rowCount())]
for radio in radios:
if radios[radio].has_rx_channels():
self.protection_levels[radio] = values
self.radio_dropdown.addItem(radio)
else:
self.radio_dropdown.clear()
values = [float(self.protection_legend_table.item(row, 0).text()) for row in range(self.protection_legend_table.rowCount())]
self.protection_levels['Global'] = values
self.global_protection_level = not self.radio_specific_levels.isChecked()
###############################################################################
# Update legend table
# ~~~~~~~~~~~~~~~~~~~
# Update shown legend table values when the radio dropdown value changes.
def radio_dropdown_changed(self):
if self.radio_dropdown.isEnabled():
self.changing = True
for row in range(self.protection_legend_table.rowCount()):
item = self.protection_legend_table.item(row, 0)
item.setText(str(self.protection_levels[self.radio_dropdown.currentText()][row]))
self.changing = False
# update the validator so min/max for each row is properly set
values = [float(self.protection_legend_table.item(row, 0).text()) for row in
range(self.protection_legend_table.rowCount())]
self.delegate.update_values(values)
###############################################################################
# Save legend table values
# ~~~~~~~~~~~~~~~~~~~~~~~~
# Save inputted radio protection level threshold values every time one is changed
# in the legend table.
def table_changed(self):
if self.changing == False:
values = [float(self.protection_legend_table.item(row, 0).text()) for row in
range(self.protection_legend_table.rowCount())]
if self.radio_dropdown.currentText() == '':
index = 'Global'
else:
index = self.radio_dropdown.currentText()
self.protection_levels[index] = values
self.delegate.update_values(values)
###############################################################################
# Save scenario matrix to as PNG file
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Save the scenario matrix table as a PNG file.
def save_image(self):
if self.tab_widget.currentIndex() == 0:
table = self.protection_matrix
else:
table = self.interference_matrix
fname, _filter = QtWidgets.QFileDialog.getSaveFileName(self, "Save Scenario Matrix", "Scenario Matrix", "png (*.png)")
if fname:
image = QtGui.QImage(table.size(), QtGui.QImage.Format_ARGB32)
table.render(image)
image.save(fname)
###############################################################################
# Save scenario matrix to Excel file
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Write the scenario matrix results to an Excel file with color coding.
def save_results_excel(self):
defaultName = ""
if self.tab_widget.currentIndex() == 0:
table = self.protection_matrix
defaultName = "Protection Level Classification"
else:
table = self.interference_matrix
defaultName = "Interference Type Classification"
fname, _filter = QtWidgets.QFileDialog.getSaveFileName(self, "Save Scenario Matrix", defaultName, "xlsx (*.xlsx)")
if fname:
workbook = openpyxl.Workbook()
worksheet = workbook.active
header = self.tx_radios[:]
header.insert(0, "Tx/Rx")
worksheet.append(header)
for row in range(2, table.rowCount()+2):
worksheet.cell(row = row, column = 1, value = str(self.rx_radios[row-2]))
for col in range(2, table.columnCount()+2):
text = str(table.item(row-2, col-2).text())
worksheet.cell(row = row, column = col, value = text)
cell = worksheet.cell(row, col)
cell.fill = PatternFill(start_color = self.color_dict[self.all_colors[col-2][row-2]][1][1:],
end_color = self.color_dict[self.all_colors[col-2][row-2]][1][1:],
fill_type = "solid")
workbook.save(fname)
###############################################################################
# Run interference type simulation
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Run interference type simulation and classify results.
def interference_results(self):
# Initialize filter check marks and expected filter results
self.interference_checks = [self.in_in_check.isChecked(), self.out_in_check.isChecked(),
self.in_out_check.isChecked(), self.out_out_check.isChecked()]
self.interference_filters =["TxFundamental:In-band", ["TxHarmonic/Spurious:In-band","Intermod:In-band", "Broadband:In-band"],
"TxFundamental:Out-of-band", ["TxHarmonic/Spurious:Out-of-band","Intermod:Out-of-band", "Broadband:Out-of-band"]]
# Create list of problem types to analyze according to inputted filters
filter = [i for (i,v) in zip(self.interference_filters, self.interference_checks) if v]
if self.file_path_box.text() != "" and self.design_name_dropdown.currentText() != "":
if self.previous_design != self.design_name_dropdown.currentText() or self.previous_project != self.file_path_box.text():
self.previous_design = self.design_name_dropdown.currentText()
self.previous_project = self.file_path_box.text()
self.emitapp.set_active_design(self.design_name_dropdown.currentText())
# Check if file is read-only
if self.emitapp.save_project() == False:
msg = QtWidgets.QMessageBox()
msg.setWindowTitle("Writing Error")
msg.setText("An error occurred while writing to the file. Is it readonly? Disk full? See AEDT log for more information.")
x = msg.exec()
return
# Get results and radios
self.rev = self.emitapp.results.analyze()
self.tx_interferer = InterfererType().TRANSMITTERS
self.rx_radios = self.rev.get_receiver_names()
self.tx_radios = self.rev.get_interferer_names(self.tx_interferer)
# Check if design is valid
if self.tx_radios is None or self.rx_radios is None:
return
# Classify the interference
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Iterate over all the transmitters and receivers and compute the power
# at the input to each receiver due to each of the transmitters. Compute
# which, if any, type of interference occurred.
domain = self.emitapp.results.interaction_domain()
self.all_colors, self.power_matrix = self.rev.interference_type_classification(domain, use_filter = True, filter_list = filter)
# Save project and plot results on table widget
self.emitapp.save_project()
self.populate_table()
###############################################################################
# Run protection level simulation
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Run protection level simulation and classify results accroding to inputted
# threshold levels.
def protection_results(self):
# Initialize filter check marks and expected filter results
self.protection_checks = [self.damage_check.isChecked(), self.overload_check.isChecked(),
self.intermodulation_check.isChecked(), self.desensitization_check.isChecked()]
self.protection_filters = ['damage', 'overload', 'intermodulation', 'desensitization']
filter = [i for (i,v) in zip(self.protection_filters, self.protection_checks) if v]
if self.file_path_box.text() != "" and self.design_name_dropdown.currentText() != "":
if self.previous_design != self.design_name_dropdown.currentText() or self.previous_project != self.file_path_box.text():
self.previous_design = self.design_name_dropdown.currentText()
self.previous_project = self.file_path_box.text()
self.emitapp.set_active_design(self.design_name_dropdown.currentText())
# Check if file is read-only
if self.emitapp.save_project() == False:
msg = QtWidgets.QMessageBox()
msg.setWindowTitle("Writing Error")
msg.setText("An error occurred while writing to the file. Is it readonly? Disk full? See AEDT log for more information.")
x = msg.exec()
return
# Get results and design radios
self.tx_interferer = InterfererType().TRANSMITTERS
self.rev = self.emitapp.results.analyze()
self.rx_radios = self.rev.get_receiver_names()
self.tx_radios = self.rev.get_interferer_names(self.tx_interferer)
# Check if there are radios in the design
if self.tx_radios is None or self.rx_radios is None:
return
domain = self.emitapp.results.interaction_domain()
self.all_colors, self.power_matrix = self.rev.protection_level_classification(domain,
self.global_protection_level,
self.protection_levels['Global'],
self.protection_levels, use_filter = True,
filter_list = filter)
self.populate_table()
###############################################################################
# Populate the scenario matrix
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Create a scenario matrix view with the transmitters defined across the top
# and receivers down the left-most column.
def populate_table(self):
if self.tab_widget.currentIndex() == 0:
table = self.protection_matrix
button = self.protection_export_btn
img_btn = self.protection_save_img_btn
else:
table = self.interference_matrix
button = self.interference_export_btn
img_btn = self.interference_save_img_btn
num_cols = len(self.all_colors)
num_rows = len(self.all_colors[0])
table.setColumnCount(num_cols)
table.setRowCount(num_rows)
table.setVerticalHeaderLabels(self.rx_radios)
table.setHorizontalHeaderLabels(self.tx_radios)
for col in range(num_cols):
for row in range(num_rows):
item = QtWidgets.QTableWidgetItem(str(self.power_matrix[col][row]))
table.setItem(row, col, item)
cell = table.item(row, col)
cell.setBackground(self.color_dict[self.all_colors[col][row]][0])
button.setEnabled(True)
img_btn.setEnabled(True)
def clear_table(self):
# get the table/buttons based on current tab
if self.tab_widget.currentIndex() == 0:
table = self.protection_matrix
button = self.protection_export_btn
img_btn = self.protection_save_img_btn
else:
table = self.interference_matrix
button = self.interference_export_btn
img_btn = self.interference_save_img_btn
# disable export options
button.setEnabled(False)
img_btn.setEnabled(False)
# clear the table
table.setColumnCount(0)
table.setRowCount(0)
###############################################################################
# GUI closing event
# ~~~~~~~~~~~~~~~~~
# Close AEDT if the GUI is closed.
def closeEvent(self, event):
msg = QtWidgets.QMessageBox()
msg.setWindowTitle("Closing GUI")
msg.setText("Closing AEDT. Wait for the GUI to close on its own.")
x = msg.exec()
if self.emitapp:
self.emitapp.close_project()
self.emitapp.close_desktop()
else:
desktop.release_desktop(True, True)
C:\actions-runner\_work\_tool\Python\3.10.9\x64\lib\subprocess.py:1072: ResourceWarning: subprocess 6576 is still running
_warn("subprocess %s is still running" % self.pid,
C:\actions-runner\_work\pyaedt\pyaedt\.venv\lib\site-packages\pyaedt\generic\settings.py:383: ResourceWarning: unclosed file <_io.TextIOWrapper name='D:\\Temp\\pyaedt_ansys.log' mode='a' encoding='cp1252'>
self._logger = val
Run GUI#
Launch the GUI. If you want to run the GUI, uncomment the window.show()
and
app.exec_()
method calls.
if __name__ == '__main__' and os.getenv("PYAEDT_DOC_GENERATION", "False") != "1":
app = QtWidgets.QApplication([])
window = MainWindow()
window.show()
app.exec()
else:
desktop.release_desktop(True, True)
Total running time of the script: (0 minutes 19.279 seconds)