Example #1
0
class OkCancelDialog2( QDialog ):
    ok_pressed = Signal()
    cancel_pressed = Signal()
    def __init__( self, title=None, parent=None ):
        QDialog.__init__( self, parent )
        self.title = title
        self._layout = QGridLayout()
        self._okButton = QPushButton( "OK" )
        self._cancelButton = QPushButton( "Abbrechen" )
        self._createGui()
        self._validationFnc = None
        self._cancellationFnc = None

    def _createGui( self ):
        self.setLayout( self._layout )

        hbox = QHBoxLayout()
        #hbox.addStretch( 1 )
        hbox.addWidget( self._okButton )
        hbox.addWidget( self._cancelButton )
        margins = hbox.contentsMargins()
        margins.setLeft( 10 )
        hbox.setContentsMargins( margins )
        self._layout.addLayout( hbox, 3, 0, alignment=Qt.AlignLeft | Qt.AlignBottom )

        self._okButton.setDefault( True )

        if self.title:
            self.setWindowTitle( self.title )
        else:
            self.setWindowTitle( "OkCancelDialog" )

        self._okButton.clicked.connect( self.ok_pressed.emit )
        self._cancelButton.clicked.connect( self.cancel_pressed.emit )


    def setOkButtonText( self, text:str ):
        self._okButton.setText( text )

    def addWidget( self, widget:QWidget, row:int ) -> None:
        if row > 2: raise Exception( "OkCancelDialog.addWidget() -- invalid row index: %d" % ( row ) )
        self._layout.addWidget( widget, row, 0 )

    def getItemAt( self, row:int ) -> QWidget:
        return self._layout.itemAt( QModelIndex( row, 0 ) )
Example #2
0
class QWaitingForMissionResultWindow(QDialog):
    def __init__(
        self,
        game: Game,
        sim_controller: SimController,
        parent: Optional[QWidget] = None,
    ) -> None:
        super(QWaitingForMissionResultWindow, self).__init__(parent=parent)
        self.setWindowModality(QtCore.Qt.WindowModal)
        self.game = game
        self.sim_controller = sim_controller
        self.setWindowTitle("Waiting for mission completion.")
        self.setWindowIcon(QIcon("./resources/icon.png"))
        self.setMinimumHeight(570)

        self.initUi()
        DebriefingFileWrittenSignal.get_instance().debriefingReceived.connect(
            self.updateLayout)
        self.wait_thread = sim_controller.wait_for_debriefing(
            lambda debriefing: self.on_debriefing_update(debriefing))

    def initUi(self):
        self.layout = QGridLayout()

        header = QLabel(self)
        header.setGeometry(0, 0, 655, 106)
        pixmap = QPixmap("./resources/ui/conflict.png")
        header.setPixmap(pixmap)
        self.layout.addWidget(header, 0, 0)

        self.gridLayout = QGridLayout()

        jinja = Environment(
            loader=FileSystemLoader("resources/ui/templates"),
            autoescape=select_autoescape(
                disabled_extensions=("", ),
                default_for_string=True,
                default=True,
            ),
            trim_blocks=True,
            lstrip_blocks=True,
        )
        self.instructions_text = QTextBrowser()
        self.instructions_text.setHtml(
            jinja.get_template("mission_start_EN.j2").render())
        self.instructions_text.setOpenExternalLinks(True)
        self.gridLayout.addWidget(self.instructions_text, 1, 0)

        progress = QLabel("")
        progress.setAlignment(QtCore.Qt.AlignCenter)
        progress_bar = QMovie("./resources/ui/loader.gif")
        progress.setMovie(progress_bar)

        self.actions = QGroupBox("Actions :")
        self.actions_layout = QHBoxLayout()
        self.actions.setLayout(self.actions_layout)

        self.manually_submit = QPushButton("Manually Submit [Advanced users]")
        self.manually_submit.clicked.connect(self.submit_manually)
        self.actions_layout.addWidget(self.manually_submit)
        self.cancel = QPushButton("Abort mission")
        self.cancel.clicked.connect(self.close)
        self.actions_layout.addWidget(self.cancel)
        self.gridLayout.addWidget(self.actions, 2, 0)

        self.actions2 = QGroupBox("Actions :")
        self.actions2_layout = QHBoxLayout()
        self.actions2.setLayout(self.actions2_layout)
        self.manually_submit2 = QPushButton("Manually Submit [Advanced users]")
        self.manually_submit2.clicked.connect(self.submit_manually)
        self.actions2_layout.addWidget(self.manually_submit2)
        self.cancel2 = QPushButton("Abort mission")
        self.cancel2.clicked.connect(self.close)
        self.actions2_layout.addWidget(self.cancel2)
        self.proceed = QPushButton("Accept results")
        self.proceed.setProperty("style", "btn-success")
        self.proceed.clicked.connect(self.process_debriefing)
        self.actions2_layout.addWidget(self.proceed)

        progress_bar.start()
        self.layout.addLayout(self.gridLayout, 1, 0)
        self.setLayout(self.layout)

    @staticmethod
    def add_update_row(description: str, count: int,
                       layout: QGridLayout) -> None:
        row = layout.rowCount()
        layout.addWidget(QLabel(f"<b>{description}</b>"), row, 0)
        layout.addWidget(QLabel(f"{count}"), row, 1)

    def updateLayout(self, debriefing: Debriefing) -> None:
        updateBox = QGroupBox("Mission status")
        update_layout = QGridLayout()
        updateBox.setLayout(update_layout)
        self.debriefing = debriefing

        self.add_update_row("Aircraft destroyed",
                            len(list(debriefing.air_losses.losses)),
                            update_layout)
        self.add_update_row(
            "Front line units destroyed",
            len(list(debriefing.front_line_losses)),
            update_layout,
        )
        self.add_update_row("Convoy units destroyed",
                            len(list(debriefing.convoy_losses)), update_layout)
        self.add_update_row(
            "Shipping cargo destroyed",
            len(list(debriefing.cargo_ship_losses)),
            update_layout,
        )
        self.add_update_row(
            "Airlift cargo destroyed",
            sum(len(loss.cargo) for loss in debriefing.airlift_losses),
            update_layout,
        )
        self.add_update_row(
            "Ground Objects destroyed",
            len(list(debriefing.ground_object_losses)),
            update_layout,
        )
        self.add_update_row(
            "Scenery Objects destroyed",
            len(list(debriefing.scenery_object_losses)),
            update_layout,
        )
        self.add_update_row("Base capture events",
                            len(debriefing.base_captures), update_layout)

        # Clear previous content of the window
        for i in reversed(range(self.gridLayout.count())):
            try:
                self.gridLayout.itemAt(i).widget().setParent(None)
            except:
                logging.exception("Failed to clear window")

        # Set new window content
        self.gridLayout.addWidget(updateBox, 0, 0)

        if not debriefing.state_data.mission_ended:
            self.gridLayout.addWidget(QLabel("<b>Mission is being played</b>"),
                                      1, 0)
            self.gridLayout.addWidget(self.actions, 2, 0)
        else:
            self.gridLayout.addWidget(QLabel("<b>Mission is over</b>"), 1, 0)
            self.gridLayout.addWidget(self.actions2, 2, 0)

    def on_debriefing_update(self, debriefing: Debriefing) -> None:
        try:
            logging.info("On Debriefing update")
            logging.debug(debriefing)
            DebriefingFileWrittenSignal.get_instance().sendDebriefing(
                debriefing)
        except Exception:
            logging.exception("Got an error while sending debriefing")
        self.wait_thread = self.sim_controller.wait_for_debriefing(
            lambda d: self.on_debriefing_update(d))

    def process_debriefing(self):
        with logged_duration("Turn processing"):
            self.sim_controller.process_results(self.debriefing)
            self.game.pass_turn()

            GameUpdateSignal.get_instance().sendDebriefing(self.debriefing)
            GameUpdateSignal.get_instance().updateGame(self.game)
        self.close()

    def debriefing_directory_location(self) -> str:
        return os.path.join(base_path(), "liberation_debriefings")

    def closeEvent(self, evt):
        super(QWaitingForMissionResultWindow, self).closeEvent(evt)
        if self.wait_thread is not None:
            self.wait_thread.stop()

    def submit_manually(self):
        file = QFileDialog.getOpenFileName(self,
                                           "Select game file to open",
                                           filter="json(*.json)",
                                           dir=".")
        logging.debug("Processing manually submitted %s", file[0])
        self.on_debriefing_update(
            self.sim_controller.debrief_current_state(
                Path(file[0], force_end=True)))
Example #3
0
class MatchStatWindow(QDialog):
    def __init__(self, parent=None):
        super(MatchStatWindow, self).__init__(parent)
        self.setModal(True)
        self.setWindowTitle("Mérkőzés választása")
        self.resize(740, 600)
        self.layout = QVBoxLayout()
        self.setLayout(self.layout)

        self.merkozesek = QListWidget()
        self.merkozesek.setFixedHeight(200)
        self.merkozesek.setFixedWidth(730)
        self.merkozesek.itemDoubleClicked.connect(self.stat_game)
        self.layout.addWidget(self.merkozesek)
        self.szumma = MatchSumWidget(self)
        self.layout.addWidget(self.szumma)
        self.history = QWidget()
        self.history.setFixedWidth(690)
        self.history_layout = QGridLayout()
        self.history.setLayout(self.history_layout)
        scroll = QScrollArea()
        scroll.setWidget(self.history)
        scroll.setWidgetResizable(True)
        self.layout.addWidget(scroll)

        self.match_valasztas()

    def match_valasztas(self):
        matches = QSqlRelationalTableModel(db=db)
        matches.setTable("match_settings")
        matches.setSort(8, Qt.DescendingOrder)
        matches.select()
        self.merkozesek.clear()
        for i in  range(matches.rowCount()):
            self.merkozesek.addItem(str(matches.record(i).value(1)) + "\t" +
                                    str(matches.record(i).value(2)) + "\t" +
                                    str(int(matches.record(i).value(3)) + matches.record(i).value(6)) + "\t" +
                                    str(int(matches.record(i).value(3)) + matches.record(i).value(7)) + "\t" +
                                    str(matches.record(i).value(4)) + "\t" +
                                    str(matches.record(i).value(5)) + "\t" +
                                    str(matches.record(i).value(8))[:16] + "\t" +
                                    str(matches.record(i).value(0))
            )

    def stat_game(self):
        # Az átvett adatok:
        para = self.merkozesek.currentItem().text().rsplit("\t")
        # Összegyűjtjük egy listába a szükséges infókat
        self.get_adatok(para)
        self.szumma.change_data(self.adatok)
        for x in reversed(range(self.history_layout.count())):
            self.history_layout.itemAt(x).widget().deleteLater()
        # kiszedjük az adott meccs összes set és leg esetére a dobásokat
        sor = 0
        for s in range(1, self.adatok[8] + 1):
            # s: a set-ek száma
            if self.adatok[8] != 1:
                self.history_layout.addWidget(QLabel("Set: " + str(s)), sor, 0, 1, 2)
                sor += 1
            for l in range(1, self.adatok[7][s-1] + 1):
                sl1_model = QSqlQueryModel()
                p1_data_list = []
                p1_data_list.append(self.adatok[4])   # start_score1
                sl1_query = QSqlQuery(f"select * from dobas where match_id ={self.adatok[6]} and set_id={s} and leg_id={l} and player_id='{self.adatok[0]}'", db=db)
                sl1_model.setQuery(sl1_query)
                for i in range(sl1_model.rowCount()):
                    # Itt a model már tartalmazza a p1 összes dobását az adott leg-ben.
                    p1_data_row = []
                    p1_data_row.append(sl1_model.record(i).value(1))
                    p1_data_row.append(sl1_model.record(i).value(2))
                    p1_data_list.append(p1_data_row)
                self.history_layout.addWidget(PlayerLegWidget(self, p1_data_list), sor, 0, Qt.AlignTop)

                sl2_model = QSqlQueryModel()
                p2_data_list = []
                p2_data_list.append(self.adatok[5])  # start_score2
                sl2_query = QSqlQuery(f"select * from dobas where match_id ={self.adatok[6]} and set_id={s} and leg_id={l} and player_id='{self.adatok[2]}'", db=db)
                sl2_model.setQuery(sl2_query)
                for j in range(sl2_model.rowCount()):
                    p2_data_row = []
                    p2_data_row.append(sl2_model.record(j).value(1))
                    p2_data_row.append(sl2_model.record(j).value(2))
                    p2_data_list.append(p2_data_row)
                self.history_layout.addWidget(PlayerLegWidget(self, p2_data_list), sor, 1, Qt.AlignTop)
                sor += 1

    def get_adatok(self, para):
        print(para)
        # self.adatok[0]  : p1_id
        # self.adatok[1]  : name1
        # self.adatok[2]  : p2_id
        # self.adatok[3]  : name2
        # self.adatok[4]  : start_score1
        # self.adatok[5]  : start_score2
        # self.adatok[6]  : match
        # self.adatok[7]  : legs
        # self.adatok[8]  : sets
        # self.adatok[9]  : dátum

        self.adatok = []
        name1_id = int(para[0])
        self.adatok.append(name1_id)
        query_name1 = QSqlQuery(f"select player_name from players where player_id={name1_id}", db=db)
        query_name1.exec_()
        while query_name1.next():
            name1 = query_name1.value(0)
        self.adatok.append(name1)
        name2_id = int(para[1])
        self.adatok.append(name2_id)
        query_name2 = QSqlQuery(f"select player_name from players where player_id={name2_id}", db=db)
        query_name2.exec_()
        while query_name2.next():
            name2 = query_name2.value(0)
        self.adatok.append(name2)

        start_score1 = int(para[2])
        start_score2 = int(para[3])
        match = int(para[7])
        setek = int(para[5])
        self.adatok.append(start_score1)
        self.adatok.append(start_score2)
        self.adatok.append(match)
        # Kell a max set-number, ezt beállítani a sets változóba
        # Ciklussal minden set-ben megnézni a max leg-numbert, és ezeket append-elni a legs[]-hez
        # Végül leg, set sorrendben append-elni az adatokhoz
        legs = []
        sets = 0
        query2 = QSqlQuery(
            f"select max(set_id) as max_set from matches where match_id={match}", db=db)
        query2.exec_()
        while query2.next():
            sets = int(query2.value(0))

        for i in range(1, sets + 1):
            query = QSqlQuery(f"select max(leg_id) as max_leg from matches where match_id={match} and set_id={i}", db=db)
            query.exec_()
            while query.next():
                legs.append(int(query.value(0)))
                # sets.append(int(query.value(1)))

        self.adatok.append(legs)
        self.adatok.append(sets)

        datum = para[6][:16]
        self.adatok.append(datum)
        print(self.adatok)
Example #4
0
class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self, ui_demo=False):
        super(MainWindow, self).__init__()
        self.setupUi(self)
        self.grid_layout = QGridLayout()

        self.btnDetectClient.clicked.connect(self.detectClients)
        self.lineEditIpRange.returnPressed.connect(self.detectClients)
        self.btnSelectAllClients.clicked.connect(self.selectAllCLients)

        shortcut = QShortcut(QKeySequence(self.tr("Ctrl+A")), self)
        shortcut.activated.connect(self.selectAllCLients)

        self.btnUnselectClients.clicked.connect(self.unselectAllCLients)
        self.btnSelectExam.clicked.connect(self.selectExamByWizard)
        self.btnSelectExam.setEnabled(True)

        self.btnPrepareExam.clicked.connect(self.prepareExam)
        self.btnPrepareExam.setEnabled(False)

        self.btnGetExams.clicked.connect(self.retrieveExamFilesByWizard)
        self.btnGetExams.setEnabled(True)
        self.btnSaveExamLog.clicked.connect(self.saveExamLog)
        # self.btnSaveExamLog.setEnabled(False)

        self.actionBearbeiten.triggered.connect(self.openConfigDialog)
        self.actionAlle_Benutzer_benachrichtigen.triggered.connect(
            self.sendMessage)
        self.actionAlle_Clients_zur_cksetzen.triggered.connect(
            self.resetClients)
        self.actionAlle_Clients_rebooten.triggered.connect(
            self.rebootAllClients)
        self.actionAlle_Clients_herunterfahren.triggered.connect(
            self.shutdownAllClients)
        self.actionOnlineHelp.triggered.connect(self.openHelpUrl)
        self.actionOfflineHelp.triggered.connect(self.openHelpUrlOffline)
        self.actionSortClientByCandidateName.triggered.connect(
            self.sortButtonsByCandidateName)
        self.actionSortClientByComputerName.triggered.connect(
            self.sortButtonsByComputerName)
        self.actionVersionInfo.triggered.connect(self.showVersionInfo)
        self.actionDisplayIPs.triggered.connect(self.toggleClientIpDisplay)

        self.btnApplyCandidateNames.clicked.connect(self.applyCandidateNames)
        self.btnNameClients.clicked.connect(self.activateNameTab)

        self.btnBlockUsb.clicked.connect(self.blockUsb)
        self.btnBlockWebAccess.clicked.connect(self.blockWebAccess)

        self.appTitle = 'ECMan - Exam Client Manager'
        self.setWindowTitle(self.appTitle)

        self.logger = Logger(self.textEditLog)

        self.lb_directory = None
        self.advancedUi = False
        self.configure()
        self.show()
        if ui_demo is False:
            self.detectClients()

        else:
            self.clientFrame.setLayout(self.grid_layout)
            for i in range(6):
                self.addTestClient(i + 100)

        self.statusBar = QStatusBar()
        self.setStatusBar(self.statusBar)

    def sortButtonsByCandidateName(self):
        '''
        sort LbClient-widgets by candidate name
        :return: nothing
        '''
        clients = [
            self.grid_layout.itemAt(i).widget()
            for i in range(self.grid_layout.count())
        ]
        clients = sorted(clients,
                         key=lambda client: client.computer.candidateName)
        self.arrangeClientButtons(clients)

    def sortButtonsByComputerName(self):
        '''
        supposed to sort LbClient-widgets by candidate name
        :return: nothing
        '''
        clients = [
            self.grid_layout.itemAt(i).widget()
            for i in range(self.grid_layout.count())
        ]
        clients = sorted(clients,
                         key=lambda client: client.computer.getHostName())
        self.arrangeClientButtons(clients)

    def toggleClientIpDisplay(self):
        clients = [
            self.grid_layout.itemAt(i).widget()
            for i in range(self.grid_layout.count())
        ]
        for client in clients:
            client.toggleShowIp()

    def arrangeClientButtons(self, clients):
        try:
            for i in reversed(range(self.grid_layout.count())):
                self.grid_layout.removeItem(self.grid_layout.itemAt(i))
        except:
            pass

        self.clientFrame.setLayout(self.grid_layout)
        for button in clients:
            self.grid_layout.addWidget(button,
                                       self.grid_layout.count() / 4,
                                       self.grid_layout.count() % 4)

        self.clientFrame.setLayout(self.grid_layout)

    def activateNameTab(self):
        if self.textEditCandidates.toPlainText() == "":
            candidates = "\n".join(
                str(x + 1) for x in range(self.grid_layout.count()))
            self.textEditCandidates.setText(candidates)

        self.tabs.setCurrentWidget(self.tab_candidates)

    def openHelpUrl(self):
        webbrowser.open(
            self.config.get(
                "General",
                "wikiurl",
                fallback="https://github.com/greenorca/ECMan/wiki"))

    def openHelpUrlOffline(self):
        webbrowser.open("file://" + os.getcwd().replace("\\", "/") +
                        "/help/Home.html")

    def checkOldLogFiles(self):
        """
        dummy,
        """
        pass

    def blockUsb(self):
        block = self.btnBlockUsb.text() == "USB blockieren"
        clients = [
            self.grid_layout.itemAt(i).widget()
            for i in range(self.grid_layout.count())
            if self.grid_layout.itemAt(i).widget().isSelected
        ]

        if block:
            for client in clients:
                client.blockUsbAccessThread()
            self.btnBlockUsb.setText("USB freigeben")
        else:
            for client in clients:
                client.allowUsbAccessThread()
            self.btnBlockUsb.setText("USB blockieren")

    def blockWebAccess(self):
        block = self.btnBlockWebAccess.text() == "Web blockieren"
        clients = [
            self.grid_layout.itemAt(i).widget()
            for i in range(self.grid_layout.count())
            if self.grid_layout.itemAt(i).widget().isSelected
        ]
        if block:
            for client in clients:
                client.blockInternetAccessThread()

            self.btnBlockWebAccess.setText("Web freigeben")

        else:
            for client in clients:
                client.allowInternetAccessThread()

            self.btnBlockWebAccess.setText("Web blockieren")

    def resetClients(self):
        """
        resets remote files and configuration for all connected clients
        """

        items = ["Ja, Namen zurücksetzen", "Nein, Namen beibehalten"]
        item, ok = QInputDialog().getItem(
            self, "LB-Status zurücksetzen",
            "USB-Sticks und Internet werden freigeben.\nDaten im Benutzerverzeichnis werden NICHT gelöscht.\nKandidaten-Namen zurücksetzen? ",
            items, 0, False)
        if ok is False:
            return

        resetCandidateName = True if item.startswith("Ja") else False

        clients = [
            self.grid_layout.itemAt(i).widget()
            for i in range(self.grid_layout.count())
        ]

        progressDialog = EcManProgressDialog(self, "Reset Clients")
        progressDialog.setMaxValue(self.grid_layout.count())
        progressDialog.resetValue()
        progressDialog.open()

        self.worker = ResetClientsWorker(clients, resetCandidateName)
        self.worker.updateProgressSignal.connect(progressDialog.incrementValue)
        self.worker.start()

    def closeEvent(self, event):
        """
        overrides closeEvent of base class, clean up

        """
        print("cleaning up...")
        #         try:* especially remove shares created on Windows hosts
        #             for share in self.sharenames:
        #                 Thread(target=self.runLocalPowerShellAsRoot("Remove-SmbShare -Name {} -Force".format(share))).start()
        #         except Exception:
        #             pass
        #
        print("done cleaning up...")
        super(MainWindow, self).closeEvent(event)

    def selectAllCLients(self):
        """
        marks / selects all connected client pcs
        """
        for i in range(self.grid_layout.count()):
            self.grid_layout.itemAt(i).widget().select()

    def unselectAllCLients(self):
        """
        unmarks / unselects all connected client pcs
        """
        for i in range(self.grid_layout.count()):
            self.grid_layout.itemAt(i).widget().unselect()

    def shutdownAllClients(self):
        for i in range(self.grid_layout.count()):
            self.grid_layout.itemAt(i).widget().shutdownClient()

    def rebootAllClients(self):
        for i in range(self.grid_layout.count()):
            self.grid_layout.itemAt(i).widget().computer.reboot()
            self.grid_layout.itemAt(i).widget().deleteLater()
        # TODO TEST sven
        #for i in range(self.grid_layout.count()):
        #    self.grid_layout.removeItem(self.grid_layout.itemAt(i))

    def sendMessage(self):

        message, ok = QInputDialog.getText(self, "Eingabe",
                                           "Nachricht an Kandidaten eingeben")
        if ok:
            for i in range(self.grid_layout.count()):
                QThreadPool.globalInstance().start(
                    SendMessageTask(
                        self.grid_layout.itemAt(i).widget(), message))

    def applyCandidateNames(self):
        """
        reads candidate names from respective textEditField (line by line)
        and applies these names to (random) client pcs
        """
        names = self.textEditCandidates.toPlainText().rstrip().splitlines()
        # cleanup and remove duplicate names
        names = [x.strip() for x in names]
        names = list(set(names))

        clients = [
            self.grid_layout.itemAt(i).widget()
            for i in range(self.grid_layout.count())
        ]

        # select only the computers without candidate name
        if self.checkBox_OverwriteExisitingNames.checkState(
        ) != Qt.CheckState.Checked:
            clients = [
                x for x in clients if x.computer.getCandidateName() == ""
                or x.computer.getCandidateName() is None
            ]

        if len(names) > len(clients):
            self.showMessageBox(
                "Fehler",
                "Nicht genug Prüfungs-PCs für alle {} Kandidaten".format(
                    str(len(names))),
                messageType=QMessageBox.Warning)
            return

        progressDialog = EcManProgressDialog(
            self, "Fortschritt Kandidatennamen setzen")
        progressDialog.setMaxValue(len(names))
        progressDialog.resetValue()

        self.worker = SetCandidateNamesWorker(clients, names)
        self.worker.updateProgressSignal.connect(progressDialog.incrementValue)
        self.worker.start()

        progressDialog.open()
        self.tabs.setCurrentWidget(self.tab_pcs)

    def configure(self):
        """
        sets inial values for app
        """
        self.port = 5986
        self.server = None

        self.configFile = Path(str(Path.home()) + "/.ecman.conf")

        if not (self.configFile.exists()):
            result = self.openConfigDialog()

        if self.configFile.exists() or result == 1:
            self.readConfigFile()

        self.result_directory = ""

        self.sharenames = [
        ]  # dump all eventually created local Windows-Shares in this array
        self.debug = True
        # fetch own ip adddress
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        try:
            s.connect(('9.9.9.9', 1))  # connect() for UDP doesn't send packets
            self.ip_address = s.getsockname()[0]
            parts = self.ip_address.split(".")[0:-1]
            self.ipRange = ".".join(parts) + ".*"
            self.lineEditIpRange.setText(self.ipRange)
            self.appTitle = self.windowTitle() + " on " + self.ip_address
            self.setWindowTitle(self.appTitle)
        except Exception as ex:
            self.ipRange, ok = QInputDialog.getText(
                self, "Keine Verbindung zum Internet",
                "Möglicherweise gelingt der Sichtflug. Bitte geben Sie die lokale IP-Adresse ein:"
            )

            self.log("no connection to internet:" + str(ex))

    def readConfigFile(self):
        """
        reads config file into class variables
        """
        self.config = ConfigParser()
        self.config.read_file(open(str(self.configFile)))

        self.port = self.config.get("General", "winrm_port", fallback=5986)

        self.client_lb_user = self.config.get("Client",
                                              "lb_user",
                                              fallback="student")
        self.user = self.config.get("Client", "user", fallback="")
        self.passwd = self.config.get("Client", "pwd", fallback="")
        self.maxFiles = int(
            self.config.get("Client", "max_files", fallback="1000"))
        self.maxFileSize = int(
            self.config.get("Client", "max_filesize",
                            fallback="1000")) * 1024 * 1024  # thats MB now...

        self.advancedUi = self.config.get('General',
                                          'advanced_ui',
                                          fallback="False") == "True"
        pass

    def openConfigDialog(self):
        """
        opens configuration dialog
        """
        config_dialog = EcManConfigDialog(self)
        result = config_dialog.exec_()
        if result == 1:
            config_dialog.saveConfig()
            self.readConfigFile()
        return result

    def log(self, message):
        """
        basic logging functionality
        TODO: improve...
        """
        self.logger.log(message)

    def updateProgressBar(self, value):
        self.progressBar.setValue(value)
        if (value == self.progressBar.maximum()):
            self.enableButtons(True)
            self.progressBar.setEnabled(False)

    def enableButtons(self, enable):

        if type(enable) != bool:
            raise Exception("Invalid parameter, must be boolean")
        self.btnNameClients.setEnabled(enable)
        self.btnSelectAllClients.setEnabled(enable)
        self.btnUnselectClients.setEnabled(enable)
        self.btnPrepareExam.setEnabled(enable)
        self.btnGetExams.setEnabled(enable)
        self.btnSaveExamLog.setEnabled(enable)
        self.btnDetectClient.setEnabled(enable)
        self.btnBlockWebAccess.setEnabled(enable)
        self.btnBlockUsb.setEnabled(enable)

    def retrieveExamFilesByWizard(self):
        """
        retrieve exam files for all clients
        """
        # find all suitable clients (required array for later threading)
        clients = [
            self.grid_layout.itemAt(i).widget()
            for i in range(self.grid_layout.count())
            if self.grid_layout.itemAt(i).widget().isSelected
            and self.grid_layout.itemAt(i).widget().computer.state in [
                Computer.State.STATE_DEPLOYED, Computer.State.STATE_FINISHED,
                Computer.State.STATE_RETRIVAL_FAIL
            ]
        ]

        if len(clients) == 0:
            self.showMessageBox("Achtung",
                                "Keine Clients ausgewählt bzw. deployed")
            return

        unknownClients = [
            c for c in clients if c.computer.getCandidateName() == ""
            or c.computer.getCandidateName() == None
        ]

        for unknown in unknownClients:
            unknown.computer.state = Computer.State.STATE_RETRIVAL_FAIL
            unknown._colorizeWidgetByClientState()

        if unknownClients != []:
            choice = QMessageBox.critical(
                self, "Achtung",
                "{} clients ohne gültigen Kandidatennamen.<br>Rückholung für alle anderen fortsetzen?"
                .format(str(len(unknownClients))), QMessageBox.Yes,
                QMessageBox.No)

            if choice == QMessageBox.No:
                return

        clients = [c for c in clients if c not in unknownClients]

        retVal = QMessageBox.StandardButton.Yes
        if self.result_directory != "":

            items = [
                "Ergebnispfad neu auswählen",
                "Weiter mit bisherigem Verzeichnis"
            ]
            item, ok = QInputDialog().getItem(
                self, "Achtung",
                "LB-Ergebnisverzeichnis ist bereits ausgewählt.\nErneutes Abholen kann existierende Ergebnisse überschreiben.\nWas möchten Sie tun?",
                items, 0, False)

            if ok is False:
                return

        if self.result_directory == "" or item == "Ergebnispfad neu auswählen":

            if self.server is None or self.server.connect() is not True:
                self.server = self.getServerCredentialsByWizard()
                if self.server is None:
                    return

            wizard = EcShareWizard(
                parent=self,
                server=self.server,
                wizardType=EcShareWizard.TYPE_RESULT_DESTINATION,
                advanced_Ui=self.advancedUi)

            wizard.setModal(True)
            result = wizard.exec_()
            print("I'm done, wizard result=" + str(result))
            if result == 1:
                print("selected values: %s - %s - %s" %
                      (wizard.field("username"), wizard.field("servername"),
                       wizard.selectedPath))

                self.result_directory = wizard.selectedPath

            else:
                print("Abbruch, kein Zielverzeichnis ausgewählt")
                return

        self.result_directory = self.result_directory.replace("/", "#")
        self.log("save result files into: " +
                 self.result_directory.replace("#", "\\"))

        progressDialog = EcManProgressDialog(
            self, "Fortschritt Ergebnisse kopieren")
        progressDialog.setMaxValue(len(clients))
        progressDialog.resetValue()
        progressDialog.open()

        self.log("starting to retrieve files")

        self.worker = RetrieveResultsWorker(clients, self.server.user,
                                            self.server.password,
                                            self.server.domain,
                                            self.result_directory,
                                            self.maxFiles, self.maxFileSize)
        self.worker.updateProgressSignal.connect(progressDialog.incrementValue)
        self.worker.start()

        self.btnSaveExamLog.setEnabled(True)

    def prepareExam(self):
        self.copyFilesToClient()
        pass

    def copyFilesToClient(self):
        """
        copies selected exam folder to all connected clients that are selected and not in STATE_DEPLOYED or STATE_FINISHED
        """
        if self.lb_directory == None or self.lb_directory == "":
            self.showMessageBox("Fehler", "Kein Prüfungsordner ausgewählt")
            return

        clients = [
            self.grid_layout.itemAt(i).widget()
            for i in range(self.grid_layout.count())
            if self.grid_layout.itemAt(i).widget().isSelected
            and self.grid_layout.itemAt(i).widget().computer.state not in
            [Computer.State.STATE_DEPLOYED, Computer.State.STATE_FINISHED]
        ]

        if len([x for x in clients if x.computer.candidateName == None]) > 0:
            self.showMessageBox("Warnung",
                                "Bitte Kandidatenname für alle PCs vergeben")
            return

        if len(clients) == 0:
            self.showMessageBox(
                "Warnung",
                "Keine Client-PCs ausgewählt oder Prüfungen bereits aktiv")
            return

        progressDialog = EcManProgressDialog(
            self, "Fortschritt LB-Client-Deployment")
        progressDialog.setMaxValue(len(clients))
        progressDialog.resetValue()
        progressDialog.open()

        self.worker = CopyExamsWorker(
            clients,
            self.server.user,
            self.server.password,
            self.server.domain,
            src=self.lb_directory,
            reset=(self.checkBoxWipeHomedir.checkState() ==
                   Qt.CheckState.Checked))
        self.worker.updateProgressSignal.connect(progressDialog.incrementValue)
        self.worker.start()

    def detectClients(self):
        """
        starts portscan to search for winrm enabled clients
        """
        ip_range = self.lineEditIpRange.text()
        if not (ip_range.endswith('*')):
            self.showMessageBox(
                'Eingabefehler',
                'Gültiger IP-V4 Bereich endet mit * (z.B. 192.168.0.*)')
            return

        try:
            self.worker.exit()
        except Exception as ex:
            print("crashed on stopping existing scanner thread: " + str(ex))

        self.ipRange = ip_range
        self.progressBar.setEnabled(True)
        self.progressBar.setValue(0)

        self.enableButtons(enable=False)
        self.clientCount = 0
        # clear previous client buttons
        try:
            for i in reversed(range(self.grid_layout.count())):
                self.grid_layout.itemAt(i).widget().close()
                self.grid_layout.itemAt(i).widget().deleteLater()
        except:
            pass
        self.clientFrame.setLayout(self.grid_layout)

        self.progressBar.setMaximum(253)
        self.worker = ScannerWorker(self.ipRange, self.ip_address)
        self.worker.updateProgressSignal.connect(self.updateProgressBar)
        self.worker.addClientSignal.connect(self.addClient)
        self.worker.start()

    def addClient(self, ip):
        """
        populate GUI with newly received client ips
        param ip: only last byte required
        param scan: wether or not scan the Client (set to False only for GUI testing)
        """
        self.log("new client signal received: " + str(ip))
        self.clientCount += 1
        self.statusBar.showMessage(
            str(self.clientCount) + " clients detected", 0)

        clientIp = self.ipRange.replace("*", str(ip))
        button = LbClient(clientIp,
                          remoteAdminUser=self.user,
                          passwd=self.passwd,
                          candidateLogin=self.client_lb_user,
                          parentApp=self)
        button.setMinimumHeight(50)
        # button.installEventFilter(self)
        self.grid_layout.addWidget(button,
                                   self.grid_layout.count() / 4,
                                   self.grid_layout.count() % 4)
        self.clientFrame.setLayout(self.grid_layout)
        # QtGui.qApp.processEvents()

    def addTestClient(self, ip):
        """
        populate GUI with dummy buttons
        param ip: only last byte required
        """
        self.log("new client signal received: " + str(ip))
        clientIp = self.ipRange.replace("*", str(ip))
        button = LbClient(clientIp,
                          remoteAdminUser=self.user,
                          passwd=self.passwd,
                          candidateLogin=self.client_lb_user,
                          parentApp=self,
                          test=True)
        button.setMinimumHeight(50)
        # button.installEventFilter(self)
        self.grid_layout.addWidget(button,
                                   self.grid_layout.count() / 4,
                                   self.grid_layout.count() % 4)
        self.clientFrame.setLayout(self.grid_layout)
        # QtGui.qApp.processEvents()

    def getExamPath(self):
        return self.lb_directory

    def getServerCredentialsByWizard(self):
        """
        open server config and login dialog, returns server object or None
        """
        wizard = EcLoginWizard(parent=self,
                               username=self.config.get("General",
                                                        "username",
                                                        fallback=""),
                               domain=self.config.get("General",
                                                      "domain",
                                                      fallback=""),
                               servername=self.config.get("General",
                                                          "lb_server",
                                                          fallback=""))

        wizard.setModal(True)
        result = wizard.exec_()
        print("I'm done, wizard result=" + str(result))
        if result == 1:
            self.config["General"]["username"] = wizard.field("username")
            self.config["General"]["domain"] = wizard.field("domainname")
            self.config["General"]["servername"] = wizard.server.serverName

            self.saveConfig()
            return wizard.server

        return None

    def selectExamByWizard(self):
        """
        provides ability to select serverName share plus logon credentials and lb directory using a wizard
        """

        if self.server == None or self.server.connect() is False:
            self.server = self.getServerCredentialsByWizard()

        wizard = EcShareWizard(parent=self,
                               server=self.server,
                               wizardType=EcShareWizard.TYPE_LB_SELECTION,
                               advanced_Ui=self.advancedUi)

        wizard.setModal(True)
        result = wizard.exec_()
        print("I'm done, wizard result=" + str(result))
        if result == 1:
            self.lb_directory = wizard.selectedPath
            self.setWindowTitle(self.appTitle + " - LB-Verzeichnis::" +
                                self.lb_directory.split("/")[-1])
            self.log("setup LB directory: " + self.lb_directory.split("/")[-1])
            self.btnPrepareExam.setEnabled(True)
            self.lblExamName.setText(self.lb_directory)
        else:
            self.log("no valid share selected")

    def saveExamLog(self):
        """
        on demand, store all client logs as PDF
        """
        if self.result_directory == None or len(self.result_directory) == 0:
            self.showMessageBox(
                "Fehler",
                "Ergebnispfad für Prüfungsdaten nicht gesetzt.<br>Bitte zuerst Prüfungsdaten abholen.",
                QMessageBox.Error)
            return

        clients = [
            self.grid_layout.itemAt(i).widget()
            for i in range(self.grid_layout.count())
        ]
        for client in clients:
            lb_dataDirectory = client.computer.lb_dataDirectory.split("#")[-1]
            pdfFileName = "protocol_" + date.today(
            ).__str__() + "_" + client.computer.getCandidateName().replace(
                " ", "_") + ".pdf"
            LogfileHandler(client.computer.logfile_name, client.computer.getCandidateName()). \
                createPdf(pdfFileName)
            if not (self.server.connect()):
                self.showMessageBox(
                    "Fehler",
                    "Verbindung zum Server kann nicht aufgebaut werden.",
                    QMessageBox.Error)
                return

            with open(pdfFileName, "rb") as file:
                sharename = self.result_directory.replace("##",
                                                          "").split("#")[1]
                destination = "/".join(
                    self.result_directory.replace(
                        "##",
                        "").split("#")[2:]) + "/" + lb_dataDirectory + "/"
                self.server.conn.storeFile(sharename,
                                           destination + pdfFileName, file)

    def __runLocalPowerShellAsRoot(self, command):
        """
        maybe useful at some stage again
        # see https://docs.microsoft.com/en-us/windows/desktop/api/shellapi/nf-shellapi-shellexecutew#parameters
        :param command:
        :return:
        """

        retval = ctypes.windll.shell32.ShellExecuteW(
            None,
            "runas",  # runas admin,
            "C:\\WINDOWS\\system32\\WindowsPowerShell\\v1.0\\powershell.exe",
            # file to run
            command,  # actual powershell command
            None,
            0)  # last param disables popup powershell window...

        if retval != 42:
            self.log("ReturnCode after creating smbShare: " + str(retval))
            subprocess.run([
                "C:\\WINDOWS\\system32\\WindowsPowerShell\\v1.0\\powershell.exe",
                "Get-SmbShare"
            ])
            raise Exception("ReturnCode after running powershell: " +
                            str(retval))

    def __openFile(self):
        fname = QFileDialog.getOpenFileName(self, 'Open file', '/home')
        if fname[0]:
            f = open(fname[0], 'r')
        with f:
            data = f.read()
            doc = QTextDocument(data, None)
            self.textEditLog.setDocument(doc)

    def saveConfig(self):
        """
        write to file what's currently set in config
        """
        if not (self.configFile.exists()):
            self.configFile.touch()

        self.config.write(open(self.configFile, 'w'))

    def eventFilter(self, currentObject, event):
        """
        unused, define mouseover events (with tooltips) for LbClient widgets
        """
        if event.type() == QEvent.Enter:
            if isinstance(currentObject, LbClient):
                print("Mouseover event catched")
                currentObject.setOwnToolTip()
                return True
            else:
                self.log(str(type(currentObject)) + " not recognized")

                # elif event.type() == QEvent.Leave:
        #    pass
        return False

    def showMessageBox(self,
                       title,
                       message,
                       messageType=QMessageBox.Information):
        """
        convinence wrapper
        """
        msg = QMessageBox(messageType, title, message, parent=self)
        if messageType != QMessageBox.Information:
            msg.setStandardButtons(QMessageBox.Abort)
        return msg.exec_()

    def showVersionInfo(self):
        info = "<b>Offizielles Release:</b><br>" + version
        try:
            # latest release - make sure to save this file before building :-)
            modDate = time.localtime(os.path.getmtime(__file__))
            info = info + "<br><br><b>Diese Version:</b><br>" + time.strftime(
                "%Y-%m-%d", modDate)
        except:
            pass

        self.showMessageBox("ECMan - Version", info)
Example #5
0
class StatComponent(QGroupBox):
    def __init__(self):
        super().__init__()

        self._series = {}
        self._legend_items = {}

        self.setTitle("Statistiques")
        self.setProperty("qss-var", "pb-0")

        self._total_label = QLabel()

        self._legend_layout = QGridLayout()
        self._legend_layout.setVerticalSpacing(0)
        self._legend_layout.setHorizontalSpacing(50)

        main_layout = QVBoxLayout()
        main_layout.addWidget(self._total_label)
        main_layout.addLayout(self._legend_layout)
        main_layout.addSpacing(5)
        main_layout.addWidget(self._createLineChart(), 1)

        self.setLayout(main_layout)

    def reset(self, parameters: Parameters):
        self._parameters = parameters

        self._total_label.setText("Éponges détectées : 0")

        for i in reversed(range(self._legend_layout.count())):
            self._legend_layout.itemAt(i).widget().setParent(None)

        self._chart.removeAllSeries()
        self._legend_items = {}
        self._series = {}
        for i, m in parameters.selectedMorphotypes().items():
            self._series[i] = QtCharts.QLineSeries(self)
            self._series[i].setName(m.name())
            self._series[i].setColor(m.color())

            pen = QPen(m.color(), 3)
            pen.setCapStyle(Qt.RoundCap)
            self._series[i].setPen(pen)

            self._series[i].append(0, 0)
            self._chart.addSeries(self._series[i])

            self._legend_items[i] = ChartLegendItem(m.name(), m.color(), self)
            self._legend_items[i].toggled.connect(self._legendItemToggled)

        for i, k in enumerate(self._legend_items.keys()):
            row = i % 2 + 1
            col = i // 2 + 1

            self._legend_layout.addWidget(self._legend_items[k], row, col)

        axis_pen = QPen(Loader.QSSColor("@dark"))
        axis_pen.setWidth(2)

        grid_line_pen = QPen(Loader.QSSColor("@light-gray"))

        labels_font = QFont(Loader.QSSVariable("@font"))
        labels_font.setPointSize(10)

        self._chart.createDefaultAxes()

        for axis in (self._chart.axisX(), self._chart.axisY()):
            axis.setRange(0, 4)
            axis.setLinePen(axis_pen)
            axis.setGridLinePen(grid_line_pen)
            axis.setLabelFormat("%d")
            axis.setLabelsFont(labels_font)

    def _createLineChart(self) -> QtCharts.QChartView:
        self._chart = QtCharts.QChart()

        self._chart.setAnimationOptions(QtCharts.QChart.SeriesAnimations)

        self._chart.setTitle("Détections cumulées")
        self._chart.legend().setVisible(False)
        self._chart.setBackgroundBrush(QBrush(QColor("transparent")))
        self._chart.setMargins(QMargins(0, 0, 0, 0))

        title_font = QFont(Loader.QSSVariable("@font"))
        title_font.setPointSize(14)
        self._chart.setTitleFont(title_font)
        self._chart.setTitleBrush(QBrush(Loader.QSSColor("@dark")))

        chart_view = QtCharts.QChartView(self._chart)
        chart_view.setRenderHint(QPainter.Antialiasing)

        return chart_view

    def update(self, analysis: Analysis):
        self._analysis = analysis

        for class_id in self._parameters.selectedMorphotypes():
            last_idx = self._series[class_id].count() - 1

            new_x = self._series[class_id].at(last_idx).x() + 1
            new_y = analysis.cumulativeDetectionsFor(class_id)

            if last_idx > 1:
                if self._series[class_id].at(last_idx - 1).y(
                ) == new_y and self._series[class_id].at(
                        last_idx).y() == new_y:
                    self._series[class_id].replace(last_idx, new_x, new_y)
                    continue

            self._series[class_id].append(new_x, new_y)
            self._legend_items[class_id].setValue(str(new_y))

        self._total_label.setText("Éponges détectées : %d" %
                                  analysis.totalDetections())

        self._recalculateAxis()

    def _recalculateAxis(self):
        base_series = list(self._series.values())[0]

        last_idx = base_series.count() - 1
        x = base_series.at(last_idx).x()

        self._chart.axisX().setRange(0, max(x, 4))

        maxY = 0
        for k in self._series:
            if not self._legend_items[k].isChecked():
                continue

            maxY = max(maxY, self._analysis.cumulativeDetectionsFor(k))

        maxY = max(maxY, 4)

        # Add 5% to show the top series below the top line
        maxY = round(1.05 * maxY)

        self._chart.axisY().setRange(0, maxY)

    @Slot(bool)
    def _legendItemToggled(self, state: bool):
        for k, v in self._legend_items.items():
            if v == self.sender():
                self._series[k].setVisible(state)
                break

        self._recalculateAxis()
Example #6
0
class QBriefingWindow(QDialog):

    def __init__(self, gameEvent: Event):
        super(QBriefingWindow, self).__init__()
        self.gameEvent = gameEvent
        self.setWindowTitle("Briefing : " + str(gameEvent))
        self.setMinimumSize(200,200)
        self.setWindowIcon(CONST.EVENT_ICONS[self.gameEvent.__class__])
        self.setModal(True)
        self.game = self.gameEvent.game

        if self.gameEvent.attacker_name == self.game.player_name:
            self.base = self.gameEvent.from_cp.base
            self.playerFromCp = self.gameEvent.from_cp
        else:
            self.base = self.gameEvent.to_cp.base
            self.playerFromCp = self.gameEvent.to_cp

        self.scramble_entries = {k: {} for k in self.gameEvent.tasks}
        self.initUi()

    def initUi(self):

        self.layout = QVBoxLayout()

        self.depart_box = QGroupBox("Departure")
        self.depart_layout = QHBoxLayout()
        self.depart_box.setLayout(self.depart_layout)
        self.depart_from_label = QLabel("Depart from : ")
        self.depart_from = QComboBox()

        for i, cp in enumerate([b for b in self.game.theater.controlpoints if b.captured]):
            self.depart_from.addItem(str(cp.name), cp)
            if cp.name == self.playerFromCp.name:
                self.depart_from.setCurrentIndex(i)

        self.depart_from.currentTextChanged.connect(self.on_departure_cp_changed)
        self.depart_layout.addWidget(self.depart_from_label)
        self.depart_layout.addWidget(self.depart_from)

        # Mission Description
        self.gridLayout = QGridLayout()
        self.initUnitRows()
        self.scramble_box = QGroupBox("Units")
        self.scramble_box.setLayout(self.gridLayout)

        self.action_layout = QHBoxLayout()
        self.commit_button = QPushButton("Commit")
        self.back_button = QPushButton("Cancel")
        self.commit_button.clicked.connect(self.start)
        self.back_button.clicked.connect(self.close)
        self.action_layout.addWidget(self.commit_button)
        self.action_layout.addWidget(self.back_button)

        self.support_box = self.initSupportBox()
        self.layout.addWidget(QLabel("<h2>{} on {}</h2>".format(self.gameEvent, self.gameEvent.to_cp.name)))
        self.layout.addWidget(self.depart_box)
        self.layout.addWidget(self.scramble_box)
        self.layout.addWidget(self.support_box)
        self.layout.addWidget(QLabel("<b>Ready?</b>"))
        self.layout.addLayout(self.action_layout)
        self.setLayout(self.layout)

    def initUnitRows(self):

        row = 0

        def header(text, row):
            self.gridLayout.addWidget(QLabel("<b>" + text + "</b>"), row, 0, 1, 2)

        def scramble_row(task_type, unit_type, unit_count, client_slots: bool, row: int):
            unit_name = QLabel("{} ({})".format(db.unit_type_name(unit_type), unit_count))
            self.gridLayout.addWidget(unit_name, row, 0)

            scramble_entry = QSpinBox()
            self.gridLayout.addWidget(scramble_entry, row, 1)

            if client_slots:
                client_entry = QSpinBox()
                self.gridLayout.addWidget(client_entry, row, 2)
            else:
                client_entry = None

            self.scramble_entries[task_type][unit_type] = scramble_entry, client_entry

        # Table headers
        self.gridLayout.addWidget(QLabel("Amount"), row, 1)
        self.gridLayout.addWidget(QLabel("Client slots"), row, 2)
        row += 1

        for flight_task in self.gameEvent.tasks:
            header("{}:".format(self.gameEvent.flight_name(flight_task)), row)
            row += 1

            if flight_task == PinpointStrike:
                if not self.base.armor:
                    self.gridLayout.addWidget(QLabel("No units"), row, 1)
                    row += 1
                for t, c in self.base.armor.items():
                    scramble_row(flight_task, t, c, False, row)
                    row += 1
            else:
                if not self.base.aircraft:
                    self.gridLayout.addWidget(QLabel("No units"), row, 1)
                    row += 1
                for t, c in self.base.aircraft.items():
                    scramble_row(flight_task, t, c, t.flyable, row)
                    row += 1

        return self.gridLayout

    def initSupportBox(self):

        self.support_box = QGroupBox("Support")
        self.support_layout = QGridLayout()
        self.support_box.setLayout(self.support_layout)

        self.awacs_label = QLabel("AWACS ({}m)".format(AWACS_BUDGET_COST))
        self.awacs_checkbox = QCheckBox()

        self.ca_slot_label = QLabel("Combined Arms Slots")
        self.ca_slot_entry = QSpinBox()
        self.ca_slot_entry.setValue(0)
        self.ca_slot_entry.setMinimum(0)
        self.ca_slot_entry.setMaximum(32)

        self.support_layout.addWidget(self.awacs_label, 0, 0)
        self.support_layout.addWidget(self.awacs_checkbox, 0, 1)
        self.support_layout.addWidget(self.ca_slot_label, 1, 0)
        self.support_layout.addWidget(self.ca_slot_entry, 1, 1)
        return self.support_box


    def initWaitingForResults(self):

        layout = QVBoxLayout()

        layout.addWidget(QLabel("<b>You are clear for takeoff</b>"))
        layout.addWidget(QLabel("In DCS open and play the mission : "))
        layout.addWidget(QLabel("<i>liberation_nextturn</i>"))
        layout.addWidget(QLabel("or"))
        layout.addWidget(QLabel("<i>liberation_nextturn_quick</i>"))

        layout.addWidget(QLabel("<b>Then save the debriefing to folder :</b>"))
        layout.addWidget(QLabel("Then save the debriefing to the folder:"))
        layout.addWidget(QLabel("<i>" + self.debriefing_directory_location() + "</i>"))
        layout.addWidget(QLabel("Waiting for results..."))

        # layout.addWidget(QLabel("In DCS open and play the mission : "))
        # layout.addWidget(QLabel("<b>You are clear for takeoff</b>"))

        self.setLayout(layout)

        pass

    def debriefing_directory_location(self) -> str:
        return os.path.join(base_path(), "liberation_debriefings")

    def start(self):

        if self.awacs_checkbox.isChecked() == 1:
            self.gameEvent.is_awacs_enabled = True
            self.game.awacs_expense_commit()
        else:
            self.gameEvent.is_awacs_enabled = False

        ca_slot_entry_value = self.ca_slot_entry.value()
        try:
            ca_slots = int(ca_slot_entry_value and ca_slot_entry_value or "0")
        except:
            ca_slots = 0
        self.gameEvent.ca_slots = ca_slots


        # Resolve Departure CP
        self.gameEvent.departure_cp = self.depart_from.itemData(self.depart_from.currentIndex())


        flights = {k: {} for k in self.gameEvent.tasks}  # type: db.TaskForceDict
        units_scramble_counts = {}  # type: typing.Dict[typing.Type[UnitType], int]
        tasks_scramble_counts = {}  # type: typing.Dict[typing.Type[Task], int]
        tasks_clients_counts = {}  # type: typing.Dict[typing.Type[Task], int]

        def dampen_count(unit_type, count: int) -> int:
            nonlocal units_scramble_counts
            total_count = self.base.total_units_of_type(unit_type)

            total_scrambled = units_scramble_counts.get(unit_type, 0)
            dampened_value = count if count + total_scrambled < total_count else total_count - total_scrambled
            units_scramble_counts[unit_type] = units_scramble_counts.get(unit_type, 0) + dampened_value

            return dampened_value

        for task_type, dict in self.scramble_entries.items():
            for unit_type, (count_entry, clients_entry) in dict.items():
                try:
                    count = int(count_entry.value())
                except:
                    count = 0

                try:
                    clients_count = int(clients_entry and clients_entry.value() or 0)
                except:
                    clients_count = 0

                dampened_count = dampen_count(unit_type, count)
                tasks_clients_counts[task_type] = tasks_clients_counts.get(task_type, 0) + clients_count
                tasks_scramble_counts[task_type] = tasks_scramble_counts.get(task_type, 0) + dampened_count

                flights[task_type][unit_type] = dampened_count, clients_count

        for task in self.gameEvent.ai_banned_tasks:
            if tasks_clients_counts.get(task, 0) == 0 and tasks_scramble_counts.get(task, 0) > 0:
                self.showErrorMessage("Need at least one player in flight {}".format(self.gameEvent.flight_name(task)))
                return

        for task in self.gameEvent.player_banned_tasks:
            if tasks_clients_counts.get(task, 0) != 0:
                self.showErrorMessage("Players are not allowed on flight {}".format(self.gameEvent.flight_name(task)))
                return

        if self.game.is_player_attack(self.gameEvent):
            if isinstance(self.gameEvent, FrontlineAttackEvent):
                if self.base.total_armor == 0:
                    self.showErrorMessage("No ground vehicles available to attack!")
                    return

            self.gameEvent.player_attacking(flights)
        else:
            if isinstance(self.gameEvent, FrontlineAttackEvent):
                if self.gameEvent.to_cp.base.total_armor == 0:
                    self.showErrorMessage("No ground vehicles available to defend!")
                    return

            self.gameEvent.player_defending(flights)

        self.game.initiate_event(self.gameEvent)

        waiting = QWaitingForMissionResultWindow(self.gameEvent, self.game)
        waiting.show()

        self.close()

    def showErrorMessage(self, text):
        about = QMessageBox()
        about.setWindowTitle("Error")
        about.setIcon(QMessageBox.Icon.Critical)
        about.setText(text)
        about.exec_()

    def on_departure_cp_changed(self):

        selectedBase = self.depart_from.itemData(self.depart_from.currentIndex())

        for i, cp in enumerate([b for b in self.game.theater.controlpoints if b.captured]):
            if cp.name == selectedBase.name:
                self.base = cp.base
                self.playerFromCp = cp
                break

        # Clear current selection
        self.scramble_entries = {k: {} for k in self.gameEvent.tasks}

        # Clear the grid layout
        for i in reversed(range(self.gridLayout.count())):
            self.gridLayout.itemAt(i).widget().setParent(None)

        # Rebuild the grid layout, so that it correspond to the newly selected CP
        self.initUnitRows()
class ChannelArithmeticDialog(ieb.ImarisExtensionBase):
    """
    Channel Arithmetic and Beyond
    =============================
    `View on GitHub <https://github.com/niaid/imaris_extensions>`_

    This program enables one to specify arithmetic expressions which are used to
    create new channels. The basic arithmetic operations are supported: +,-,*,/,**.
    More advanced operations that run short `SimpleITK <https://simpleitk.org/>`_
    code snippets are also supported.

    Channels are referenced using square brackets and the channel index, starting
    at **zero**. To apply an expression to all channels, use the channel index 'i'.

    When creating a single new channel, the arithmetic expression consists of literal
    channel numbers, one can select a name and color for the new channel. When
    creating multiple new channels, the arithmetic expression is applied to all channels,
    the postfix '_modified' is appended to the original channel names and the original
    color is copied over. Note that for all channels created by the program the
    channel description will include the arithmetic expression used to create that
    channel. This transparently supports your efforts to conduct reproducible
    research.

    Because an Imaris image has a specific pixel type (8, 16, 32 bit unsigned integer
    and 32 bit floating point) all computations are performed using a 32 bit floating
    point representation and then clamped to the range of the image's pixel type.

    The program allows you to use the same expression on multiple files. In this
    case literal channel values are limited by the number of shared channels. Thus,
    if working with two files one with three channels and one with four channels,
    the valid literal channel values are limited to 0, 1, 2. We cannot use 3 as it does not
    exist in all files. On the other hand, if our autofluorescence channel is one
    of these channels, e.g. channel 0, we can subtract it from all channels in
    both files, `[i]-[0]`.

    Basic Examples
    --------------

    Multiply channels zero and three:

    .. code-block:: Python

      [0]*[3]

    Multiply channels zero and three and subtract the result from channel four:

    .. code-block:: Python

      [4] - ([0]*[3])

    Duplicate all channels:

    .. code-block:: Python

      [i]

    Subtract channel zero from all channels:

    .. code-block:: Python

      [i]-[0]


    Advanced Examples
    -----------------

    Threshold channel one using a value of 100, resulting image is binary
    values in {0,1}:

    .. code-block:: Python

      [1]>100

    Threshold a specific channel to create a binary result using the Otsu
    filter:

    .. code-block:: Python

      sitk.OtsuThreshold([1], 0, 1)

    Threshold a specific channel retaining the values above the threshold:

    .. code-block:: Python

      sitk.Cast([1]>100, sitk.sitkFloat32)*[1]

    Threshold a specific channel, get all connected components, then
    sort the components according to size, discarding those smaller than a minimum
    size and create a binary mask corresponding to the largest component, which is
    the first label(second largest component label is 2 etc.)

    .. code-block:: Python

      sitk.RelabelComponent(sitk.ConnectedComponent([1]>100), minimumObjectSize = 50)==1

    Create a binary mask representing the colocalization of two channels,
    intensity values below 20 are considred noise:

    .. code-block:: Python

      ([1]>20)*([2]>20)

    Create a binary mask representing the colocalization of two channels.
    We are interested in all pixels in channel 2 that have a value above 20
    and that are less than 1.0um away from pixels in channel 1 that have a value
    above 100 (**note**: this operation yields different results when run using
    a slice-by-slice approach vs. a volumetric approach):

    .. code-block:: Python

        (sitk.Cast([2]>20, sitk.sitkFloat32) *
         sitk.Abs(sitk.SignedMaurerDistanceMap([1]>100, insideIsPositive=False, squaredDistance=False, useImageSpacing=True)))<=1.0

    Create a binary mask using thresholding and then perform morphological
    closing (dilation followed by erosion) with distance maps, useful
    for filling holes:

    .. code-block:: Python

      sitk.SignedMaurerDistanceMap(sitk.SignedMaurerDistanceMap([1]>100, insideIsPositive=False, squaredDistance=False, useImageSpacing=True) < 1.0, insideIsPositive=False, squaredDistance=False, useImageSpacing=True)<-1.0

    Create a binary mask using thresholding and then perform morphological
    opening (erosion followed by dilation) with distance maps, useful
    for removing small islands:

    .. code-block:: Python

      sitk.SignedMaurerDistanceMap(sitk.SignedMaurerDistanceMap([1]>100, insideIsPositive=False, squaredDistance=False, useImageSpacing=True) < -0.2, insideIsPositive=False, squaredDistance=False, useImageSpacing=True)<0.2
    """  # noqa

    def __init__(self):
        super(ChannelArithmeticDialog, self).__init__()
        # Channel indexes in the arithmetic calculator are denoted using a
        # regular expression: one or more digits in square brackets (e.g. [1234]).
        # First digit is zero and nothing afterwards or first digit is in [1-9] and
        # there are possibly more digits afterwards.
        # Starting index is zero.
        self.channel_pattern = re.compile(r"\[(0|[1-9]\d*)\]")

        # Use QT's global threadpool, documentation says: "This global thread pool
        # automatically maintains an optimal number of threads based on the
        # number of cores in the CPU."
        self.threadpool = QThreadPool.globalInstance()

        # Configure the help dialog.
        self.help_dialog = HelpDialog(w=700, h=500)
        self.help_dialog.setWindowTitle("Channel Arithmetic Help")
        self.help_dialog.set_rst_text(
            inspect.getdoc(self), pygments_css_file_name="pygments_dark.css")

        self.__create_gui()
        self.setWindowTitle("Channel Arithmetic")
        self.processing_error = False

        self.show()

    def __create_gui(self):
        menu_bar = self.menuBar()
        # Force menubar to be displayed in the application on OSX/Linux, otherwise it
        # is displayed in the system menubar
        menu_bar.setNativeMenuBar(False)
        self.help_button = QPushButton("Help")
        self.help_button.clicked.connect(self.help_dialog.show)
        menu_bar.setCornerWidget(self.help_button, Qt.TopLeftCorner)

        central_widget = QWidget(self)
        gui_layout = QVBoxLayout()
        central_widget.setLayout(gui_layout)
        self.setCentralWidget(central_widget)

        select_files_widget = self.__create_select_files_widget()
        arithmetic_widget = self.__create_arithmetic_widget()

        self.stack = QStackedWidget(self)
        self.stack.addWidget(select_files_widget)
        self.stack.addWidget(arithmetic_widget)
        gui_layout.addWidget(self.stack)

        self.status_bar = self.statusBar()

    def closeEvent(self, event):
        """
        Override the closeEvent method so that clicking the 'x' button also
        closes all of the dialogs.
        """
        self.help_dialog.close()
        event.accept()

    def __create_arithmetic_widget(self):
        wid = QWidget(self)
        arithmetic_layout = QVBoxLayout()
        wid.setLayout(arithmetic_layout)

        self.valid_indexes_label = QLabel("")
        arithmetic_layout.addWidget(self.valid_indexes_label)

        layout = QHBoxLayout()
        layout.setAlignment(Qt.AlignLeft)
        layout.addWidget(QLabel("Enter new channel arithmetic expression:"))
        arithmetic_layout.addLayout(layout)

        self.arithmetic_expression_text_edit = QTextEdit()
        arithmetic_layout.addWidget(self.arithmetic_expression_text_edit)

        self.slice_by_slice_checkbox = QCheckBox(
            "Slice by slice (smaller memory footprint).")
        arithmetic_layout.addWidget(self.slice_by_slice_checkbox)

        layout = QHBoxLayout()
        layout.addWidget(QLabel("New channel name:"))
        self.new_channel_name_line_edit = QLineEdit()
        layout.addWidget(self.new_channel_name_line_edit)
        arithmetic_layout.addLayout(layout)

        layout = QHBoxLayout()
        layout.addWidget(QLabel("New channel color:"))
        self.new_channel_color_button = QPushButton()
        self.new_channel_color_button.clicked.connect(
            self.__select_color_callback)
        layout.addWidget(self.new_channel_color_button)
        arithmetic_layout.addLayout(layout)

        self.apply_button = QPushButton("Apply")
        self.apply_button.clicked.connect(self.__channel_arithmetic_wrapper)
        arithmetic_layout.addWidget(self.apply_button)

        progress_wid = QWidget()
        self.progress_grid_layout = QGridLayout()
        progress_wid.setLayout(self.progress_grid_layout)
        scroll_area = QScrollArea()
        scroll_area.setWidget(progress_wid)
        scroll_area.setWidgetResizable(True)
        arithmetic_layout.addWidget(scroll_area)

        layout = QHBoxLayout()
        layout.setAlignment(Qt.AlignLeft)
        self.processing_prev_button = QPushButton("Prev")
        self.processing_prev_button.clicked.connect(
            lambda: self.stack.setCurrentIndex(0))
        layout.addWidget(self.processing_prev_button)
        arithmetic_layout.addLayout(layout)

        return wid

    def __configure_and_show_arithmetic_widget(self):
        file_names = self.input_files_edit.toPlainText().split("\n")
        num_channels = []
        problematic_images = []
        for file_name in file_names:
            try:
                meta_data = sio.read_metadata(file_name)
                num_channels.append(len(meta_data["channels_information"]))
            except Exception:
                problematic_images.append(file_name)
        if problematic_images:
            self._error_function(
                "Problem encountered reading the following file(s):\n" +
                "\n".join(problematic_images))
            return
        self.max_channel_index = min(num_channels) - 1
        self.valid_indexes_label.setText(
            f"Valid channel indexes: 0...{self.max_channel_index}, i")
        self.arithmetic_expression_text_edit.clear()
        self.slice_by_slice_checkbox.setChecked(False)
        self.new_channel_name_line_edit.clear()

        # Remove all widgets from layout, done in reverse order because
        # removing from the begining shifts the rest of the items
        for i in reversed(range(self.progress_grid_layout.count())):
            self.progress_grid_layout.itemAt(i).widget().setParent(None)

        for i, file_name in enumerate(file_names):
            self.progress_grid_layout.addWidget(
                QLabel(os.path.basename(file_name)), i, 0)
            progress_bar = QProgressBar()
            progress_bar.setMaximum(100)
            self.progress_grid_layout.addWidget(progress_bar, i, 1)

        self.stack.setCurrentIndex(1)

    def __create_select_files_widget(self):
        wid = QWidget()
        input_layout = QVBoxLayout()
        wid.setLayout(input_layout)

        layout = QHBoxLayout()
        layout.addWidget(QLabel("File names:"))
        layout.setAlignment(Qt.AlignLeft)
        button = QPushButton("Browse")
        button.setToolTip("Select input files for arithmetic operation.")
        button.clicked.connect(self.__browse_select_input_callback)
        layout.addWidget(button)
        input_layout.addLayout(layout)

        self.input_files_edit = QTextEdit()
        self.input_files_edit.setReadOnly(True)
        input_layout.addWidget(self.input_files_edit)

        layout = QHBoxLayout()
        layout.setAlignment(Qt.AlignRight)
        self.input_files_next_button = QPushButton("Next")
        self.input_files_next_button.setEnabled(False)
        self.input_files_next_button.clicked.connect(
            self.__configure_and_show_arithmetic_widget)
        layout.addWidget(self.input_files_next_button)
        input_layout.addLayout(layout)

        return wid

    def __browse_select_input_callback(self):
        file_names, _ = QFileDialog.getOpenFileNames(
            self,
            "QFileDialog.getOpenFileNames()",
            "",
            "Imaris Images (*.ims);;All Files (*)",
        )
        if file_names:
            self.input_files_edit.setText("\n".join(file_names))
            self.input_files_next_button.setEnabled(True)

    def __select_color_callback(self):
        color = QColorDialog.getColor()
        if color.isValid():
            self.new_channel_color_button.setStyleSheet(
                f"background-color :rgb({color.red()},{color.green()},{color.blue()})"
            )

    def __channel_arithmetic_wrapper(self):
        # Get the arithmetic expression after removing all whitespace
        arithmetic_expression = "".join(
            self.arithmetic_expression_text_edit.toPlainText().split())
        color = self.new_channel_color_button.palette().button().color()

        if arithmetic_expression:
            # Get the explicit channel indexes that appear in the expression and
            # check that they are in the valid range.
            channel_indexes = re.findall(self.channel_pattern,
                                         arithmetic_expression)
            invalid_channels = [
                ci for ci in channel_indexes
                if int(ci) not in range(self.max_channel_index + 1)
            ]
            if invalid_channels:
                self._error_function(
                    "The following channels specified in the arithmetic expression"
                    +
                    f" are outside the valid range [0,{self.max_channel_index}]: "
                    + ", ".join(invalid_channels))
                return

            # Disable the UI interaction during computation
            self.arithmetic_expression_text_edit.setReadOnly(True)
            self.slice_by_slice_checkbox.setEnabled(False)
            self.new_channel_name_line_edit.setReadOnly(True)
            self.new_channel_color_button.setEnabled(False)
            self.apply_button.setEnabled(False)
            self.processing_prev_button.setEnabled(False)

            QApplication.setOverrideCursor(Qt.WaitCursor)
            file_names = self.input_files_edit.toPlainText().split("\n")
            self.num_threads_left = len(file_names)
            for i, input_file_name in enumerate(file_names):
                # Configure and perform computation in another thread.
                arithmetic_calculator = ArithmeticCalculator(
                    self.channel_pattern)
                arithmetic_calculator.signals.finished.connect(
                    self.__arithmetic_finished)
                arithmetic_calculator.signals.processing_error.connect(
                    self._processing_error_function)
                arithmetic_calculator.signals.progress_signal.connect(
                    self.progress_grid_layout.itemAtPosition(
                        i, 1).widget().setValue)
                arithmetic_calculator.signals.update_state_signal.connect(
                    self.status_bar.showMessage)
                arithmetic_calculator.input_file_name = input_file_name
                arithmetic_calculator.arithmetic_expression = arithmetic_expression
                arithmetic_calculator.new_channel_color = [
                    color.red() / 255.0,
                    color.green() / 255.0,
                    color.blue() / 255.0,
                ]
                arithmetic_calculator.new_channel_alpha = color.alpha() / 255.0
                arithmetic_calculator.new_channel_name = (
                    self.new_channel_name_line_edit.text().strip())
                arithmetic_calculator.slice_by_slice = (
                    self.slice_by_slice_checkbox.isChecked())
                self.threadpool.start(arithmetic_calculator)
        else:
            self._error_function(
                "No action taken: arithmetic expression not set.")

    def __arithmetic_finished(self):
        self.num_threads_left = self.num_threads_left - 1
        if self.num_threads_left == 0:
            QApplication.restoreOverrideCursor()
            self.status_bar.clearMessage()
            for i in range(self.progress_grid_layout.rowCount()):
                self.progress_grid_layout.itemAtPosition(
                    i, 1).widget().setValue(0)
            # Enable the UI interaction after computation
            self.arithmetic_expression_text_edit.setReadOnly(False)
            self.slice_by_slice_checkbox.setEnabled(True)
            self.new_channel_name_line_edit.setReadOnly(False)
            self.new_channel_color_button.setEnabled(True)
            self.apply_button.setEnabled(True)
            self.processing_prev_button.setEnabled(True)

            # Inform the user that the calculations completed. If processing errors
            # occured then the desired operation may not have happened, but the
            # calculation was completed.
            QMessageBox().information(self, "Message",
                                      "Calculation completed.")
            self.processing_error = False
Example #8
0
class QWaitingForMissionResultWindow(QDialog):
    def __init__(self, gameEvent: Event, game: Game,
                 unit_map: UnitMap) -> None:
        super(QWaitingForMissionResultWindow, self).__init__()
        self.setModal(True)
        self.gameEvent = gameEvent
        self.game = game
        self.unit_map = unit_map
        self.setWindowTitle("Waiting for mission completion.")
        self.setWindowIcon(QIcon("./resources/icon.png"))
        self.setMinimumHeight(570)

        self.initUi()
        DebriefingFileWrittenSignal.get_instance().debriefingReceived.connect(
            self.updateLayout)
        self.wait_thread = wait_for_debriefing(
            lambda debriefing: self.on_debriefing_update(debriefing),
            self.game, self.unit_map)

    def initUi(self):
        self.layout = QGridLayout()

        header = QLabel(self)
        header.setGeometry(0, 0, 655, 106)
        pixmap = QPixmap("./resources/ui/conflict.png")
        header.setPixmap(pixmap)
        self.layout.addWidget(header, 0, 0)

        self.gridLayout = QGridLayout()

        jinja = Environment(
            loader=FileSystemLoader("resources/ui/templates"),
            autoescape=select_autoescape(
                disabled_extensions=("", ),
                default_for_string=True,
                default=True,
            ),
            trim_blocks=True,
            lstrip_blocks=True,
        )
        self.instructions_text = QTextBrowser()
        self.instructions_text.setHtml(
            jinja.get_template("mission_start_EN.j2").render())
        self.instructions_text.setOpenExternalLinks(True)
        self.gridLayout.addWidget(self.instructions_text, 1, 0)

        progress = QLabel("")
        progress.setAlignment(QtCore.Qt.AlignCenter)
        progress_bar = QMovie("./resources/ui/loader.gif")
        progress.setMovie(progress_bar)

        self.actions = QGroupBox("Actions :")
        self.actions_layout = QHBoxLayout()
        self.actions.setLayout(self.actions_layout)

        self.manually_submit = QPushButton("Manually Submit [Advanced users]")
        self.manually_submit.clicked.connect(self.submit_manually)
        self.actions_layout.addWidget(self.manually_submit)
        self.cancel = QPushButton("Abort mission")
        self.cancel.clicked.connect(self.close)
        self.actions_layout.addWidget(self.cancel)
        self.gridLayout.addWidget(self.actions, 2, 0)

        self.actions2 = QGroupBox("Actions :")
        self.actions2_layout = QHBoxLayout()
        self.actions2.setLayout(self.actions2_layout)
        self.manually_submit2 = QPushButton("Manually Submit [Advanced users]")
        self.manually_submit2.clicked.connect(self.submit_manually)
        self.actions2_layout.addWidget(self.manually_submit2)
        self.cancel2 = QPushButton("Abort mission")
        self.cancel2.clicked.connect(self.close)
        self.actions2_layout.addWidget(self.cancel2)
        self.proceed = QPushButton("Accept results")
        self.proceed.setProperty("style", "btn-success")
        self.proceed.clicked.connect(self.process_debriefing)
        self.actions2_layout.addWidget(self.proceed)

        progress_bar.start()
        self.layout.addLayout(self.gridLayout, 1, 0)
        self.setLayout(self.layout)

    def updateLayout(self, debriefing: Debriefing) -> None:
        updateBox = QGroupBox("Mission status")
        updateLayout = QGridLayout()
        updateBox.setLayout(updateLayout)
        self.debriefing = debriefing

        updateLayout.addWidget(QLabel("<b>Aircraft destroyed</b>"), 0, 0)
        updateLayout.addWidget(
            QLabel(str(len(list(debriefing.air_losses.losses)))), 0, 1)

        updateLayout.addWidget(QLabel("<b>Front line units destroyed</b>"), 1,
                               0)
        updateLayout.addWidget(
            QLabel(str(len(list(debriefing.front_line_losses)))), 1, 1)

        updateLayout.addWidget(QLabel("<b>Other ground units destroyed</b>"),
                               2, 0)
        updateLayout.addWidget(
            QLabel(str(len(list(debriefing.ground_object_losses)))), 2, 1)

        updateLayout.addWidget(QLabel("<b>Buildings destroyed</b>"), 3, 0)
        updateLayout.addWidget(
            QLabel(str(len(list(debriefing.building_losses)))), 3, 1)

        updateLayout.addWidget(QLabel("<b>Base Capture Events</b>"), 4, 0)
        updateLayout.addWidget(
            QLabel(str(len(debriefing.base_capture_events))), 4, 1)

        # Clear previous content of the window
        for i in reversed(range(self.gridLayout.count())):
            try:
                self.gridLayout.itemAt(i).widget().setParent(None)
            except:
                logging.exception("Failed to clear window")

        # Set new window content
        self.gridLayout.addWidget(updateBox, 0, 0)

        if not debriefing.state_data.mission_ended:
            self.gridLayout.addWidget(QLabel("<b>Mission is being played</b>"),
                                      1, 0)
            self.gridLayout.addWidget(self.actions, 2, 0)
        else:
            self.gridLayout.addWidget(QLabel("<b>Mission is over</b>"), 1, 0)
            self.gridLayout.addWidget(self.actions2, 2, 0)

    def on_debriefing_update(self, debriefing: Debriefing) -> None:
        try:
            logging.info("On Debriefing update")
            logging.debug(debriefing)
            DebriefingFileWrittenSignal.get_instance().sendDebriefing(
                debriefing)
        except Exception:
            logging.exception("Got an error while sending debriefing")
        self.wait_thread = wait_for_debriefing(
            lambda d: self.on_debriefing_update(d), self.game, self.unit_map)

    def process_debriefing(self):
        self.game.finish_event(event=self.gameEvent,
                               debriefing=self.debriefing)
        self.game.pass_turn()

        GameUpdateSignal.get_instance().sendDebriefing(self.debriefing)
        GameUpdateSignal.get_instance().updateGame(self.game)
        self.close()

    def debriefing_directory_location(self) -> str:
        return os.path.join(base_path(), "liberation_debriefings")

    def closeEvent(self, evt):
        super(QWaitingForMissionResultWindow, self).closeEvent(evt)
        if self.wait_thread is not None:
            self.wait_thread.stop()

    def submit_manually(self):
        file = QFileDialog.getOpenFileName(self,
                                           "Select game file to open",
                                           filter="json(*.json)",
                                           dir=".")
        print(file)
        try:
            with open(file[0], "r") as json_file:
                json_data = json.load(json_file)
                json_data["mission_ended"] = True
                debriefing = Debriefing(json_data, self.game, self.unit_map)
                self.on_debriefing_update(debriefing)
        except Exception as e:
            logging.error(e)
            msg = QMessageBox()
            msg.setIcon(QMessageBox.Information)
            msg.setText("Invalid file : " + file[0])
            msg.setWindowTitle("Invalid file.")
            msg.setStandardButtons(QMessageBox.Ok)
            msg.setWindowFlags(Qt.WindowStaysOnTopHint)
            msg.exec_()
            return
Example #9
0
class ObjectToolBox(QWidget):
    object_icon_clicked: SignalInstance = Signal(ObjectIcon)
    object_placed: SignalInstance = Signal(ObjectIcon)

    def __init__(self, parent: Optional[QWidget] = None):
        super(ObjectToolBox, self).__init__(parent)

        self.setSizePolicy(QSizePolicy.MinimumExpanding,
                           QSizePolicy.MinimumExpanding)

        self._layout = QGridLayout(self)
        self._layout.setAlignment(Qt.AlignCenter)

        self._layout.setAlignment(Qt.AlignHCenter)

    def add_object(self,
                   level_object: Union[EnemyItem, LevelObject],
                   index: int = -1):
        icon = ObjectIcon(level_object)

        icon.clicked.connect(self._on_icon_clicked)
        icon.object_placed.connect(lambda: self.object_placed.emit(icon))

        if index == -1:
            index = self._layout.count()

        self._layout.addWidget(icon, index // 2, index % 2)

    def add_from_object_set(self,
                            object_set_index: int,
                            graphic_set_index: int = -1):
        if graphic_set_index == -1:
            graphic_set_index = object_set_index

        factory = LevelObjectFactory(object_set_index,
                                     graphic_set_index,
                                     0, [],
                                     vertical_level=False,
                                     size_minimal=True)

        object_ids = list(range(0x00, 0x10)) + list(
            range(0x10, MAX_ID_VALUE, 0x10))

        for domain, obj_index in product(range(MAX_DOMAIN + 1), object_ids):
            level_object = factory.from_properties(domain=domain,
                                                   object_index=obj_index,
                                                   x=0,
                                                   y=0,
                                                   length=None,
                                                   index=0)

            if not isinstance(level_object,
                              LevelObject) or level_object.name in [
                                  "MSG_NOTHING", "MSG_CRASH"
                              ]:
                continue

            self.add_object(level_object)

    def add_from_enemy_set(self, object_set_index: int):
        factory = EnemyItemFactory(object_set_index, 0)

        for obj_index in range(MAX_ENEMY_ITEM_ID + 1):
            enemy_item = factory.from_properties(obj_index, x=0, y=0)

            if enemy_item.name in ["MSG_NOTHING", "MSG_CRASH"]:
                continue

            self.add_object(enemy_item)

    def clear(self):
        self._extract_objects()

    def _on_icon_clicked(self):
        self.object_icon_clicked.emit(self.sender())

    @property
    def draw_background_color(self):
        return self._layout.itemAt(0).draw_background_color

    @draw_background_color.setter
    def draw_background_color(self, value):
        for index in range(self._layout.count()):
            self._layout.itemAt(index).draw_background_color = value

    def has_object(self, level_object):
        return self.index_of_object(level_object) != -1

    def index_of_object(self, level_object):
        for index in range(self._layout.count()):
            if self._layout.itemAtPosition(index // 2, index %
                                           2).widget().object == level_object:
                return index
        else:
            return -1

    def _extract_objects(self):
        objects = []

        while True:
            item = self._layout.takeAt(0)

            if item is None:
                break
            else:
                objects.append(item.widget().object)
                item.widget().deleteLater()

        return objects

    def place_at_front(self, level_object):
        objects = self._extract_objects()

        if level_object in objects:
            objects.remove(level_object)

        objects.insert(0, level_object)

        assert self._layout.count() == 0

        for obj in objects:
            self.add_object(obj)
Example #10
0
class CodePreview_Widget(QWidget):
    def __init__(self):
        super(CodePreview_Widget, self).__init__()

        self.text_edit = CodePreview_TextEdit()
        self.node_instance = None
        self.buttons_obj_dict = {}
        self.active_class_index = -1
        self.edited_codes = {}

        settings_layout = QHBoxLayout()

        info_and_SH_layout = QVBoxLayout()

        # info label
        info_label = QLabel('Click on edit for more info!')
        info_label.setFont(QFont('Poppins', 8))
        info_and_SH_layout.addWidget(info_label)

        # syntax highlighting
        self.syntax_highlighting_check_box = QCheckBox('syntax highlighting (alpha)')
        self.syntax_highlighting_check_box.toggled.connect(self.syntax_highlighting_toggled)
        self.syntax_highlighting_check_box.setChecked(True)
        info_and_SH_layout.addWidget(self.syntax_highlighting_check_box)

        settings_layout.addLayout(info_and_SH_layout)

        # class radio buttons widget
        self.class_selection_layout = QGridLayout()

        settings_layout.addLayout(self.class_selection_layout)
        settings_layout.setAlignment(self.class_selection_layout, Qt.AlignRight)

        # edit source code buttons
        edit_buttons_layout = QVBoxLayout()
        self.edit_code_button = QPushButton('edit')
        self.edit_code_button.setMaximumWidth(100)
        self.edit_code_button.clicked.connect(self.edit_code_button_clicked)
        self.override_code_button = QPushButton('override')
        self.override_code_button.setMaximumWidth(100)
        self.override_code_button.setEnabled(False)
        self.override_code_button.clicked.connect(self.override_code_button_clicked)
        self.reset_code_button = QPushButton('reset')
        self.reset_code_button.setMaximumWidth(206)
        self.reset_code_button.setEnabled(False)
        self.reset_code_button.clicked.connect(self.reset_code_button_clicked)
        edit_buttons_top_layout = QHBoxLayout()
        edit_buttons_top_layout.addWidget(self.edit_code_button)
        edit_buttons_top_layout.addWidget(self.override_code_button)
        edit_buttons_layout.addLayout(edit_buttons_top_layout)
        edit_buttons_layout.addWidget(self.reset_code_button)

        settings_layout.addLayout(edit_buttons_layout)


        main_layout = QVBoxLayout()
        main_layout.addLayout(settings_layout)
        main_layout.addWidget(self.text_edit)
        self.setLayout(main_layout)

        self.set_new_NI(None)

    def set_new_NI(self, ni):
        self.disable_editing()

        self.rebuild_class_selection(ni)
        self.update_edit_status()

        self.node_instance = ni

        if ni is None:  # no NI selected
            self.text_edit.set_code('')
            self.edit_code_button.setEnabled(False)
            self.override_code_button.setEnabled(False)
            self.reset_code_button.setEnabled(False)
            return
        self.edit_code_button.setEnabled(True)

        self.update_code()

    def update_code(self):

        self.disable_editing()

        if self.active_class_index == -1 or self.node_instance is None:
            return

        if self.get_current_code_obj() not in self.edited_codes:
            self.text_edit.set_code(inspect.getsource(self.get_current_code_class()))
            self.reset_code_button.setEnabled(False)
        else:
            self.text_edit.set_code(self.edited_codes[self.get_current_code_obj()])
            self.reset_code_button.setEnabled(True)

    def get_current_code_class(self):
        return self.get_current_code_obj().__class__

    def get_current_code_obj(self):
        return list(self.buttons_obj_dict.values())[self.active_class_index]

    def rebuild_class_selection(self, obj):
        # clear layout
        for i in range(self.class_selection_layout.count()):
            item = self.class_selection_layout.itemAt(0)
            widget = item.widget()
            widget.hide()
            self.class_selection_layout.removeItem(item)

        self.buttons_obj_dict = {}
        self.active_class_index = -1

        if find_type_in_object(obj, NodeInstance):
            # NI class
            node_inst_class_RB = QRadioButton('NodeInstance')
            node_inst_class_RB.toggled.connect(self.class_RB_toggled)
            self.buttons_obj_dict[node_inst_class_RB] = obj
            self.class_selection_layout.addWidget(node_inst_class_RB, 0, 0)

            # main_widget class
            if obj.main_widget is not None:
                main_widget_class_RB = QRadioButton('MainWidget')
                main_widget_class_RB.toggled.connect(self.class_RB_toggled)
                self.buttons_obj_dict[main_widget_class_RB] = obj.main_widget
                self.class_selection_layout.addWidget(main_widget_class_RB, 1, 0)

            # data input widgets
            row_count = 0
            for inp in obj.inputs:
                if inp.widget is not None:
                    inp_widget_class_RB = QRadioButton('Input '+str(obj.inputs.index(inp)))
                    inp_widget_class_RB.toggled.connect(self.class_RB_toggled)
                    self.buttons_obj_dict[inp_widget_class_RB] = inp.widget
                    self.class_selection_layout.addWidget(inp_widget_class_RB, row_count, 1)
                    row_count += 1

            node_inst_class_RB.setChecked(True)

    def update_edit_status(self):
        for o in list(self.buttons_obj_dict.keys()):
            if self.edited_codes.keys().__contains__(self.buttons_obj_dict[o]):
                o.setStyleSheet('color: #3B9CD9;')
                f = o.font()
                f.setBold(True)
                o.setFont(f)
            else:
                o.setStyleSheet('color: white;')
                f = o.font()
                f.setBold(False)
                o.setFont(f)

    def class_RB_toggled(self, checked):
        if checked:
            self.active_class_index = list(self.buttons_obj_dict.keys()).index(self.sender())
            self.update_code()

    def syntax_highlighting_toggled(self):
        if self.syntax_highlighting_check_box.isChecked():
            self.text_edit.enable_highlighting()
        else:
            self.text_edit.disable_highlighting()

    def edit_code_button_clicked(self):
        info_dialog = EditSourceCode_Dialog(self)
        accepted = info_dialog.exec_()
        if accepted:
            self.enable_editing()

    def enable_editing(self):
        self.text_edit.enable_editing()
        self.override_code_button.setEnabled(True)

    def disable_editing(self):
        self.text_edit.disable_editing()
        self.override_code_button.setEnabled(False)

    def override_code_button_clicked(self):
        new_code = self.text_edit.get_code()
        override_code(self.get_current_code_obj(), new_code)
        self.disable_editing()

        self.edited_codes[self.get_current_code_obj()] = new_code
        self.reset_code_button.setEnabled(True)
        self.update_edit_status()

    def reset_code_button_clicked(self):
        code = inspect.getsource(self.get_current_code_class())
        override_code(self.get_current_code_obj(), code)
        del self.edited_codes[self.get_current_code_obj()]
        self.update_code()
        self.update_edit_status()
Example #11
0
class CommandWidget(TabWidgetExtension, QWidget):
    """Output for running queue"""

    # log state
    __log = False
    insertTextSignal = Signal(str, dict)
    updateCommandSignal = Signal(str)
    cliButtonsSateSignal = Signal(bool)
    cliValidateSignal = Signal(bool)
    resetSignal = Signal()

    def __init__(self, parent=None, proxyModel=None, controlQueue=None, log=None):
        super(CommandWidget, self).__init__(parent=parent, tabWidgetChild=self)

        self.__log = log
        self.__output = None
        self.__rename = None
        self.__tab = None

        # self.oCommand = MKVCommand()
        self.algorithm = None
        self.oCommand = MKVCommandParser()
        self.controlQueue = controlQueue
        self.parent = parent
        self.proxyModel = proxyModel
        self.model = proxyModel.sourceModel()
        self.outputWindow = QOutputTextWidget(self)
        self.log = log

        self._initControls()
        self._initUI()
        self._initHelper()

    def _initControls(self):
        #
        # command line
        #
        self.frmCmdLine = QFormLayout()
        btnPasteClipboard = QPushButtonWidget(
            Text.txt0164,
            function=lambda: qtRunFunctionInThread(self.pasteClipboard),
            margins="  ",
            toolTip=Text.txt0165,
        )
        self.cmdLine = QLineEdit()
        self.cmdLine.setValidator(
            ValidateCommand(self, self.cliValidateSignal, log=self.log)
        )
        self.frmCmdLine.addRow(btnPasteClipboard, self.cmdLine)
        self.frmCmdLine.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
        self.command = QWidget()
        self.command.setLayout(self.frmCmdLine)

        #
        # Button group definition
        #
        self.btnGroup = QGroupBox()
        self.btnGrid = QGridLayout()

        btnAddCommand = QPushButtonWidget(
            Text.txt0160,
            function=lambda: self.addCommand(JobStatus.Waiting),
            margins="  ",
            toolTip=Text.txt0161,
        )
        btnRename = QPushButtonWidget(
            Text.txt0182,
            function=self.parent.renameWidget.setAsCurrentTab,
            margins="  ",
            toolTip=Text.txt0183,
        )
        btnAddQueue = QPushButtonWidget(
            Text.txt0166,
            function=lambda: self.addCommand(JobStatus.AddToQueue),
            margins="  ",
            toolTip=Text.txt0167,
        )
        btnStartQueue = QPushButtonWidget(
            Text.txt0126,
            function=self.parent.jobsQueue.run,
            margins="  ",
            toolTip=Text.txt0169,
        )
        btnAnalysis = QPushButtonWidget(
            Text.txt0170,
            function=lambda: qtRunFunctionInThread(
                runAnalysis,
                command=self.cmdLine.text(),
                output=self.output,
                log=self.log,
            ),
            margins="  ",
            toolTip=Text.txt0171,
        )
        btnShowCommands = QPushButtonWidget(
            Text.txt0172,
            function=lambda: qtRunFunctionInThread(
                showCommands,
                output=self.output,
                command=self.cmdLine.text(),
                oCommand=self.oCommand,
                log=self.log,
            ),
            margins="  ",
            toolTip=Text.txt0173,
        )
        btnCheckFiles = QPushButtonWidget(
            Text.txt0174,
            function=lambda: qtRunFunctionInThread(
                checkFiles,
                output=self.output,
                command=self.cmdLine.text(),
                oCommand=self.oCommand,
                log=self.log,
            ),
            margins="  ",
            toolTip=Text.txt0175,
        )
        btnClear = QPushButtonWidget(
            Text.txt0176,
            function=self.clearOutputWindow,
            margins="  ",
            toolTip=Text.txt0177,
        )
        btnReset = QPushButtonWidget(
            Text.txt0178,
            function=self.reset,
            margins="  ",
            toolTip=Text.txt0179,
        )

        self.btnGrid.addWidget(btnAddCommand, 0, 0)
        self.btnGrid.addWidget(btnRename, 0, 1)
        self.btnGrid.addWidget(btnAddQueue, 1, 0)
        self.btnGrid.addWidget(btnStartQueue, 1, 1)
        self.btnGrid.addWidget(HorizontalLine(), 2, 0, 1, 2)
        self.btnGrid.addWidget(btnAnalysis, 3, 0)
        self.btnGrid.addWidget(btnShowCommands, 3, 1)
        self.btnGrid.addWidget(btnCheckFiles, 4, 0)
        self.btnGrid.addWidget(HorizontalLine(), 5, 0, 1, 2)
        self.btnGrid.addWidget(btnClear, 6, 0)
        self.btnGrid.addWidget(btnReset, 6, 1)
        self.btnGroup.setLayout(self.btnGrid)

        self.btnGroupBox = QGroupBox()
        self.btnHBox = QHBoxLayout()

        self.lblAlgorithm = QLabelWidget(
            Text.txt0094,
            textSuffix=":  ",
        )
        self.rbZero = QRadioButton("0", self)
        self.rbOne = QRadioButton("1", self)
        self.rbTwo = QRadioButton("2", self)

        btnDefaultAlgorithm = QPushButtonWidget(
            Text.txt0092,
            function=self.setDefaultAlgorithm,
            margins="  ",
            toolTip=Text.txt0093,
        )

        self.radioButtons = [self.rbZero, self.rbOne, self.rbTwo]

        self.btnHBox.addWidget(self.lblAlgorithm)
        self.btnHBox.addWidget(self.rbZero)
        self.btnHBox.addWidget(self.rbOne)
        self.btnHBox.addWidget(self.rbTwo)
        self.btnHBox.addWidget(btnDefaultAlgorithm)
        self.btnGroupBox.setLayout(self.btnHBox)

    def _initUI(self):

        grid = QGridLayout()
        grid.addWidget(self.command, 0, 0, 1, 2)
        grid.addWidget(self.btnGroupBox, 1, 0)
        grid.addWidget(self.btnGroup, 2, 0)
        grid.addWidget(self.outputWindow, 2, 1, 10, 1)

        self.setLayout(grid)

    def _initHelper(self):
        #
        # Signal interconnections
        #

        # local button state connect to related state
        self.parent.jobsQueue.addQueueItemSignal.connect(
            lambda: self.jobStartQueueState(True)
        )
        self.parent.jobsQueue.queueEmptiedSignal.connect(
            lambda: self.jobStartQueueState(False)
        )

        # job related
        self.parent.jobsQueue.runJobs.startSignal.connect(lambda: self.jobStatus(True))
        self.parent.jobsQueue.runJobs.finishedSignal.connect(
            lambda: self.jobStatus(False)
        )

        # map insertText signal to outputWidget one
        self.insertText = self.outputWindow.insertTextSignal

        # command
        self.updateCommandSignal.connect(self.updateCommand)
        self.cliButtonsSateSignal.connect(self.cliButtonsState)
        self.cliValidateSignal.connect(self.cliValidate)

        #
        # button state
        #

        # Command related
        # self.frmCmdLine.itemAt(0, QFormLayout.LabelRole).widget().setEnabled(False)
        self.cliButtonsState(False)
        self.btnGrid.itemAt(_Button.ANALYSIS).widget().setEnabled(False)

        # Clear buttons related
        self.btnGrid.itemAt(_Button.CLEAR).widget().setEnabled(False)
        self.btnGrid.itemAt(_Button.RESET).widget().setEnabled(False)

        # connect text windows textChanged to clearButtonState function
        self.outputWindow.textChanged.connect(self.clearButtonState)

        # connect command line textChanged to analysisButtonState function
        self.cmdLine.textChanged.connect(self.analysisButtonState)

        # Job Queue related
        self.btnGrid.itemAt(_Button.STARTQUEUE).widget().setEnabled(False)

        # Job Added to Queue
        self.parent.jobsQueue.addQueueItemSignal.connect(self.printJobIDAdded)

        #
        # Misc
        #
        self.cmdLine.setClearButtonEnabled(True)  # button at end of line to clear it

        # Algorithm radio buttons
        self.rbZero.toggled.connect(lambda: self.toggledRadioButton(self.rbZero))
        self.rbOne.toggled.connect(lambda: self.toggledRadioButton(self.rbOne))
        self.rbTwo.toggled.connect(lambda: self.toggledRadioButton(self.rbTwo))

        self.setDefaultAlgorithm()

    @classmethod
    def classLog(cls, setLogging=None):
        """
        get/set logging at class level
        every class instance will log
        unless overwritten

        Args:
            setLogging (bool):
                - True class will log
                - False turn off logging
                - None returns current Value

        Returns:
            bool:

            returns the current value set
        """

        if setLogging is not None:
            if isinstance(setLogging, bool):
                cls.__log = setLogging

        return cls.__log

    @property
    def log(self):
        """
        class property can be used to override the class global
        logging setting

        Returns:
            bool:

            True if logging is enable False otherwise
        """
        if self.__log is not None:
            return self.__log

        return CommandWidget.classLog()

    @log.setter
    def log(self, value):
        """set instance log variable"""

        if isinstance(value, bool) or value is None:
            self.__log = value
            # No variable used so for now use class log
            ValidateCommand.classLog(value)
            self.outputWindow.log = value

    @property
    def output(self):
        return self.__output

    @output.setter
    def output(self, value):
        self.__output = value

    @property
    def rename(self):
        return self.__rename

    @rename.setter
    def rename(self, value):
        if isinstance(value, object):
            self.__rename = value

    @Slot(list)
    def applyRename(self, renameFiles):

        if self.oCommand:
            self.oCommand.renameOutputFiles(renameFiles)

    @Slot(bool)
    def cliButtonsState(self, validateOK):
        """
        cliButtonsState change enabled status for buttons related with command line

        Args:
            validateOK (bool): True to enable, False to disable
        """

        for b in [
            _Button.ADDCOMMAND,
            _Button.RENAME,
            _Button.ADDQUEUE,
            _Button.SHOWCOMMANDS,
            _Button.CHECKFILES,
        ]:
            button = self.btnGrid.itemAt(b).widget()
            button.setEnabled(validateOK)

    @Slot(bool)
    def cliValidate(self, validateOK):
        """
        cliValidate Slot used by ValidateCommnad

        Args:
            validateOK (bool): True if command line is Ok.  False otherwise.
        """

        if validateOK:
            self.output.command.emit(
                "Command looks ok.\n", {LineOutput.AppendEnd: True}
            )
        else:
            if self.cmdLine.text() != "":
                self.output.command.emit("Bad command.\n", {LineOutput.AppendEnd: True})

        self.cliButtonsState(validateOK)
        self.updateObjCommnad(validateOK)

    @Slot(bool)
    def jobStartQueueState(self, state):

        if state and not isThreadRunning(config.WORKERTHREADNAME):
            self.btnGrid.itemAt(_Button.STARTQUEUE).widget().setEnabled(True)
        else:
            self.btnGrid.itemAt(_Button.STARTQUEUE).widget().setEnabled(False)

    @Slot(bool)
    def updateObjCommnad(self, valid):
        """Update the command object"""

        if valid:
            self.oCommand.command = self.cmdLine.text()
            if self.rename is not None:
                self.rename.setFilesSignal.emit(self.oCommand)
                self.rename.applyFileRenameSignal.connect(self.applyRename)
        else:
            self.oCommand.command = ""
            if self.rename is not None:
                self.rename.clear()

    @Slot(str)
    def updateCommand(self, command):
        """Update command input widget"""

        self.cmdLine.clear()
        self.cmdLine.setText(command)
        self.cmdLine.setCursorPosition(0)

    @Slot(int)
    def updateAlgorithm(self, algorithm):

        if 0 <= algorithm < len(self.radioButtons):
            self.radioButtons[algorithm].setChecked(True)

    @Slot(bool)
    def jobStatus(self, running):
        """
        jobStatus receive Signals for job start/end

        Args:
            running (bool): True if job started. False if ended.
        """

        if running:
            self.jobStartQueueState(False)
            palette = QPalette()
            color = checkColor(
                QColor(42, 130, 218), config.data.get(config.ConfigKey.DarkMode)
            )
            palette.setColor(QPalette.WindowText, color)
            self.parent.jobsLabel.setPalette(palette)
        else:
            palette = QPalette()
            color = checkColor(None, config.data.get(config.ConfigKey.DarkMode))
            palette.setColor(QPalette.WindowText, color)
            self.parent.jobsLabel.setPalette(palette)

    def addCommand(self, status):
        """
        addCommand add command row in jobs table

        Args:
            status (JobStatus): Status for job to be added should be either
                                JobStatus.Waiting or JobStatus.AddToQueue
        """

        totalJobs = self.model.rowCount()
        command = self.cmdLine.text()
        # [cell value, tooltip, obj]
        data = [
            ["", "", self.algorithm],
            [status, "Status code", None],
            [command, command, self.oCommand],
        ]
        self.model.insertRows(totalJobs, 1, data=data)
        self.cmdLine.clear()

    def analysisButtonState(self):
        """Set clear button state"""

        if self.cmdLine.text() != "":
            self.btnGrid.itemAt(_Button.ANALYSIS).widget().setEnabled(True)
        else:
            self.btnGrid.itemAt(_Button.ANALYSIS).widget().setEnabled(False)

    def clearButtonState(self):
        """Set clear button state"""

        if self.outputWindow.toPlainText() != "":
            self.btnGrid.itemAt(_Button.CLEAR).widget().setEnabled(True)
        else:
            self.btnGrid.itemAt(_Button.CLEAR).widget().setEnabled(False)

    def clearOutputWindow(self):
        """
        clearOutputWindow clear the command output window
        """

        language = config.data.get(config.ConfigKey.Language)
        bAnswer = False

        # Clear output window?
        title = "Clear output"
        msg = "¿" if language == "es" else ""
        msg += "Clear output window" + "?"
        bAnswer = yesNoDialog(self, msg, title)

        if bAnswer:
            self.outputWindow.clear()

    def printJobIDAdded(self, index):

        jobID = self.model.dataset[index.row(), index.column()]

        self.output.command.emit(
            f"Job: {jobID} added to Queue...\n", {LineOutput.AppendEnd: True}
        )

    def pasteClipboard(self):
        """Paste clipboard to command QLineEdit"""

        clip = QApplication.clipboard().text()

        if clip:
            self.output.command.emit(
                "Checking command...\n", {LineOutput.AppendEnd: True}
            )
            self.update()
            self.updateCommandSignal.emit(clip)

    def reset(self):
        """
        reset program status
        """

        language = config.data.get(config.ConfigKey.Language)

        if not isThreadRunning(config.WORKERTHREADNAME):

            language = config.data.get(config.ConfigKey.Language)
            bAnswer = False

            # Clear output window?
            title = "Reset"
            msg = "¿" if language == "es" else ""
            msg += "Reset Application" + "?"
            bAnswer = yesNoDialog(self, msg, title)

            if bAnswer:
                self.cmdLine.clear()
                self.outputWindow.clear()
                self.output.jobOutput.clear()
                self.output.errorOutput.clear()
                self.resetSignal.emit()

        else:
            messageBox(self, "Reset", "Jobs are running..")

    def resetButtonState(self):
        """Set clear button state"""

        if self.output.jobOutput.toPlainText() != "":
            self.btnGrid.itemAt(_Button.RESET).widget().setEnabled(True)
        else:
            self.btnGrid.itemAt(_Button.RESET).widget().setEnabled(False)

    def setDefaultAlgorithm(self):
        #
        # Algorithm
        #
        if config.data.get(config.ConfigKey.Algorithm) is not None:
            currentAlgorithm = config.data.get(config.ConfigKey.Algorithm)
            self.radioButtons[currentAlgorithm].setChecked(True)

    def setLanguage(self):
        """
        setLanguage language use in buttons/labels to be called by MainWindow
        """

        for index in range(self.frmCmdLine.rowCount()):
            widget = self.frmCmdLine.itemAt(index, QFormLayout.LabelRole).widget()
            if isinstance(widget, QPushButtonWidget):
                widget.setLanguage()
                # widget.setText("  " + _(widget.originalText) + "  ")
                # widget.setToolTip(_(widget.toolTip))

        for index in range(self.btnHBox.count()):
            widget = self.btnHBox.itemAt(index).widget()
            if isinstance(
                widget,
                (
                    QLabelWidget,
                    QPushButtonWidget,
                ),
            ):
                widget.setLanguage()

        for index in range(self.btnGrid.count()):
            widget = self.btnGrid.itemAt(index).widget()
            if isinstance(widget, QPushButtonWidget):
                widget.setLanguage()
                # widget.setText("  " + _(widget.originalText) + "  ")
                # widget.setToolTip(_(widget.toolTip))

    def toggledRadioButton(self, rButton):
        for index, rb in enumerate(self.radioButtons):
            if rb.isChecked():
                self.algorithm = index
class QWaitingForMissionResultWindow(QDialog):
    def __init__(self, gameEvent: Event, game: Game):
        super(QWaitingForMissionResultWindow, self).__init__()
        self.setModal(True)
        self.gameEvent = gameEvent
        self.game = game
        self.setWindowTitle("Waiting for mission completion.")
        self.setWindowIcon(QIcon("./resources/icon.png"))
        self.setMinimumHeight(570)

        self.initUi()
        DebriefingFileWrittenSignal.get_instance().debriefingReceived.connect(
            self.updateLayout)
        self.wait_thread = wait_for_debriefing(
            lambda debriefing: self.on_debriefing_udpate(debriefing),
            self.game)

    def initUi(self):
        self.layout = QGridLayout()

        header = QLabel(self)
        header.setGeometry(0, 0, 655, 106)
        pixmap = QPixmap("./resources/ui/conflict.png")
        header.setPixmap(pixmap)
        self.layout.addWidget(header, 0, 0)

        self.gridLayout = QGridLayout()
        TEXT = "" + \
                "<b>You are clear for takeoff</b>" + \
                "" + \
                "<h2>For Singleplayer :</h2>\n" + \
                "In DCS, open the Mission Editor, and load the file : \n" + \
                "<i>liberation_nextturn</i>\n" + \
                "<p>Then once the mission is loaded in ME, in menu \"Flight\",\n" + \
                "click on FLY Mission to launch.</p>\n" + \
                "" + \
                "<h2>For Multiplayer :</h2>" + \
                "In DCS, open the Mission Editor, and load the file : " + \
                "<i>liberation_nextturn</i>" + \
                "<p>Click on File/Save. Then exit the mission editor, and go to Multiplayer.</p>" + \
                "<p>Then host a server with the mission, and tell your friends to join !</p>" + \
                "<i>(The step in the mission editor is important, and fix a game breaking bug.)</i>" + \
                "<h2>Finishing</h2>" + \
                "<p>Once you have played the mission, click on the \"Accept Results\" button.</p>" + \
                "<p>If DCS Liberation does not detect mission end, use the manually submit button, and choose the state.json file.</p>"

        self.instructions_text = QTextEdit(TEXT)
        self.instructions_text.setReadOnly(True)
        self.gridLayout.addWidget(self.instructions_text, 1, 0)

        progress = QLabel("")
        progress.setAlignment(QtCore.Qt.AlignCenter)
        progress_bar = QMovie("./resources/ui/loader.gif")
        progress.setMovie(progress_bar)

        self.actions = QGroupBox("Actions :")
        self.actions_layout = QHBoxLayout()
        self.actions.setLayout(self.actions_layout)

        self.manually_submit = QPushButton("Manually Submit [Advanced users]")
        self.manually_submit.clicked.connect(self.submit_manually)
        self.actions_layout.addWidget(self.manually_submit)
        self.cancel = QPushButton("Abort mission")
        self.cancel.clicked.connect(self.close)
        self.actions_layout.addWidget(self.cancel)
        self.gridLayout.addWidget(self.actions, 2, 0)

        self.actions2 = QGroupBox("Actions :")
        self.actions2_layout = QHBoxLayout()
        self.actions2.setLayout(self.actions2_layout)
        self.manually_submit2 = QPushButton("Manually Submit [Advanced users]")
        self.manually_submit2.clicked.connect(self.submit_manually)
        self.actions2_layout.addWidget(self.manually_submit2)
        self.cancel2 = QPushButton("Abort mission")
        self.cancel2.clicked.connect(self.close)
        self.actions2_layout.addWidget(self.cancel2)
        self.proceed = QPushButton("Accept results")
        self.proceed.setProperty("style", "btn-success")
        self.proceed.clicked.connect(self.process_debriefing)
        self.actions2_layout.addWidget(self.proceed)

        progress_bar.start()
        self.layout.addLayout(self.gridLayout, 1, 0)
        self.setLayout(self.layout)

    def updateLayout(self, debriefing):
        updateBox = QGroupBox("Mission status")
        updateLayout = QGridLayout()
        updateBox.setLayout(updateLayout)
        self.debriefing = debriefing

        updateLayout.addWidget(QLabel("<b>Aircraft destroyed</b>"), 0, 0)
        updateLayout.addWidget(QLabel(str(len(debriefing.killed_aircrafts))),
                               0, 1)

        updateLayout.addWidget(QLabel("<b>Ground units destroyed</b>"), 1, 0)
        updateLayout.addWidget(
            QLabel(str(len(debriefing.killed_ground_units))), 1, 1)

        #updateLayout.addWidget(QLabel("<b>Weapons fired</b>"), 2, 0)
        #updateLayout.addWidget(QLabel(str(len(debriefing.weapons_fired))), 2, 1)

        updateLayout.addWidget(QLabel("<b>Base Capture Events</b>"), 2, 0)
        updateLayout.addWidget(
            QLabel(str(len(debriefing.base_capture_events))), 2, 1)

        # Clear previous content of the window
        for i in reversed(range(self.gridLayout.count())):
            try:
                self.gridLayout.itemAt(i).widget().setParent(None)
            except:
                pass

        # Set new window content
        self.gridLayout.addWidget(updateBox, 0, 0)

        if not debriefing.mission_ended:
            self.gridLayout.addWidget(QLabel("<b>Mission is being played</b>"),
                                      1, 0)
            self.gridLayout.addWidget(self.actions, 2, 0)
        else:
            self.gridLayout.addWidget(QLabel("<b>Mission is over</b>"), 1, 0)
            self.gridLayout.addWidget(self.actions2, 2, 0)

    def on_debriefing_udpate(self, debriefing):
        try:
            logging.info("On Debriefing update")
            print(debriefing)
            DebriefingFileWrittenSignal.get_instance().sendDebriefing(
                debriefing)
        except Exception as e:
            logging.error("Got an error while sending debriefing")
            logging.error(e)
        self.wait_thread = wait_for_debriefing(
            lambda debriefing: self.on_debriefing_udpate(debriefing),
            self.game)

    def process_debriefing(self):
        self.game.finish_event(event=self.gameEvent,
                               debriefing=self.debriefing)
        self.game.pass_turn(ignored_cps=[
            self.gameEvent.to_cp,
        ])

        GameUpdateSignal.get_instance().sendDebriefing(self.game,
                                                       self.gameEvent,
                                                       self.debriefing)
        self.close()

    def debriefing_directory_location(self) -> str:
        return os.path.join(base_path(), "liberation_debriefings")

    def closeEvent(self, evt):
        super(QWaitingForMissionResultWindow, self).closeEvent(evt)
        if self.wait_thread is not None:
            self.wait_thread.stop()

    def submit_manually(self):
        file = QFileDialog.getOpenFileName(self,
                                           "Select game file to open",
                                           filter="json(*.json)",
                                           dir=".")
        print(file)
        try:
            with open(file[0], "r") as json_file:
                json_data = json.load(json_file)
                json_data["mission_ended"] = True
                debriefing = Debriefing(json_data, self.game)
                self.on_debriefing_udpate(debriefing)
        except Exception as e:
            logging.error(e)
            msg = QMessageBox()
            msg.setIcon(QMessageBox.Information)
            msg.setText("Invalid file : " + file[0])
            msg.setWindowTitle("Invalid file.")
            msg.setStandardButtons(QMessageBox.Ok)
            msg.setWindowFlags(Qt.WindowStaysOnTopHint)
            msg.exec_()
            return
class MainWindow(QWidget):
    def __init__(self):
        super(MainWindow, self).__init__()

        self.system_id = DataBase.select('system_id')
        self.device_version = DataBase.select('app_version')
        self.device_mode = DataBase.select('device_mode')

        loader = QUiLoader()
        self.ui = loader.load('main.ui', None)

        sp_retain = QSizePolicy()
        sp_retain.setRetainSizeWhenHidden(True)
        self.ui.btn_left.setSizePolicy(sp_retain)
        self.ui.btn_right.setSizePolicy(sp_retain)
        self.ui.lbl_device_info.setSizePolicy(sp_retain)
        self.ui.btn_setting.setSizePolicy(sp_retain)

        self.btnOwnerLogin = CustomButton()
        self.btnOwnerLogin.setGif("animations/Rolling-white.gif")
        self.ui.vLayoutSignInOwner.addWidget(self.btnOwnerLogin)
        self.ui.vLayoutSignInOwner.setAlignment(Qt.AlignHCenter)

        self.btnUserLoginID = CustomButton()
        self.btnUserLoginID.setGif("animations/Rolling-white.gif")
        self.lbl = QLabel(None)
        self.lbl.setStyleSheet(BTN_PASS_RECOVERY_STYLE)
        self.ui.vLayoutSignInUser.addWidget(self.btnUserLoginID)
        self.ui.vLayoutSignInUser.addWidget(self.lbl)
        self.ui.vLayoutSignInUser.setAlignment(Qt.AlignHCenter)

        self.btnUserLoginMobile = CustomButton()
        self.btnUserLoginMobile.setGif("animations/Rolling-white.gif")
        self.lbl = QLabel(None)
        self.lbl.setStyleSheet(BTN_PASS_RECOVERY_STYLE)

        # Threads
        self.auto_delivery_items_thread = AutoDeliveryItemsThread()

        self.rfid_thread = RFIDThread()
        self.rfid_thread.success_signal.connect(self.successTransferToRFIDCard)
        self.rfid_thread.fail_signal.connect(self.transferToRFIDCard)

        # signals
        self.ui.btn_refresh_loading.clicked.connect(self.refresh)
        self.ui.btn_main_menu_1.clicked.connect(self.checkDeviceMode)
        self.ui.btn_start.hide()
        #self.ui.btn_main_menu_3.clicked.connect(self.stackFastCharging)
        self.ui.btn_main_menu_4.clicked.connect(self.stackWalletServices)
        self.ui.btn_print_receipt_yes.clicked.connect(self.printReceipt)
        self.ui.btn_print_receipt_no.clicked.connect(self.stackStart)
        self.ui.btn_other_services_after_delivery.clicked.connect(
            self.stackWalletServices)
        self.ui.btn_no_exit_app_setting.clicked.connect(self.stackSetting)
        self.ui.btn_yes_exit_app_setting.clicked.connect(self.exitProgram)
        self.ui.btn_setting_start.clicked.connect(self.stackStart)
        self.ui.btn_setting_1.clicked.connect(self.stackDeviceMode)
        self.ui.btn_setting_5.clicked.connect(self.stackConveyorPort)
        self.ui.btn_setting_2.clicked.connect(self.stackPressMotor)
        # self.ui.btn_setting_10.clicked.connect(self.stackSeparationMotor)
        self.ui.btn_setting_3.clicked.connect(self.stackSensor1Ports)
        self.ui.btn_setting_9.clicked.connect(self.stackSensor2Ports)
        self.ui.btn_setting_6.clicked.connect(self.stackExitApp)
        self.ui.btn_wallet_services_1.clicked.connect(
            self.stackChargingResidentialUnit)
        self.ui.btn_wallet_services_2.clicked.connect(self.stackRFID)
        self.ui.btn_wallet_services_3.clicked.connect(self.stackCharity)
        self.ui.btn_wallet_services_4.clicked.connect(
            self.stackEnvirnmentalProtection)
        self.ui.btn_wallet_services_5.clicked.connect(self.stackWallet)
        self.ui.btn_plus_charity.clicked.connect(self.plusCharity)
        self.ui.btn_minus_charity.clicked.connect(self.minusCharity)
        self.ui.btn_plus_envirnmental_protection.clicked.connect(
            self.plusEnvirnment)
        self.ui.btn_minus_envirnmental_protection.clicked.connect(
            self.minusEnvirnment)
        self.ui.btn_plus_rfid.clicked.connect(self.plusRFID)
        self.ui.btn_minus_rfid.clicked.connect(self.minusRFID)
        self.ui.btn_confirm_transfer_to_RFIDcard.clicked.connect(
            self.transferToRFIDCard)

        self.ui.btn_charity_1.clicked.connect(
            lambda: self.ui.lbl_selected_charity.setText(self.ui.lbl_charity_1.
                                                         text()))
        self.ui.btn_charity_2.clicked.connect(
            lambda: self.ui.lbl_selected_charity.setText(self.ui.lbl_charity_2.
                                                         text()))
        self.ui.btn_charity_3.clicked.connect(
            lambda: self.ui.lbl_selected_charity.setText(self.ui.lbl_charity_3.
                                                         text()))
        self.ui.btn_charity_4.clicked.connect(
            lambda: self.ui.lbl_selected_charity.setText(self.ui.lbl_charity_4.
                                                         text()))

        self.ui.btn_envirnmental_protection_1.clicked.connect(
            lambda: self.ui.lbl_selected_envirnmental_protection.setText(
                self.ui.lbl_envirnmental_protection_1.text()))
        self.ui.btn_envirnmental_protection_2.clicked.connect(
            lambda: self.ui.lbl_selected_envirnmental_protection.setText(
                self.ui.lbl_envirnmental_protection_2.text()))
        self.ui.btn_envirnmental_protection_3.clicked.connect(
            lambda: self.ui.lbl_selected_envirnmental_protection.setText(
                self.ui.lbl_envirnmental_protection_3.text()))
        self.ui.btn_envirnmental_protection_4.clicked.connect(
            lambda: self.ui.lbl_selected_envirnmental_protection.setText(
                self.ui.lbl_envirnmental_protection_4.text()))

        self.ui.setWindowFlags(Qt.FramelessWindowHint | Qt.Dialog)
        self.ui.showMaximized()

        self.back_delivery_item_flag = False
        self.flag_system_startup_now = True

        self.delivery_state = 'none'

        # self.categories = Server.getCategories()
        self.image_classifier = ImageClassifier()
        self.predict_item_threshold = float(
            DataBase.select('predict_item_threshold'))
        self.initHardwares()
        self.readFile()
        self.stackSetting()
        self.playSound('audio2')
        self.refresh()

    def readFile(self):
        f = open('items.csv', encoding='utf-8')
        self.items = []
        for line in f:
            item = line.split(',')
            self.items.append({
                'id': int(item[0]),
                'category_id': int(item[1]),
                'price': int(item[2]),
                'name': item[3]
            })

    def initHardwares(self):

        try:
            if hasattr(self, 'press_motor'):
                self.press_motor.close()

            if hasattr(self, 'conveyor_motor'):
                self.conveyor_motor.close()

            if hasattr(self, 'distance_sensor1'):
                self.distance_sensor1.close()
                print("distance sensor 1 close")

            if hasattr(self, 'distance_sensor2'):
                self.distance_sensor2.close()
                print("distance sensor 2 close")

        except Exception as e:
            print("error:", e)

        try:
            self.press_motor = Motor(name='press_motor', pin_factory=factory)

            self.setButton(self.ui.btn_press_motor_forward_on,
                           function=self.press_motor.forward)
            self.setButton(self.ui.btn_press_motor_backward_on,
                           function=self.press_motor.backward)
            self.setButton(self.ui.btn_press_motor_off,
                           function=self.press_motor.stop)
        except Exception as e:
            print("error:", e)
            ErrorLog.writeToFile(
                str(e) + ' In press_motor initHardwares Method')

        try:

            # normal
            # self.conveyor_motor = Motor(name='conveyor_motor', pin_factory=factory)

            # red relay
            self.conveyor_motor = Motor(name='conveyor_motor',
                                        pin_factory=factory,
                                        active_high=True)

            self.conveyor_motor_time_2 = float(
                DataBase.select('conveyor_motor_time_2'))

            self.setButton(self.ui.btn_conveyor_motor_forward_on,
                           function=self.conveyor_motor.forward)
            self.setButton(self.ui.btn_conveyor_motor_backward_on,
                           function=self.conveyor_motor.backward)
            self.setButton(self.ui.btn_conveyor_motor_off,
                           function=self.conveyor_motor.stop)
        except Exception as e:
            print("error:", e)
            ErrorLog.writeToFile(
                str(e) + ' In conveyor_motor initHardwares Method')

        try:
            distance_sensor1_trig_port = int(
                DataBase.select('distance_sensor1_trig_port'))
            distance_sensor1_echo_port = int(
                DataBase.select('distance_sensor1_echo_port'))
            distance_sensor1_threshold_distance = float(
                DataBase.select('distance_sensor1_threshold_distance'))
            self.distance_sensor1 = DistanceSensor(
                distance_sensor1_echo_port,
                distance_sensor1_trig_port,
                max_distance=1,
                threshold_distance=distance_sensor1_threshold_distance / 100,
                pin_factory=factory)
            self.distance_sensor1.when_in_range = self.distanceSensor1WhenInRange
            self.distance_sensor1.when_out_of_range = self.distanceSensor1WhenOutOfRange
            print('distance sensor 1 ready')
        except Exception as e:
            print("error:", e)
            ErrorLog.writeToFile(
                str(e) + ' In distance_sensor1 initHardwares Method')

        try:
            distance_sensor2_trig_port = int(
                DataBase.select('distance_sensor2_trig_port'))
            distance_sensor2_echo_port = int(
                DataBase.select('distance_sensor2_echo_port'))
            distance_sensor2_threshold_distance = float(
                DataBase.select('distance_sensor2_threshold_distance'))
            self.distance_sensor2 = DistanceSensor(
                distance_sensor2_echo_port,
                distance_sensor2_trig_port,
                max_distance=1,
                threshold_distance=distance_sensor2_threshold_distance / 100,
                pin_factory=factory)
            self.distance_sensor2.when_in_range = self.distanceSensor2WhenInRange
            self.distance_sensor2.when_out_of_range = self.distanceSensor2WhenOutOfRange
            print('distance sensor 2 ready')
        except Exception as e:
            print("error:", e)
            ErrorLog.writeToFile(
                str(e) + ' In distance_sensor2 initHardwares Method')

        try:
            if not hasattr(self, 'rfid_sensor'):
                self.rfid_sensor = SimpleMFRC522()
                print('RFID sensor ready')
        except Exception as e:
            print("error:", e)
            ErrorLog.writeToFile(
                str(e) + ' In rfid_sensor initHardwares Method')

    def setButton(self,
                  button,
                  function=None,
                  text=None,
                  icon=None,
                  show=True):
        try:
            button.clicked.disconnect()
        except:
            pass
        finally:
            if function:
                button.clicked.connect(function)
        if text:
            button.setText(text)
        if icon:
            button.setIcon(QIcon(icon))
        if show:
            button.show()
        else:
            button.hide()

    def showNotification(self, text):
        self.ui.lbl_notification.setText(text)
        self.ui.lbl_notification.show()

    def hideNotification(self):
        self.ui.lbl_notification.hide()

    def playSound(self, path):
        try:
            path = os.path.join('sounds', path + '.mp3')
            if os.path.isfile(path):
                mixer.music.load(path)
                mixer.music.play()
        except Exception as e:
            print("error:", e)
            ErrorLog.writeToFile(str(e) + ' In playSound Method')

    def stopSound(self):
        try:
            mixer.music.stop()
        except Exception as e:
            print("error:", e)
            ErrorLog.writeToFile(str(e) + ' In stopSound Method')

    def makeGif(self):
        pngdir = 'images/slider'
        images = []
        kargs = {'duration': 5}
        for file_name in os.listdir(pngdir):
            if file_name.endswith('.JPG'):
                file_path = os.path.join(pngdir, file_name)
        #         images.append(imageio.imread(file_path))
        # imageio.mimsave('animations/slider1.gif', images, 'GIF', **kargs)

    def loadingFail(self):
        self.ui.btn_refresh_loading.show()
        self.showNotification(SERVER_ERROR_MESSAGE)

    def refresh(self):
        self.showNotification(PLEASE_WAIT_MESSAGE)

    def showSoonNotification(self):
        self.showNotification(SOON_MESSAGE)

    def stackStart(self):
        self.setButton(self.ui.btn_left, show=False)
        self.setButton(self.ui.btn_right, show=False)
        self.ui.lbl_notification.hide()
        self.playSound('audio11')
        gif_start = QMovie("animations/slider1.gif")
        self.ui.lbl_slider_start.setMovie(gif_start)
        gif_start.start()
        self.delivery_state = 'default'
        self.ui.Stack.setCurrentWidget(self.ui.pageStart)

    def stackMainMenu(self):
        self.setButton(self.ui.btn_left,
                       function=self.signOutUser,
                       text='خروج',
                       icon='images/icon/log-out.png',
                       show=True)
        self.setButton(self.ui.btn_right, show=False)
        self.ui.lbl_notification.hide()
        self.stopSound()
        self.delivery_state = 'default'
        self.ui.Stack.setCurrentWidget(self.ui.pageMainMenu)

    def stackWalletServices(self):
        self.setButton(self.ui.btn_left,
                       function=self.stackMainMenu,
                       text='بازگشت',
                       icon='images/icon/back.png',
                       show=True)
        self.setButton(self.ui.btn_right, show=False)
        self.ui.lbl_notification.hide()
        self.ui.Stack.setCurrentWidget(self.ui.pageWalletServices)

    def manualDeliveryRecycleItem(self):
        try:
            self.showNotification(RECYCLE_MESSAGE)

            try:
                self.conveyor_motor.forward(timer=True)
            except Exception as e:
                print("error:", e)
                ErrorLog.writeToFile(
                    str(e) +
                    ' In press_motor_stop_timer startDeliveryItem Method')

            self.playSound('audio3')
            self.ui.btn_right.show()
            self.selected_item['count'] += 1
            self.ui.lbl_selected_item_count.setText(
                str(self.selected_item['count']))

            for user_item in self.user_items:
                if self.selected_item['id'] == user_item['id']:
                    break
            else:
                self.user_items.append(self.selected_item)
            self.total_price = sum(user_item['price'] * user_item['count']
                                   for user_item in self.user_items)
            self.ui.lbl_total.setText(str(self.total_price))

            try:
                self.press_motor.forward(timer=True)
            except Exception as e:
                print("error:", e)
                ErrorLog.writeToFile(
                    str(e) +
                    ' In press_motor_stop_timer startDeliveryItem Method')

        except Exception as e:
            print("error:", e)
            ErrorLog.writeToFile(str(e) + ' In endDeliveryItem Method')

    def stackManualDeliveryItems(self):
        self.setButton(self.ui.btn_left, show=False)
        self.setButton(self.ui.btn_right,
                       function=self.stackAfterDelivery,
                       text='پایان',
                       icon='images/icon/tick.png',
                       show=False)
        self.setButton(self.ui.btn_manual_delivery_recycle_item,
                       function=self.manualDeliveryRecycleItem)
        self.playSound('audio7')
        self.ui.lbl_total.setText("0")
        self.ui.lbl_recycled_done.hide()
        self.user_items = []
        self.layout_FArea = QGridLayout()
        i = 0
        row = 0
        while row < len(self.items) // 2:
            for col in range(2):
                btn = QPushButton()
                self.items[i]['count'] = 0
                btn.setText(self.items[i]['name'])

                btn.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
                btn.setStyleSheet(
                    'QPushButton:pressed {background-color: #6fdc89;border-style: inset;} QPushButton{background-color: #ffffff; border: 2px solid #28a745; border-radius: 10px; outline-style: none; font: 22pt "IRANSansFaNum"}'
                )
                btn.setMinimumSize(250, 100)
                btn.clicked.connect(
                    partial(self.SelectItem, self.items[i], btn))
                self.layout_FArea.addWidget(btn, row, col)
                i += 1
                if i >= len(self.items):
                    break
            row += 1

        self.total_price = 0
        self.SelectItem(self.items[0], self.layout_FArea.itemAt(0))
        self.ui.scroll_widget_manual_delivery.setLayout(self.layout_FArea)
        self.ui.Stack.setCurrentWidget(self.ui.pageManualDeliveryItems)

    def stackAutoDeliveryItems(self):
        self.setButton(self.ui.btn_left, show=False)
        self.setButton(self.ui.btn_right,
                       function=self.stackAfterDelivery,
                       text='پایان',
                       icon='images/icon/tick.png',
                       show=True)
        self.ui.lbl_notification.hide()
        self.ui.btn_setting.hide()
        # self.ui.list_auto_delivery_items.clear()
        self.ui.lbl_pixmap_category_1.setPixmap(
            QPixmap("images/item/category1.png").scaledToHeight(128))
        self.ui.lbl_pixmap_category_3.setPixmap(
            QPixmap("images/item/category3.png").scaledToHeight(128))
        self.ui.lbl_pixmap_category_4.setPixmap(
            QPixmap("images/item/category4.png").scaledToHeight(128))

        self.ui.lbl_num_category_1.setText(str(0))
        self.ui.lbl_num_category_3.setText(str(0))
        self.ui.lbl_num_category_4.setText(str(0))

        self.ui.lbl_total_price_auto_delivery_items.setText(str(0))

        self.total_price = 0

        self.playSound('audio7')
        self.delivery_state = 'ready'

        self.user_items = []
        for item in self.items:
            item['count'] = 0

        self.ui.Stack.setCurrentWidget(self.ui.pageAutoDeliveryItems)

    def distanceSensor1WhenInRange(self):
        print('distanceSensor1WhenInRange')
        if self.delivery_state != 'none':
            if self.device_mode == 'auto' and self.delivery_state == 'default':
                self.delivery_state = 'ready'
                print('default to ready')
                self.stackAutoDeliveryItems()

            elif self.device_mode == 'manual' and self.delivery_state == 'default':
                self.delivery_state = 'ready'
                self.stackManualDeliveryItems()

            if self.device_mode == 'auto' and self.delivery_state != 'default':
                if self.delivery_state == 'ready':
                    self.delivery_state = 'enter'
                    print('delivery state changed: ready to enter')
                    self.enterDeliveryItem()

                elif self.delivery_state == 'reject':
                    self.delivery_state = 'pickup'
                    print('delivery state changed: reject to pickup')
                    self.pickupDeliveryItem()

                else:
                    self.delivery_state = 'cancel'
                    print('delivery state changed: cancel')
                    self.cancelDeliveryItem()

            elif self.device_mode == 'manual':
                self.manualDeliveryRecycleItem()

    def distanceSensor1WhenOutOfRange(self):
        print('distanceSensor1WhenOutOfRange')
        if self.delivery_state == 'enter':
            self.delivery_state = 'recognize'
            print('delivery state changed: enter to recognize')
            self.startDeliveryItem()
        elif self.delivery_state == 'pickup':
            self.delivery_state = 'ready'
            print('delivery state changed: pickup to ready')

    def distanceSensor2WhenInRange(self):
        print('distanceSensor2WhenInRange')
        # if self.delivery_state == 'accept':
        #     self.endDeliveryItem()

    def distanceSensor2WhenOutOfRange(self):
        print('distanceSensor2WhenOutOfRange')

    def pickupDeliveryItem(self):
        print('distanceSensor2WhenOutOfRange')
        try:
            self.cancel_delivery_item_timer.cancel()
            self.conveyor_motor.stop()
        except Exception as e:
            print("error:", e)

    def enterDeliveryItem(self):
        print('enterDeliveryItem')
        try:
            self.conveyor_motor.forward(timer=True)

        except Exception as e:
            print("error:", e)

    def startDeliveryItem(self):
        print('startDeliveryItem')
        try:
            self.conveyor_motor.stop()
            self.auto_delivery_items_thread.start()
            self.auto_delivery_items_timer = Timer(camera_time,
                                                   self.validationDeliveryItem)
            self.auto_delivery_items_timer.start()
            self.cancel_delivery_item_timer = Timer(delivery_cancel_time,
                                                    self.cancelDeliveryItem)
            self.cancel_delivery_item_timer.start()
        except Exception as e:
            print("error:", e)

    def rejectDeliveryItem(self):
        print('rejectDeliveryItem')
        # self.showNotification(ITEM_NOT_RECOGNIZED_ERROR_MESSAGE)
        # sleep(0.01)
        self.conveyor_motor.backward(timer=True)

    def acceptDeliveryItem(self):
        print('acceptDeliveryItem')
        most_probability_item_index = stats.mode(
            self.auto_delivery_items_thread.predicted_items).mode[0]
        self.selected_item = self.items[most_probability_item_index]
        print('most probability item:', window.selected_item['name'])

        # self.ui.list_auto_delivery_items.insertItem(0, self.selected_item['name'])

        if self.selected_item['category_id'] == 1:
            self.ui.lbl_num_category_1.setText(
                str(int(self.ui.lbl_num_category_1.text()) + 1))
        elif self.selected_item['category_id'] == 3:
            self.ui.lbl_num_category_3.setText(
                str(int(self.ui.lbl_num_category_3.text()) + 1))
        elif self.selected_item['category_id'] == 4:
            self.ui.lbl_num_category_4.setText(
                str(int(self.ui.lbl_num_category_4.text()) + 1))
        # elif self.selected_item['category_id'] == 5:
        #     self.ui.lbl_num_category_5.setText(str(int(self.ui.lbl_num_category_5.text()) + 1))

        self.total_price += int(self.selected_item['price'])
        # self.total_price = sum(user_item['price'] * user_item['count'] for user_item in self.user_items)
        self.ui.lbl_total_price_auto_delivery_items.setText(
            str(self.total_price))

        self.conveyor_motor.forward(timer=True)
        self.end_delivery_items_timer = Timer(self.conveyor_motor_time_2,
                                              self.endDeliveryItem)
        self.end_delivery_items_timer.start()
        self.delivery_state = 'end'
        # self.endDeliveryItem()

    def validationDeliveryItem(self):
        print('validationDeliveryItem')
        if self.delivery_state == 'recognize':
            self.delivery_state = 'validate'
            print('delivery state changed: recognize to validate')

            sleep(0.1)

            if len(self.auto_delivery_items_thread.predicted_items) > 0:
                self.delivery_state = 'accept'
                print('delivery state changed: validate to accept')
                self.acceptDeliveryItem()
            else:
                self.delivery_state = 'reject'
                print('delivery state changed: validate to reject')
                self.rejectDeliveryItem()

    def cancelDeliveryItem(self):
        # self.showNotification(DELIVERY_ERROR_MESSAGE)
        sleep(0.01)
        print('cancelDeliveryItem')
        self.conveyor_motor.stop()
        self.press_motor.stop()
        # self.separation_motor.stop()
        self.delivery_state = 'ready'
        print('delivery state changed: ready')

    def endDeliveryItem(self):
        print('endDeliveryItem')
        if self.delivery_state == 'end':
            try:
                # self.showNotification(RECYCLE_MESSAGE)
                sleep(0.01)
                self.cancel_delivery_item_timer.cancel()

                self.playSound('audio3')
                self.selected_item['count'] += 1
                self.ui.lbl_selected_item_count.setText(
                    str(self.selected_item['count']))

                for user_item in self.user_items:
                    if self.selected_item['id'] == user_item['id']:
                        break
                else:
                    self.user_items.append(self.selected_item)

                # self.conveyor_motor.stop()

                try:
                    self.press_motor.forward(True)
                except Exception as e:
                    print("error:", e)
                    ErrorLog.writeToFile(
                        str(e) +
                        ' In press_motor_stop_timer startDeliveryItem Method')

                sleep(1)
                self.delivery_state = 'ready'

            except Exception as e:
                print("error:", e)
                ErrorLog.writeToFile(str(e) + ' In endDeliveryItem Method')

    def SelectItem(self, item, this_btn):
        self.selected_item = item
        self.selected_item['name'] = item['name']
        self.ui.lbl_selected_item.setText(self.selected_item['name'])
        self.ui.lbl_unit.setText(str(self.selected_item['price']))
        self.ui.lbl_selected_item_count.setText(
            str(self.selected_item['count']))
        # for btn in self.layout_FArea.findChildren(QPushButton):
        #     btn.setStyleSheet('background-color: #ffffff; border: 2px solid #28a745; border-radius: 10px; outline-style: none; font: 24pt "IRANSansFaNum"')
        # this_btn.setStyleSheet('background-color: #28a745; color:#ffffff; border-radius: 10px; outline-style: none; font: 24pt "IRANSansFaNum"')

    def printReceipt(self):
        self.playSound('audio4')
        # printer = Usb(idVendor=0x0416, idProduct=0x5011, timeout=0, in_ep=0x81, out_ep=0x03)
        os.system('sudo -S python3 printer.py ' + str(self.total_price) +
                  ' --datetime "' +
                  QDate.currentDate().toString(Qt.DefaultLocaleShortDate) +
                  '-' +
                  QTime.currentTime().toString(Qt.DefaultLocaleShortDate) +
                  '"')
        self.stackStart()

    def stackAfterDelivery(self):
        try:
            # self.total_price = sum(user_item['price'] * user_item['count'] for user_item in window.user_items)
            self.delivery_state = 'default'
            self.playSound('audio12')
            # self.setButton(self.ui.btn_left, show=False)
            # self.setButton(self.ui.btn_right, show=False)
            self.ui.lbl_notification.hide()
            # gif_afterDelivery = QMovie("animations/earth.gif")
            # self.ui.lbl_gif_after_delivery.setMovie(gif_afterDelivery)
            # gif_afterDelivery.start()
            # self.ui.lbl_total_price.setText(str(self.total_price))
            self.printReceipt()
        except:
            self.showNotification(SERVER_ERROR_MESSAGE)
            ErrorLog.writeToFile('Server Error Message')

        try:
            self.press_motor.stop()
            self.conveyor_motor.stop()
        except Exception as e:
            print("error:", e)
            ErrorLog.writeToFile(str(e) + ' In stackAfterDelivery Method')

    def fastChargingDeliveryRecycleItem(self):
        pass

    def stackFastCharging(self):
        self.setButton(self.ui.btn_left,
                       function=self.stackMainMenu,
                       text='بازگشت',
                       icon='images/icon/back.png',
                       show=True)
        self.setButton(self.ui.btn_right, show=False)
        self.setButton(self.ui.btn_recycle_item_fast_charging,
                       function=self.fastChargingDeliveryRecycleItem)
        self.ui.lbl_recycled_done_fast_charging.hide()
        self.ui.tb_unit_fast_charging.setText('')
        self.ui.tb_weight_fast_charging.setText('')

        self.layout_SArea_FastCharging = QGridLayout()
        for row in range(4):
            for col in range(2):
                btn = QPushButton()
                #self.items[i]['count'] = 0
                btn.setText('آهن')
                btn.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
                btn.setMinimumSize(250, 100)
                btn.setStyleSheet(
                    'QPushButton:pressed { background-color: #9caf9f } QPushButton{ background-color: #ffffff} QPushButton{ border: 2px solid #28a745} QPushButton{ border-radius: 10px} QPushButton{ font: 24pt "IRANSans"} QPushButton{ font: 24pt "IRANSansFaNum"} QPushButton{ color: #000000}'
                )
                #btn.clicked.connect(partial(self.SelectItem, self.items[i]))
                self.layout_SArea_FastCharging.addWidget(btn, row, col)

        #self.SelectItem(self.items[0])
        self.ui.scroll_area_widget_fast_charging.setLayout(
            self.layout_SArea_FastCharging)
        self.ui.Stack.setCurrentWidget(self.ui.pageFastDelivery)

    def stackWallet(self):
        self.setButton(self.ui.btn_left,
                       function=self.stackWalletServices,
                       text='بازگشت',
                       icon='images/icon/back.png',
                       show=True)
        self.setButton(self.ui.btn_right, show=False)
        self.ui.lbl_notification.hide()
        gif_wallet = QMovie("animations/wallet.gif")
        gif_wallet.setScaledSize(QSize().scaled(256, 256, Qt.KeepAspectRatio))
        self.ui.lbl_gif_wallet.setMovie(gif_wallet)
        gif_wallet.start()
        self.ui.lbl_wallet.setText(str(
            ("{:,.0f}").format(self.user['wallet'])))
        self.ui.Stack.setCurrentWidget(self.ui.pageWallet)

    def stackChargingResidentialUnit(self):
        self.setButton(self.ui.btn_left,
                       function=self.stackWalletServices,
                       text='بازگشت',
                       icon='images/icon/back.png',
                       show=True)
        self.setButton(self.ui.btn_right, show=False)
        self.ui.lbl_user_address.setText(self.user['address'])
        print(self.user['address'])
        sp_retain = QSizePolicy()
        sp_retain.setRetainSizeWhenHidden(True)
        self.ui.tb_user_new_address.setSizePolicy(sp_retain)
        self.ui.btn_changed_user_address.setSizePolicy(sp_retain)
        self.ui.tb_user_new_address.hide()
        self.ui.btn_changed_user_address.hide()
        self.ui.btn_edit_user_address.clicked.connect(self.editUserAddress)
        self.ui.Stack.setCurrentWidget(self.ui.pageChargingResidentialUnit)

    def editUserAddress(self):
        self.ui.tb_user_new_address.show()
        self.ui.btn_changed_user_address.show()

    def transferToRFIDCard(self):
        self.showNotification(TRANSFER_TO_RFID_MESSAGE)
        self.rfid_thread.start()

    def successTransferToRFIDCard(self):
        self.stackWalletServices()
        self.showNotification(SUCCESS_TRANSFER_TO_RFID_MESSAGE)

    def plusRFID(self):
        if self.user_wallet < int(self.ui.lbl_payment_rfid.text()):
            self.showNotification(MONEY_ERROR_MESSAGE)
        else:
            self.ui.lbl_transfer_to_rfid.setText(
                str(
                    int(self.ui.lbl_transfer_to_rfid.text()) +
                    self.money_RFID))
            self.user_wallet -= self.money_RFID
            self.ui.lbl_total_wallet_rfid.setText(
                str("{:,.0f}".format(self.user_wallet)))

    def minusRFID(self):
        if int(self.ui.lbl_transfer_to_rfid.text()) > 0:
            self.ui.lbl_transfer_to_rfid.setText(
                str(
                    int(self.ui.lbl_transfer_to_rfid.text()) -
                    self.money_RFID))
            self.user_wallet += self.money_RFID
            self.ui.lbl_total_wallet_rfid.setText(
                str("{:,.0f}".format(self.user_wallet)))
        else:
            print('End of minus operations')

    def stackRFID(self):
        self.setButton(self.ui.btn_left,
                       function=self.stackWalletServices,
                       text='بازگشت',
                       icon='images/icon/back.png',
                       show=True)
        self.setButton(self.ui.btn_right, show=False)
        self.hideNotification()

        self.user_wallet = self.user['wallet']
        self.money_RFID = int(self.ui.lbl_payment_rfid.text())

        self.ui.lbl_transfer_to_rfid.setText('0')
        self.ui.lbl_total_wallet_rfid.setText(
            str("{:,.0f}".format(self.user_wallet)))
        self.ui.Stack.setCurrentWidget(self.ui.pageRFID)

    def plusCharity(self):
        if self.user_wallet < int(self.ui.lbl_payment_charity.text()):
            self.showNotification(MONEY_ERROR_MESSAGE)
        else:
            self.ui.lbl_deposit_price_charity_organization.setText(
                str(
                    int(self.ui.lbl_deposit_price_charity_organization.text())
                    + self.money_charity_organization))
            self.user_wallet -= self.money_charity_organization
            self.ui.lbl_total_price_charity.setText(
                str("{:,.0f}".format(self.user_wallet)))

    def minusCharity(self):
        if int(self.ui.lbl_deposit_price_charity_organization.text()) > 0:
            self.ui.lbl_deposit_price_charity_organization.setText(
                str(
                    int(self.ui.lbl_deposit_price_charity_organization.text())
                    - self.money_charity_organization))
            self.user_wallet += self.money_charity_organization
            self.ui.lbl_total_price_charity.setText(
                str("{:,.0f}".format(self.user_wallet)))
        else:
            print('End of minus operations')

    def stackCharity(self):
        self.setButton(self.ui.btn_left,
                       function=self.stackWalletServices,
                       text='بازگشت',
                       icon='images/icon/back.png',
                       show=True)
        self.setButton(self.ui.btn_right,
                       text='تایید',
                       icon='images/icon/tick.png',
                       show=True)
        self.hideNotification()

        self.user_wallet = self.user['wallet']
        self.money_charity_organization = int(
            self.ui.lbl_payment_charity.text())

        self.ui.lbl_deposit_price_charity_organization.setText('0')
        self.ui.lbl_total_price_charity.setText(
            str("{:,.0f}".format(self.user_wallet)))
        self.ui.lbl_selected_charity.setText(self.ui.lbl_charity_1.text())
        self.ui.Stack.setCurrentWidget(self.ui.pageCharity)

    def plusEnvirnment(self):
        if self.user_wallet < int(
                self.ui.lbl_payment_envirnmental_protection.text()):
            self.showNotification(MONEY_ERROR_MESSAGE)
        else:
            self.ui.lbl_deposit_price_environmental_organization.setText(
                str(
                    int(self.ui.lbl_deposit_price_environmental_organization.
                        text()) + self.money_envirnmental_organization))
            self.user_wallet -= self.money_envirnmental_organization
            self.ui.lbl_total_price_envirnmental_protection.setText(
                str("{:,.0f}".format(self.user_wallet)))

    def minusEnvirnment(self):
        if int(self.ui.lbl_deposit_price_environmental_organization.text()
               ) > 0:
            self.ui.lbl_deposit_price_environmental_organization.setText(
                str(
                    int(self.ui.lbl_deposit_price_environmental_organization.
                        text()) - self.money_envirnmental_organization))
            self.user_wallet += self.money_envirnmental_organization
            self.ui.lbl_total_price_envirnmental_protection.setText(
                str("{:,.0f}".format(self.user_wallet)))
        else:
            print('End of minus operations')

    def stackEnvirnmentalProtection(self):
        self.setButton(self.ui.btn_left,
                       function=self.stackWalletServices,
                       text='بازگشت',
                       icon='images/icon/back.png',
                       show=True)
        self.setButton(self.ui.btn_right,
                       text='تایید',
                       icon='images/icon/tick.png',
                       show=True)
        self.hideNotification()

        self.user_wallet = self.user['wallet']
        self.money_envirnmental_organization = int(
            self.ui.lbl_payment_envirnmental_protection.text())

        self.ui.lbl_deposit_price_environmental_organization.setText('0')
        self.ui.lbl_total_price_envirnmental_protection.setText(
            str("{:,.0f}".format(self.user_wallet)))
        self.ui.lbl_selected_envirnmental_protection.setText(
            self.ui.lbl_envirnmental_protection_1.text())

        self.ui.Stack.setCurrentWidget(self.ui.pageEnvirnmentalProtection)

    def stackSetting(self):
        self.setButton(self.ui.btn_left,
                       function=self.stackStart,
                       text='بازگشت',
                       icon='images/icon/back.png',
                       show=True)
        self.setButton(self.ui.btn_right,
                       function=self.saveSetting,
                       text='ذخیره',
                       icon='images/icon/save.png',
                       show=True)

        result = DataBase.select('device_mode')
        if result == 'manual':
            self.ui.rb_manual_device_mode_setting.setChecked(True)
        elif result == 'auto':
            self.ui.rb_auto_device_mode_setting.setChecked(True)

        self.ui.lbl_notification.hide()
        self.ui.StackSetting.setCurrentWidget(self.ui.pageSettingEmpty)
        self.ui.Stack.setCurrentWidget(self.ui.pageSetting)

    def stackDisableDevice(self):
        self.ui.btn_left.hide()
        self.ui.btn_right.hide()
        self.ui.lbl_notification.hide()
        self.ui.Stack.setCurrentWidget(self.ui.pageDisableDevice)

    def checkDeviceMode(self):
        if self.device_mode == 'manual':
            self.stackManualDeliveryItems()
        elif self.device_mode == 'auto':
            self.stackAutoDeliveryItems()

    def stackDeviceMode(self):
        result = DataBase.select('device_mode')
        if result == 'manual':
            self.ui.rb_manual_device_mode_setting.setChecked(True)
        elif result == 'auto':
            self.ui.rb_auto_device_mode_setting.setChecked(True)
        self.ui.StackSetting.setCurrentWidget(self.ui.pageSettingDeviceMode)

    def stackExitApp(self):
        self.ui.lbl_notification.hide()
        self.ui.StackSetting.setCurrentWidget(self.ui.pageSettingExit)

    def stackPressMotor(self):
        self.ui.lbl_notification.hide()
        self.ui.tb_press_motor_forward_port.setText(
            str(DataBase.select('press_motor_forward_port')))
        self.ui.tb_press_motor_backward_port.setText(
            str(DataBase.select('press_motor_backward_port')))
        self.ui.tb_press_motor_time.setText(
            str(DataBase.select('press_motor_time')))
        self.ui.StackSetting.setCurrentWidget(self.ui.pageSettingPressMotor)

    def stackSeparationMotor(self):
        self.ui.lbl_notification.hide()
        # self.ui.tb_separation_motor_forward_port.setText(str(DataBase.select('separation_motor_forward_port')))
        # self.ui.tb_separation_motor_backward_port.setText(str(DataBase.select('separation_motor_backward_port')))
        # self.ui.tb_separation_motor_time.setText(str(DataBase.select('separation_motor_time')))
        self.ui.StackSetting.setCurrentWidget(
            self.ui.pageSettingSeparationMotor)

    def stackSensor1Ports(self):
        self.ui.lbl_notification.hide()
        self.ui.tb_sensor1_trig_port.setText(
            str(DataBase.select('distance_sensor1_trig_port')))
        self.ui.tb_sensor1_echo_port.setText(
            str(DataBase.select('distance_sensor1_echo_port')))
        self.ui.tb_sensor1_depth_threshold.setText(
            str(DataBase.select('distance_sensor1_threshold_distance')))
        self.ui.StackSetting.setCurrentWidget(
            self.ui.pageSettingDistanceSensor1)

    def stackSensor2Ports(self):
        self.ui.lbl_notification.hide()
        self.ui.tb_sensor2_trig_port.setText(
            str(DataBase.select('distance_sensor2_trig_port')))
        self.ui.tb_sensor2_echo_port.setText(
            str(DataBase.select('distance_sensor2_echo_port')))
        self.ui.tb_sensor2_depth_threshold.setText(
            str(DataBase.select('distance_sensor2_threshold_distance')))
        self.ui.StackSetting.setCurrentWidget(
            self.ui.pageSettingDistanceSensor2)

    def stackConveyorPort(self):
        self.ui.lbl_notification.hide()
        self.ui.tb_conveyor_motor_forward_port.setText(
            str(DataBase.select('conveyor_motor_forward_port')))
        self.ui.tb_conveyor_motor_backward_port.setText(
            str(DataBase.select('conveyor_motor_backward_port')))
        self.ui.tb_conveyor_motor_time_1.setText(
            str(DataBase.select('conveyor_motor_time')))
        self.ui.tb_conveyor_motor_time_2.setText(
            str(DataBase.select('conveyor_motor_time_2')))
        self.ui.StackSetting.setCurrentWidget(self.ui.pageSettingConveyorMotor)

    def stackAddOpetator(self):
        self.ui.StackSetting.setCurrentWidget(self.ui.pageSettingAddOperator)

    def stackHelp(self):
        self.ui.StackSetting.setCurrentWidget(self.ui.pageSettingHelp)
        self.ui.lbl_version.setText(DEVICE_VERSION.format(self.device_version))
        self.ui.lbl_version.show()

    def stackLicense(self):
        self.ui.tb_license.setText(str(DataBase.select('app_version')))
        self.ui.StackSetting.setCurrentWidget(self.ui.pageSettingLicense)

    def changePredictItemFlag(self, value):
        self.predict_item_flag = value
        self.ui.lblDeliveryItems.clear()

    def saveSetting(self):
        self.showNotification(SETTING_SAVE_MESSAGE)
        if self.ui.rb_manual_device_mode_setting.isChecked():
            DataBase.update('device_mode', 'manual')
        elif self.ui.rb_auto_device_mode_setting.isChecked():
            DataBase.update('device_mode', 'auto')
        self.device_mode = DataBase.select('device_mode')

        if self.ui.tb_sensor1_trig_port.text() != '':
            result = DataBase.update('distance_sensor1_trig_port',
                                     self.ui.tb_sensor1_trig_port.text())
        if self.ui.tb_sensor1_echo_port.text() != '':
            result = DataBase.update('distance_sensor1_echo_port',
                                     self.ui.tb_sensor1_echo_port.text())
        if self.ui.tb_sensor1_depth_threshold.text() != '':
            result = DataBase.update('distance_sensor1_threshold_distance',
                                     self.ui.tb_sensor1_depth_threshold.text())

        if self.ui.tb_sensor2_trig_port.text() != '':
            result = DataBase.update('distance_sensor2_trig_port',
                                     self.ui.tb_sensor2_trig_port.text())
        if self.ui.tb_sensor2_echo_port.text() != '':
            result = DataBase.update('distance_sensor2_echo_port',
                                     self.ui.tb_sensor2_echo_port.text())
        if self.ui.tb_sensor2_depth_threshold.text() != '':
            result = DataBase.update('distance_sensor2_threshold_distance',
                                     self.ui.tb_sensor2_depth_threshold.text())

        if self.ui.tb_press_motor_forward_port.text() != '':
            result = DataBase.update(
                'press_motor_forward_port',
                self.ui.tb_press_motor_forward_port.text())
        if self.ui.tb_press_motor_backward_port.text() != '':
            result = DataBase.update(
                'press_motor_backward_port',
                self.ui.tb_press_motor_backward_port.text())
        if self.ui.tb_press_motor_time.text() != '':
            result = DataBase.update('press_motor_time',
                                     self.ui.tb_press_motor_time.text())

        # if self.ui.tb_separation_motor_forward_port.text() != '':
        #     result = DataBase.update('separation_motor_forward_port', self.ui.tb_separation_motor_forward_port.text())
        # if self.ui.tb_separation_motor_backward_port.text() != '':
        #     result = DataBase.update('separation_motor_backward_port', self.ui.tb_separation_motor_backward_port.text())
        # if self.ui.tb_separation_motor_time.text() != '':
        #     result = DataBase.update('separation_motor_time', self.ui.tb_separation_motor_time.text())

        if self.ui.tb_conveyor_motor_forward_port.text() != '':
            result = DataBase.update(
                'conveyor_motor_forward_port',
                self.ui.tb_conveyor_motor_forward_port.text())
        if self.ui.tb_conveyor_motor_backward_port.text() != '':
            result = DataBase.update(
                'conveyor_motor_backward_port',
                self.ui.tb_conveyor_motor_backward_port.text())
        if self.ui.tb_conveyor_motor_time_1.text() != '':
            result = DataBase.update('conveyor_motor_time',
                                     self.ui.tb_conveyor_motor_time_1.text())
        if self.ui.tb_conveyor_motor_time_2.text() != '':
            result = DataBase.update('conveyor_motor_time_2',
                                     self.ui.tb_conveyor_motor_time_2.text())

        self.initHardwares()

    def exitProgram(self):
        # Server.turnOffSystemSMS(self.owner, self.system)
        self.delivery_item_flag = False
        self.close()
        QApplication.quit()
Example #14
0
class QGroundObjectTemplateLayout(QGroupBox):
    close_dialog_signal = Signal()

    def __init__(
            self,
            game: Game,
            ground_object: TheaterGroundObject,
            layout: QTgoLayout,
            layout_changed_signal: Signal(QTgoLayout),
            current_group_value: int,
    ):
        super().__init__()
        # Connect to the signal to handle template updates
        self.game = game
        self.ground_object = ground_object
        self.layout_changed_signal = layout_changed_signal
        self.layout_model = layout
        self.layout_changed_signal.connect(self.load_for_layout)

        self.current_group_value = current_group_value

        self.buy_button = QPushButton("Buy")
        self.buy_button.setEnabled(False)
        self.buy_button.clicked.connect(self.buy_group)

        self.template_layout = QGridLayout()
        self.setLayout(self.template_layout)

        self.template_grid = QGridLayout()
        self.template_layout.addLayout(self.template_grid, 0, 0, 1, 2)
        self.template_layout.addWidget(self.buy_button, 1, 1)
        stretch = QVBoxLayout()
        stretch.addStretch()
        self.template_layout.addLayout(stretch, 2, 0)

        # Load Layout
        self.load_for_layout(self.layout_model)

    def load_for_layout(self, layout: QTgoLayout) -> None:
        self.layout_model = layout
        # Clean the current grid
        self.layout_model.groups = defaultdict(list)
        for id in range(self.template_grid.count()):
            self.template_grid.itemAt(id).widget().deleteLater()
        for group_name, groups in self.layout_model.layout.groups.items():
            self.add_theater_group(group_name, self.layout_model.force_group,
                                   groups)
        self.group_template_changed()

    @property
    def cost(self) -> int:
        return self.layout_model.price - self.current_group_value

    @property
    def affordable(self) -> bool:
        return self.cost <= self.game.blue.budget

    def add_theater_group(self, group_name: str, force_group: ForceGroup,
                          groups: list[TgoLayoutGroup]) -> None:
        group_box = QGroupBox(group_name)
        vbox_layout = QVBoxLayout()
        for group in groups:
            try:
                group_row = QTgoLayoutGroupRow(force_group, group)
            except LayoutException:
                continue
            self.layout_model.groups[group_name].append(group_row.group_layout)
            group_row.group_template_changed.connect(
                self.group_template_changed)
            vbox_layout.addWidget(group_row)
        group_box.setLayout(vbox_layout)
        self.template_grid.addWidget(group_box)

    def group_template_changed(self) -> None:
        price = self.layout_model.price
        self.buy_button.setText(
            f"Buy [${price}M][-${self.current_group_value}M]")
        self.buy_button.setEnabled(self.affordable)
        if self.buy_button.isEnabled():
            self.buy_button.setToolTip(f"Buy the group for ${self.cost}M")
        else:
            self.buy_button.setToolTip("Not enough money to buy this group")

    def buy_group(self) -> None:
        if not self.affordable:
            # Something went wrong. Buy button should be disabled!
            logging.error("Not enough money to buy the group")
            return

        # Change the heading of the new group to head to the conflict
        self.ground_object.heading = (
            self.game.theater.heading_to_conflict_from(
                self.ground_object.position) or self.ground_object.heading)
        self.game.blue.budget -= self.cost
        self.ground_object.groups = []
        for group_name, groups in self.layout_model.groups.items():
            for group in groups:
                self.layout_model.force_group.create_theater_group_for_tgo(
                    self.ground_object,
                    group.layout,
                    f"{self.ground_object.name} ({group_name})",
                    self.game,
                    group.dcs_unit_type,  # Forced Type
                    group.amount,  # Forced Amount
                )
        self.close_dialog_signal.emit()