# -*- coding: utf-8 -*-
from os.path import basename, join, dirname, isfile
from logging import getLogger
from ....Functions.GUI.log_error import log_error
from ....loggers import GUI_LOG_NAME
from PySide2.QtCore import Qt, Signal
from PySide2.QtWidgets import QFileDialog, QMessageBox, QWidget
from ....Functions.load import load, load_machine_materials
from ....GUI.Dialog.DMachineSetup import mach_index, mach_list
from ....GUI.Dialog.DMachineSetup.Ui_DMachineSetup import Ui_DMachineSetup
from ....GUI.Dialog.DMachineSetup.SPreview.SPreview import SPreview
from ....GUI.Dialog.DMachineSetup.SSimu.SSimu import SSimu
from ....definitions import config_dict
from ....Classes.Machine import Machine
# Flag for set the enable property of w_nav (List_Widget)
DISABLE_ITEM = Qt.NoItemFlags
ENABLE_ITEM = Qt.ItemIsSelectable | Qt.ItemIsEnabled
[docs]class DMachineSetup(Ui_DMachineSetup, QWidget):
"""Main windows of the Machine Setup Tools"""
# Signal to update the simulation
machineChanged = Signal()
rejected = Signal()
def __init__(self, machine=None, material_dict=None, machine_path=""):
"""Initialize the GUI according to machine type
Parameters
----------
self : DMachineSetup
a DMachineSetup object
machine : Machine
Machine to edit
material_dict: dict
Materials dictionary (library + machine)
machine_path : str
Default loading path for machine
"""
# Build the interface according to the .ui file
QWidget.__init__(self)
self.setWindowFlag(Qt.WindowMinimizeButtonHint, True)
self.setWindowFlag(Qt.WindowMaximizeButtonHint, True)
self.setupUi(self)
self.is_save_needed = False
self.material_dict = material_dict
self.last_index = 0 # Index of the last step available
# Saving arguments
self.machine = machine
if machine_path == "":
self.machine_path = config_dict["MAIN"]["MACHINE_DIR"].replace("\\", "/")
else:
self.machine_path = machine_path.replace("\\", "/")
# Initialize the machine if needed
if machine is None:
self.machine = type(mach_list[0]["init_machine"])(
init_dict=mach_list[0]["init_machine"].as_dict()
)
self.update_nav()
self.set_nav(self.last_index)
# Connect save/load button
self.nav_step.currentRowChanged.connect(self.set_nav)
self.b_save.clicked.connect(self.s_save)
self.b_load.clicked.connect(self.s_load)
self.qmessagebox_question = None
[docs] def save_needed(self):
"""Set is_save_needed to True"""
self.is_save_needed = True
[docs] def closeEvent(self, event):
"""Display a message before leaving
Parameters
----------
self : DMachineSetup
A DMachineSetup object
event :
The closing event
"""
if self.is_save_needed:
quit_msg = self.tr(
"Unsaved changes will be lost.\nDo you want to save the machine?"
)
reply = QMessageBox.question(
self,
self.tr("Please save before closing"),
quit_msg,
QMessageBox.Yes,
QMessageBox.No,
)
self.qmessagebox_question = reply
if reply == QMessageBox.Yes:
self.s_save()
[docs] def reject(self):
""" """
self.rejected.emit()
self.close()
[docs] def s_save(self):
"""Slot for saving the current machine to a json file
Parameters
----------
self : DMachineSetup
A DMachineSetup object
"""
# Ask the user to select a .m file to save
if self.machine.name in ["", None]:
save_file_path = QFileDialog.getSaveFileName(
self, self.tr("Save file"), self.machine_path, "Json (*.json)"
)[0]
else:
def_path = join(self.machine_path, self.machine.name + ".json")
save_file_path = QFileDialog.getSaveFileName(
self, self.tr("Save file"), def_path, "Json (*.json)"
)[0]
save_file_path = save_file_path.replace("\\", "/")
# Avoid bug due to user closing the popup witout selecting a file
if save_file_path != "":
# Set the machine name to match the file name
self.machine.name = str(basename(str(save_file_path)))[:-5]
# Save the machine file
getLogger(GUI_LOG_NAME).info(
"Saving " + self.machine.name + " in folder " + dirname(save_file_path)
)
try:
self.machine.save(save_file_path)
except Exception as e:
err_msg = (
"Error while saving machine " + self.machine.name + ":\n" + str(e)
)
log_error(self, err_msg)
return False
# To update the machine name field (if first page)
self.set_nav(self.nav_step.currentRow())
# Update the machine path to remember the last used folder
self.machine_path = dirname(save_file_path)
# Notify the project GUI that the machine has changed
self.machineChanged.emit()
self.is_save_needed = False
return True
return False
[docs] def s_save_close(self):
"""Signal to save and close
Parameters
----------
self : DMachineSetup
A DMachineSetup object
"""
to_close = self.s_save()
if to_close:
self.close()
[docs] def s_load(self):
"""Slot to load a machine from a .json file (triggered by b_load)
Parameters
----------
self : DMachineSetup
A DMachineSetup object
"""
### TODO: handle material data, i.e. "connect", set new material, etc.
# Ask the user to select a .json file to load
load_path = str(
QFileDialog.getOpenFileName(
self, self.tr("Load file"), self.machine_path, "Json (*.json)"
)[0]
)
if load_path != "":
try:
# Update the machine path to remember the last used folder
self.machine_path = dirname(load_path)
# Load and check type of instance
machine = load(load_path)
if isinstance(machine, Machine):
self.machine = machine
load_machine_materials(self.material_dict, self.machine)
else:
QMessageBox().critical(
self,
self.tr("Error"),
self.tr("The choosen file is not a machine file."),
)
return
self.machineChanged.emit()
self.is_save_needed = False
except Exception as e:
QMessageBox().critical(
self,
self.tr("Error"),
self.tr(
"The machine file is incorrect:\n",
"Please keep the \n, another " "message is following this one",
)
+ type(e).__name__
+ ": "
+ str(e),
)
return
self.update_nav()
[docs] def update_nav(self, next_step=None):
"""Update the nav list to match the step of the current machine"""
mach_dict = mach_list[self.get_machine_index()]
self.nav_step.blockSignals(True)
self.nav_step.clear()
index = 1
for step in mach_dict["start_step"]:
self.nav_step.addItem(" " + str(index) + ": " + step.step_name)
index += 1
for step in mach_dict["stator_step"]:
self.nav_step.addItem(" " + str(index) + ": Stator " + step.step_name)
index += 1
for step in mach_dict["rotor_step"]:
if index < 10:
self.nav_step.addItem(" " + str(index) + ": Rotor " + step.step_name)
else:
self.nav_step.addItem(str(index) + ": Rotor " + step.step_name)
index += 1
# Adding step Machine Summary
if index < 10:
self.nav_step.addItem(" " + str(index) + ": " + SPreview.step_name)
else:
self.nav_step.addItem(str(index) + ": " + SPreview.step_name)
index += 1
# Adding Simulation Step
if index < 10:
self.nav_step.addItem(" " + str(index) + ": " + SSimu.step_name)
else:
self.nav_step.addItem(str(index) + ": " + SSimu.step_name)
# Update GUI and select correct step
self.update_enable_nav()
self.nav_step.blockSignals(False)
if next_step is None:
self.nav_step.setCurrentRow(self.last_index)
else:
self.nav_step.setCurrentRow(next_step)
[docs] def update_enable_nav(self):
# Load for readibility
nav = self.nav_step
machine = self.machine
mach_dict = mach_list[self.get_machine_index()]
# First we disable all the item in the navigation widget
for ii in range(0, nav.count()):
nav.item(ii).setFlags(DISABLE_ITEM)
# First step is always available
nav.item(0).setFlags(ENABLE_ITEM)
index = 1
# Check the start steps
for step in mach_dict["start_step"]:
if step.check(machine) is not None:
self.last_index = index - 1
return None # Exit at the first fail
nav.item(index).setFlags(ENABLE_ITEM)
index += 1
# Check the stator steps
for step in mach_dict["stator_step"]:
if step.check(machine.stator) is not None:
self.last_index = index - 1
return None # Exit at the first fail
nav.item(index).setFlags(ENABLE_ITEM)
index += 1
# Check the rotor steps
for step in mach_dict["rotor_step"]:
if step.check(machine.rotor) is not None:
self.last_index = index - 1
return None # Exit at the first fail
nav.item(index).setFlags(ENABLE_ITEM)
index += 1
# Enable and select FEMM Simulation
nav.item(index).setFlags(ENABLE_ITEM)
self.last_index = index
[docs] def get_machine_index(self):
"""Get the index corresponding to the current machine in the mach_list"""
# Get the correct machine dictionary
index = mach_index.index(type(self.machine))
if index == -1:
QMessageBox().critical(
self, self.tr("Error"), self.tr("Unknown machine type")
)
self.machine = type(mach_list[0]["init_machine"])(
init_dict=mach_list[0]["init_machine"].as_dict()
)
return self.get_machine_index()
return index
[docs] def set_nav(self, index):
"""Select the current widget according to the current machine type
and the current nav index
Parameters
----------
self : DMachineSetup
A DMachineSetup object
index : int
Current index of nav_step
"""
# Get the step list of the current machine
mach_dict = mach_list[self.get_machine_index()]
step_list = list()
step_list.extend(mach_dict["start_step"])
step_list.extend(mach_dict["stator_step"])
step_list.extend(mach_dict["rotor_step"])
step_list.append(SPreview)
step_list.append(SSimu)
is_stator = "Stator" in self.nav_step.currentItem().text()
# Regenerate the step with the current values
self.w_step.setParent(None)
self.w_step = step_list[index](
machine=self.machine, material_dict=self.material_dict, is_stator=is_stator
)
self.w_step.b_previous.clicked.connect(self.s_previous)
if index != len(step_list) - 1:
self.w_step.b_next.setText(self.tr("Next"))
self.w_step.b_next.clicked.connect(self.s_next)
# else:
# self.w_step.b_next.setText(self.tr("Save and Close"))
# self.w_step.b_next.clicked.connect(self.s_save_close)
self.w_step.saveNeeded.connect(self.save_needed)
# Refresh the GUI
self.main_layout.insertWidget(1, self.w_step)
[docs] def s_next(self):
"""Signal to set the next step as accessible (and selected)
Parameters
----------
self : DMachineSetup
A DMachineSetup object
"""
next_index = self.nav_step.currentRow() + 1
mach_dict = mach_list[self.get_machine_index()]
if next_index - 1 < len(mach_dict["start_step"]):
error = self.w_step.check(self.machine)
elif next_index - 1 < len(mach_dict["start_step"]) + len(
mach_dict["stator_step"]
):
error = self.w_step.check(self.machine.stator)
else:
error = self.w_step.check(self.machine.rotor)
if error is not None: # An error: display it in a popup
QMessageBox().critical(self, self.tr("Error"), error)
else: # No error => Go to the next page
self.nav_step.item(next_index).setFlags(ENABLE_ITEM)
self.last_index = next_index
self.nav_step.setCurrentRow(next_index)
# As the current row have changed, set_nav is called
[docs] def s_previous(self):
"""Signal to set the previous page of w_page_stack as accessible (and selected)
Parameters
----------
self : DMachineSetup
A DMachineSetup object
"""
next_index = self.nav_step.currentRow() - 1
self.nav_step.setCurrentRow(next_index)
# As the current row have change, set_nav is called