class QuickRenameView(QWidget):
    """Main view for QuickRename.

    Attributes:
        layout: Layout associated with QuickRenameView.
        folder_view: View which represents folder selection where renamable files live.
        limit_view: View which provides options for limiting what is renamed.
        file_view: View which provides an interface for renaming selected files.
        options_view: View which provides options which control how renaming is performed.
        buttons_view: View which provides buttons to trigger actions in rename process.
    """
    def __init__(self, folder_view: object, limit_view: object,
                 file_view: object, options_view: object,
                 buttons_view: object):
        """Initialization of QuickRenameView.

        Args:
            folder_view: View which represents folder selection where renamable files live.
            limit_view: View which provides options for limiting what is renamed.
            file_view: View which provides an interface for renaming selected files.
            options_view: View which provides options which control how renaming is performed.
            buttons_view: View which provides buttons to trigger actions in rename process.
        """
        super(QuickRenameView, self).__init__()

        self.layout = QVBoxLayout()
        self.folder_view = folder_view
        self.limit_view = limit_view
        self.file_view = file_view
        self.options_view = options_view
        self.buttons_view = buttons_view
        self._instructions_layout = QHBoxLayout()
        self._instructions = QLabel(prefs.INSTRUCTIONS)

        self._configure()

    def _configure(self) -> None:
        """Configure QuickRenameView."""
        self.layout.setAlignment(Qt.AlignTop)
        self._instructions_layout.setAlignment(Qt.AlignCenter)
        self._instructions.setObjectName(prefs.INSTRUCTIONS_NAME)
        self._instructions_layout.addWidget(self._instructions)
        self.layout.addWidget(self.folder_view)
        self.layout.addWidget(self.limit_view)
        self.layout.addSpacerItem(QSpacerItem(*prefs.SPACING))
        self.layout.addLayout(self._instructions_layout)
        self.layout.addWidget(self.file_view)
        self.layout.addWidget(self.options_view)
        self.layout.addWidget(self.buttons_view)

        self.setWindowTitle(prefs.TITLE)
        self.setWindowIcon(QIcon("icons/quick_rename_icon.png"))
        self.setStyleSheet(CSS)
        self.setLayout(self.layout)
    def __init__(self, parent=None):
        super(Repertoire, self).__init__(parent)
        global filename
        self.monRepertoire = {}
        self.labelNom = QLabel("Nom")
        self.labelPrenom = QLabel("Prenom")
        self.labelTel = QLabel("Tel")
        self.leNom = QLineEdit()
        self.lePrenom = QLineEdit()
        self.leTel = QLineEdit()
        self.lwListeNoms = QListWidget()
        self.pbAjouter = QPushButton("Ajouter")
        self.pbModifier = QPushButton("Modifier")

        self.monRepertoire = self.lireJSON(filename)

        #self.lwListeNoms.itemClicked.connect(self.userSelected)
        self.pbAjouter.clicked.connect(self.addUser)
        #self.pbModifier.clicked.connect(self.modifyUser)

        layoutLabels = QVBoxLayout()
        layoutLabels.addWidget(self.labelNom)
        layoutLabels.addWidget(self.labelPrenom)
        layoutLabels.addWidget(self.labelTel)
        layoutLabels.addSpacerItem(
            QSpacerItem(10, 100, QSizePolicy.Expanding, QSizePolicy.Expanding))

        layoutLineEdit = QVBoxLayout()
        layoutLineEdit.addWidget(self.leNom)
        layoutLineEdit.addWidget(self.lePrenom)
        layoutLineEdit.addWidget(self.leTel)
        layoutLineEdit.addSpacerItem(
            QSpacerItem(10, 100, QSizePolicy.Expanding, QSizePolicy.Expanding))

        HLayout = QHBoxLayout()
        HLayout.addWidget(self.lwListeNoms)
        HLayout.addLayout(layoutLabels)
        HLayout.addLayout(layoutLineEdit)

        HLayoutButtons = QHBoxLayout()
        HLayoutButtons.addSpacerItem(QSpacerItem(10, 10,
                                                 QSizePolicy.Expanding))
        HLayoutButtons.addWidget(self.pbModifier)
        HLayoutButtons.addWidget(self.pbAjouter)

        genLayout = QVBoxLayout()
        genLayout.addLayout(HLayout)
        genLayout.addLayout(HLayoutButtons)

        self.setLayout(genLayout)
        self.updateListw()
class ConfigureUsersDialog(QDialog):
    def __init__(self, users):
        super().__init__()
        self.setWindowTitle("Configure Users")
        self.model = TableModel(users)

        self.layout = QVBoxLayout()
        self.table_layout = QHBoxLayout()
        self.users_table = QTableView()
        self.users_table.setModel(self.model)
        self.users_table.horizontalHeader().setStretchLastSection(True)
        self.table_layout.addWidget(self.users_table)

        self.table_buttons_layout = QVBoxLayout()
        self.add_user_button = QPushButton()
        self.add_user_button.setText("Add User")
        self.add_user_button.clicked.connect(self._add_user_clicked)
        self.delete_user_button = QPushButton()
        self.delete_user_button.setText("Delete User")
        self.delete_user_button.clicked.connect(self._delete_user_clicked)
        self.table_buttons_layout.addSpacerItem(
            QSpacerItem(0, 0, vData=QSizePolicy.Expanding))
        self.table_buttons_layout.addWidget(self.add_user_button)
        self.table_buttons_layout.addWidget(self.delete_user_button)
        self.table_buttons_layout.addSpacerItem(
            QSpacerItem(0, 0, vData=QSizePolicy.Expanding))
        self.table_layout.addLayout(self.table_buttons_layout)

        self.layout.addLayout(self.table_layout)

        self.error_text = QLabel()
        self.error_text.setStyleSheet("color: red;")
        self.layout.addWidget(self.error_text)

        self.button_box = QDialogButtonBox(QDialogButtonBox.Ok
                                           | QDialogButtonBox.Cancel)
        self.button_box.accepted.connect(self._on_accepted_clicked)
        self.button_box.rejected.connect(self.reject)
        self.layout.addWidget(self.button_box)
        self.setLayout(self.layout)

        self.resize(600, 400)

    def _on_accepted_clicked(self):
        self._complete_table()
        if not self.model.are_users_unique():
            self.error_text.setText("Name must be unique for each user")
            return

        self.error_text.setText("")
        self.accept()

    def _complete_table(self):
        # The currently selected cell only updates the model when 'return' is
        # pressed or another cell is selected. If the user is updating a value
        # then clicks 'OK' without pressing 'return' then the change is lost.
        # This is a Qt thing - the workaround is to take focus from the table.
        self.button_box.button(self.button_box.Ok).setFocus()

    def _add_user_clicked(self):
        self.users_table.model().insertRow(self.model.num_rows)

    def _delete_user_clicked(self):
        rows_to_remove = set()
        for index in self.users_table.selectedIndexes():
            rows_to_remove.add(index.row())
        self.users_table.model().removeRows(list(rows_to_remove))

    def get_users(self):
        return self.model.users
class RenameOptionsView(QWidget):
    """View responsible for holding renaming options.
    
    Attributes:
        layout (QVBoxLayout): Main layout of view.
        frame_layout (QVBoxLayout: Layout of frame which holds options.
        frame (QFrame): Frame surrounding options.
        prefix_h_layout (QHBoxLayout): Layout holding prefix options.
        complete_rename_h_layout (QHBoxLayout): Layout holding complete rename options.
        search_and_replace_h_layout (QHBoxLayout): Layout holding search and replace options.
        renumber_h_layout (QHBoxLayout): Layout holding renumber options.
        remove_ext_h_layout (QHBoxLayout): Layout holding remove options.
        change_ext_h_layout (QHBoxLayout): Layout holding change extension options.
        create_backup_h_layout (QHBoxLayout): Layout holding backup options.
        preview_h_layout (QHBoxLayout): Layout holding preview options.
        start_lbl (QLabel): Label for renumbering start.
        padding_lbl (QLabel): Label for renumbering padding.
        add_prefix_cb (QCheckBox): Used to signify the user wants to add a prefix to the renaming.
        prefix (QLineEdit): prefix to add.
        complete_rename_cb (QCheckBox): Used to signify the user wants to completely rename the file.
        new_name (QLineEdit): New name used when renaming.
        search_and_replace_cb (QCheckBox): Used to signify the user wants to partially rename files.
        find (QLineEdit): When searching and replacing this is what the user wants to search for.
        replace (QLineEdit): When searching and replacing this is what the user wants to replace with.
        renumber_cb (QCheckBox): Used to signify the user wants to renumber while renaming.
        start_num (QSpinBox): Number to start with when renumbering files.
        padding (QComboBox): Padding to apply to renaming when renumbering files.
        dot_cb (QCheckBox): When checked a dot will be used to separate the renumber from the name.
        remove_ext_cb (QCheckBox): Used to signify the user wants to remove extensions when renaming.
        backup_files_cb (QCheckBox): Used to signify the user wants to backup old files before renaming.
        change_ext_cb (QCheckBox): Used to signify the user wants to change the extension while renaming.
        change_ext (QLineEdit): New extension to add to the renamed file.
        preview_cb (QCheckBox): Used to signify the user wants to preview the rename before renaming.
    """
    def __init__(self):
        super(RenameOptionsView, self).__init__()
        self.layout = QVBoxLayout()
        self.frame_layout = QVBoxLayout()
        self.options_lbl = QLabel(prefs.OPTIONS)
        self.frame = QFrame()
        self.prefix_h_layout = QHBoxLayout()
        self.complete_rename_h_layout = QHBoxLayout()
        self.search_and_replace_h_layout = QHBoxLayout()
        self.renumber_h_layout = QHBoxLayout()
        self.remove_ext_h_layout = QHBoxLayout()
        self.change_ext_h_layout = QHBoxLayout()
        self.create_backup_h_layout = QHBoxLayout()
        self.preview_h_layout = QHBoxLayout()
        self.start_lbl = QLabel(prefs.START_NUM)
        self.padding_lbl = QLabel(prefs.PADDING)
        self.add_prefix_cb = QCheckBox(prefs.PREFIX)
        self.prefix = QLineEdit(prefs.PREFIX_DEFAULT)
        self.complete_rename_cb = QCheckBox(prefs.COMPLETE_RENAME)
        self.new_name = QLineEdit(prefs.COMPLETE_RENAME_DEFAULT)
        self.search_and_replace_cb = QCheckBox(prefs.SEARCH_AND_REPLACE)
        self.find = QLineEdit(prefs.SEARCH_AND_REPLACE_DEFAULT)
        self.replace = QLineEdit(prefs.REPLACE_WITH_DEFAULT)
        self.renumber_cb = QCheckBox(prefs.RENUMBER)
        self.start_num = QSpinBox()
        self.padding = QComboBox()
        self.dot_cb = QCheckBox(prefs.USE_DOT)
        self.remove_ext_cb = QCheckBox(prefs.REMOVE_EXT)
        self.backup_files_cb = QCheckBox(prefs.BACKUP)
        self.change_ext_cb = QCheckBox(prefs.CHANGE_EXT)
        self.change_ext = QLineEdit(prefs.CHANGE_EXT_DEFAULT)
        self.preview_cb = QCheckBox(prefs.PREVIEW)

        self._configure()

    def _configure(self) -> None:
        """Configure the RenameOptionsView."""
        self.frame.setLayout(self.frame_layout)
        self.frame.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)
        self.layout.addWidget(self.options_lbl)
        self.layout.addWidget(self.frame)
        self.add_prefix_cb.setToolTip(prefs.PREFIX_TOOLTIP)
        self.prefix.setDisabled(True)
        self.prefix.setMaximumWidth(prefs.PREFIX_WIDTH)
        self.prefix.setMinimumWidth(prefs.PREFIX_WIDTH)
        self.complete_rename_cb.setToolTip(prefs.COMPLETE_RENAME_TOOLTIP)
        self.new_name.setDisabled(True)
        self.new_name.setMaximumWidth(prefs.NEW_NAME_WIDTH)
        self.new_name.setMinimumWidth(prefs.NEW_NAME_WIDTH)
        self.search_and_replace_cb.setToolTip(prefs.SEARCH_AND_REPLACE_TOOLTIP)
        self.find.setDisabled(True)
        self.find.setMinimumWidth(prefs.FIND_WIDTH)
        self.find.setMaximumWidth(prefs.FIND_WIDTH)
        self.replace.setDisabled(True)
        self.replace.setMaximumWidth(prefs.REPLACE_WIDTH)
        self.replace.setMinimumWidth(prefs.REPLACE_WIDTH)
        self.renumber_cb.setToolTip(prefs.RENUMBER_TOOLTIP)
        self.start_num.setToolTip(prefs.START_NUM_TOOLTIP)
        self.start_num.setDisabled(True)
        self.start_num.setValue(prefs.START_NUM_DEFAULT)
        self.start_num.setMinimumWidth(prefs.START_NUM_MIN_WIDTH)
        self.padding.setToolTip(prefs.PADDING_TOOLTIP)
        self.padding.setDisabled(True)
        self.padding.addItems([str(x) for x in range(10)])
        self.padding.setCurrentIndex(4)
        self.padding.setMinimumWidth(prefs.PADDING_MIN_WIDTH)
        self.dot_cb.setToolTip(prefs.USE_DOT_TOOLTIP)
        self.dot_cb.setDisabled(True)
        self.dot_cb.setChecked(True)
        self.dot_cb.setMinimumWidth(prefs.DOT_WIDTH)
        self.dot_cb.setMaximumWidth(prefs.DOT_WIDTH)
        self.remove_ext_cb.setToolTip(prefs.REMOVE_EXT_TOOLTIP)
        self.change_ext.setToolTip(prefs.CHANGE_EXT_TOOLTIP)
        self.change_ext.setDisabled(True)
        self.backup_files_cb.setToolTip(prefs.BACKUP_TOOLTIP)
        self.backup_files_cb.setChecked(True)
        self.preview_cb.setToolTip(prefs.PREVIEW_TOOLTIP)
        self.preview_cb.setChecked(True)

        self.prefix_h_layout.addWidget(self.add_prefix_cb)
        self.prefix_h_layout.addWidget(self.prefix)
        self.frame_layout.addLayout(self.prefix_h_layout)

        self.complete_rename_h_layout.addWidget(self.complete_rename_cb)
        self.complete_rename_h_layout.addWidget(self.new_name)
        self.frame_layout.addLayout(self.complete_rename_h_layout)

        self.search_and_replace_h_layout.addWidget(self.search_and_replace_cb)
        self.search_and_replace_h_layout.addWidget(self.find)
        self.search_and_replace_h_layout.addWidget(self.replace)
        self.frame_layout.addLayout(self.search_and_replace_h_layout)

        self.renumber_h_layout.addWidget(self.renumber_cb)
        self.renumber_h_layout.addStretch(1)
        self.renumber_h_layout.addWidget(self.start_lbl)
        self.renumber_h_layout.addWidget(self.start_num)
        self.renumber_h_layout.addSpacerItem(QSpacerItem(*prefs.SPACER_SIZE))
        self.renumber_h_layout.addWidget(self.padding_lbl)
        self.renumber_h_layout.addWidget(self.padding)
        self.renumber_h_layout.addSpacerItem(QSpacerItem(*prefs.SPACER_SIZE))
        self.renumber_h_layout.addWidget(self.dot_cb)
        self.frame_layout.addLayout(self.renumber_h_layout)

        self.change_ext_h_layout.addWidget(self.change_ext_cb)
        self.change_ext_h_layout.addWidget(self.change_ext)
        self.frame_layout.addLayout(self.change_ext_h_layout)

        self.remove_ext_h_layout.addWidget(self.remove_ext_cb)
        self.frame_layout.addLayout(self.remove_ext_h_layout)

        self.create_backup_h_layout.addWidget(self.backup_files_cb)
        self.frame_layout.addLayout(self.create_backup_h_layout)

        self.preview_h_layout.addWidget(self.preview_cb)
        self.frame_layout.addLayout(self.preview_h_layout)

        self.frame_layout.addSpacerItem(QSpacerItem(*prefs.SPACER_SIZE))

        self.setLayout(self.layout)

    def disable_change_ext(self) -> None:
        """Disable change extension."""
        self.change_ext.setDisabled(True)

    def disable_dot(self) -> None:
        """Disable dot checkbox."""
        self.dot_cb.setDisabled(True)

    def disable_find(self) -> None:
        """Disable find."""
        self.find.setDisabled(True)

    def disable_new_name(self) -> None:
        """Disable new name."""
        print("disable new name")
        self.new_name.setDisabled(True)

    def disable_padding(self) -> None:
        """Disable padding."""
        self.padding.setDisabled(True)

    def disable_prefix(self) -> None:
        """Disable prefix."""
        self.prefix.setDisabled(True)

    def disable_start_num(self) -> None:
        """Disable start num."""
        self.start_num.setDisabled(True)

    def disable_replace(self) -> None:
        """Disable replace."""
        self.replace.setDisabled(True)

    def enable_change_ext(self) -> None:
        """Disable change extension."""
        self.change_ext.setDisabled(False)

    def enable_dot(self) -> None:
        """Enable dot checkbox."""
        self.dot_cb.setEnabled(True)

    def enable_find(self) -> None:
        """Enable find."""
        self.find.setEnabled(True)

    def enable_new_name(self) -> None:
        """Enable new name."""
        print("enable new name.")
        self.new_name.setEnabled(True)

    def enable_padding(self) -> None:
        """Enable padding."""
        self.padding.setEnabled(True)

    def enable_prefix(self) -> None:
        """Enable prefix."""
        self.prefix.setEnabled(True)

    def enable_replace(self) -> None:
        """Enable replace."""
        self.replace.setEnabled(True)

    def enable_start_num(self) -> None:
        """Enable start num."""
        self.start_num.setEnabled(True)

    def get_add_prefix(self) -> bool:
        """Return if end user wants to add a prefix and it is not the default value."""
        result = self.get_prefix_checked()
        if result and self.get_prefix() == prefs.PREFIX_DEFAULT:
            result = False
        return result

    def get_do_backup(self) -> bool:
        """Return if end user wants to backup files."""
        return self.backup_files_cb.isChecked()

    def get_change_ext(self) -> bool:
        """Return if the change extension checkbox is checked."""
        return self.change_ext_cb.isChecked()

    def get_do_complete_rename(self) -> bool:
        """Get if end user wants to completely rename."""
        return self.complete_rename_cb.isChecked()

    def get_dot(self) -> str:
        """Return dot string based on end users configuration.

        Note:
            If the end user has not enable using dot separators an empty string will be returned.
        """
        return "." if self.get_do_dot() else ""

    def get_do_dot(self) -> bool:
        """Return if the end user wants to use dot separators when renaming."""
        return self.dot_cb.isChecked()

    def get_do_change_ext(self) -> bool:
        """Return if the end user wants to change the extension."""
        result = self.change_ext_cb.isChecked()
        if self.get_new_ext() == prefs.CHANGE_EXT_DEFAULT:
            return False
        return result

    def get_do_padding(self) -> bool:
        """Return if the end user wants to add padding."""
        return False if self.get_padding() == 0 else True

    def get_do_preview(self) -> bool:
        """Return if the end user wants to preview changes."""
        return self.preview_cb.isChecked()

    def get_do_rename(self) -> bool:
        """Return if end user wants to rename the full item and it is not the default value."""
        result = self.complete_rename_cb.isChecked()
        if result and self.get_new_name() == prefs.COMPLETE_RENAME_DEFAULT:
            result = False
        return result

    def get_do_renumber(self) -> bool:
        """Return if the end user wants to renumber."""
        return self.renumber_cb.isChecked()

    def get_do_search(self) -> bool:
        """Return if end user wants to perform a search and replace AND it is not the default values respectfully.

        Note:
            If you only want to know if search and replace is checked use get_search_and_replace.
        """
        result = self.search_and_replace_cb.isChecked()
        if result and (self.get_find() == prefs.SEARCH_AND_REPLACE_DEFAULT
                       or self.get_replace() == prefs.REPLACE_WITH_DEFAULT):
            result = False
        return result

    def get_do_search_and_replace(self) -> bool:
        """Return if end user wants to perform a search and replace."""
        return self.search_and_replace_cb.isChecked()

    def get_find(self) -> str:
        """Return find value."""
        return str(self.find.text())

    def get_new_ext(self) -> str:
        """Return new ext."""
        return str(self.change_ext.text())

    def get_new_name(self) -> str:
        """Return new_name value."""
        return str(self.new_name.text())

    def get_padding(self) -> int:
        """Return the current padding value."""
        return int(self.padding.currentText())

    def get_prefix_checked(self) -> bool:
        """Return if the prefix checkbox is checked."""
        return self.add_prefix_cb.isChecked()

    def get_prefix(self) -> str:
        """Return the current prefix value end user has entered."""
        return str(self.prefix.text())

    def get_remove_ext(self) -> bool:
        """Return if end user has checked the remove extension checkbox."""
        return self.remove_ext_cb.isChecked()

    def get_replace(self) -> str:
        """Return the current replace value end user has entered."""
        return str(self.replace.text())

    def get_start_num(self) -> int:
        """Return start number from view."""
        return int(self.start_num.value())

    def set_change_ext_style(self, style: str) -> None:
        """Set style of change extension.

        Args:
            style: Style sheet applied to change extension.
        """
        self.change_ext.setStyleSheet(style)

    def set_disabled(self) -> None:
        """Disable View."""
        self.setDisabled(True)

    def set_enable(self) -> None:
        """Enable View."""
        self.setEnabled(True)

    def set_find(self, value: str) -> None:
        """Set the value of find.

        Args:
            value: Value applied to find
        """
        self.find.setText(value)

    def set_find_style(self, style: str) -> None:
        """Set style of find.

        Args:
            style: Style sheet applied to find.
        """
        self.find.setStyleSheet(style)

    def set_new_name(self, value: str) -> None:
        """Set the value of new name.

        Args:
            value: Value applied to new_name
        """
        self.new_name.setText(value)

    def set_new_name_style(self, style: str) -> None:
        """Set style of new_name.
        
        Args:
            style: Style sheet applied to new_name.
        """
        self.new_name.setStyleSheet(style)

    def set_prefix(self, value: str) -> None:
        """Set the value of prefix.

        Args:
            value: Value applied to prefix
        """
        self.prefix.setText(value)

    def set_prefix_style(self, style: str) -> None:
        """Set style of prefix.

        Args:
            style: Style sheet applied to prefix.
        """
        self.prefix.setStyleSheet(style)

    def set_remove_ext(self, state: bool) -> None:
        """Set the remove_ext checkbox as checked or unchecked.

        Args:
            state: Check state of remove_ext.
        """
        self.remove_ext_cb.setCheckState(Qt.Checked if state else Qt.Unchecked)

    def set_replace(self, value: str) -> None:
        """Set the value of replace.

        Args:
            value: Value applied to replace
        """
        self.replace.setText(value)

    def set_replace_style(self, style: str) -> None:
        """Set style of replace.

        Args:
            style: Style sheet applied to replace.
        """
        self.replace.setStyleSheet(style)
Exemple #5
0
class RootsApp(QMainWindow):
    standard_deviation_threshold = 0.1                      # when I receive a measurement from the sensor I check if its standard deviation; if it's too low it means the sensor is not working
    temporary_database_filename = "temporary.db"            # the current session is stored in a temporary database. When the user saves, it is copied at the desired location
    def __init__(self):
        super().__init__();
        self.setWindowTitle("Roots")
        self.setFixedWidth(1200)
        self.resize(1200, 1200)
        self.threadpool = QThreadPool();
        self.object_list = list()
        self.is_training_on = False
        self.interaction_under_training = None
        self.n_measurements_collected = 0
        self.n_measurements_to_collect = 3
        self.sensor_not_responding = True
        self.sensor_not_responding_timeout = 2000        # milliseconds
        self.database_connection = self.create_temporary_database()
        self.active_object = None
        self.number_of_objects_added = 0
        self.sensor_start_freq = 250000
        self.sensor_end_freq = 3000000

    # creates the plot
        self.plotWidget = pyqtgraph.PlotWidget(title = "Sensor Response")
        self.plotWidget.setFixedHeight(300)
        self.plotWidget.getAxis("bottom").setLabel("Excitation frequency", "Hz")
        self.plotWidget.getAxis("left").setLabel("Volts", "V")
        self.dataPlot = self.plotWidget.plot()

    # timer used to see if the sensor is responding
        self.timer = QTimer()
        self.timer.setInterval(self.sensor_not_responding_timeout)
        self.timer.timeout.connect(self.timer_timeout)
        self.timer_timeout()

    # defines the actions in the file menu with button actions
        iconExit = QIcon("icons/icon_exit.png")
        btnActionExit = QAction(iconExit, "Exit", self)
        btnActionExit.setStatusTip("Click to terminate the program")
        btnActionExit.triggered.connect(self.exit)

        iconSave = QIcon("icons/icon_save.ico")
        buttonActionSave = QAction(iconSave, "Save current set of objects", self)
        # buttonActionSave.setStatusTip("Click to perform action 2")
        buttonActionSave.triggered.connect(self.save)

        iconOpen = QIcon("icons/icon_load.png")
        buttonActionOpen = QAction(iconOpen, "Load set of objects", self)
        buttonActionOpen.triggered.connect(self.open)

    # toolbar
        toolBar = QToolBar("Toolbar")
        toolBar.addAction(buttonActionSave)
        toolBar.addAction(buttonActionOpen)
        toolBar.setIconSize(QSize(64, 64))
        toolBar.setStyleSheet(styles.toolbar)
        self.addToolBar(toolBar)

    # menu
        menuBar = self.menuBar()
        menuBar.setStyleSheet(styles.menuBar)
        menuFile = menuBar.addMenu("File")
        menuOptions = menuBar.addMenu("Options")
        menuView = menuBar.addMenu("View")
        menuConnect = menuBar.addMenu("Connect")
        menuFile.addAction(buttonActionSave)
        menuFile.addAction(buttonActionOpen)
        menuFile.addAction(btnActionExit)

    # status bar
        self.setStatusBar(QStatusBar(self))

    # creates the "My Objects" label
        labelMyObjects = QLabel("My Objects")
        labelMyObjects.setFixedHeight(100)
        labelMyObjects.setAlignment(Qt.AlignCenter)
        labelMyObjects.setStyleSheet(styles.labelMyObjects)

    # button "add object"
        icon_plus = QIcon("icons/icon_add.png")
        self.btn_create_object = QPushButton("Add Object")
        self.btn_create_object.setCheckable(False)
        self.btn_create_object.setIcon(icon_plus)
        self.btn_create_object.setFixedHeight(80)
        self.btn_create_object.setStyleSheet(styles.addObjectButton)
        self.btn_create_object.clicked.connect(self.create_object)

    # defines the layout of the "My Objects" section
        self.verticalLayout = QVBoxLayout()
        self.verticalLayout.setContentsMargins(0,0,0,0)
        self.verticalLayout.addWidget(labelMyObjects)
        self.verticalLayout.addWidget(self.btn_create_object)
        self.spacer = QSpacerItem(0,2000, QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.verticalLayout.setSpacing(0)
        self.verticalLayout.addSpacerItem(self.spacer)  #adds spacer

    # defines the ComboBox which holds the names of the objects
        self.comboBox = QComboBox()
        self.comboBox.addItem("- no object selected")
        self.comboBox.currentIndexChanged.connect(self.comboBox_index_changed)
        self.comboBox.setFixedSize(300, 40)
        self.comboBox.setStyleSheet(styles.comboBox)
        self.update_comboBox()

    # defines the label "Selected Object" (above the comboBox)
        self.labelComboBox = QLabel()
        self.labelComboBox.setText("Selected Object:")
        self.labelComboBox.setStyleSheet(styles.labelComboBox)
        self.labelComboBox.adjustSize()

    # vertical layout for the combobox and its label
        self.VLayoutComboBox = QVBoxLayout()
        self.VLayoutComboBox.addWidget(self.labelComboBox)
        self.VLayoutComboBox.addWidget(self.comboBox)

    # label with the output text (the big one on the right)
        self.labelClassification = QLabel()
        self.labelClassification.setText("No interaction detected")
        self.labelClassification.setFixedHeight(80)
        self.labelClassification.setStyleSheet(styles.labelClassification)
        self.labelClassification.adjustSize()

        HLayoutComboBox = QHBoxLayout()
        HLayoutComboBox.addLayout(self.VLayoutComboBox)
        HLayoutComboBox.addSpacerItem(QSpacerItem(1000,0, QSizePolicy.Expanding, QSizePolicy.Expanding));  #adds spacer
        HLayoutComboBox.addWidget(self.labelClassification)

    # creates a frame that contains the combobox and the labels
        frame = QFrame()
        frame.setStyleSheet(styles.frame)
        frame.setLayout(HLayoutComboBox)

    # sets the window layout with the elements created before
        self.windowLayout = QVBoxLayout()
        self.windowLayout.addWidget(self.plotWidget)
        self.windowLayout.addWidget(frame)
        self.windowLayout.addLayout(self.verticalLayout)

    # puts everything into a frame and displays it on the window
        self.mainWindowFrame = QFrame()
        self.mainWindowFrame.setLayout(self.windowLayout)
        self.mainWindowFrame.setStyleSheet(styles.mainWindowFrame)
        self.setCentralWidget(self.mainWindowFrame)

        self.create_object()        # creates one object at the beginning



# -----------------------------------------------------------------------------------------------------------
# Shows a welcome message
    def show_welcome_msg(self):
        welcome_msg = QMessageBox()
        welcome_msg.setText("Welcome to the Roots application!")
        welcome_msg.setIcon(QMessageBox.Information)
        welcome_msg.setInformativeText(strings.welcome_text)
        welcome_msg.setWindowTitle("Welcome")
        welcome_msg.exec_()


# -----------------------------------------------------------------------------------------------------------
# When the user changes the object in the combobox, updates the active object
    def comboBox_index_changed(self, index):
        object_name = self.comboBox.currentText()
        for object in self.object_list:
            if object.name == object_name:
                self.set_active_object(object)
                print("DEBUG: selected object changed. Object name: {0}".format(object.name))
                return

# -----------------------------------------------------------------------------------------------------------
# This function allows to save the current objects on a file
    def save(self):
        current_path = os.getcwd()
        directory_path = current_path + "/Saved_Workspaces"

        if not os.path.exists(directory_path):
            os.mkdir(directory_path)

        file_path = None
        [file_path, file_extension] = QFileDialog.getSaveFileName(self,"Roots", directory_path, "Roots database (*.db)")
        if file_path is None:
            return

        temp_database_path = current_path + "/" + RootsApp.temporary_database_filename
        shutil.copyfile(temp_database_path, file_path)      # copies the temporary database to save the current workspace
        return



# -----------------------------------------------------------------------------------------------------------
# this function creates a clean database where all the data of this session will be temporarily stored
    def create_temporary_database(self):
        current_path = os.getcwd()
        file_path = current_path + "/" + RootsApp.temporary_database_filename

        if os.path.exists(file_path):   # if the database is already there it deletes it to reset it
            os.remove(file_path)
            print("DEBUG: removing database. (in 'RootsApp.create_temporary_database()'")

        database_connection = database.create_connection(RootsApp.temporary_database_filename)  # creates the temporary database
        database.create_tables(database_connection)                                             # initializes the database
        database.reset_db(database_connection)                                                  # resets the database (not needed but it doesn't cost anything to put it)

        return database_connection


# -----------------------------------------------------------------------------------------------------------
# This function allows to load previously created objects from a file
    def open(self):
        current_path = os.getcwd()
        saved_files_directory = current_path + "/Saved_Workspaces"

        [file_path, file_extension] = QFileDialog.getOpenFileName(self,"Roots", saved_files_directory, "Roots database (*.db)");
        if file_path == '':
            return

        for object in self.object_list.copy():     # deletes all the objects
            print("DEBUG: deleting object {0} (in 'open()')".format(object.name))
            self.delete_object(object)

        temp_database_path = current_path + "/" + RootsApp.temporary_database_filename
        self.database_connection.close()
        os.remove(temp_database_path)
        shutil.copyfile(file_path, temp_database_path)      # replaces the temporary database with the file to open
        self.database_connection = database.create_connection(temp_database_path)

        object_tuples = database.get_all_objects(self.database_connection)
        for object_tuple in object_tuples:
            object_ID, object_name = object_tuple
            location_IDs = database.get_locations_id_for_object(self.database_connection, object_ID)
            formatted_location_IDs = []
            for location_ID in location_IDs:
                formatted_location_IDs.append(location_ID[0])

            print("DEBUG: loading object {0} with location IDs {1}. (in 'RootsApp.open()')".format(object_name, formatted_location_IDs))
            self.add_object(object_name, object_ID, formatted_location_IDs)
            self.train_classifiers()
        return


# -----------------------------------------------------------------------------------------------------------
# This function updates the ComboBox whenever objects are created, destroyed or the active object has changed
    def update_comboBox(self):
        print("DEBUG: repainting ComboBox. (in 'RootsApp.update_comboBox()'")
        self.comboBox.clear()
        self.comboBox.addItem("none")
        for object in self.object_list:
            self.comboBox.addItem(object.name)
        self.comboBox.adjustSize()


# -----------------------------------------------------------------------------------------------------------
# This is a timer which is restarted every time a measurement is received. If it elapses it means that the sesnor is not connected
    def timer_timeout(self):
        print("DEBUG: timer timeout. (in 'RootsApp.timer_timeout()'")
        self.sensor_not_responding = True
        self.statusBar().showMessage(strings.sensor_disconnected)
        self.statusBar().setStyleSheet(styles.statusBarError)
        self.plotWidget.setTitle("Sensor not connected")

# -----------------------------------------------------------------------------------------------------------
# This function creates a new object in the database and then calls the "add_object" function, which adds the newly created object to the application
    def create_object(self):
        new_object_name = "Object {0}".format(self.number_of_objects_added + 1)
        [new_object_ID, location_IDs] = database.create_object(self.database_connection, new_object_name)
        self.add_object(new_object_name, new_object_ID, location_IDs)


# -----------------------------------------------------------------------------------------------------------
# This function deletes an object from the database, and from the application object list. It alsos destroys the object
    def delete_object(self, object):
        print("DEBUG: deleting object {0}. (in 'RootsApp.delete_object()')".format(object.ID))
        database.delete_object(self.database_connection, object.ID)
        self.object_list.remove(object)
        self.verticalLayout.removeItem(object.layout)
        self.update_comboBox()
        object.delete()


# -----------------------------------------------------------------------------------------------------------
# This function adds an object to the current application. Note that if you want to create an object ex-novo you should call "create_object". This function is useful when loading existing objects from a file
    def add_object(self, name, object_ID, location_IDs):
        self.number_of_objects_added += 1
        new_object = Object(name, object_ID, location_IDs, self)
        self.object_list.append(new_object)

        for ID in location_IDs:                                         # initializes the measurements with 0 if the measurement is empty
            #print("DEBUG: initializing location ID {0}".format(ID))
            measurements = database.get_measurements_for_location(self.database_connection, ID)

            print("DEBUG: location {0} of object {1} is trained: {2}. (in 'RootsApp.add_object()')".format(ID, new_object.name, database.is_location_trained(self.database_connection, ID)))
            if len(measurements) == 0:
                database.save_points(self.database_connection, [0], ID)
                database.set_location_trained(self.database_connection, ID, "FALSE")
            elif database.is_location_trained(self.database_connection, ID) == "TRUE":
                new_object.get_interaction_by_ID(ID).setCalibrated(True)

        # inserts the newly created object before the "Add Object" button
        index = self.verticalLayout.indexOf(self.btn_create_object)
        self.verticalLayout.insertLayout(index, new_object.layout)
        self.update_comboBox()
        print("DEBUG: object {0} added. (in 'RootsApp.add_object()')".format(new_object.name))
        return



# -----------------------------------------------------------------------------------------------------------
# This function takes as input the measurement data and formats it to plot it on the graph
    def update_graph(self, data):
        frequency_step = (self.sensor_end_freq - self.sensor_start_freq) / len(data)
        x_axis = numpy.arange(self.sensor_start_freq, self.sensor_end_freq, frequency_step)
        formatted_data = numpy.transpose(numpy.asarray([x_axis, data]))
        self.dataPlot.setData(formatted_data)


# -----------------------------------------------------------------------------------------------------------
# This function starts the UDP server that receives the measurements
    def run_UDP_server(self, UDP_IP, UDP_PORT):
        self.UDPServer = UDPServer(UDP_IP, UDP_PORT)
        self.UDPServer.signals.measurementReceived.connect(self.process_measurement)
        self.threadpool.start(self.UDPServer)


# -----------------------------------------------------------------------------------------------------------
# This function changes some global variables to tell the application to save the incoming measurements into the database. The measurements belong to the interaction passed as argument
    def start_collecting_measurements(self, interaction):
        if self.sensor_not_responding:
            print("DEBUG: warning! Can't start calibration, the sensor is not responding! (in 'RootsApp.start_collecting_measurements()')")
            error_msg = QMessageBox()
            error_msg.setText("Can't start calibration!")
            error_msg.setIcon(QMessageBox.Critical)
            error_msg.setInformativeText('The sensor is not responding, make sure it is connected')
            error_msg.setWindowTitle("Error")
            error_msg.exec_()
        else:
            print("starting to collect measurements into the database at location ID {0} (in 'RootsApp.start_collecting_measurements()')".format(interaction.ID));
            self.is_training_on = True
            self.interaction_under_training = interaction
            database.delete_measurements_from_location(self.database_connection, interaction.ID)   # resets the location measurements

            self.progress_dialog = QProgressDialog("Calibrating", "Abort", 0, self.n_measurements_to_collect, self)
            self.progress_dialog.setWindowModality(Qt.WindowModal)
            self.progress_dialog.setWindowTitle("Calibration")
            self.progress_dialog.setFixedSize(400, 200)
            self.progress_dialog.setValue(0)
            self.progress_dialog.exec_()



# -----------------------------------------------------------------------------------------------------------
# This function is called by the UDP thread every time that a measurement is received. It does the following:
#   1. Plots the incoming measurement
#   2. IF training mode IS on:
#           Predicts the interaction (tries to guess where the user is touching)
#      ELSE:
#           Saves the measurement and retrains the classifier with the new data
    def process_measurement(self, received_data):
        self.sensor_not_responding = False
        self.plotWidget.setTitle("Sensor response")
        self.timer.start()                                          # starts the timer that checks if we are receiving data from the sensor

        measurement = received_data.split(' ')                      # get rid of separator
        measurement = [float(i) for i in measurement]               # convert strings to float
        self.update_graph(measurement)
        self.predict_interaction(measurement)

        # checks the standard deviation of the received data to see if the sensor is working well
        if (numpy.std(measurement) < self.standard_deviation_threshold):
            self.statusBar().showMessage(strings.sensor_not_working)
            self.statusBar().setStyleSheet(styles.statusBarError)
        else:
            self.statusBar().setStyleSheet(styles.statusBar)

        if self.is_training_on:
            print("saving measurement {0} into database at location_ID {1}. (in 'RootsApp.process_measurement()')".format(self.n_measurements_collected + 1, self.interaction_under_training.ID))
            database.save_points(self.database_connection, measurement, self.interaction_under_training.ID)
            self.n_measurements_collected += 1
            self.progress_dialog.setValue(self.n_measurements_collected)
            if (self.n_measurements_collected >= self.n_measurements_to_collect):
                self.is_training_on = False
                self.n_measurements_collected = 0
                print("DEBUG: {0} measurements were saved at location_ID {1}. (in 'RootsApp.process_measurement()')".format(self.n_measurements_to_collect, self.interaction_under_training.ID))
                self.train_classifiers()
                self.interaction_under_training.setCalibrated(True)     # this makes the button "Calibrate" change coulour


# -----------------------------------------------------------------------------------------------------------
# This function retrains the classifiers using all the measurements present in the database and assigns to each object its classifier
    def train_classifiers(self):
        #[objects_ID, classifiers]
        classifiers = classifier.get_classifiers(self.database_connection)
        print("DEBUG: the following classifiers were created: {0}. (in 'RootsApp.train_classifiers')".format(classifiers))
        for object in self.object_list:
            for index, tuple in enumerate(classifiers):
                object_ID, classif = tuple;  # extracts the object ID and the classifier from the tuple
                if object_ID == object.ID:
                    object.classifier = classif
                    del classifiers[index]


# -----------------------------------------------------------------------------------------------------------
# This function changes the current active object (the software tries to guess where the user is touching using the calibration data from the active object)
    def set_active_object(self, active_object):
        self.active_object = active_object

        for obj in self.object_list:
            if obj == active_object:
                active_object.set_highlighted(True)
            else:
                obj.set_highlighted(False)

        index = self.comboBox.findText(self.active_object.name)     # updates the index of the ComboBox
        self.comboBox.setCurrentIndex(index)


# -----------------------------------------------------------------------------------------------------------
# This function changes the name of an object. It updates the database AND the application data structure.
    def rename_object(self, object, new_name):
        print("DEBUG: changing name of object '{0}' (in 'RootsApp.rename_object')".format(object.name))
        object.set_name(new_name)
        database.rename_object(self.database_connection, object.ID, new_name)
        self.update_comboBox()



# -----------------------------------------------------------------------------------------------------------
# This function uses the classifier of the active object to guess where the user is touching, based on the incoming measurement
    def predict_interaction(self, measurement):
        if (len(self.object_list) <= 0):
            self.labelClassification.setText("No objects available")
            self.statusBar().showMessage(strings.no_objects)
            return
        if self.active_object is None:
            self.labelClassification.setText("No object selected")
            self.statusBar().showMessage(strings.no_object_selected)
            return
        if self.active_object.classifier is None:
            self.labelClassification.setText("The object is not calibrated")
            self.statusBar().showMessage(strings.object_not_calibrated)
            return
        else:
            predicted_interaction_id = self.active_object.classifier(measurement)
            interaction = self.active_object.get_interaction_by_ID(predicted_interaction_id)
            self.labelClassification.setText(interaction.name)
            self.statusBar().showMessage("")
            #print("DEBUG: predicted interaction ID: ", interaction.ID)



# -----------------------------------------------------------------------------------------------------------
# This is a system event that gets called whenever the user tries to close the application. It calls the "exit()"
# function (just below) to open a dialog to make sure the user really wants to quit.
    def closeEvent(self, event):
        if not self.exit():
            event.ignore()


# -----------------------------------------------------------------------------------------------------------
# This function gets called when the user cliks on the "Exit" button in the "File" menu or when it tries to close the window (indirectly)
# Here we open a dialog to make sure the user really wants to quit.
    def exit(self):
        dialogWindow = DialogExit()
        answer = dialogWindow.exec_()
        if (answer == True):
            self.UDPServer.stop()
            self.close()
        return answer
Exemple #6
0
    def executeProject(self, mainWindow, project_name):
        if project_name == "SmartGroceries":
            self.referenceItemsLabels = {}
            self.itemInHand = None
            self.itemsInCart = list()
            self.currentOrderReferenceItems = list()
            self.currentOrderUserId = 0
            self.recommendedItemsButtons = list()
            self.previousRecommendations = list()
            self.numberOfGoodRecommendations = 0
            self.supportedUsers = range(1, 1001)

            products = pd.read_csv(
                os.path.join(
                    os.path.dirname(os.path.dirname(
                        os.path.abspath(__file__))), "projects",
                    "SmartGroceries", "data", "products.csv"))
            aisles = pd.read_csv(
                os.path.join(
                    os.path.dirname(os.path.dirname(
                        os.path.abspath(__file__))), "projects",
                    "SmartGroceries", "data", "aisles.csv"))
            test_data = pd.read_csv(
                os.path.join(
                    os.path.dirname(os.path.dirname(
                        os.path.abspath(__file__))), "projects",
                    "SmartGroceries", "data", "test_set.csv"))
            # print(products)
            # print(aisles)
            # print(test_data)
            aisle_name_to_id = {
                k: v
                for k, v in zip(aisles.aisle, aisles.aisle_id)
            }
            product_name_to_id = {
                k: v
                for k, v in zip(products.product_name, products.product_id)
            }
            product_id_to_name = {
                k: v
                for k, v in zip(products.product_id, products.product_name)
            }

            def changeCurrentitem(itemName):
                if itemName == "":
                    self.itemInHand = None
                    currentItemLabel.setText(
                        f"<b>Select an item from the list or from the recommendations</b>s"
                    )
                    addToCartButton.setEnabled(False)
                else:
                    self.itemInHand = product_name_to_id[itemName]
                    currentItemLabel.setText(f"Add <b>{itemName}</b> to Cart")
                    addToCartButton.setEnabled(True)
                    addToCartButton.setFocus()

            def handleNewOrderButtonClicked():
                grouped = test_data.groupby('order_id')
                while True:
                    order_number = random.sample(grouped.indices.keys(), 1)[0]
                    currentOrder = grouped.get_group(order_number)
                    self.currentOrderReferenceItems = currentOrder.product_id.tolist(
                    )
                    self.currentOrderUserId = currentOrder.user_id.iloc[0]
                    if len(
                            self.currentOrderReferenceItems
                    ) > 1 and self.currentOrderUserId in self.supportedUsers:
                        break
                print(self.currentOrderReferenceItems)
                orderInfo = f"<b>Order ID: </b>{currentOrder.order_id.iloc[0]}<br>"
                orderInfo += f"<b>User ID: </b>{self.currentOrderUserId} | <b>DOW: </b>{calendar.day_name[currentOrder.order_dow.iloc[0]]} | <b>Hour of Day: </b>{currentOrder.order_hour_of_day.iloc[0]}  | <b>Number of Items: </b>{len(self.currentOrderReferenceItems)}"
                orderInfo += "<br><b>Items in the Reference Order:</b>"

                for widget in self.referenceItemsLabels.values():
                    item = referenceItemsLayout.itemAt(0)
                    widget.setVisible(False)
                    referenceItemsLayout.removeItem(item)
                    del item
                self.referenceItemsLabels.clear()
                currentCartItems.clear()
                self.itemsInCart.clear()
                self.previousRecommendations.clear()
                self.numberOfGoodRecommendations = 0

                updateCurrentRecommendations(list())
                for product in self.currentOrderReferenceItems:
                    refItemName = product_id_to_name[product]
                    refItemLabel = QPushButton(refItemName)
                    refItemLabel.setContentsMargins(QMargins(0, 0, 0, 0))
                    refItemLabel.setStyleSheet("Text-align:left")
                    refItemLabel.setFlat(False)
                    refItemLabel.clicked.connect(
                        partial(changeCurrentitem, refItemName))
                    self.referenceItemsLabels[product] = refItemLabel
                    orderInfoLabel.setText(
                        f"<b>Order Information</b><br>{orderInfo}")

                for referenceItemLabel in self.referenceItemsLabels.values():
                    referenceItemsLayout.addWidget(referenceItemLabel)

                runAutoButton.setFocus()

            def handleRunAutomatically():
                for referenceItemLabel in self.referenceItemsLabels.values():
                    referenceItemLabel.click()
                    addToCartButton.click()

            def updateCurrentRecommendations(recommendations):
                for widget in self.recommendedItemsButtons:
                    item = recommendationsLayout.itemAt(0)
                    widget.setVisible(False)
                    recommendationsLayout.removeItem(item)
                    del item
                self.recommendedItemsButtons.clear()
                for product in recommendations:
                    recItemName = product_id_to_name[product]
                    recItemButton = QPushButton(recItemName)
                    recItemButton.setContentsMargins(QMargins(0, 0, 0, 0))
                    recItemButton.setStyleSheet("Text-align:left;")
                    if product not in self.currentOrderReferenceItems:
                        recItemButton.setFlat(True)
                    recItemButton.clicked.connect(
                        partial(changeCurrentitem, recItemName))
                    self.recommendedItemsButtons.append(recItemButton)
                for recItemButton in self.recommendedItemsButtons:
                    recommendationsLayout.addWidget(recItemButton)
                if len(recommendations) > 0:
                    currentRecommendationsLabel.setVisible(True)
                else:
                    currentRecommendationsLabel.setVisible(False)
                self.previousRecommendations += recommendations

            def handleAddToCartButtonClicked():
                print(self.currentOrderReferenceItems)
                print(self.itemInHand)
                if self.itemInHand not in self.currentOrderReferenceItems:
                    QMessageBox(
                        QMessageBox.Critical, "Error adding item to cart",
                        "You can only add items that exists in the reference order"
                    ).exec_()
                    return
                elif self.itemInHand in self.itemsInCart:
                    QMessageBox(QMessageBox.Critical,
                                "Error adding item to cart",
                                "This item is already in the cart").exec_()
                    return
                self.referenceItemsLabels[self.itemInHand].setFlat(True)
                self.itemsInCart.append(self.itemInHand)
                currentCartItems.addItem(product_id_to_name[self.itemInHand])
                if self.itemInHand in self.previousRecommendations:
                    self.numberOfGoodRecommendations += 1
                    self.referenceItemsLabels[self.itemInHand].setStyleSheet(
                        "Text-align:left; background-color:green;")
                    self.referenceItemsLabels[self.itemInHand].setFlat(False)

                #update recommendations
                result = self._sendCommand(
                    "PROCESS_PROJECT_GROUP_2", ";".join([
                        str(self.currentOrderUserId),
                        ",".join([str(x) for x in self.itemsInCart]),
                        ",".join([
                            str(x) for x in set(self.previousRecommendations)
                        ])
                    ]))
                if result == FAILURE_CODE:
                    self.log(
                        "Processing Failed, error getting recommendations from the RPi"
                    )
                    return
                else:
                    try:
                        recommendations = [int(id) for id in result.split(',')]
                    except:
                        recommendations = []

                updateCurrentRecommendations(recommendations)
                if len(self.itemsInCart) == len(
                        self.currentOrderReferenceItems):
                    completionMessage = QMessageBox(
                        QMessageBox.Information, "Order Completed",
                        f"Order Completed with {self.numberOfGoodRecommendations} Good Recommendation(s)\nPress New Order to start a new order"
                    )
                    if self.numberOfGoodRecommendations == 0:
                        completionMessage.setIconPixmap(
                            QPixmap('images/this_is_fine.jpg'))
                    completionMessage.setWindowIcon(appIcon)
                    completionMessage.exec_()
                    newOrderButton.setFocus()

            def aisleChanged():
                aisle_number = aisle_name_to_id[
                    selectAisleCombobox.currentText()]
                products_in_aisle = products[
                    products.aisle_id == aisle_number].product_name.tolist()
                selectproductCombobox.clear()
                selectproductCombobox.addItem("")
                selectproductCombobox.addItems(products_in_aisle)

            def itemChanged():
                current_item = selectproductCombobox.currentText()
                changeCurrentitem(current_item)

            dialog = QDialog(mainWindow)
            appIcon = QIcon("images/this_is_fine.jpg")
            dialog.setWindowIcon(appIcon)
            dialog.setMinimumWidth(600)
            dialog.setWindowTitle("Smart Groceries Demo")
            layout = QVBoxLayout()
            newOrderButton = QPushButton("New Order")
            orderInfoLabel = QLabel()
            orderInfoLabel.setTextFormat(Qt.RichText)
            chooseItemLayout = QHBoxLayout()
            verticalSpacer = QSpacerItem(20, 20)
            currentCartItems = QListWidget()

            layoutWidget = QWidget()
            referenceItemsLayout = QVBoxLayout(layoutWidget)
            referenceItemsLayout.setSpacing(0)
            referenceItemsLayout.setMargin(0)
            scroll = QScrollArea(dialog)
            scroll.setWidgetResizable(True)
            scroll.setMinimumHeight(150)
            scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
            scroll.setWidget(layoutWidget)

            selectAisleLabel = QLabel("Aisle: ")
            selectProductLabel = QLabel("Product: ")
            selectAisleCombobox = QComboBox()
            selectproductCombobox = QComboBox()
            chooseItemLayout.addWidget(selectAisleLabel, 0, Qt.AlignLeft)
            chooseItemLayout.addWidget(selectAisleCombobox, 0, Qt.AlignLeft)
            chooseItemLayout.addWidget(selectProductLabel, 0, Qt.AlignLeft)
            chooseItemLayout.addWidget(selectproductCombobox, 0, Qt.AlignLeft)

            addToCartButton = QPushButton("Add to Cart")
            currentItemLabel = QLabel()
            currentItemLabel.setTextFormat(Qt.RichText)
            if self.itemInHand is None:
                currentItemLabel.setText(
                    f"<b>Select an item from the list or from the recommendations</b>"
                )
            addToCartButton.setDisabled(True)
            currentItemLayout = QHBoxLayout()
            currentItemLayout.addWidget(currentItemLabel)
            currentItemLayout.addWidget(addToCartButton)
            recommendationsLayout = QVBoxLayout()
            recommendationsLayout.setSpacing(0)
            recommendationsLayout.setMargin(0)

            newOrderButton.clicked.connect(handleNewOrderButtonClicked)
            addToCartButton.clicked.connect(handleAddToCartButtonClicked)
            selectproductCombobox.currentIndexChanged.connect(itemChanged)
            selectAisleCombobox.currentIndexChanged.connect(aisleChanged)
            selectAisleCombobox.addItems(aisles.aisle.tolist())

            layout.addWidget(newOrderButton)
            layout.addSpacerItem(verticalSpacer)
            layout.addWidget(orderInfoLabel)
            layout.addWidget(scroll)
            layout.addSpacerItem(verticalSpacer)
            layout.addLayout(chooseItemLayout)
            layout.addSpacerItem(verticalSpacer)
            itemsInTheCartLabel = QLabel("<b>Items in the Cart<b>")
            layout.addWidget(itemsInTheCartLabel)
            itemsInTheCartLabel.setTextFormat(Qt.RichText)
            layout.addWidget(currentCartItems)
            layout.addSpacerItem(verticalSpacer)
            currentRecommendationsLabel = QLabel(
                "<b>Current Recommendations<b>")
            layout.addWidget(currentRecommendationsLabel)
            currentRecommendationsLabel.setTextFormat(Qt.RichText)
            currentRecommendationsLabel.setVisible(False)
            layout.addLayout(recommendationsLayout)
            layout.addSpacerItem(verticalSpacer)
            layout.addLayout(currentItemLayout)
            runAutoButton = QPushButton("Run and Watch. TRUST ME, IT IS FUN!")
            layout.addWidget(runAutoButton)
            runAutoButton.clicked.connect(handleRunAutomatically)
            dialog.setLayout(layout)
            handleNewOrderButtonClicked()
            dialog.exec_()
            return