Example #1
0
class ConsoleDock(QDockWidget):
    def __init__(self, parent = None):
        super().__init__(parent)

        self.setObjectName("ConsoleDock")
        self.setWindowTitle(self.tr("Debug Console"))
        widget = QWidget(self)
        layout = QVBoxLayout(widget)
        layout.setContentsMargins(5, 5, 5, 5)
        self.plainTextEdit = QPlainTextEdit()
        self.plainTextEdit.setReadOnly(True)
        self.plainTextEdit.setStyleSheet(QString(
                                "QAbstractScrollArea {"
                                " background-color: black;"
                                " color:green;"
                                "}"
                                ))
        layout.addWidget(self.plainTextEdit)
        
        for output in PluginManager.objects(LoggingInterface):
            self.registerOutput(output)

        PluginManager.instance().objectAdded.connect(self.onObjectAdded)

        self.setWidget(widget)

    def __del__(self):
        pass

    def appendInfo(self, s):
        self.plainTextEdit.appendHtml('<pre>'+s+'</pre>')

    def appendError(self, s):
        self.plainTextEdit.appendHtml("<pre style='color:red'>"+s+"</pre>")


    def onObjectAdded(self, object):
        if type(object) == LoggingInterface:
            self.registerOutput(object)

    def registerOutput(self, output):
        output.info.connect(self.appendInfo)
        output.error.connect(self.appendError)
Example #2
0
class MainWidget(QWidget):
    def __init__(self, parent):
        super(QWidget, self).__init__(parent)

        self.main_layout = QVBoxLayout(self)
        self.tabs = QTabWidget(self)
        self.text_log_frame = None

        self.init_ui()

    def init_ui(self):
        """
        Initialize the interface
        """

        # layout the tab and textedit widgets inside the splitter
        splitter = QSplitter(self)
        splitter.setOrientation(Qt.Vertical)

        # Add tabs
        self.add_new_tab("Login...")
        self.add_new_tab("", False)
        self.tabs.currentChanged.connect(self.on_change)
        splitter.addWidget(self.tabs)

        # Add text log
        self.text_log_frame = QPlainTextEdit(self)
        splitter.addWidget(self.text_log_frame)

        # configure logging
        text_log_handler.logger_signals.log_message.connect(self.on_log)

        # add the splitter to the main layout
        self.main_layout.addWidget(splitter)

        # Set main layout
        self.setLayout(self.main_layout)

    @pyqtSlot(str)
    def on_log(self, html_msg):
        self.text_log_frame.moveCursor(QTextCursor.EndOfLine)
        self.text_log_frame.appendHtml(html_msg)

    @pyqtSlot()
    def on_change(self):
        """
        Add a new "+" tab and substitute "+" with "login" in the previous tab
        if the last tab is selected
        :return:
        """
        if self.tabs.currentIndex() == self.tabs.count() - 1:
            self.tabs.setTabText(self.tabs.currentIndex(), "Login...")

            uuid = self.tabs.currentWidget().uuid

            kill_btn = QPushButton()
            kill_btn.setIcon(self.style().standardIcon(
                QStyle.SP_DialogCloseButton))
            kill_btn.setToolTip('Close session')
            kill_btn.clicked.connect(lambda: self.on_close(uuid))
            self.tabs.tabBar().setTabButton(self.tabs.currentIndex(),
                                            QTabBar.RightSide, kill_btn)
            self.add_new_tab("", False)

    @pyqtSlot()
    def on_new(self):
        """
        Add a new "+" tab and substitute "+" with "login" in the previous tab
        if the last tab button is pressed
        :return:
        """
        last_tab = self.tabs.count() - 1
        self.tabs.setTabText(last_tab, "Login...")

        kill_btn = QPushButton()
        kill_btn.setIcon(self.style().standardIcon(
            QStyle.SP_DialogCloseButton))
        kill_btn.setToolTip('Close session')
        uuid = self.tabs.widget(last_tab).uuid
        kill_btn.clicked.connect(lambda: self.on_close(uuid))
        self.tabs.tabBar().setTabButton(last_tab, QTabBar.RightSide, kill_btn)
        self.add_new_tab("", False)
        self.tabs.setCurrentIndex(last_tab)

    def add_new_tab(self, session_name, show_close_btn=True):
        """
        Add a new tab in the tab widget
        :param session_name: name to be displayed
        :param show_close_btn: if true we add the close button
        :return:
        """
        new_tab = QSSHSessionWidget(self)
        uuid = new_tab.uuid
        self.tabs.addTab(new_tab, session_name)

        if show_close_btn:
            kill_btn = QPushButton()
            kill_btn.setIcon(self.style().standardIcon(
                QStyle.SP_DialogCloseButton))
            kill_btn.clicked.connect(lambda: self.on_close(uuid))
            kill_btn.setToolTip('Close session')
            self.tabs.tabBar().setTabButton(self.tabs.count() - 1,
                                            QTabBar.RightSide, kill_btn)
        else:
            kill_btn = QPushButton()
            ico = QIcon()
            ico.addFile(resource_path('gui/icons/plus.png'))
            kill_btn.setIcon(ico)
            kill_btn.clicked.connect(self.on_new)
            kill_btn.setToolTip('New session')
            self.tabs.tabBar().setTabButton(self.tabs.count() - 1,
                                            QTabBar.RightSide, kill_btn)

        new_tab.logged_in.connect(self.on_login)
        new_tab.sessions_changed.connect(self.on_sessions_changed)
        logger.debug("Added new tab " + str(uuid))

    @pyqtSlot(str, str)
    def on_login(self, session_name, uuid):
        for tab_id in range(0, self.tabs.count()):
            widget = self.tabs.widget(tab_id)
            if widget.uuid == uuid:
                self.tabs.setTabText(tab_id, session_name)

    @pyqtSlot()
    def on_close(self, uuid):
        # loop over the tabs and found the tab with the right uuid
        for tab_id in range(0, self.tabs.count()):
            widget = self.tabs.widget(tab_id)
            if widget.uuid == uuid:
                if self.tabs.currentIndex() == tab_id:
                    self.tabs.setCurrentIndex(tab_id - 1)
                # kill all the pending threads
                if widget.remote_connection_manager:
                    widget.remote_connection_manager.vncsession_kill()
                widget.kill_all_threads()
                self.tabs.removeTab(tab_id)
                widget.setParent(None)
                return

    @pyqtSlot(collections.deque)
    def on_sessions_changed(self, sessions_list):
        for tab_id in range(0, self.tabs.count()):
            widget = self.tabs.widget(tab_id)
            widget.session_combo.clear()
            widget.session_combo.addItems(sessions_list)
            widget.session_combo.activated.emit(0)
Example #3
0
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.IsBusy = False
        self.IsUpdating = False

        self.currentSelectedTabIndex = 0
        self.setWindowTitle("TOR")
        self.setWindowIcon(TORIcons.APP_ICON)

        self.cdvs = []
        self.cds = []
        clients = DBManager.getAllClients()
        layClientDetails = QHBoxLayout()
        layClientDetails.setContentsMargins(0, 0, 0, 0)
        grpClientDetailsRegions = [QGroupBox() for i in range(3)]
        for g in grpClientDetailsRegions:
            g.setObjectName("ClientGroup")
        grpClientDetailsRegions[0].setTitle("Front")
        grpClientDetailsRegions[1].setTitle("Middle")
        grpClientDetailsRegions[2].setTitle("Back")
        layClientDetailsRegions = [QGridLayout() for i in range(3)]
        for i in range(3):
            #layClientDetailsRegions[i].setContentsMargins(0, 0, 0, 0)
            #layClientDetailsRegions[i].setSpacing(0)
            grpClientDetailsRegions[i].setLayout(layClientDetailsRegions[i])
            #grpClientDetailsRegions[i].setContentsMargins(0, 0, 0, 0)
            for j in range(3):
                for k in range(3):
                    c = clients[i*9 + j*3 + k]
                    cdv = ClientDetailView()
                    cd = ClientDetails()
                    cd.Id = c.Id
                    cd.IP = c.IP
                    cd.Material = c.Material
                    cd.Position = c.Position
                    cd.Latin = c.Latin
                    cd.AllowUserMode = c.AllowUserMode
                    cd.IsActive = c.IsActive
                    cdv.clientDetails = cd
                    cdv.grpMainGroup.setTitle("#{}: {}...".format(cd.Position, cd.Latin[0:9]))
                    layClientDetailsRegions[i].addWidget(cdv, k, 3*i + j)
                    self.cdvs.append(cdv)
                    self.cds.append(cd)
            layClientDetails.addWidget(grpClientDetailsRegions[i])
        wdgClientDetails = QWidget()
        wdgClientDetails.setLayout(layClientDetails)


        self.btnStartAllTORPrograms = QPushButton()
        self.btnStartAllTORPrograms.setText("START installation")
        self.btnStartAllTORPrograms.clicked.connect(self.btnStartAllTORPrograms_clicked)
        self.btnStartAllTORPrograms.setStyleSheet("QPushButton { font-weight: bold }; ")

        self.btnStopAllTORPrograms = QPushButton()
        self.btnStopAllTORPrograms.setText("STOP installation")
        self.btnStopAllTORPrograms.clicked.connect(self.btnStopAllTORPrograms_clicked)
        self.btnStopAllTORPrograms.setStyleSheet("QPushButton { font-weight: bold }; ")

        self.btnStartAllClientService = QPushButton()
        self.btnStartAllClientService.setText("Start all active TORClients")
        self.btnStartAllClientService.clicked.connect(self.btnStartAllClientService_clicked)

        self.btnStopAllClientService = QPushButton()
        self.btnStopAllClientService.setText("Stop all TORClients")
        self.btnStopAllClientService.clicked.connect(self.btnStopAllClientService_clicked)

        self.btnSaveSettings = QPushButton()
        self.btnSaveSettings.setText("Save Settings")
        self.btnSaveSettings.clicked.connect(self.btnSaveSettings_clicked)

        self.btnRestoreSettings = QPushButton()
        self.btnRestoreSettings.setText("Restore Settings")
        self.btnRestoreSettings.clicked.connect(self.btnRestoreSettings_clicked)

        self.btnStartTORServer = QPushButton()
        self.btnStartTORServer.setText("Start TOR Server")
        self.btnStartTORServer.clicked.connect(self.btnStartTORServer_clicked)

        self.btnStopTORServer = QPushButton()
        self.btnStopTORServer.setText("Stop TOR Server")
        self.btnStopTORServer.clicked.connect(self.btnStopTORServer_clicked)

        self.btnStartTORInteractive = QPushButton()
        self.btnStartTORInteractive.setText("Start Visitor App")
        self.btnStartTORInteractive.clicked.connect(self.btnStartTORInteractive_clicked)

        self.btnStopTORInteractive = QPushButton()
        self.btnStopTORInteractive.setText("Stop Visitor App")
        self.btnStopTORInteractive.clicked.connect(self.btnStopTORInteractive_clicked)

        self.btnEndAllUserModes = QPushButton()
        self.btnEndAllUserModes.setText("End all visitor control")
        self.btnEndAllUserModes.clicked.connect(self.btnEndAllUserModes_clicked)

        self.btnTurnOnLEDs = QPushButton()
        self.btnTurnOnLEDs.setText("Turn ON all LEDs")
        self.btnTurnOnLEDs.clicked.connect(self.btnTurnOnLEDs_clicked)

        self.btnTurnOffLEDs = QPushButton()
        self.btnTurnOffLEDs.setText("Turn OFF all LEDs")
        self.btnTurnOffLEDs.clicked.connect(self.btnTurnOffLEDs_clicked)

        self.btnUpdateDashboard = QPushButton()
        self.btnUpdateDashboard.setText("Update dashboard")
        self.btnUpdateDashboard.clicked.connect(self.btnUpdateDashboard_clicked)

        self.lblLastUpdateTime = QLabel()

        spacerSize = 30
        layDashboardButtons = QVBoxLayout()
        layDashboardButtons.addSpacing(spacerSize)
        layDashboardButtons.addWidget(QLabel("<h3>Installation</h3>"))
        layDashboardButtons.addWidget(self.btnStartAllTORPrograms)
        layDashboardButtons.addWidget(self.btnStopAllTORPrograms)
        layDashboardButtons.addSpacing(spacerSize)
        layDashboardButtons.addWidget(QLabel("Clients"))
        layDashboardButtons.addWidget(self.btnStartAllClientService)
        layDashboardButtons.addWidget(self.btnStopAllClientService)
        layDashboardButtons.addSpacing(spacerSize)
        layDashboardButtons.addWidget(QLabel("LEDs"))
        layDashboardButtons.addWidget(self.btnTurnOnLEDs)
        layDashboardButtons.addWidget(self.btnTurnOffLEDs)
        layDashboardButtons.addSpacing(spacerSize)
        layDashboardButtons.addWidget(QLabel("Server"))
        layDashboardButtons.addWidget(self.btnStartTORServer)
        layDashboardButtons.addWidget(self.btnStopTORServer)
        layDashboardButtons.addSpacing(spacerSize)
        layDashboardButtons.addWidget(QLabel("Visitor App"))
        layDashboardButtons.addWidget(self.btnStartTORInteractive)
        layDashboardButtons.addWidget(self.btnStopTORInteractive)
        layDashboardButtons.addWidget(self.btnEndAllUserModes)
        layDashboardButtons.addSpacerItem(QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Expanding))
        layDashboardButtons.addWidget(self.btnUpdateDashboard)
        layDashboardButtons.addWidget(self.lblLastUpdateTime)


        wdgDashboardButtons = QWidget()
        wdgDashboardButtons.setLayout(layDashboardButtons)

        """
        layDashboardButtonsTop = QHBoxLayout()
        layDashboardButtonsTop.addWidget(self.btnStartAllClientService)
        layDashboardButtonsTop.addWidget(self.btnStopAllClientService)
        layDashboardButtonsTop.addWidget(self.btnSaveSettings)
        layDashboardButtonsTop.addWidget(self.btnRestoreSettings)
        wdgDashboardButtonsTop = QWidget()
        wdgDashboardButtonsTop.setLayout(layDashboardButtonsTop)

        layDashboardButtons2 = QHBoxLayout()
        layDashboardButtons2.addWidget(self.btnTurnOnLEDs)
        layDashboardButtons2.addWidget(self.btnTurnOffLEDs)
        wdgDashboardButtons2 = QWidget()
        wdgDashboardButtons2.setLayout(layDashboardButtons2)

        layDashboardButtonsBottom = QHBoxLayout()
        layDashboardButtonsBottom.addWidget(self.btnStartTORServer)
        layDashboardButtonsBottom.addWidget(self.btnStopTORServer)
        layDashboardButtonsBottom.addWidget(self.btnStartTORInteractive)
        layDashboardButtonsBottom.addWidget(self.btnStopTORInteractive)
        layDashboardButtonsBottom.addWidget(self.btnEndAllUserModes)
        wdgDashboardButtonsBottom = QWidget()
        wdgDashboardButtonsBottom.setLayout(layDashboardButtonsBottom)
        """

        layDashboard = QHBoxLayout()
        #layDashboard.addWidget(wdgDashboardButtonsTop)
        #layDashboard.addWidget(wdgDashboardButtons2)
        layDashboard.addWidget(wdgClientDetails)
        #layDashboard.addWidget(wdgDashboardButtonsBottom)
        layDashboard.addWidget(wdgDashboardButtons)

        wdgDashboard = QWidget()
        wdgDashboard.setLayout(layDashboard)


        programNames = DBManager.getAllJobProgramNames()
        self.jobProgramNames = [pn.Name for pn in programNames]
        self.cmbTour = QComboBox()
        self.cmbTour.insertItem(-1, NEW_PROGRAM_NAME)
        for i in range(len(self.jobProgramNames)):
            self.cmbTour.insertItem(i, self.jobProgramNames[i])
        self.cmbTour.currentIndexChanged.connect(self.cmbTour_currentIndexChanged)
        self.btnStartTour = QPushButton("Start")
        self.btnStartTour.clicked.connect(self.btnStartTour_clicked)
        self.btnEditTour = QPushButton("Edit")
        self.btnEditTour.clicked.connect(self.btnEditTour_clicked)

        layTourSelection = QHBoxLayout()
        layTourSelection.addWidget(QLabel("Program: "))
        layTourSelection.addWidget(self.cmbTour)
        layTourSelection.addSpacing(100)
        layTourSelection.addWidget(self.btnStartTour)
        layTourSelection.addWidget(self.btnEditTour)
        layTourSelection.addSpacerItem(QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Expanding))

        wdgTourSelection = QWidget()
        wdgTourSelection.setLayout(layTourSelection)

        self.jobListWidgets = []
        layJobList = QGridLayout()
        jobs = DBManager.getCurrentJobs()
        layJobList.addWidget(QLabel("<h3>Box</h3>"), 0, 0)
        layJobList.addWidget(QLabel("<h3>Quit</h3>"), 0, 1)
        layJobList.addWidget(QLabel("<h3>Wait</h3>"), 0, 2)
        layJobList.addWidget(QLabel("<h3>Run</h3>"), 0, 3)
        layJobList.addWidget(QLabel("<h3>Run & Wait</h3>"), 0, 4)
        layJobList.addWidget(QLabel("<h3>Parameters</h3>"), 0, 5)
        chkQuitAll = QCheckBox()
        chkQuitAll.clicked.connect(self.chkQuitAll_clicked)
        chkWaitAll = QCheckBox()
        chkWaitAll.clicked.connect(self.chkWaitAll_clicked)
        chkRunAll = QCheckBox()
        chkRunAll.clicked.connect(self.chkRunAll_clicked)
        chkRunAndWaitAll = QCheckBox()
        chkAllGroup = QButtonGroup(self)
        chkAllGroup.addButton(chkQuitAll)
        chkAllGroup.addButton(chkWaitAll)
        chkAllGroup.addButton(chkRunAll)
        chkAllGroup.addButton(chkRunAndWaitAll)
        chkRunAndWaitAll.clicked.connect(self.chkRunAndWaitAll_clicked)
        txtParametersAll = QLineEdit()
        txtParametersAll.textChanged.connect(self.txtParametersAll_textChanged)
        layJobList.addWidget(chkQuitAll, 1, 1)
        layJobList.addWidget(chkWaitAll, 1, 2)
        layJobList.addWidget(chkRunAll, 1, 3)
        layJobList.addWidget(chkRunAndWaitAll, 1, 4)
        layJobList.addWidget(txtParametersAll, 1, 5)
        row = 2
        clientCount = 0
        for c in self.cds:
            chkQuit = QCheckBox()
            chkWait = QCheckBox()
            chkRun = QCheckBox()
            chkRunAndWait = QCheckBox()
            txtParameters = QLineEdit()
            chkGroup = QButtonGroup(self)
            chkGroup.addButton(chkQuit)
            chkGroup.addButton(chkWait)
            chkGroup.addButton(chkRun)
            chkGroup.addButton(chkRunAndWait)
            layJobList.addWidget(QLabel("Pos {}: {}".format(c.Position, c.Latin)), row, 0)
            layJobList.addWidget(chkQuit, row, 1)
            layJobList.addWidget(chkWait, row, 2)
            layJobList.addWidget(chkRun, row, 3)
            layJobList.addWidget(chkRunAndWait, row, 4)
            layJobList.addWidget(txtParameters, row, 5)
            self.jobListWidgets.append([c.Id, chkQuit, chkWait, chkRun, chkRunAndWait, txtParameters])
            row += 1
            clientCount += 1
            if clientCount % 9 == 0 and clientCount < 27:
                line = QFrame()
                line.setGeometry(QRect())
                line.setFrameShape(QFrame.HLine)
                line.setFrameShadow(QFrame.Sunken)
                layJobList.addWidget(line, row, 0, 1, 6)
                row += 1

        self.fillJobList(jobs)

        self.wdgJobList = QWidget()
        self.wdgJobList.setEnabled(False)
        self.wdgJobList.setLayout(layJobList)

        lblJobDescriptionText = QLabel(
            """
                The parameters for Job 'W' are of the form 't' where 't' is optional<br>check every t seconds if there is another job to do
                <br><br><br> 
                The parameters for Job 'RW' are of the form 'r w t'<br>run r times, then wait w times for t seconds
            """)
        lblJobDescriptionText.setAlignment(Qt.AlignTop)

        layJobListAndDescriptions = QHBoxLayout()
        layJobListAndDescriptions.addWidget(self.wdgJobList)
        layJobListAndDescriptions.addSpacing(100)
        layJobListAndDescriptions.addWidget(lblJobDescriptionText)
        layJobListAndDescriptions.addSpacerItem(QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Expanding))

        wdgJobListAndDescriptions = QWidget()
        wdgJobListAndDescriptions.setLayout(layJobListAndDescriptions)

        layJobOverview = QVBoxLayout()
        #layJobOverview.addWidget(QLabel("Job Overview"))
        layJobOverview.addWidget(wdgTourSelection)
        layJobOverview.addWidget(wdgJobListAndDescriptions)

        wdgJobOverivew = QWidget()
        wdgJobOverivew.setLayout(layJobOverview)

        # Client Details

        self.cmbClient = QComboBox()
        self.cmbClient.setFixedWidth(180)
        self.cmbClient.insertItem(-1, "All", -1)
        for c in self.cds:
            self.cmbClient.insertItem(c.Position, "#{}: {}".format(c.Position, c.Latin), c.Position)
        self.cmbClient.currentIndexChanged.connect(self.cmbClient_currentIndexChanged)

        self.btnRereshClientDetails = QPushButton("Refresh")
        self.btnRereshClientDetails.clicked.connect(self.btnRereshClientDetails_clicked)

        layClientSelection = QHBoxLayout()
        layClientSelection.addWidget(QLabel("Box: "))
        layClientSelection.addWidget(self.cmbClient)
        layClientSelection.addSpacing(100)
        layClientSelection.addWidget(self.btnRereshClientDetails)
        layClientSelection.addSpacerItem(QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Fixed))

        wdgClientSelection = QWidget()
        wdgClientSelection.setLayout(layClientSelection)

        self.tblResults = QTableView()
        self.tblResults.horizontalHeader().setStretchLastSection(True)
        self.tblResults.setWordWrap(False)
        self.tblResults.setTextElideMode(Qt.ElideRight)

        self.clientDetailInfoList = [
            ["Name", QLabel("-")],
            ["Position", QLabel("-")],
            ["ID", QLabel("-")],
            ["IP", QLabel("-")],
            ["MAC", QLabel("-")],
            ["Allow User Mode", QLabel("-")],
            ["User Mode Active", QLabel("-")],
            ["Is Active", QLabel("-")],
            ["Current State", QLabel("-")],
            ["Current Job", QLabel("-")],
            ["Recent results", QLabel("-")],
            ["Average contribution", QLabel("-")],
            ["Average result (3.5)", QLabel("-")],
            ["Results last hour", QLabel("-")],
            ["Results last 2 hours", QLabel("-")],
            ["Results today", QLabel("-")],
            ["Results Total", QLabel("-")]
        ]
        layClientDetailInfos = QGridLayout()
        for num, (text, widget) in enumerate(self.clientDetailInfoList):
            layClientDetailInfos.addWidget(QLabel(text), num, 0)
            layClientDetailInfos.addWidget(widget, num, 1)

        grpClientDetailInfos = QGroupBox()
        grpClientDetailInfos.setTitle("Details")
        grpClientDetailInfos.setLayout(layClientDetailInfos)

        layClientDetailsTop = QHBoxLayout()
        layClientDetailsTop.addWidget(self.tblResults)
        layClientDetailsTop.addSpacing(100)
        layClientDetailsTop.addWidget(grpClientDetailInfos)
        layClientDetailsTop.addSpacerItem(QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Fixed))

        wdgClientDetailsTop = QWidget()
        wdgClientDetailsTop.setLayout(layClientDetailsTop)


        self.tblLogMessages = QTableView()
        self.tblLogMessages.horizontalHeader().setStretchLastSection(True)
        self.tblLogMessages.setWordWrap(False)
        self.tblLogMessages.setTextElideMode(Qt.ElideRight)

        self.tblResultStatistics = QTableView()
        #self.tblResultStatistics.horizontalHeader().setStretchLastSection(True)
        self.tblResultStatistics.setWordWrap(False)
        self.tblResultStatistics.setTextElideMode(Qt.ElideRight)
        self.tblResultStatistics.setSortingEnabled(True)

        layClientDetailsBottom = QHBoxLayout()
        layClientDetailsBottom.addWidget(self.tblLogMessages)
        layClientDetailsBottom.addSpacing(20)
        layClientDetailsBottom.addWidget(self.tblResultStatistics)
        #layClientDetailsBottom.addSpacerItem(QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Fixed))

        wdgClientDetailsBottom = QWidget()
        wdgClientDetailsBottom.setLayout(layClientDetailsBottom)

        layDetails = QVBoxLayout()
        layDetails.addWidget(wdgClientSelection)
        layDetails.addWidget(QLabel("Results"))
        layDetails.addWidget(wdgClientDetailsTop)
        layDetails.addWidget(QLabel("Log messages"))
        layDetails.addWidget(wdgClientDetailsBottom)


        wdgDetails = QWidget()
        wdgDetails.setLayout(layDetails)


        layTORServer = QVBoxLayout()
        layTORServer.addWidget(QLabel("TOR server"))

        wdgTORServer = QWidget()
        wdgTORServer.setLayout(layTORServer)

        self.clientDetailsTabIndex = 2

        self.tabDashboard = QTabWidget()
        self.tabDashboard.addTab(wdgDashboard, "Dashboard")
        #self.tabDashboard.addTab(wdgTORServer, "TORServer")
        self.tabDashboard.addTab(wdgJobOverivew, "Jobs")
        self.tabDashboard.addTab(wdgDetails, "Detail View")
        self.dashboardTabIndex = 0
        self.tabDashboard.currentChanged.connect(self.tabDashboard_currentChanged)

        self.txtStatus = QPlainTextEdit()
        self.txtStatus.setReadOnly(True)

        layMain = QVBoxLayout()
        layMain.addWidget(self.tabDashboard)
        layMain.addWidget(self.txtStatus)

        wdgMain = QWidget()
        wdgMain.setLayout(layMain)
        self.setCentralWidget(wdgMain)

        self.initDashboard()
        self.updateDashboard()
        timerFetchData = QTimer(self)
        timerFetchData.timeout.connect(self.updateDashboardFromTimer)
        timerFetchData.start(120 * 1000)

    ###############
    ### methods ###
    ###############

    def tabDashboard_currentChanged(self, index):
        if index == self.clientDetailsTabIndex:
            self.loadAllClientDetails()
        self.currentSelectedTabIndex = index

    def executeCommandOnTORServer(self, cmd, timeout=DEFAULT_TIMEOUT_SERVER):
        val = -1
        with WaitCursor():
            cmdSSH = TORCommands.SERVER_SSH_CONNECTION.format(tsl.PATH_TO_SSH_KEY, tsl.SERVER_IP)
            cmdFull = cmdSSH + " \"" + cmd + "\""
            print("SERVEXE: {}".format(cmdFull))
            if window is not None:
                window.addStatusText("<font color=\"Red\">{}</font>".format(cmdFull))
            val = executeCommand(cmdFull, timeout=timeout)
        return val

    def __executeCommandOnClient(self, client, cmd, timeout=DEFAULT_TIMEOUT_SSH, onlyActive=False):
        if client.IsOnline:
            if not onlyActive or client.IsActive:
                client.executeSSH(cmd, timeout=timeout)

    def executeCommandOnAllClients(self, cmd, timeout=DEFAULT_TIMEOUT_SSH, onlyActive=False):
        threadPool = concurrent.futures.ThreadPoolExecutor(THREAD_POOL_SIZE)
        threadFutures = [threadPool.submit(self.__executeCommandOnClient, c, cmd, timeout, onlyActive) for c in self.cds]
        concurrent.futures.wait(threadFutures)

    def checkOnlineAndServiceStatusForClient(self, client):
        client.checkOnlineStatus()
        client.getClientServiceStatus()

    def initDashboard(self):
        for cdv in self.cdvs:
            cdv.lblIsOnline.setToolTip("Id: {}\nIP: {}\nMaterial: {}\nLatin name: {}".format(cdv.clientDetails.Id, cdv.clientDetails.IP, cdv.clientDetails.Material, cdv.clientDetails.Latin))

    def updateDashboardFromTimer(self):
        if not self.IsBusy and self.tabDashboard.currentIndex() == self.dashboardTabIndex:
            self.updateDashboard()

    def updateDashboard(self):
        if self.IsUpdating:
            return
        self.IsUpdating = True
        print("updateDashboard")
        jobs = DBManager.getCurrentJobs()
        for j in jobs:
            for c in self.cds:
                if c.Id == j.Id:
                    c.CurrentJobCode = j.JobCode
                    c.CurrentJobParameters = j.JobParameters
                    break
        results = DBManager.getAllClientStatistics()
        for r in results:
            for c in self.cds:
                if c.Id == r.Id:
                    c.ResultAverage = r.ResultAverage
                    c.ResultStddev = r.ResultStddev
                    break
        data = DBManager.getAllClients()
        for d in data:
            for c in self.cds:
                if c.Id == d.Id:
                    c.IP = d.IP
                    c.Material = d.Material
                    c.Position = d.Position
                    c.Latin = d.Latin
                    c.AllowUserMode = d.AllowUserMode
                    c.IsActive = d.IsActive
                    break
        for cdv in self.cdvs:
            cdv.lblCurrentJob.setText("{} {}".format(cdv.clientDetails.CurrentJobCode, cdv.clientDetails.CurrentJobParameters))
            cdv.lblResultAverage.setText("{:.2f}±{:.2f}".format(cdv.clientDetails.ResultAverage, cdv.clientDetails.ResultStddev))
            cdv.lblResultStddev.setText("+-{}".format(cdv.clientDetails.ResultStddev))
            if cdv.clientDetails.IsBadStatistics():
                cdv.lblResultAverage.setStyleSheet("QLabel { color: \"red\"; }")
                cdv.lblResultStddev.setStyleSheet("QLabel { color: \"red\"; }")
            else:
                cdv.lblResultAverage.setStyleSheet("")
                cdv.lblResultStddev.setStyleSheet("")

        threadPool = concurrent.futures.ThreadPoolExecutor(THREAD_POOL_SIZE)
        threadFutures = [threadPool.submit(self.checkOnlineAndServiceStatusForClient, cdv.clientDetails) for cdv in self.cdvs]
        concurrent.futures.wait(threadFutures)

        for cdv in self.cdvs:
            cdv.chkUserMode.setChecked(cdv.clientDetails.AllowUserMode)
            cdv.chkIsActivated.setChecked(cdv.clientDetails.IsActive)
            cdv.refreshClientServiceStatus()
            if cdv.clientDetails.IsOnline:
                cdv.lblIsOnline.setPixmap(TORIcons.LED_GREEN)
            else:
                cdv.lblIsOnline.setPixmap(TORIcons.LED_RED)
        self.lblLastUpdateTime.setText("last update: {}".format(datetime.now().strftime("%H:%M:%S")))
        print("updateDashboard finished")
        self.IsUpdating = False

    def addSpacerLineToStatusText(self):
        self.txtStatus.appendPlainText("----------------------")

    def addStatusText(self, text, spacerLineBefore=False, spacerLineAfter=False):
        if QThread.currentThread() != self.thread():
            return
        if spacerLineBefore:
            self.addSpacerLineToStatusText()
        self.txtStatus.appendHtml(text)
        if spacerLineAfter:
            self.addSpacerLineToStatusText()
        self.txtStatus.moveCursor(QTextCursor.End)
        app.processEvents()

    def btnStartAllTORPrograms_clicked(self):
        with WaitCursor():
            DBManager.clearAllCurrentStates()
            self.executeCommandOnTORServer(TORCommands.SERVER_SERVICE_START)
            self.executeCommandOnAllClients(TORCommands.CLIENT_SERVICE_START, onlyActive=True)
            self.executeCommandOnTORServer(TORCommands.INTERACTIVE_START)

    def btnStopAllTORPrograms_clicked(self):
        with WaitCursor():
            jobs = []
            for c in self.cds:
                j = copy.deepcopy(DefaultJobs.WAIT)
                j.ClientId = c.Id
                jobs.append(j)
            DBManager.saveJobs(jobs)

            allWaiting = False
            while allWaiting == False:
                time.sleep(5)
                busyClients = DBManager.getBusyClients()
                if len(busyClients) == 0:
                    allWaiting = True
                else:
                    msg = "waiting for {} boxes to finish...".format(len(busyClients))
                    log.info(msg)
                    self.addStatusText(msg)

            msg = "all boxes ready waiting..."
            log.info(msg)
            self.addStatusText(msg)
            time.sleep(3)
            self.executeCommandOnTORServer(TORCommands.INTERACTIVE_STOP)
            self.executeCommandOnAllClients(TORCommands.CLIENT_SERVICE_STOP)
            self.executeCommandOnTORServer(TORCommands.SERVER_SERVICE_STOP)
            DBManager.clearAllCurrentStates()
            time.sleep(3)
            self.executeCommandOnAllClients(TORCommands.CLIENT_TURN_OFF_LEDS)

    def btnStartAllClientService_clicked(self):
        print("start")
        with WaitCursor():
            self.executeCommandOnAllClients(TORCommands.CLIENT_SERVICE_START, onlyActive=True)
        self.updateDashboard()

    def btnStopAllClientService_clicked(self):
        print("stop")
        with WaitCursor():
            self.executeCommandOnAllClients(TORCommands.CLIENT_SERVICE_STOP)
        self.updateDashboard()

    def btnSaveSettings_clicked(self):
        print("saved")

    def btnRestoreSettings_clicked(self):
        print("restored")

    def btnStartTORServer_clicked(self):
        self.executeCommandOnTORServer(TORCommands.SERVER_SERVICE_START)
        print("start TORServer")

    def btnStopTORServer_clicked(self):
        self.executeCommandOnTORServer(TORCommands.SERVER_SERVICE_STOP)
        print("stop TORServer")

    def btnStartTORInteractive_clicked(self):
        self.executeCommandOnTORServer(TORCommands.INTERACTIVE_START)
        print("start tor interactive")

    def btnStopTORInteractive_clicked(self):
        self.executeCommandOnTORServer(TORCommands.INTERACTIVE_STOP)
        print("stop tor interactive")

    def btnEndAllUserModes_clicked(self):
        print("ended all user modes")

    def btnTurnOnLEDs_clicked(self):
        self.executeCommandOnAllClients(TORCommands.CLIENT_TURN_ON_LEDS)
        print("turn on LEDs")

    def btnTurnOffLEDs_clicked(self):
        self.executeCommandOnAllClients(TORCommands.CLIENT_TURN_OFF_LEDS)
        print("turn off LEDs")

    def btnUpdateDashboard_clicked(self):
        with WaitCursor():
            self.updateDashboard()

    ############
    ### Jobs ###
    ############

    def fillJobList(self, jobs):
        for j in jobs:
            for w in self.jobListWidgets:
                if j.Id == w[0]:
                    if j.JobCode == DefaultJobs.QUIT.JobCode:
                        w[1].setChecked(True)
                    elif j.JobCode == DefaultJobs.WAIT.JobCode:
                        w[2].setChecked(True)
                    elif j.JobCode == DefaultJobs.RUN.JobCode:
                        w[3].setChecked(True)
                    elif j.JobCode == DefaultJobs.RUN_AND_WAIT.JobCode:
                        w[4].setChecked(True)
                    if j.JobParameters != "None":
                        w[5].setText(j.JobParameters)
                    break

    def cmbTour_currentIndexChanged(self, index):
        programName = self.cmbTour.currentText()
        if programName != NEW_PROGRAM_NAME:
            programJobs = DBManager.getJobsByProgramName(programName)
            self.fillJobList(programJobs)
            self.wdgJobList.setEnabled(False)

    def btnStartTour_clicked(self):
        jobs = []
        for w in self.jobListWidgets:
            j = Job()
            if w[1].isChecked():
                j = copy.deepcopy(DefaultJobs.QUIT)
            elif w[2].isChecked():
                j = copy.deepcopy(DefaultJobs.WAIT)
            elif w[3].isChecked():
                j = copy.deepcopy(DefaultJobs.RUN)
            elif w[4].isChecked():
                j = copy.deepcopy(DefaultJobs.RUN_AND_WAIT)
            j.JobParameters = w[5].text()
            j.ClientId = w[0]
            jobs.append(j)
        DBManager.saveJobs(jobs)

    def btnEditTour_clicked(self):
        self.cmbTour.setCurrentText(NEW_PROGRAM_NAME)
        self.wdgJobList.setEnabled(True)

    def chkQuitAll_clicked(self, checked):
        for w in self.jobListWidgets:
            w[1].setChecked(checked)

    def chkWaitAll_clicked(self, checked):
        for w in self.jobListWidgets:
            w[2].setChecked(checked)

    def chkRunAll_clicked(self, checked):
        for w in self.jobListWidgets:
            w[3].setChecked(checked)

    def chkRunAndWaitAll_clicked(self, checked):
        for w in self.jobListWidgets:
            w[4].setChecked(checked)

    def txtParametersAll_textChanged(self, text):
        for w in self.jobListWidgets:
            w[5].setText(text)

    ###############
    ### Details ###
    ###############

    def getClientDetailsByPosition(self, position):
        return self.cds[position - 1]

    def showDataInTable(self, data, table, tableModel):
        model = tableModel(data)
        proxyModel = QSortFilterProxyModel()
        proxyModel.setSourceModel(model)
        table.setModel(proxyModel)

    def setTableWidths(self, table, widths):
        for i, w in enumerate(widths):
            table.setColumnWidth(i, w)

    def setClientDetailInfoListText(self, num, text):
        self.clientDetailInfoList[num][1].setText(str(text))

    def loadResultStatistics(self):
        resultStatistics = DBManager.getResultStatistics(ts.DICE_RESULT_EVENT_SOURCE)
        self.showDataInTable(resultStatistics, self.tblResultStatistics, ResultStatisticsTableModel)
        w = 50
        self.setTableWidths(self.tblResultStatistics, [w, 100, w, w, w, w, w, w, w, w])

    def loadAllClientDetails(self):
        logMessages = DBManager.getClientLog()
        self.showDataInTable(logMessages, self.tblLogMessages, LogMessageTableModel)
        self.setTableWidths(self.tblLogMessages, [50, 150, 50, 150, 200, 200])

        diceResults = DBManager.getResults()
        self.showDataInTable(diceResults, self.tblResults, DiceResultTableModel)
        self.setTableWidths(self.tblResults, [50, 150, 50, 50, 50, 50, 200])

        self.loadResultStatistics()

        for i in range(len(self.clientDetailInfoList)):
            self.setClientDetailInfoListText(i, "---")

    def loadClientDetails(self, client):
        logMessages = DBManager.getClientLogByClientId(client.Id)
        self.showDataInTable(logMessages, self.tblLogMessages, LogMessageTableModel)
        self.setTableWidths(self.tblLogMessages, [50, 150, 200, 200])

        diceResults = DBManager.getResultsByClientId(client.Id)
        self.showDataInTable(diceResults, self.tblResults, DiceResultTableModel)
        self.setTableWidths(self.tblResults, [50, 50, 50, 50, 200])

        self.loadResultStatistics()

        self.setClientDetailInfoListText(0, client.Latin)
        self.setClientDetailInfoListText(1, client.Position)
        self.setClientDetailInfoListText(2, client.Id)
        self.setClientDetailInfoListText(3, client.IP)
        self.setClientDetailInfoListText(4, "")
        self.setClientDetailInfoListText(5, "YES" if client.AllowUserMode else "NO")
        self.setClientDetailInfoListText(6, "YES" if client.IsActive else "NO")
        self.setClientDetailInfoListText(7, client.Position)

        clientContributions = DBManager.getAllClientResultContribution(TAKE_N_RESULTS_FOR_RECENT_CONTRIBUTIONS)
        for cc in clientContributions:
            if cc.Id == client.Id:
                self.setClientDetailInfoListText(11, "{:.2f}".format(cc.Contribution))
                self.setClientDetailInfoListText(12, "{:.2f}".format(cc.AverageResult))
                break

    def reloadClientDetailsBySelectedIndex(self, index):
        position = self.cmbClient.itemData(index)
        if position == -1:
            self.addStatusText("show details for all clients")
            self.loadAllClientDetails()
        else:
            c = self.getClientDetailsByPosition(position)
            self.addStatusText("select client at position {}".format(c.Position))
            self.loadClientDetails(c)

    def cmbClient_currentIndexChanged(self, index):
        self.reloadClientDetailsBySelectedIndex(index)

    def btnRereshClientDetails_clicked(self):
        index = self.cmbClient.currentIndex()
        self.reloadClientDetailsBySelectedIndex(index)
Example #4
0
class Window(object):
    def __init__(self, path_to_options):
        """
        Инициализируем наше окно
        :param path_to_options: путь до директории с файлами настройки
        """

        self.path_to_options = path_to_options

        # Устанавливаем окно с его настройками
        self.window = QMainWindow()
        self.window.setWindowState(Qt.WindowMaximized)
        self.window.setWindowTitle(
            "Программа для задачи №9. Кишкин Владислав 381903-3")
        self.window.setWindowIcon(QIcon('options\\favicon.ico'))

        # Меню
        self.window.statusBar()
        menubar = self.window.menuBar()
        self.dialog_message = QMessageBox()
        self.dialog_input = QInputDialog()
        self.dialog_message.setStyleSheet('QMessageBox { font-size: 13pt;}; ')

        # Кнопка "Начать"
        self.workMenu = QAction('&Начать')
        self.workMenu.triggered.connect(self.work)

        # Кнопка "Условия задачи"
        self.conditionMenu = QAction('&Условия задачи')
        self.conditionMenu.triggered.connect(self.condition)

        # Кнопка "О методе"
        self.rkm = QAction('&О методе')
        self.rkm.triggered.connect(self.rungeKutta_method)

        # Кнопка "Очистить график"
        self.clearGr = QAction('&Очистить график')
        self.clearGr.triggered.connect(self.clearGraph)

        # Кнопка "Очистить справку"
        self.clearRe = QAction('&Очистить справку')
        self.clearRe.triggered.connect(self.clearReference)

        menubar.addAction(self.workMenu)
        menubar.addAction(self.conditionMenu)
        menubar.addAction(self.rkm)
        menubar.addAction(self.clearGr)
        menubar.addAction(self.clearRe)

        # Настраиваем макет
        self.initUI()

        self.window.show()

    def initUI(self):
        """
        Строим начальный макет окна
        """

        # Порядок метода
        self.p = 3
        """-----------------------------------------Гланый блок с подблоками-----------------------------------------"""
        main = QWidget(self.window)
        mainHBox1 = QHBoxLayout()
        self.mainHBox2 = QHBoxLayout()
        self.mainHBox2.setSpacing(21)
        mainVBox = QVBoxLayout()
        """-------------------------------------------Блок с настройками---------------------------------------------"""
        option = QWidget(main)
        option.setStyleSheet('QLineEdit { border: none; }'
                             'QLabel { font-size: 10pt; };')
        option.setFixedSize(910, 530)
        optionVBox = QVBoxLayout()
        optionVBox.setSpacing(13)

        optionHBox1 = QHBoxLayout()
        optionHBox2 = QHBoxLayout()
        optionHBox3 = QHBoxLayout()
        optionHBox4 = QHBoxLayout()
        optionHBox5 = QHBoxLayout()
        optionHBox6 = QHBoxLayout()
        optionHBox7 = QHBoxLayout()
        optionHBox8 = QHBoxLayout()
        optionHBox9 = QHBoxLayout()
        optionHBox10 = QHBoxLayout()
        optionHBox11 = QHBoxLayout()
        optionHBox12 = QHBoxLayout()
        optionHBox13 = QHBoxLayout()

        # Элементы
        label_Var = QLabel('Параметры системы')
        label_Var.setStyleSheet('font-size: 9pt;')

        label_a1 = QLabel()
        label_a1.setText('a<sub>1</sub>&#160;&#160;&#160;')
        label_a1.setToolTip('Положительная постоянная a<sub>1</sub>')
        self.a1 = QLineEdit()
        self.a1.setFixedHeight(25)
        self.a1.setText('5')
        box_a1 = QHBoxLayout()
        box_a1.addWidget(label_a1)
        box_a1.addWidget(self.a1)

        label_a3 = QLabel()
        label_a3.setText('a<sub>3</sub>&#160;&#160;&#160;')
        label_a3.setToolTip('Положительная постоянная a<sub>3</sub>')
        self.a3 = QLineEdit()
        self.a3.setFixedHeight(25)
        self.a3.setText('2')
        box_a3 = QHBoxLayout()
        box_a3.addWidget(label_a3)
        box_a3.addWidget(self.a3)

        label_m = QLabel('m <sub>&#160;</sub>&#160;')
        label_m.setToolTip('Масса точки')
        self.m = QLineEdit()
        self.m.setFixedHeight(25)
        self.m.setText('10')
        box_m = QHBoxLayout()
        box_m.addWidget(label_m)
        box_m.addWidget(self.m)

        label_startVar = QLabel('Начальные условия')
        label_startVar.setStyleSheet('font-size: 9pt;')

        label_x0 = QLabel()
        label_x0.setText('x<sub>0</sub>&#160;&#160;&#160;')
        label_x0.setToolTip('Начальное время')
        self.x0 = QLineEdit()
        self.x0.setFixedHeight(25)
        self.x0.setText('0')
        box_x0 = QHBoxLayout()
        box_x0.addWidget(label_x0)
        box_x0.addWidget(self.x0)

        label_u0 = QLabel()
        label_u0.setText('u<sub>0</sub>&#160;&#160;&#160;')
        label_u0.setToolTip('Начальное скорость точки')
        self.u0 = QLineEdit()
        self.u0.setFixedHeight(25)
        self.u0.setText('100')
        box_u0 = QHBoxLayout()
        box_u0.addWidget(label_u0)
        box_u0.addWidget(self.u0)

        label_VarM = QLabel('Параметры метода')
        label_VarM.setStyleSheet('font-size: 9pt;')

        label_h = QLabel('h &#160;&#160;<sub>&#160;</sub>')
        label_h.setToolTip('Начальный шаг')
        self.h = QLineEdit()
        self.h.setFixedHeight(25)
        self.h.setText('0.0001')
        box_h = QHBoxLayout()
        box_h.addWidget(label_h)
        box_h.addWidget(self.h)

        label_control = QLabel('Контроль')
        label_control.setStyleSheet('font-size: 9pt;')

        label_n = QLabel('n    &#160;&#160;<sub>&#160;</sub>')
        label_n.setToolTip('Максимальное количество шагов')
        self.n = QLineEdit()
        self.n.setFixedHeight(25)
        self.n.setText('10000')
        box_n = QHBoxLayout()
        box_n.addWidget(label_n)
        box_n.addWidget(self.n)

        label_b = QLabel('b    ')
        label_b.setToolTip('Нижняя граница для скорости')
        self.b = QLineEdit()
        self.b.setFixedHeight(25)
        self.b.setText('0')
        box_b = QHBoxLayout()
        box_b.addWidget(label_b)
        box_b.addWidget(self.b)

        label_Egr = QLabel()
        label_Egr.setText('E<sub>гр</sub>  &#160;')
        label_Egr.setToolTip(
            'Параметр контроля выхода на нижнюю границу для скорости')
        self.Egr = QLineEdit()
        self.Egr.setFixedHeight(25)
        self.Egr.setText('0.000005')
        box_Egr = QHBoxLayout()
        box_Egr.addWidget(label_Egr)
        box_Egr.addWidget(self.Egr)

        label_E = QLabel()
        label_E.setText('E    ')
        label_E.setToolTip('Параметр контроля локальной погрешности "сверху"')
        self.E = QLineEdit()
        self.E.setFixedHeight(25)
        self.E.setText('0.000005')

        box_E = QHBoxLayout()
        box_E.addWidget(label_E)
        box_E.addWidget(self.E)

        def editEmin():
            try:
                tmp = float_to_str(float(self.E.text()) / 2**(self.p + 1))
            except ValueError:
                tmp = 'Недопустимое значение'

            self.Emin.setText(tmp)

        label_Emin = QLabel()
        label_Emin.setText('E<sub>min</sub>')
        label_Emin.setToolTip(
            'Параметр контроля локальной погрешности "снизу"')
        self.Emin = QLineEdit()
        self.Emin.setToolTip(
            'Формируется автоматически при заполнение E. Emin = E / 2^(p + 1)')
        self.Emin.setReadOnly(True)
        self.Emin.setStyleSheet(
            'QLineEdit { background: rgba(146, 145, 145, 0); }')
        tmp = float(self.E.text()) / 2**(self.p + 1)
        self.Emin.setFixedHeight(25)
        self.Emin.setText(float_to_str(tmp))
        del tmp
        box_Emin = QHBoxLayout()
        box_Emin.addWidget(label_Emin)
        box_Emin.addWidget(self.Emin)

        self.E.textEdited.connect(editEmin)

        self.combobox = QComboBox()
        self.combobox.setFixedHeight(25)
        self.combobox.addItem('Контроль погрешности "сверху" и "снизу"')
        self.combobox.addItem('Отказ от контроля погрешности "снизу"')
        self.combobox.addItem('Отказ от контроля погрешности "снизу и сверху"')

        self.comboboxV = QComboBox()
        self.comboboxV.setFixedHeight(25)
        self.comboboxV.addItem('В качестве Vn итог берем Vn')
        self.comboboxV.addItem('В качестве Vn итог берем Vn удв')
        self.comboboxV.addItem('В качестве Vn итог берем Vn кор')

        # Заполняем все горизонтальные блоки
        optionHBox1.addLayout(box_a1)
        optionHBox2.addLayout(box_a3)
        optionHBox3.addLayout(box_m)
        optionHBox4.addLayout(box_x0)
        optionHBox5.addLayout(box_u0)
        optionHBox6.addLayout(box_h)
        optionHBox7.addLayout(box_n)
        optionHBox8.addLayout(box_b)
        optionHBox9.addLayout(box_Egr)
        optionHBox10.addLayout(box_E)
        optionHBox11.addLayout(box_Emin)
        optionHBox12.addWidget(self.combobox)
        optionHBox13.addWidget(self.comboboxV)

        # Заполняем все вертикальные блоки
        optionVBox.addWidget(label_Var)
        optionVBox.addLayout(optionHBox1)
        optionVBox.addLayout(optionHBox2)
        optionVBox.addLayout(optionHBox3)
        optionVBox.addWidget(label_startVar)
        optionVBox.addLayout(optionHBox4)
        optionVBox.addLayout(optionHBox5)
        optionVBox.addWidget(label_VarM)
        optionVBox.addLayout(optionHBox6)
        optionVBox.addWidget(label_control)
        optionVBox.addLayout(optionHBox7)
        optionVBox.addLayout(optionHBox8)
        optionVBox.addLayout(optionHBox9)
        optionVBox.addLayout(optionHBox10)
        optionVBox.addLayout(optionHBox11)
        optionVBox.addLayout(optionHBox12)
        optionVBox.addLayout(optionHBox13)

        option.setLayout(optionVBox)
        """--------------------------------------------Блок со справкой----------------------------------------------"""
        reference = QWidget(main)
        referenceVBox = QVBoxLayout()

        label_at = QLabel('Справка')
        label_at.setAlignment(Qt.AlignCenter)
        self.textarea = QPlainTextEdit()
        self.textarea.setFixedSize(600, 530)
        self.textarea.setFrameStyle(QFrame.NoFrame)
        self.textarea.setReadOnly(True)

        referenceVBox.addWidget(label_at)
        referenceVBox.addWidget(self.textarea)
        reference.setLayout(referenceVBox)
        """---------------------------------------------Блок с таблицей----------------------------------------------"""
        self.table = QTableWidget(main)
        self.table.setColumnCount(9)
        self.table.setRowCount(15)
        # self.table.setFixedSize(594, 400)
        self.table.verticalHeader().setVisible(False)
        self.table.setFixedSize(900, 400)

        self.table.setFrameStyle(QFrame.NoFrame)
        # self.table.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        header = [
            'N', 'H', 'Xn', 'Vn', 'Vn удв', 'S*', 'Vn итог', 'Ум.шага',
            'Ув.шага'
        ]

        self.table.setHorizontalHeaderLabels(header)
        for i in range(9):
            self.table.horizontalHeaderItem(i).setTextAlignment(
                Qt.AlignHCenter)
            if i in [0, 7, 8]:
                self.table.setColumnWidth(i, 5 * 9)
            elif i == 1:
                self.table.setColumnWidth(i, 10 * 9)
            else:
                self.table.setColumnWidth(i, 15 * 9)

        # Всплывающие подсказки на хедер таблицы
        hints = [
            'Номер шага метода', 'Шаг численного интегрирования', 'n-ое время',
            'Скорость точки в момент времени xn',
            'Скорость точки в момент времени xn (Удвоенный шаг)', 'Оценка ЛП',
            'Скорость точки в момент времени xn (итоговое значение)',
            'Кол-во уменьшений шага', 'Кол-во увеличений шага'
        ]
        for i, hint in enumerate(hints):
            self.table.horizontalHeaderItem(i).setToolTip(hint)
        """---------------------------------------------Блок с графиком----------------------------------------------"""

        class mlpCanvas(FigureCanvas):
            """
            Чтобы нарисовать новый график, нужно объект.plot(list, list)
            """
            def __init__(self, width=5, height=4, dpi=100):
                # Задаем размеры
                fig = Figure(figsize=(width, height), dpi=dpi)
                FigureCanvas.__init__(self, fig)
                self.ax = self.figure.add_subplot(111)
                self.ax.set_title('Зависимость скорости от времени')
                self.ax.set_xlabel('Время')
                self.ax.set_ylabel('Скорость')
                FigureCanvas.updateGeometry(self)

            def plot(self, x: list, y: list):
                self.ax.plot(x, y)
                self.draw_idle()

        self.canvas = mlpCanvas()

        mainHBox1.addWidget(option)
        mainHBox1.addWidget(reference)

        self.mainHBox2.addWidget(self.table)
        self.mainHBox2.addWidget(self.canvas)

        mainVBox.addLayout(mainHBox1)
        mainVBox.addLayout(self.mainHBox2)

        main.setLayout(mainVBox)

        self.window.setCentralWidget(main)

    def condition(self):
        """
        Открытие условия задачи
        """

        message = '<p><b>Вариант 4</b></p>' \
                  '<p>Свободный (горизонтальный) полет точки массы <b>m</b> под действием превоначального толчка ' \
                  'силы сопротивления среды <b>R</b> описывается уравнением</p>' \
                  '<p><b>m * du/dx = R, u(0) = u<sub>0</sub></b></p>' \
                  '<p>где <b>R = -(a<sub>1</sub> * u + a<sub>3</sub> * u<sup>3</sup>)</b>, где ' \
                  '<b>a<sub>1</sub>, a<sub>3</sub></b> - положительные постоянные, <b>u<sub>0</sub></b> - начальная ' \
                  'скорость точки, <b>u(x)</b> - скорость в момент времени <b>x</b>. Численно решая задачу Коши, ' \
                  'установите общие закономерности зависимости скорости от времени. ' \
                  'Параметры системы: <b>a<sub>1</sub>, a<sub>3</sub></b>. </p>'

        self.dialog_message.setWindowTitle('Условие задачи')
        self.dialog_message.setWindowIcon(QIcon('options\\favicon.ico'))
        self.dialog_message.setText(message)
        self.dialog_message.exec()

    def rungeKutta_method(self):
        """
        Открытие справки о методе
        :return:
        """

        message = '<p><b>Метод Рунге-Кутта явный 3-ого порядка, p = 3</b></p>' \
                  '' \
                  '<p>x<sub>0</sub>, v<sub>0</sub> = u<sub>0</sub>,</p>' \
                  '' \
                  '<p>x<sub>n+1</sub> = x<sub>n</sub> + h<sub>n</sub>,</p>' \
                  '' \
                  '<p>v<sub>n+1</sub> = v<sub>n</sub> + h<sub>n</sub>/6 * ' \
                  '(k<sub>1</sub> + 4k<sub>2</sub> + k<sub>5</sub>),</p>' \
                  '' \
                  '<p>k<sub>1</sub> = f(x<sub>n</sub>, v<sub>n</sub>),</p>' \
                  '' \
                  '<p>k<sub>2</sub> = f(x<sub>n</sub> + h<sub>n</sub>/2, ' \
                  'v<sub>n</sub> + h<sub>n</sub>/2 * k<sub>1</sub>),</p>' \
                  '' \
                  '<p>k<sub>3</sub> = f(x<sub>n</sub> + h<sub>n</sub>, ' \
                  'v<sub>n</sub> + h<sub>n</sub>(-k<sub>1</sub> + 2k<sub>2</sub>)).</p>'

        self.dialog_message.setWindowTitle('Метод')
        self.dialog_message.setWindowIcon(QIcon('options\\favicon.ico'))
        self.dialog_message.setText(message)
        self.dialog_message.exec()

    def clearGraph(self):
        class mlpCanvas(FigureCanvas):
            """
            Чтобы нарисовать новый график, нужно объект.plot(list, list)
            """
            def __init__(self, width=5, height=4, dpi=100):
                # Задаем размеры
                fig = Figure(figsize=(width, height), dpi=dpi)
                FigureCanvas.__init__(self, fig)
                self.ax = self.figure.add_subplot(111)
                self.ax.set_title('Зависимость скорости от времени')
                self.ax.set_xlabel('Время')
                self.ax.set_ylabel('Скорость')
                FigureCanvas.updateGeometry(self)

            def plot(self, x: list, y: list):
                self.ax.plot(x, y)
                self.draw_idle()

        self.mainHBox2.removeWidget(self.canvas)
        self.canvas = mlpCanvas()
        self.mainHBox2.addWidget(self.canvas)
        self.window.show()

    def clearReference(self):
        self.textarea.setPlainText('')

    def error_input(self, message):
        """
        Сообщение об ошибке в модальном окне
        """

        self.dialog_message.setWindowTitle('Ошибка')
        self.dialog_message.setWindowIcon(QIcon('options\\favicon.ico'))
        self.dialog_message.setText(message)
        self.dialog_message.exec()

    def data_to_table(self, data: dict):
        """
        Записываем полученные данные в таблицу
        :param data: словарь с информацией
        """
        def create_item(row: int, col: int, text: str):
            item = QTableWidgetItem(str(text))
            item.setFlags(Qt.ItemIsEnabled)
            self.table.setItem(row, col, item)

        self.table.setRowCount(len(data['n']))

        for i, n in enumerate(data['n']):
            create_item(i, 0, n)
            create_item(i, 1, data['hn-1'][i])
            create_item(i, 2, data['xn'][i])
            create_item(i, 3, data['vn'][i])
            create_item(i, 4, data['vn_ud'][i])
            create_item(i, 5, data['S*'][i])
            create_item(i, 6, data['vn_res'][i])
            create_item(i, 7, data['step_decrease'][i])
            create_item(i, 8, data['step_increase'][i])

    def except_errors(self) -> bool:
        """
        Отлавливаем все возможные ошибки
        :return: True - есть ошибка, False - нет ошибок
        """

        error = False
        try:
            if float(self.u0.text()) <= 0:
                error = True
                self.error_input(
                    'Значение начальной скорости u0 должно быть положительным')

            if float(self.a1.text()) <= 0:
                error = True
                self.error_input(
                    'Значение параметра a1 должно быть положительным')
            if float(self.a3.text()) <= 0:
                error = True
                self.error_input(
                    'Значение параметра a3 должно быть положительным')
            if float(self.m.text()) <= 0:
                error = True
                self.error_input(
                    'Значение параметра m должно быть положительным')
            if float(self.h.text()) <= 0:
                error = True
                self.error_input('Шаг должен быть положительным')

            try:
                if int(self.n.text()) <= 0:
                    error = True
                    self.error_input('Кол-во шагов должно быть положительным')
            except ValueError:
                error = True
                self.error_input('Кол-во шагов должно быть целым')

            if float(self.Egr.text()) < 0:
                error = True
                self.error_input(
                    'Контроль выхода за нижнюю границу для скорости должен быть неотрицательным'
                )
            if float(self.E.text()) <= 0:
                error = True
                self.error_input(
                    'Контроль ЛП "сверху" должен быть неотрицательным')
            if self.Emin.text() == 'Недопустимое значение':
                error = True
                self.error_input('Недопустимое значение Emin')
            if float(self.Emin.text()) <= 0:
                error = True
                self.error_input(
                    'Контроль ЛП "снизу" должен быть неотрицательным')
            if float(self.Emin.text()) > float(self.E.text()):
                error = True
                self.error_input(
                    'Контроль ЛП "сверху" должен быть не меньше контроля ЛП "снизу"'
                )
        except ValueError:  # Если нельзя перевести строку в float или int
            error = True
            self.error_input(
                'Входные данные должны быть числами с плавающей запятой кроме n, оно должны быть целым'
            )

        return error

    def work(self):
        error = self.except_errors()

        if not error:
            data = {
                'x0': Decimal(self.x0.text()),
                'u0': Decimal(self.u0.text()),
                'a1': Decimal(self.a1.text()),
                'a3': Decimal(self.a3.text()),
                'm': Decimal(self.m.text()),
                'h': Decimal(self.h.text()),
                'n': int(self.n.text()),
                'b': Decimal(self.b.text()),
                'Egr': Decimal(self.Egr.text()),
                'E': Decimal(self.E.text()),
                'Emin': Decimal(self.Emin.text()),
                'cb': int(self.combobox.currentIndex()),
                'cbV': int(self.comboboxV.currentIndex()),
                'p': self.p
            }

            analysis = Analysis(data)
            del data
            data = analysis.work()

            # Запишем справку
            self.textarea.appendHtml(data['message'])

            # Запишем данные в таблицу
            self.data_to_table(data)

            # Построим график
            self.canvas.plot(data['xn'], data['vn_res'])
Example #5
0
class MainWindow(QMainWindow):
    def __init__(self,
                 x: int = 760,
                 y: int = 300,
                 width: int = 400,
                 height: int = 520) -> None:
        super().__init__()
        self.setFixedSize(width, height)
        self.move(x, y)
        self.setWindowTitle('File Scanner')
        self.setWindowIcon(QIcon('imgs/icon.png'))
        self.api_key = ''
        self.initUI()

    def initUI(self) -> None:
        """Creates the program main widgets"""

        # File select button
        self.b1 = QtWidgets.QPushButton(self)
        self.b1.setText('Select File')
        self.b1.setGeometry(80, 320, 100, 30)
        self.b1.clicked.connect(lambda: self.get_path(True))

        # Folder select button
        self.b2 = QtWidgets.QPushButton(self)
        self.b2.setText('Select Folder')
        self.b2.setGeometry(210, 320, 100, 30)
        self.b2.clicked.connect(lambda: self.get_path(False))

        # Scan output textbox
        self.textbox = QPlainTextEdit(self)
        self.textbox.setGeometry(25, 20, 350, 280)
        self.textbox.setReadOnly(True)

        # Path overview
        self.scan_path = QLineEdit(self)
        self.scan_path.setGeometry(70, 365, 250, 20)
        self.scan_path.setReadOnly(True)

        # Path status
        self.status = QtWidgets.QLabel(self)
        self.status.setText('❌')
        self.status.move(43, 358)

        # Clear scan and path button
        self.clear_button = QtWidgets.QPushButton(self)
        self.clear_button.setText('Clear Scans')
        self.clear_button.setGeometry(210, 400, 100, 30)
        self.clear_button.clicked.connect(self.clear)

        # Scan start button
        self.scanner = QtWidgets.QPushButton(self)
        self.scanner.setText('Scan')
        self.scanner.setGeometry(80, 400, 100, 30)
        self.scanner.clicked.connect(self.scan)

        # API key add button
        self.add_key = QtWidgets.QPushButton(self)
        self.add_key.setText('Add API Key')
        self.add_key.setGeometry(140, 447, 100, 30)
        self.add_key.clicked.connect(self.add_api_key)

        # Hash operation checkbox
        self.hash_status = QCheckBox(self)
        self.hash_status.setText('Manual Hashing')
        self.hash_status.move(50, 482)
        self.hash_status.setChecked(True)

        # .txt output checkbox
        self.file_output = QCheckBox(self)
        self.file_output.setText('.txt Output')
        self.file_output.move(160, 482)

        # Show undetected checkbox
        self.show_undetected = QCheckBox(self)
        self.show_undetected.setText('View Undetected')
        self.show_undetected.setGeometry(250, 482, 105,
                                         30)  # Increase size so text could fit

    def clear(self) -> None:
        """Clears the scan textbox and the file path and resets the path status"""
        self.textbox.clear()
        self.scan_path.clear()
        self.status.setText('❌')

    def get_path(self, isfile: bool) -> None:
        """Adds path to scan if the path is a file/folder"""
        if isfile:
            path = QFileDialog.getOpenFileName(
                self, 'Select File',
                os.path.dirname(os.path.abspath(__file__)))
            if path[0]:
                self.scan_path.setText(os.path.abspath(path[0]))
                self.status.setText('✔')
        else:
            path = QFileDialog.getExistingDirectory(
                self, 'Select Directory',
                os.path.dirname(os.path.abspath(__file__)))
            if path:
                self.scan_path.setText(os.path.abspath(path))
                self.status.setText('✔')

    async def append_report(self, path: str) -> None:
        """Outputs a scan to the textbox"""
        if os.path.isfile(path):
            self.textbox.appendPlainText('-' * 64)
            self.textbox.appendHtml(
                'File scan started at: <span style="color: blue;">' +
                f'{datetime.now().strftime("%H:%M:%S, %d/%m/%Y")}</span>' +
                f'<br>Selected File: <span style="color: blue;">{path}</span>'
                + '<br>Scanning...<br>')
            async with aiohttp.ClientSession() as session:
                report = await filereport.get_file_report(
                    path,
                    self.api_key,
                    session,
                    manual=self.hash_status.isChecked(),
                    show_undetected=self.show_undetected.isChecked())
            if report:
                self.textbox.appendHtml(report + '<br>')
            self.textbox.appendHtml(
                'File scan ended at: <span style="color: blue;">'
                f'{datetime.now().strftime("%H:%M:%S, %d/%m/%Y")}</span>')
            self.textbox.appendPlainText('-' * 64)
            if self.file_output.isChecked():
                self.txt_output_to_file()

        if os.path.isdir(path):
            self.textbox.appendPlainText('-' * 64)
            self.textbox.appendHtml(
                'File scan started at: <span style="color: blue;">' +
                f'{datetime.now().strftime("%H:%M:%S, %d/%m/%Y")}</span>' +
                f'<br>Selected Folder: <span style="color: blue;">{path}</span>'
                + '<br>Scanning...<br>')
            async for report in filereport.scan(
                    path,
                    self.api_key,
                    self.show_undetected.isChecked(),
                    manual=self.hash_status.isChecked()):
                if report:
                    self.textbox.appendHtml(report + '<br>')
            self.textbox.appendHtml(
                'File scan ended at: <span style="color: blue;">'
                f'{datetime.now().strftime("%H:%M:%S, %d/%m/%Y")}</span>')
            self.textbox.appendPlainText('-' * 64)
            if self.file_output.isChecked():
                self.txt_output_to_file()

    def scan(self) -> None:
        """Starts the scanning process if an API key and a path are inserted"""
        path = self.scan_path.text()
        if path and self.api_key:
            # Start report fetching and parsing if a path and an API key are inserted
            loop = asyncio.get_event_loop()
            loop.run_until_complete(self.append_report(path))
        else:
            # Create a message box if an API key or a path is missing
            msg = QtWidgets.QMessageBox()
            msg.setIcon(QtWidgets.QMessageBox.Information)
            msg.setText('Please enter an API key and a file/folder path!')
            msg.setWindowTitle('Missing key or path')
            msg.setWindowIcon(QIcon('imgs/icon.png'))
            msg.setStandardButtons(QtWidgets.QMessageBox.Ok)
            msg.exec_()

    def add_api_key(self) -> None:
        """Adds an API key for the program from input"""
        key, ok = QInputDialog.getText(self, 'Enter Virus Total API Key',
                                       'Enter Virus Total API Key',
                                       QLineEdit.Normal, self.api_key)
        if ok and key:  # If the user clicked OK and entered a key
            self.api_key = key

    def txt_output_to_file(self) -> None:
        """Outputs the last scan on screen to .txt file"""
        scans = self.textbox.toPlainText()
        last_scan = scans.split('-' * 64)[-2].split(
            '-' *
            64)[-1][1:]  # Find last scan using split and remove line feed
        # Fetch name by date and modify string
        filename = 'Scan ' + findall('..:..:.., ../../.{4}',
                                     last_scan)[0].replace(':', '-').replace(
                                         '/', '-') + '.txt'
        with open(filename, 'w') as f:
            f.write(last_scan)
Example #6
0
class ExecuteWidget(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.ctrl = QApplication.instance().ctrl
        self.ctrl.switch_language.connect(self.on_switch_language)
        self.ctrl.switch_configuration.connect(self.on_switch_configuration)
        self.paras = self.ctrl.paras
        self.conf = GuiConf()
        self.setWindowTitle(_("Execute %s") % self.paras.configuration_name())
        self.executor_thread = None
        self.splitter_event_moved = False
        self.splitter_title_moved = False
        self.init_ui()
        self.show()

    def init_ui(self):
        vbox = QVBoxLayout()

        self.splitter_title = QSplitter(Qt.Vertical)
        self.splitter_event = QSplitter(Qt.Vertical)
        self.splitter_title.splitterMoved.connect(self.on_splitter_title_moved)
        self.splitter_event.splitterMoved.connect(self.on_splitter_event_moved)

        self.lbl_events_title1 = QLabel(_("Main events"))
        self.splitter_title.addWidget(self.lbl_events_title1)

        self.lbl_events_title2 = QLabel(_("Resources"))
        self.splitter_title.addWidget(self.lbl_events_title2)

        self.lbl_events_title3 = QLabel(_("Errors"))
        self.splitter_title.addWidget(self.lbl_events_title3)

        self.pte_events1 = QTextBrowser()
        self.pte_events1.setOpenExternalLinks(True)
        self.pte_events1.setOpenLinks(False)
        self.pte_events1.anchorClicked.connect(self.on_anchor_clicked)
        self.pte_events1.setLineWrapMode(QTextEdit.NoWrap)
        self.splitter_event.addWidget(self.pte_events1)

        self.pte_events2 = QTextBrowser()
        self.pte_events2.setOpenExternalLinks(True)
        self.pte_events2.setOpenLinks(False)
        self.pte_events2.anchorClicked.connect(self.on_anchor_clicked)
        self.pte_events2.setLineWrapMode(QTextEdit.NoWrap)
        self.splitter_event.addWidget(self.pte_events2)

        self.pte_events3 = QPlainTextEdit()
        self.pte_events3.setReadOnly(True)
        self.pte_events3.setLineWrapMode(QPlainTextEdit.NoWrap)
        self.pte_events3.setStyleSheet(Style.red_text())
        self.pte_events3.setCenterOnScroll(True)
        self.splitter_event.addWidget(self.pte_events3)

        self.splitter_title.setStretchFactor(0, 5)
        self.splitter_title.setStretchFactor(1, 3)
        self.splitter_title.setStretchFactor(2, 1)
        self.splitter_event.setStretchFactor(0, 5)
        self.splitter_event.setStretchFactor(1, 3)
        self.splitter_event.setStretchFactor(2, 1)
        hbox_splitters = QHBoxLayout()
        hbox_splitters.addWidget(self.splitter_title, 0)
        hbox_splitters.addWidget(self.splitter_event, 5)
        vbox.addLayout(hbox_splitters)

        lbl_box = QHBoxLayout()
        self.lbl_processing = QLabel(_("Processing:"))
        self.lbl_processing_file = QLabel("")
        self.lbl_processing.setVisible(False)
        self.lbl_processing_file.setVisible(False)
        lbl_box.addWidget(self.lbl_processing)
        lbl_box.addWidget(self.lbl_processing_file)
        lbl_box.addStretch(1)
        vbox.addLayout(lbl_box)

        btn_box = QHBoxLayout()
        btn_box.addStretch(1)
        self.chk_trial_run = QCheckBox(_("Trial run"))
        self.chk_trial_run.setChecked(not self.paras.is_saving_sitemaps)
        btn_box.addWidget(self.chk_trial_run)
        self.btn_run = QPushButton(_("Run"))
        self.btn_run.clicked.connect(self.on_btn_run_clicked)
        btn_box.addWidget(self.btn_run)
        self.btn_stop = QPushButton(_("Stop"))
        self.btn_stop.clicked.connect(self.on_btn_stop_clicked)
        self.normal_style = self.btn_stop.styleSheet()
        self.btn_stop.setEnabled(False)
        btn_box.addWidget(self.btn_stop)
        self.btn_close = QPushButton(_("Close"))
        self.btn_close.clicked.connect(self.on_btn_close_clicked)
        btn_box.addWidget(self.btn_close)
        vbox.addLayout(btn_box)

        self.setLayout(vbox)
        self.resize(self.conf.execute_widget_width(),
                    self.conf.execute_widget_height())

    def on_splitter_title_moved(self, pos, index):
        self.splitter_title_moved = True
        if not self.splitter_event_moved:
            self.splitter_event.moveSplitter(pos, index)
        self.splitter_title_moved = False

    def on_splitter_event_moved(self, pos, index):
        self.splitter_event_moved = True
        if not self.splitter_title_moved:
            self.splitter_title.moveSplitter(pos, index)
        self.splitter_event_moved = False

    def on_switch_language(self):
        self.setWindowTitle(_("Execute %s") % self.paras.configuration_name())
        self.lbl_events_title1.setText(_("Main events"))
        self.lbl_events_title2.setText(_("Resources"))
        self.lbl_events_title3.setText(_("Errors"))
        self.lbl_processing.setText(_("Processing:"))
        self.chk_trial_run.setText(_("Trial run"))
        self.btn_run.setText(_("Run"))
        self.btn_stop.setText(_("Stop"))
        self.btn_close.setText(_("Close"))

    def on_switch_configuration(self, name=None):
        LOG.debug("Switch configuration: %s" % name)
        self.paras = self.ctrl.paras
        self.setWindowTitle(_("Execute %s") % self.paras.configuration_name())
        self.chk_trial_run.setChecked(not self.paras.is_saving_sitemaps)

    def on_btn_run_clicked(self):
        if self.paras.select_mode == SelectMode.simple:
            selector = Selector()
            if self.paras.simple_select_file:
                selector.include(self.paras.simple_select_file)
        else:
            selector = self.ctrl.selector
        self.paras.is_saving_sitemaps = not self.chk_trial_run.isChecked()
        self.pte_events1.setPlainText("")
        self.pte_events2.setPlainText("")
        self.pte_events3.setPlainText("")
        self.lbl_processing.setVisible(True)
        self.lbl_processing_file.setVisible(True)

        self.btn_close.setEnabled(False)
        self.btn_run.setEnabled(False)
        self.chk_trial_run.setEnabled(False)
        self.btn_stop.setEnabled(True)
        self.btn_stop.setStyleSheet(Style.alarm())

        self.executor_thread = ExecutorThread(self.paras, selector, self)
        self.executor_thread.signal_exception.connect(self.on_signal_exception)
        self.executor_thread.ask_confirmation.connect(self.on_ask_confirmation)
        self.executor_thread.signal_main_event.connect(
            self.on_signal_main_event)
        self.executor_thread.signal_minor_event.connect(
            self.on_signal_minor_event)
        self.executor_thread.signal_next_file.connect(self.on_signal_next_file)
        self.executor_thread.signal_end_processing.connect(
            self.on_signal_end_processing)
        self.executor_thread.finished.connect(self.on_executor_thread_finished)
        self.executor_thread.start()
        self.update()

    def on_btn_stop_clicked(self):
        self.btn_stop.setStyleSheet(self.normal_style)
        self.btn_stop.setEnabled(False)
        self.executor_thread.requestInterruption()

    def on_signal_exception(self, msg):
        self.pte_events3.appendHtml(msg)
        self.update()

    def on_ask_confirmation(self, text, i_text, answer):
        msg_box = QMessageBox()
        msg_box.setText(text)
        i_text += "\n\n"
        i_text += _("Ok to proceed?")
        msg_box.setInformativeText(i_text)
        msg_box.setIcon(QMessageBox.Question)
        msg_box.setStandardButtons(QMessageBox.No | QMessageBox.Yes)
        msg_box.setDefaultButton(QMessageBox.Yes)
        exe = msg_box.exec()
        if exe == QMessageBox.No:
            answer.answer = False
        else:
            answer.answer = True
        answer.answered = True

    def on_signal_main_event(self, msg):
        self.pte_events1.append(msg)
        self.update()

    def on_signal_minor_event(self, msg):
        self.pte_events2.append(msg)
        self.update()

    def on_signal_next_file(self, filename):
        lfn = len(filename)
        ww = (self.width() - 150) / 7
        www = int(ww / 2)
        if lfn > ww:
            filename = filename[:www] + "..." + filename[lfn - www:]
        self.lbl_processing_file.setText(filename)
        self.update()

    def on_signal_end_processing(self, paras):
        self.ctrl.update_configuration(paras)

    def on_executor_thread_finished(self):
        self.btn_stop.setStyleSheet(self.normal_style)
        self.btn_stop.setEnabled(False)
        self.btn_close.setEnabled(True)
        self.btn_run.setEnabled(True)
        self.chk_trial_run.setEnabled(True)
        self.lbl_processing.setVisible(False)
        self.lbl_processing_file.setVisible(False)
        self.update()

    def on_anchor_clicked(self, url):
        QDesktopServices.openUrl(QUrl(url))

    def on_btn_close_clicked(self):
        if self.windowState() & Qt.WindowFullScreen:
            self.setWindowState(Qt.WindowMaximized)
        else:
            self.close()

    def closeEvent(self, event):
        if self.executor_thread:
            self.executor_thread.requestInterruption()
        if self.windowState() & Qt.WindowFullScreen:
            self.setWindowState(Qt.WindowMaximized)
            event.ignore()
        else:
            self.conf.set_execute_widget_width(self.width())
            self.conf.set_execute_widget_height(self.height())
            self.conf.persist()
            event.accept()
Example #7
0
class LogWindow(QMainWindow):
    emit_msg = pyqtSignal(str)

    def __init__(self,
                 level: int = logging.INFO,
                 window_title: str = "Python log",
                 logger: logging.Logger = None,
                 min_width: int = 800,
                 min_height: int = 400,
                 maximum_block_count: int = 1000) -> None:
        super().__init__()
        self.setStyleSheet(LOGEDIT_STYLESHEET)

        self.handler = HtmlColorHandler(self.log_message, level)
        self.may_close = False
        self.set_may_close(self.may_close)

        self.setWindowTitle(window_title)
        if min_width:
            self.setMinimumWidth(min_width)
        if min_height:
            self.setMinimumHeight(min_height)

        log_group = StyledQGroupBox("Log")
        log_layout_1 = QVBoxLayout()
        log_layout_2 = QHBoxLayout()
        self.log = QPlainTextEdit()
        # QPlainTextEdit better than QTextEdit because it supports
        # maximumBlockCount while still allowing HTML (via appendHtml,
        # not insertHtml).
        self.log.setReadOnly(True)
        self.log.setLineWrapMode(QPlainTextEdit.NoWrap)
        self.log.setMaximumBlockCount(maximum_block_count)
        log_clear_button = QPushButton('Clear log')
        log_clear_button.clicked.connect(self.log.clear)
        log_copy_button = QPushButton('Copy to clipboard')
        log_copy_button.clicked.connect(self.copy_whole_log)
        log_layout_2.addWidget(log_clear_button)
        log_layout_2.addWidget(log_copy_button)
        log_layout_2.addStretch()
        log_layout_1.addWidget(self.log)
        log_layout_1.addLayout(log_layout_2)
        log_group.setLayout(log_layout_1)

        main_widget = QWidget(self)
        self.setCentralWidget(main_widget)
        main_layout = QVBoxLayout(main_widget)
        main_layout.addWidget(log_group)

        self.emit_msg.connect(self.log_internal)

        if logger:
            logger.addHandler(self.get_handler())

    def get_handler(self) -> logging.Handler:
        return self.handler

    def set_may_close(self, may_close: bool) -> None:
        # log.debug("LogWindow: may_close({})".format(may_close))
        self.may_close = may_close
        # return
        if may_close:
            self.setWindowFlags(self.windowFlags() | Qt.WindowCloseButtonHint)
        else:
            self.setWindowFlags(self.windowFlags() & ~Qt.WindowCloseButtonHint)
        self.show()
        # ... or it will be hidden (in a logical not a real way!) by
        # setWindowFlags(), and thus mess up the logic for the whole Qt app
        # exiting (since qt_app.exec_() runs until there are no more windows
        # being shown).

    def copy_whole_log(self) -> None:
        # Ctrl-C will copy the selected parts.
        # log.copy() will copy the selected parts.
        self.log.selectAll()
        self.log.copy()
        self.log.moveCursor(QTextCursor.End)
        self.scroll_to_end_of_log()

    def scroll_to_end_of_log(self) -> None:
        vsb = self.log.verticalScrollBar()
        vsb.setValue(vsb.maximum())
        hsb = self.log.horizontalScrollBar()
        hsb.setValue(0)

    # noinspection PyPep8Naming
    def closeEvent(self, event: QCloseEvent) -> None:
        """Trap exit."""
        if not self.may_close:
            # log.debug("LogWindow: ignore closeEvent")
            event.ignore()
        else:
            # log.debug("LogWindow: accept closeEvent")
            event.accept()

    def log_message(self, html: str) -> None:
        # Jump threads via a signal
        self.emit_msg.emit(html)

    @pyqtSlot(str)
    def log_internal(self, html: str) -> None:
        # self.log.moveCursor(QTextCursor.End)
        # self.log.insertHtml(html)
        self.log.appendHtml(html)
        # self.scroll_to_end_of_log()
        # ... unnecessary; if you're at the end, it scrolls, and if you're at
        # the top, it doesn't bug you.

    @pyqtSlot()
    def exit(self) -> None:
        # log.debug("LogWindow: exit")
        self.may_close = True
        # closed = QMainWindow.close(self)
        # log.debug("closed: {}".format(closed))
        QMainWindow.close(self)

    @pyqtSlot()
    def may_exit(self) -> None:
        # log.debug("LogWindow: may_exit")
        self.set_may_close(True)
Example #8
0
class Window(QMainWindow, ClipableObserver):
    def __init__(self, brres_files=[]):
        super().__init__()
        self.open_files = []
        self.brres = None
        self.image_updater = {}  # maps brres to list of subscribers
        self.cwd = os.getcwd()
        self.__init_threads()
        self.locked_files = set(
        )  # lock files that are pending conversion etc...
        # AutoFix.get().set_pipe(self)
        self.__init_UI()
        self.shell_is_shown = False
        self.shell = None
        for file in brres_files:
            self.open(file.name)
        AutoFix.get().info('Initialized main window', 5)
        self.show()

    def __init_threads(self):
        AutoFix.get().info('Starting threads...', 5)
        self.threadpool = QThreadPool()  # for multi-threading
        self.threadpool.setMaxThreadCount(5)
        self.converter = converter = ConvertManager.get()
        converter.signals.on_conversion_finish.connect(
            self.on_conversion_finish)
        self.image_manager = image_manager = ImageManager.get()
        if image_manager.enabled:
            image_manager.signals.on_image_update.connect(self.on_image_update)
            self.threadpool.start(image_manager)
        else:
            AutoFix.get().warn(
                'Image Manager disabled, do you have Wiimms SZS Tools installed?'
            )
        self.threadpool.start(converter)
        log_pipe = LoggerPipe()
        log_pipe.info_sig.connect(self.info)
        log_pipe.warn_sig.connect(self.warn)
        log_pipe.error_sig.connect(self.error)

    def __init_menus(self):
        # Files
        # Exit
        exit_act = QAction('&Exit', self)
        exit_act.setShortcut('Ctrl+q')
        exit_act.setStatusTip('Exit Application')
        exit_act.triggered.connect(self.close)
        # Open
        open_act = QAction('&Open', self)
        open_act.setShortcut('Ctrl+o')
        open_act.setStatusTip('Open a Brres file')
        open_act.triggered.connect(self.open_dialog)
        # Save
        save_act = QAction('&Save', self)
        save_act.setShortcut('Ctrl+s')
        save_act.setStatusTip('Save file')
        save_act.triggered.connect(self.save)
        # Save as
        save_as = QAction('Save &As', self)
        save_as.setStatusTip('Save file as')
        save_as.triggered.connect(self.save_as_dialog)
        # import
        import_act = QAction('&Import', self)
        import_act.setShortcut('Ctrl+i')
        import_act.setStatusTip('Import file')
        import_act.triggered.connect(self.import_file_dialog)
        # export
        export_act = QAction('&Export', self)
        export_act.setShortcut('Ctrl+e')
        export_act.setStatusTip('Export file')
        export_act.triggered.connect(self.export_file_dialog)
        # File Menu
        menu = self.menuBar()
        fileMenu = menu.addMenu('&File')
        fileMenu.addAction(open_act)
        fileMenu.addAction(save_act)
        fileMenu.addAction(save_as)
        fileMenu.addSeparator()
        fileMenu.addAction(import_act)
        fileMenu.addAction(export_act)
        fileMenu.addSeparator()
        fileMenu.addAction(exit_act)

        # Tools
        shell_Act = QAction('&Interactive Shell', self)
        shell_Act.setShortcut('Ctrl+Shift+I')
        shell_Act.setStatusTip('Run interactive commands')
        shell_Act.triggered.connect(self.open_interactive_shell)
        kcl_calc_Act = QAction('&KCL Calculator', self)
        kcl_calc_Act.setShortcut('Ctrl+k')
        kcl_calc_Act.setStatusTip('KCL Flag Calculator')
        kcl_calc_Act.triggered.connect(self.open_kcl_calculator)
        toolMenu = menu.addMenu('&Tools')
        toolMenu.addAction(shell_Act)
        toolMenu.addAction(kcl_calc_Act)

        # Help
        report_Act = QAction('&Report Issue', self)
        report_Act.setStatusTip('Report an issue')
        report_Act.triggered.connect(self.report_issue)
        website_Act = QAction('&Website', self)
        website_Act.setStatusTip('Visit website')
        website_Act.triggered.connect(self.open_website)
        about_Act = QAction('&About', self)
        about_Act.setStatusTip('Information about ABMatt')
        about_Act.triggered.connect(self.about_abmatt)
        help_menu = menu.addMenu('&Help')
        help_menu.addAction(report_Act)
        help_menu.addAction(website_Act)
        help_menu.addSeparator()
        help_menu.addAction(about_Act)

    def open_website(self):
        webbrowser.open('https://github.com/Robert-N7/abmatt')

    def report_issue(self):
        webbrowser.open('https://github.com/Robert-N7/abmatt/issues')

    def about_abmatt(self):
        self.box = QMessageBox()
        bit_size = '64-Bit' if sys.maxsize > 2**32 else '32-Bit'
        self.box.setText(
            f'ABMatt Version {load_config.VERSION} {platform.platform()} {bit_size}'
        )
        self.box.setWindowTitle('ABMatt')
        self.box.show()

    def open_kcl_calculator(self):
        self.calculator = KCLCalculator()

    def open_interactive_shell(self):
        if not self.shell_is_shown:
            if self.shell is None:
                self.shell = InteractiveCmd()
                self.left.addWidget(self.shell)
            else:
                self.shell.show()
            self.shell_is_shown = True
        else:
            self.shell_is_shown = False
            self.shell.hide()

    def locate_material(self, brres_path):
        return self.material_browser.locate_material(brres_path)

    def __init_child_UI(self, top_layout):
        # left
        vert_widget = QWidget(self)
        # policy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.MinimumExpanding)
        # policy.setHorizontalStretch(2)
        # vert_widget.setSizePolicy(policy)
        top_layout.addWidget(vert_widget)
        self.left = vert_layout = QVBoxLayout()
        vert_widget.setLayout(vert_layout)
        widget = QWidget(self)
        vert_layout.addWidget(widget)
        center_layout = QHBoxLayout()
        widget.setLayout(center_layout)
        self.logger = QPlainTextEdit(self)
        self.logger.setReadOnly(True)
        self.logger.setFixedHeight(100)
        vert_layout.addWidget(self.logger)

        self.treeview = BrresTreeView(self)
        self.treeview.setMinimumWidth(300)
        center_layout.addWidget(self.treeview)
        self.poly_editor = PolyEditor(self)
        center_layout.addWidget(self.poly_editor)
        # center_widget.setGeometry(0, 0, 300, 300)

        # right
        # top_layout.addSpacing(30)
        self.material_browser = MaterialTabs(self)
        policy = QSizePolicy(QSizePolicy.Preferred,
                             QSizePolicy.MinimumExpanding)
        policy.setHorizontalStretch(1)
        self.material_browser.setSizePolicy(policy)
        top_layout.addWidget(self.material_browser)
        # self.material_browser.setFixedWidth(300)

    def __init_UI(self):
        self.setWindowTitle('ANoobs Brres Material Tool')
        self.resize(1000, 700)
        self.__init_menus()
        main_layout = QHBoxLayout()
        self.__init_child_UI(main_layout)
        widget = QWidget()
        widget.setLayout(main_layout)
        self.setCentralWidget(widget)
        self.statusBar().showMessage('Ready')

    def on_image_update(self, brres_dir):
        # simply gives it back to the image manager to update observers
        self.image_manager.notify_image_observers(*brres_dir)

    def on_update_polygon(self, poly):
        enable_edits = poly.parent.parent not in self.locked_files
        self.poly_editor.on_update_polygon(poly, enable_edits=enable_edits)

    def emit(self, message, head=None, tail=None):
        message = message.replace('\n', '<br/>').replace(' ', '&nbsp;')
        if head:
            message = head + message
        if tail:
            message += tail
        self.logger.appendHtml(message)

    def get_brres_by_fname(self, fname):
        path = os.path.abspath(fname)
        for x in self.open_files:
            if os.path.abspath(x.name) == path:
                return x

    def set_brres(self, brres):
        if brres != self.brres:
            self.brres = brres

    def open_dialog(self):
        fname, filter = QFileDialog.getOpenFileName(self, 'Open file',
                                                    self.cwd,
                                                    "Brres files (*.brres)")
        if fname:
            self.open(fname)

    def on_node_update(self, node):
        self.treeview.on_brres_update(node)

    def on_rename_update(self, node, old_name):
        if type(node) == Brres:
            self.treeview.on_brres_rename(old_name, node.name)
        self.material_browser.on_name_update()

    def on_child_update(self, child):
        self.treeview.on_brres_update(child.parent)

    def open(self, fname, force_update=False):
        self.cwd = os.path.dirname(fname)
        opened = self.get_brres_by_fname(fname)
        if opened:
            if not force_update:
                return self.set_brres(opened)
        else:
            opened = Brres.get_brres(fname)
            self.open_files.append(opened)
            opened.register_observer(self)
        # either it's newly opened or forcing update
        self.set_brres(opened)
        self.treeview.on_brres_update(opened)
        self.material_browser.add_brres_materials_to_scene(opened)

    def save(self):
        if len(self.open_files):
            last = None
            for x in self.open_files:
                # A precaution against overwriting old models
                # overwrite = True if not x.has_new_model else Brres.OVERWRITE
                if x.save(overwrite=True):
                    last = x
            if last is not None:
                self.update_status('Wrote file {}'.format(last.name))

    def save_as_dialog(self):
        if self.brres:
            fname, filter = QFileDialog.getSaveFileName(
                self, 'Save as', self.cwd, 'Brres files (*.brres)')
            if fname:
                self.cwd = os.path.dirname(fname)
                self.brres.save(fname, overwrite=True)
                self.update_status('Wrote file {}'.format(fname))
        else:
            AutoFix.get().error('No Brres file selected.')

    def import_texture(self, filename):
        raise NotImplementedError()

    def on_material_select(self, material):
        self.material_browser.on_material_select(material)

    def import_file(self, fname, brres_name=None, brres=None, mdl0=None):
        if mdl0 is not None:
            brres = mdl0.parent
        if not brres:
            if brres_name is not None:
                brres = self.get_brres_by_fname(brres_name)
            elif self.brres and os.path.splitext(os.path.basename(self.brres.name))[0] == \
                    os.path.splitext(os.path.basename(fname))[0]:
                brres = self.brres
        self.cwd, name = os.path.split(fname)
        base_name, ext = os.path.splitext(name)
        lower = ext.lower()
        if lower == '.dae':
            converter = DaeConverter2(brres, fname, mdl0=mdl0)
        elif lower == '.obj':
            converter = ObjConverter(brres, fname, mdl0=mdl0)
        # elif lower in ('.png', '.jpg', '.bmp', '.tga'):
        #     return self.import_texture(fname)
        else:
            self.statusBar().showMessage('Unknown extension {}'.format(ext))
            return
        # converter.load_model()
        # self.on_conversion_finish(converter)
        self.update_status('Added {} to queue...'.format(fname))
        self.lock_file(converter.brres)
        self.converter.enqueue(converter)

    def lock_file(self, brres):
        self.locked_files.add(brres)
        self.poly_editor.on_brres_lock(brres)

    def unlock_file(self, brres):
        try:
            self.locked_files.remove(brres)
        except KeyError:
            pass
        self.poly_editor.on_brres_unlock(brres)

    def on_conversion_finish(self, converter):
        self.open(converter.brres.name, force_update=True)
        self.unlock_file(converter.brres)
        self.update_status('Finished Converting {}'.format(
            converter.brres.name))

    def import_file_dialog(self, brres=None, mdl0=None):
        fname, filter = QFileDialog.getOpenFileName(self, 'Import model',
                                                    self.cwd, '(*.dae *.obj)')
        if fname:
            self.import_file(fname, brres=brres, mdl0=mdl0)

    def export_file_dialog(self, brres=None, mdl0=None):
        if mdl0 is not None:
            brres = mdl0.parent
        multiple_models = False
        if not brres:
            brres = self.brres
            if not brres:
                AutoFix.get().error('Nothing to export!')
                return
        elif mdl0 is None:
            if len(brres.models) > 1:
                multiple_models = True
        fname, fil = QFileDialog.getSaveFileName(self, 'Export model',
                                                 self.cwd,
                                                 'Model files (*.dae *.obj)')
        if fname:
            self.cwd, name = os.path.split(fname)
            base_name, ext = os.path.splitext(name)
            lower = ext.lower()
            if lower == '.obj':
                klass = ObjConverter
            elif lower == '.dae':
                klass = DaeConverter2
            else:
                self.statusBar().showMessage(
                    'Unknown extension {}'.format(ext))
                return
            if multiple_models:
                for x in brres.models:
                    export_name = os.path.join(self.cwd,
                                               base_name + '-' + x.name + ext)
                    converter = klass(brres, export_name, encode=False, mdl0=x)
                    self.update_status('Added {} to queue...'.format(x.name))
                    self.converter.enqueue(converter)
            else:
                if mdl0 is None:
                    mdl0 = brres.models[0]
                converter = klass(brres, fname, encode=False, mdl0=mdl0)
                self.update_status('Added {} to queue...'.format(mdl0.name))
                self.converter.enqueue(converter)

    def close_file(self, brres=None):
        if brres is None:
            brres = self.brres
        if brres.is_modified:
            m = QMessageBox(QMessageBox.Warning,
                            'Save Before Closing',
                            f'Save {brres.name} before closing?',
                            buttons=QMessageBox.Yes | QMessageBox.No,
                            parent=self)
            if m.exec_() == QMessageBox.Yes:
                brres.save(overwrite=True)
        if brres in self.open_files:
            self.open_files.remove(brres)
            brres.unregister(self)
        brres.close(try_save=False)
        self.brres = None
        self.poly_editor.on_brres_lock(brres)
        self.treeview.on_file_close(brres)

    def shouldExitAnyway(self, result):
        return result == QMessageBox.Ok

    def closeEvent(self, event):
        self.material_browser.on_close()
        files_to_save = []
        for x in self.open_files:
            if x.is_modified:
                files_to_save.append(x)
        if Brres.OVERWRITE:
            for x in files_to_save:
                x.close()
        elif files_to_save:
            self.files_to_save = files_to_save
            fnames = ', '.join([x.name for x in files_to_save])

            m = QMessageBox(QMessageBox.Warning,
                            'Confirm Exit?',
                            f'Exit without saving {fnames}?',
                            buttons=QMessageBox.Yes | QMessageBox.No,
                            parent=self)

            if m.exec_() == QMessageBox.No:
                event.ignore()
                return
            for x in files_to_save:
                x.close()
        self.threadpool.clear()
        self.image_manager.stop()
        self.converter.stop()
        event.accept()

    def info(self, message):
        self.emit(message, '<p style="color:Blue;">', '</p>')

    def warn(self, message):
        self.emit(message, '<p style="color:Red;">', '</p>')

    def error(self, message):
        self.emit(message, '<p style="color:Red;">', '</p>')

    def update_status(self, message):
        self.statusBar().showMessage(message)
Example #9
0
class SerialWidget(QWidget):
    """A serial widget."""
    def __init__(self, model, client):
        """Create camera widget."""
        super().__init__()
        # receive temps
        self._model = model
        self._client = client
        self._model.sendRaw.connect(self._on_send_raw)
        self._model.recvRaw.connect(self._on_recv_raw)
        self._model.updateState.connect(self._on_update_state)
        # ui
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(1)
        self.setLayout(layout)
        self._w_log = QPlainTextEdit()
        self._w_log.setReadOnly(True)
        self._w_log.setMaximumBlockCount(1000)
        layout.addWidget(self._w_log, 100)
        # buttons
        hlayout = QHBoxLayout()
        layout.addLayout(hlayout)
        hlayout.setContentsMargins(0, 0, 0, 0)
        self._b_connect = QPushButton("Connect")
        self._b_connect.clicked.connect(self._on_connect)
        self._b_connect.setEnabled(False)
        hlayout.addWidget(self._b_connect)
        self._b_disconnect = QPushButton("Disconnect")
        self._b_disconnect.clicked.connect(self._on_disconnect)
        self._b_disconnect.setEnabled(False)
        hlayout.addWidget(self._b_disconnect)
        # state
        self._last_ts = None

    def configure(self, cfg):
        """Configure widget from config file."""
        fnt = QFont("Courier", 8)
        if 'font_size' in cfg:
            size = int(cfg['font_size'])
            fnt.setPixelSize(size)
        if 'font_family' in cfg:
            family = cfg['font_family']
            fnt.setFamily(family)
        self._w_log.setFont(fnt)

    def _on_update_state(self, state):
        if state == "Offline":
            connect = True
            disconnect = False
        elif state == "Operational":
            connect = False
            disconnect = True
        else:
            connect = False
            disconnect = False
        self._b_connect.setEnabled(connect)
        self._b_disconnect.setEnabled(disconnect)

    def _calc_delta(self):
        now = time.time()
        if self._last_ts:
            delta = (now - self._last_ts) * 1000.0
        else:
            delta = 0
        self._last_ts = now
        return delta

    def _on_send_raw(self, line):
        delta = self._calc_delta()
        txt = '<font color="#0F0">TX %04d: %s</font>' % (delta, line)
        self._w_log.appendHtml(txt)

    def _on_recv_raw(self, line):
        delta = self._calc_delta()
        txt = "RX %04d: %s" % (delta, line)
        self._w_log.appendHtml(txt)

    def _on_connect(self):
        self._client.connect()

    def _on_disconnect(self):
        self._client.disconnect()
Example #10
0
class QtRunWindow(QWidget):
    resized = pyqtSignal()

    def __init__(self, master, info, firmware):
        super(QtRunWindow, self).__init__()
        self.master = master
        self.master.globalStop.connect(self.urgentStop)
        self.firmware = firmware
        self.info = info
        self.connection = self.master.connection
        self.firmwareName = self.firmware.getBoardName()
        self.ModuleType = self.firmware.getModuleByIndex(0).getModuleType()
        self.RunNumber = "-1"

        self.GroupBoxSeg = [1, 10, 1]
        self.HorizontalSeg = [3, 5]
        self.VerticalSegCol0 = [1, 3]
        self.VerticalSegCol1 = [2, 2]
        self.DisplayH = self.height() * 3. / 7
        self.DisplayW = self.width() * 3. / 7

        self.processingFlag = False
        self.ProgressBarList = []
        self.input_dir = ''
        self.output_dir = ''
        self.config_file = ''  #os.environ.get('GUI_dir')+ConfigFiles.get(self.calibration, "None")
        self.rd53_file = {}
        self.grade = -1
        self.currentTest = ""
        self.outputFile = ""
        self.errorFile = ""

        self.backSignal = False
        self.haltSignal = False
        self.finishSingal = False
        self.proceedSignal = False

        self.runNext = threading.Event()
        self.testIndexTracker = -1
        self.listWidgetIndex = 0
        self.outputDirQueue = []
        #Fixme: QTimer to be added to update the page automatically
        self.grades = []
        self.modulestatus = []
        self.autoSave = False

        self.mainLayout = QGridLayout()
        self.setLayout(self.mainLayout)

        self.setLoginUI()
        self.initializeRD53Dict()
        self.createHeadLine()
        self.createMain()
        self.createApp()
        self.occupied()

        self.resized.connect(self.rescaleImage)

        self.run_process = QProcess(self)
        self.run_process.readyReadStandardOutput.connect(
            self.on_readyReadStandardOutput)
        self.run_process.finished.connect(self.on_finish)
        self.readingOutput = False
        self.ProgressingMode = "Configure"
        self.ProgressValue = 0

        self.info_process = QProcess(self)
        self.info_process.readyReadStandardOutput.connect(
            self.on_readyReadStandardOutput_info)

    def setLoginUI(self):
        X = self.master.dimension.width() / 10
        Y = self.master.dimension.height() / 10
        Width = self.master.dimension.width() * 8. / 10
        Height = self.master.dimension.height() * 8. / 10
        self.setGeometry(X, Y, Width, Height)
        self.setWindowTitle('Run Control Page')
        self.DisplayH = self.height() * 3. / 7
        self.DisplayW = self.width() * 3. / 7
        self.show()

    def initializeRD53Dict(self):
        self.rd53_file = {}
        for module in self.firmware.getAllModules().values():
            #moduleId = module.getModuleID()
            moduleId = module.getOpticalGroupID()
            moduleType = module.getModuleType()
            for i in range(BoxSize[moduleType]):
                self.rd53_file["{0}_{1}".format(moduleId, i)] = None

    def createHeadLine(self):
        self.HeadBox = QGroupBox()

        self.HeadLayout = QHBoxLayout()

        HeadLabel = QLabel(
            '<font size="4"> Module: {0}  Test: {1} </font>'.format(
                self.info[0], self.info[1]))
        HeadLabel.setMaximumHeight(30)

        statusString, colorString = checkDBConnection(self.connection)
        StatusLabel = QLabel()
        StatusLabel.setText(statusString)
        StatusLabel.setStyleSheet(colorString)

        self.HeadLayout.addWidget(HeadLabel)
        self.HeadLayout.addStretch(1)
        self.HeadLayout.addWidget(StatusLabel)

        self.HeadBox.setLayout(self.HeadLayout)

        self.mainLayout.addWidget(self.HeadBox, 0, 0, self.GroupBoxSeg[0], 1)

    def destroyHeadLine(self):
        self.HeadBox.deleteLater()
        self.mainLayout.removeWidget(self.HeadBox)

    def createMain(self):
        self.testIndexTracker = 0
        self.MainBodyBox = QGroupBox()

        mainbodylayout = QHBoxLayout()

        kMinimumWidth = 120
        kMaximumWidth = 150
        kMinimumHeight = 30
        kMaximumHeight = 80

        # Splitters
        MainSplitter = QSplitter(Qt.Horizontal)
        LeftColSplitter = QSplitter(Qt.Vertical)
        RightColSplitter = QSplitter(Qt.Vertical)

        #Group Box for controller
        ControllerBox = QGroupBox()
        ControllerSP = ControllerBox.sizePolicy()
        ControllerSP.setVerticalStretch(self.VerticalSegCol0[0])
        ControllerBox.setSizePolicy(ControllerSP)

        self.ControlLayout = QGridLayout()

        self.CustomizedButton = QPushButton("&Customize...")
        self.CustomizedButton.clicked.connect(self.customizeTest)
        self.ResetButton = QPushButton("&Reset")
        self.ResetButton.clicked.connect(self.resetConfigTest)
        self.RunButton = QPushButton("&Run")
        self.RunButton.setDefault(True)
        self.RunButton.clicked.connect(self.initialTest)
        self.RunButton.clicked.connect(self.runTest)
        #self.ContinueButton = QPushButton("&Continue")
        #self.ContinueButton.clicked.connect(self.sendProceedSignal)
        self.AbortButton = QPushButton("&Abort")
        self.AbortButton.clicked.connect(self.abortTest)
        #self.SaveButton = QPushButton("&Save")
        #self.SaveButton.clicked.connect(self.saveTest)
        self.saveCheckBox = QCheckBox("&auto-save to DB")
        self.saveCheckBox.setMaximumHeight(30)
        self.saveCheckBox.setChecked(self.autoSave)
        if not isActive(self.connection):
            self.saveCheckBox.setChecked(False)
            self.saveCheckBox.setDisabled(True)
        self.saveCheckBox.clicked.connect(self.setAutoSave)

        self.ControlLayout.addWidget(self.CustomizedButton, 0, 0, 1, 2)
        self.ControlLayout.addWidget(self.ResetButton, 0, 2, 1, 1)
        self.ControlLayout.addWidget(self.RunButton, 1, 0, 1, 1)
        self.ControlLayout.addWidget(self.AbortButton, 1, 1, 1, 1)
        self.ControlLayout.addWidget(self.saveCheckBox, 1, 2, 1, 1)

        ControllerBox.setLayout(self.ControlLayout)

        #Group Box for ternimal display
        TerminalBox = QGroupBox("&Terminal")
        TerminalSP = TerminalBox.sizePolicy()
        TerminalSP.setVerticalStretch(self.VerticalSegCol0[1])
        TerminalBox.setSizePolicy(TerminalSP)
        TerminalBox.setMinimumWidth(400)

        ConsoleLayout = QGridLayout()

        self.ConsoleView = QPlainTextEdit()
        self.ConsoleView.setStyleSheet(
            "QTextEdit { background-color: rgb(10, 10, 10); color : white; }")
        #self.ConsoleView.setCenterOnScroll(True)
        self.ConsoleView.ensureCursorVisible()

        ConsoleLayout.addWidget(self.ConsoleView)
        TerminalBox.setLayout(ConsoleLayout)

        #Group Box for output display
        OutputBox = QGroupBox("&Result")
        OutputBoxSP = OutputBox.sizePolicy()
        OutputBoxSP.setVerticalStretch(self.VerticalSegCol1[0])
        OutputBox.setSizePolicy(OutputBoxSP)

        OutputLayout = QGridLayout()
        self.ResultWidget = ResultTreeWidget(self.info, self.DisplayW,
                                             self.DisplayH, self.master)
        #self.DisplayTitle = QLabel('<font size="6"> Result: </font>')
        #self.DisplayLabel = QLabel()
        #self.DisplayLabel.setScaledContents(True)
        #self.DisplayView = QPixmap('test_plots/test_best1.png').scaled(QSize(self.DisplayW,self.DisplayH), Qt.KeepAspectRatio, Qt.SmoothTransformation)
        #self.DisplayLabel.setPixmap(self.DisplayView)
        #self.ReferTitle = QLabel('<font size="6"> Reference: </font>')
        #self.ReferLabel = QLabel()
        #self.ReferLabel.setScaledContents(True)
        #self.ReferView = QPixmap('test_plots/test_best1.png').scaled(QSize(self.DisplayW,self.DisplayH), Qt.KeepAspectRatio, Qt.SmoothTransformation)
        #self.ReferLabel.setPixmap(self.ReferView)

        #self.ListWidget = QListWidget()
        #self.ListWidget.setMinimumWidth(150)
        #self.ListWidget.clicked.connect(self.clickedOutputItem)
        ##To be removed (END)

        #OutputLayout.addWidget(self.DisplayTitle,0,0,1,2)
        #OutputLayout.addWidget(self.DisplayLabel,1,0,1,2)
        #OutputLayout.addWidget(self.ReferTitle,0,2,1,2)
        #OutputLayout.addWidget(self.ReferLabel,1,2,1,2)
        #OutputLayout.addWidget(self.ListWidget,0,4,2,1)

        OutputLayout.addWidget(self.ResultWidget, 0, 0, 1, 1)
        OutputBox.setLayout(OutputLayout)

        #Group Box for history
        self.HistoryBox = QGroupBox("&History")
        HistoryBoxSP = self.HistoryBox.sizePolicy()
        HistoryBoxSP.setVerticalStretch(self.VerticalSegCol1[1])
        self.HistoryBox.setSizePolicy(HistoryBoxSP)

        self.HistoryLayout = QGridLayout()

        # Display the table for module history
        #self.dataList = getLocalRemoteTests(self.connection, self.info[0])
        #self.proxy = QtTableWidget(self.dataList)
        #self.lineEdit       = QLineEdit()
        #self.lineEdit.textChanged.connect(self.proxy.on_lineEdit_textChanged)
        #self.view           = QTableView()
        #self.view.setSortingEnabled(True)
        #self.comboBox       = QComboBox()
        #self.comboBox.addItems(["{0}".format(x) for x in self.dataList[0]])
        #self.comboBox.currentIndexChanged.connect(self.proxy.on_comboBox_currentIndexChanged)
        #self.label          = QLabel()
        #self.label.setText("Regex Filter")

        #self.view.setModel(self.proxy)
        #self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)

        self.HistoryLayout = QGridLayout()
        #self.HistoryLayout.addWidget(self.lineEdit, 0, 1, 1, 1)
        #self.HistoryLayout.addWidget(self.view, 1, 0, 1, 3)
        #self.HistoryLayout.addWidget(self.comboBox, 0, 2, 1, 1)
        #self.HistoryLayout.addWidget(self.label, 0, 0, 1, 1)

        #self.StatusCanvas = RunStatusCanvas(parent=self,width=5, height=4, dpi=100)
        self.StatusTable = QTableWidget()
        self.header = ["TestName"]
        for key in self.rd53_file.keys():
            ChipName = key.split("_")
            self.header.append("Module{}_Chip{}".format(
                ChipName[0], ChipName[1]))
        self.StatusTable.setColumnCount(len(self.header))
        self.StatusTable.setHorizontalHeaderLabels(self.header)
        self.StatusTable.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.HistoryLayout.addWidget(self.StatusTable)
        self.HistoryBox.setLayout(self.HistoryLayout)

        LeftColSplitter.addWidget(ControllerBox)
        LeftColSplitter.addWidget(TerminalBox)
        RightColSplitter.addWidget(OutputBox)
        RightColSplitter.addWidget(self.HistoryBox)

        LeftColSplitterSP = LeftColSplitter.sizePolicy()
        LeftColSplitterSP.setHorizontalStretch(self.HorizontalSeg[0])
        LeftColSplitter.setSizePolicy(LeftColSplitterSP)

        RightColSplitterSP = RightColSplitter.sizePolicy()
        RightColSplitterSP.setHorizontalStretch(self.HorizontalSeg[1])
        RightColSplitter.setSizePolicy(RightColSplitterSP)

        MainSplitter.addWidget(LeftColSplitter)
        MainSplitter.addWidget(RightColSplitter)

        mainbodylayout.addWidget(MainSplitter)
        #mainbodylayout.addWidget(ControllerBox, sum(self.VerticalSegCol0[:0]), sum(self.HorizontalSeg[:0]), self.VerticalSegCol0[0], self.HorizontalSeg[0])
        #mainbodylayout.addWidget(TerminalBox, sum(self.VerticalSegCol0[:1]), sum(self.HorizontalSeg[:0]), self.VerticalSegCol0[1], self.HorizontalSeg[0])
        #mainbodylayout.addWidget(OutputBox, sum(self.VerticalSegCol1[:0]), sum(self.HorizontalSeg[:1]), self.VerticalSegCol1[0], self.HorizontalSeg[1])
        #mainbodylayout.addWidget(HistoryBox, sum(self.VerticalSegCol1[:1]), sum(self.HorizontalSeg[:1]), self.VerticalSegCol1[1], self.HorizontalSeg[1])

        self.MainBodyBox.setLayout(mainbodylayout)
        self.mainLayout.addWidget(self.MainBodyBox, sum(self.GroupBoxSeg[0:1]),
                                  0, self.GroupBoxSeg[1], 1)

    def destroyMain(self):
        self.MainBodyBox.deleteLater()
        self.mainLayout.removeWidget(self.MainBodyBox)

    def createApp(self):
        self.AppOption = QGroupBox()
        self.StartLayout = QHBoxLayout()

        self.ConnectButton = QPushButton("&Connect to DB")
        self.ConnectButton.clicked.connect(self.connectDB)

        self.BackButton = QPushButton("&Back")
        self.BackButton.clicked.connect(self.sendBackSignal)
        self.BackButton.clicked.connect(self.closeWindow)
        self.BackButton.clicked.connect(self.creatStartWindow)

        self.FinishButton = QPushButton("&Finish")
        self.FinishButton.setDefault(True)
        self.FinishButton.clicked.connect(self.closeWindow)

        self.StartLayout.addStretch(1)
        self.StartLayout.addWidget(self.ConnectButton)
        self.StartLayout.addWidget(self.BackButton)
        self.StartLayout.addWidget(self.FinishButton)
        self.AppOption.setLayout(self.StartLayout)

        self.mainLayout.addWidget(self.AppOption, sum(self.GroupBoxSeg[0:2]),
                                  0, self.GroupBoxSeg[2], 1)

    def destroyApp(self):
        self.AppOption.deleteLater()
        self.mainLayout.removeWidget(self.AppOption)

    def closeWindow(self):
        self.close()

    def creatStartWindow(self):
        if self.backSignal == True:
            self.master.openNewTest()

    def occupied(self):
        self.master.ProcessingTest = True

    def release(self):
        self.run_process.kill()
        self.master.ProcessingTest = False
        self.master.NewTestButton.setDisabled(False)
        self.master.LogoutButton.setDisabled(False)
        self.master.ExitButton.setDisabled(False)

    def refreshHistory(self):
        #self.dataList = getLocalRemoteTests(self.connection, self.info[0])
        #self.proxy = QtTableWidget(self.dataList)
        #self.view.setModel(self.proxy)
        #self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)
        #self.view.update()
        self.HistoryLayout.removeWidget(self.StatusTable)
        self.StatusTable.setRowCount(0)
        for index, test in enumerate(self.modulestatus):
            row = self.StatusTable.rowCount()
            self.StatusTable.setRowCount(row + 1)
            if isCompositeTest(self.info[1]):
                self.StatusTable.setItem(
                    row, 0,
                    QTableWidgetItem(CompositeList[self.info[1]][index]))
            else:
                self.StatusTable.setItem(row, 0,
                                         QTableWidgetItem(self.info[1]))
            for moduleKey in test.keys():
                for chipKey in test[moduleKey].keys():
                    ChipID = "Module{}_Chip{}".format(moduleKey, chipKey)
                    status = "Pass" if test[moduleKey][
                        chipKey] == True else "Failed"
                    if ChipID in self.header:
                        columnId = self.header.index(ChipID)
                        self.StatusTable.setItem(row, columnId,
                                                 QTableWidgetItem(status))
                        if status == "Pass":
                            self.StatusTable.item(row, columnId).setBackground(
                                QColor(Qt.green))
                        elif status == "Failed":
                            self.StatusTable.item(row, columnId).setBackground(
                                QColor(Qt.red))

        self.HistoryLayout.addWidget(self.StatusTable)

    def sendBackSignal(self):
        self.backSignal = True

    def sendProceedSignal(self):
        self.proceedSignal = True
        #self.runNext.set()

    def connectDB(self):
        if isActive(self.master.connection):
            self.connection = self.master.connection
            self.refresh()
            self.saveCheckBox.setDisabled(False)
            return

        LoginDialog = QtLoginDialog(self.master)
        response = LoginDialog.exec_()
        if response == QDialog.Accepted:
            self.connectDB()
        else:
            return

    def configTest(self):
        try:
            RunNumberFileName = os.environ.get(
                'Ph2_ACF_AREA') + "/test/RunNumber.txt"
            if os.path.isfile(RunNumberFileName):
                runNumberFile = open(RunNumberFileName, "r")
                runNumberText = runNumberFile.readlines()
                self.RunNumber = runNumberText[0].split('\n')[0]
                logger.info("RunNumber: {}".format(self.RunNumber))
        except:
            logger.warning("Failed to retrive RunNumber")

        if self.currentTest == "" and isCompositeTest(self.info[1]):
            testName = CompositeList[self.info[1]][0]
        elif self.currentTest == None:
            testName = self.info[1]
        else:
            testName = self.currentTest

        ModuleIDs = []
        for module in self.firmware.getAllModules().values():
            #ModuleIDs.append(str(module.getModuleID()))
            ModuleIDs.append(str(module.getOpticalGroupID()))

        self.output_dir, self.input_dir = ConfigureTest(
            testName, "_Module".join(ModuleIDs), self.output_dir,
            self.input_dir, self.connection)

        for key in self.rd53_file.keys():
            if self.rd53_file[key] == None:
                self.rd53_file[key] = os.environ.get(
                    'Ph2_ACF_AREA') + "/settings/RD53Files/CMSIT_RD53.txt"
        if self.input_dir == "":
            SetupRD53ConfigfromFile(self.rd53_file, self.output_dir)
        else:
            SetupRD53Config(self.input_dir, self.output_dir, self.rd53_file)

        if self.input_dir == "":
            if self.config_file == "":
                tmpDir = os.environ.get('GUI_dir') + "/Gui/.tmp"
                if not os.path.isdir(tmpDir) and os.environ.get('GUI_dir'):
                    try:
                        os.mkdir(tmpDir)
                        logger.info("Creating " + tmpDir)
                    except:
                        logger.warning("Failed to create " + tmpDir)
                config_file = GenerateXMLConfig(self.firmware,
                                                self.currentTest, tmpDir)
                #config_file = os.environ.get('GUI_dir')+ConfigFiles.get(testName, "None")
                if config_file:
                    SetupXMLConfigfromFile(config_file, self.output_dir,
                                           self.firmwareName, self.rd53_file)
                else:
                    logger.warning("No Valid XML configuration file")
                #QMessageBox.information(None,"Noitce", "Using default XML configuration",QMessageBox.Ok)
            else:
                SetupXMLConfigfromFile(self.config_file, self.output_dir,
                                       self.firmwareName, self.rd53_file)
        else:
            if self.config_file != "":
                SetupXMLConfigfromFile(self.config_file, self.output_dir,
                                       self.firmwareName, self.rd53_file)
            else:
                tmpDir = os.environ.get('GUI_dir') + "/Gui/.tmp"
                if not os.path.isdir(tmpDir) and os.environ.get('GUI_dir'):
                    try:
                        os.mkdir(tmpDir)
                        logger.info("Creating " + tmpDir)
                    except:
                        logger.warning("Failed to create " + tmpDir)
                config_file = GenerateXMLConfig(self.firmware,
                                                self.currentTest, tmpDir)
                #config_file = os.environ.get('GUI_dir')+ConfigFiles.get(testName, "None")
                if config_file:
                    SetupXMLConfigfromFile(config_file, self.output_dir,
                                           self.firmwareName, self.rd53_file)
                else:
                    logger.warning("No Valid XML configuration file")

                # To be remove:
                #config_file = os.environ.get('GUI_dir')+ConfigFiles.get(testName, "None")
                #SetupXMLConfigfromFile(config_file,self.output_dir,self.firmwareName,self.rd53_file)
                #SetupXMLConfig(self.input_dir,self.output_dir)

        self.initializeRD53Dict()
        self.config_file = ""
        return

    def saveConfigs(self):
        for key in self.rd53_file.keys():
            try:
                os.system(
                    "cp {0}/test/CMSIT_RD53_{1}.txt {2}/CMSIT_RD53_{1}_OUT.txt"
                    .format(os.environ.get("Ph2_ACF_AREA"), key,
                            self.output_dir))
            except:
                print(
                    "Failed to copy {0}/test/CMSIT_RD53_{1}.txt {2}/CMSIT_RD53_{1}_OUT.txt"
                    .format(os.environ.get("Ph2_ACF_AREA"), key,
                            self.output_dir))

    def customizeTest(self):
        print("Customize configuration")
        self.CustomizedButton.setDisabled(True)
        self.ResetButton.setDisabled(True)
        self.RunButton.setDisabled(True)
        self.CustomizedWindow = QtCustomizeWindow(self, self.rd53_file)
        self.CustomizedButton.setDisabled(False)
        self.ResetButton.setDisabled(False)
        self.RunButton.setDisabled(False)

    def resetConfigTest(self):
        self.input_dir = ""
        self.output_dir = ""
        self.config_file = ""
        self.initializeRD53Dict()

    def initialTest(self):
        if "Re" in self.RunButton.text():
            self.grades = []
            for index in range(len(CompositeList[self.info[1]])):
                self.ResultWidget.ProgressBar[index].setValue(0)

    def runTest(self):
        self.ResetButton.setDisabled(True)
        #self.ControlLayout.removeWidget(self.RunButton)
        #self.RunButton.deleteLater()
        #self.ControlLayout.addWidget(self.ContinueButton,1,0,1,1)
        testName = self.info[1]

        self.input_dir = self.output_dir
        self.output_dir = ""

        #self.StatusCanvas.renew()
        #self.StatusCanvas.update()
        #self.HistoryLayout.removeWidget(self.StatusCanvas)
        #self.HistoryLayout.addWidget(self.StatusCanvas)

        if isCompositeTest(testName):
            self.runCompositeTest(testName)
        elif isSingleTest(testName):
            self.runSingleTest(testName)
        else:
            QMessageBox.information(None, "Warning", "Not a valid test",
                                    QMessageBox.Ok)
            return

    def runCompositeTest(self, testName):
        if self.haltSignal:
            return
        if self.testIndexTracker == len(CompositeList[self.info[1]]):
            self.testIndexTracker = 0
            return
        testName = CompositeList[self.info[1]][self.testIndexTracker]
        self.runSingleTest(testName)

    def runSingleTest(self, testName):
        self.currentTest = testName
        self.configTest()
        self.outputFile = self.output_dir + "/output.txt"
        self.errorFile = self.output_dir + "/error.txt"
        #self.ContinueButton.setDisabled(True)
        #self.run_process.setProgram()
        self.info_process.setProcessChannelMode(QtCore.QProcess.MergedChannels)
        self.info_process.setWorkingDirectory(
            os.environ.get("Ph2_ACF_AREA") + "/test/")
        self.info_process.start("echo", [
            "Running COMMAND: CMSITminiDAQ  -f  CMSIT.xml  -c  {}".format(
                Test[self.currentTest])
        ])
        self.info_process.waitForFinished()

        self.run_process.setProcessChannelMode(QtCore.QProcess.MergedChannels)
        self.run_process.setWorkingDirectory(
            os.environ.get("Ph2_ACF_AREA") + "/test/")
        #self.run_process.setStandardOutputFile(self.outputFile)
        #self.run_process.setStandardErrorFile(self.errorFile)

        #self.run_process.start("python", ["signal_generator.py"])
        #self.run_process.start("tail" , ["-n","6000", "/Users/czkaiweb/Research/Ph2_ACF_GUI/Gui/forKai.txt"])
        #self.run_process.start("./SignalGenerator")

        if Test[self.currentTest] in [
                "pixelalive", "noise", "latency", "injdelay", "clockdelay",
                "threqu", "thrmin", "scurve"
        ]:
            self.run_process.start(
                "CMSITminiDAQ",
                ["-f", "CMSIT.xml", "-c", "{}".format(Test[self.currentTest])])
        else:
            self.info_process.start("echo", [
                "test {} not runnable, quitting...".format(
                    Test[self.currentTest])
            ])

        #self.run_process.start("ping", ["-c","5","www.google.com"])
        #self.run_process.waitForFinished()
        self.displayResult()

        #Question = QMessageBox()
        #Question.setIcon(QMessageBox.Question)
        #Question.setWindowTitle('SingleTest Finished')
        #Question.setText('Save current result and proceed?')
        #Question.setStandardButtons(QMessageBox.No| QMessageBox.Save | QMessageBox.Yes)
        #Question.setDefaultButton(QMessageBox.Yes)
        #customizedButton = Question.button(QMessageBox.Save)
        #customizedButton.setText('Save Only')
        #reply  = Question.exec_()

        #if reply == QMessageBox.Yes or reply == QMessageBox.Save:
        #	self.saveTest()
        #if reply == QMessageBox.No or reply == QMessageBox.Save:
        #	self.haltSignal = True
        #self.refreshHistory()
        #self.finishSingal = False

    def abortTest(self):
        reply = QMessageBox.question(None, "Abort", "Are you sure to abort?",
                                     QMessageBox.No | QMessageBox.Yes,
                                     QMessageBox.No)

        if reply == QMessageBox.Yes:
            self.run_process.kill()
            self.haltSignal = True
            self.sendProceedSignal()
        else:
            return
        self.RunButton.setText("Re-run")
        self.RunButton.setDisabled(False)

    def urgentStop(self):
        self.run_process.kill()
        self.haltSignal = True
        self.sendProceedSignal()

        self.RunButton.setText("Re-run")
        self.RunButton.setDisabled(True)

    def validateTest(self):
        # Fixme: the grading for test results
        grade, passmodule = ResultGrader(self.output_dir, self.currentTest,
                                         self.RunNumber)
        self.grades.append(grade)
        self.modulestatus.append(passmodule)

        self.ResultWidget.StatusLabel[self.testIndexTracker -
                                      1].setText("Pass")
        self.ResultWidget.StatusLabel[self.testIndexTracker -
                                      1].setStyleSheet("color: green")
        for module in passmodule.values():
            if False in module.values():
                self.ResultWidget.StatusLabel[self.testIndexTracker -
                                              1].setText("Failed")
                self.ResultWidget.StatusLabel[self.testIndexTracker -
                                              1].setStyleSheet("color: red")

        time.sleep(0.5)
        #self.StatusCanvas.renew()
        #self.StatusCanvas.update()
        #self.HistoryLayout.removeWidget(self.StatusCanvas)
        #self.HistoryLayout.addWidget(self.StatusCanvas)

    def saveTest(self):
        #if self.parent.current_test_grade < 0:
        if self.run_process.state() == QProcess.Running:
            QMessageBox.critical(self, "Error", "Process not finished",
                                 QMessageBox.Ok)
            return

        try:
            os.system("cp {0}/test/Results/Run{1}*.root {2}/".format(
                os.environ.get("Ph2_ACF_AREA"), self.RunNumber,
                self.output_dir))
        except:
            print("Failed to copy file to output directory")

    def saveTestToDB(self):
        if isActive(self.connection) and self.autoSave:
            try:
                localDir = self.output_dir
                getFiles = subprocess.run(
                    'find {0} -mindepth 1  -maxdepth 1 -type f -name "*.root"  '
                    .format(localDir),
                    shell=True,
                    stdout=subprocess.PIPE)
                fileList = getFiles.stdout.decode('utf-8').rstrip('\n').split(
                    '\n')
                moduleList = [
                    module for module in localDir.split('_')
                    if "Module" in module
                ]

                if fileList == [""]:
                    logger.warning(
                        "No ROOT file found in the local folder, skipping...")
                    return

                ## Submit all files
                for submitFile in fileList:
                    data_id = hashlib.md5(
                        '{}'.format(submitFile).encode()).hexdigest()
                    if not self.checkRemoteFile(data_id):
                        self.uploadFile(submitFile, data_id)

                    ## Submit records for all modules
                    for module in moduleList:
                        getConfigInFiles = subprocess.run(
                            'find {0} -mindepth 1  -maxdepth 1 -type f -name "CMSIT_RD53_{1}_*_IN.txt"  '
                            .format(localDir, module_id),
                            shell=True,
                            stdout=subprocess.PIPE)
                        configInFileList = getConfigInFiles.stdout.decode(
                            'utf-8').rstrip('\n').split('\n')
                        getConfigOutFiles = subprocess.run(
                            'find {0} -mindepth 1  -maxdepth 1 -type f -name "CMSIT_RD53_{1}_*_OUT.txt"  '
                            .format(localDir, module_id),
                            shell=True,
                            stdout=subprocess.PIPE)
                        configOutFileList = getConfigOutFiles.stdout.decode(
                            'utf-8').rstrip('\n').split('\n')

                        configcolumns = []
                        configdata = []
                        for configInFile in configInFileList:
                            if configInFile != [""]:
                                configcolumns.append("Chip{}InConfig".format(
                                    configInFile.split('_')[-2]))
                                configInBuffer = open(configInFile, 'rb')
                                configInBin = configInBuffer.read()
                                configdata.append(configInBin)
                        for configOutFile in configOutFileList:
                            if configOutFile != [""]:
                                configcolumns.append("Chip{}OutConfig".format(
                                    configOutFile.split('_')[-2]))
                                configOutBuffer = open(configOutFile, 'rb')
                                configOutBin = configOutBuffer.read()
                                configdata.append(configOutBin)

                        Columns = [
                            "part_id", "date", "testname", "description",
                            "grade", "data_id", "username"
                        ]
                        SubmitArgs = []
                        Value = []

                        record = formatter(localDir, Columns, part_id=module)
                        for column in ['part_id']:
                            if column == "part_id":
                                SubmitArgs.append(column)
                                Value.append(module)
                            if column == "date":
                                SubmitArgs.append(column)
                                Value.append(record[Columns.index(column)])
                            if column == "testname":
                                SubmitArgs.append(column)
                                Value.append(record[Columns.index(column)])
                            if column == "description":
                                SubmitArgs.append(column)
                                Value.append("No Comment")
                            if column == "grade":
                                SubmitArgs.append(column)
                                Value.append(-1)
                            if column == "data_id":
                                SubmitArgs.append(column)
                                Value.append(data_id)
                            if column == "username":
                                SubmitArgs.append(column)
                                Value.append(self.master.TryUsername)

                        SubmitArgs = SubmitArgs + configcolumns
                        Value = Value + configdata

                        try:
                            insertGenericTable(self.connection, "tests",
                                               SubmitArgs, Value)
                        except:
                            print("Failed to insert")
            except Exception as err:
                QMessageBox.information(self, "Error", "Unable to save to DB",
                                        QMessageBox.Ok)
                print("Error: {}".format(repr(err)))
                return

    def checkRemoteFile(self, file_id):
        remoteRecords = retrieveWithConstraint(self.connection,
                                               "result_files",
                                               file_id=file_id,
                                               columns=["file_id"])
        return remoteRecords != []

    def uploadFile(self, fileName, file_id):
        fileBuffer = open(fileName, 'rb')
        data = fileBuffer.read()
        insertGenericTable(self.connection, "result_files",
                           ["file_id", "file_content"], [file_id, data])

    #######################################################################
    ##  For result display
    #######################################################################
    def displayResult(self):
        #Fixme: remake the list
        #updatePixmap = QPixmap("test_plots/pixelalive_ex.png").scaled(QSize(self.DisplayW,self.DisplayH), Qt.KeepAspectRatio, Qt.SmoothTransformation)
        #self.DisplayLabel.setPixmap(updatePixmap)
        pass

    def clickedOutputItem(self, qmodelindex):
        #Fixme: Extract the info from ROOT file
        item = self.ListWidget.currentItem()
        referName = item.text().split("_")[0]
        if referName in [
                "GainScan", "Latency", "NoiseScan", "PixelAlive", "SCurveScan",
                "ThresholdEqualization"
        ]:
            self.ReferView = QPixmap(
                os.environ.get('GUI_dir') +
                '/Gui/test_plots/{0}.png'.format(referName)).scaled(
                    QSize(self.DisplayW, self.DisplayH), Qt.KeepAspectRatio,
                    Qt.SmoothTransformation)
            self.ReferLabel.setPixmap(self.ReferView)

    #######################################################################
    ##  For real-time terminal display
    #######################################################################

    @QtCore.pyqtSlot()
    def on_readyReadStandardOutput(self):
        if self.readingOutput == True:
            print("Thread competition detected")
            return
        self.readingOutput = True
        if os.path.exists(self.outputFile):
            outputfile = open(self.outputFile, "a")
        else:
            outputfile = open(self.outputFile, "w")

        alltext = self.run_process.readAllStandardOutput().data().decode()
        outputfile.write(alltext)
        outputfile.close()
        textline = alltext.split('\n')
        #fileLines = open(self.outputFile,"r")
        #textline = fileLines.readlines()

        for textStr in textline:
            if "@@@ Initializing the Hardware @@@" in textStr:
                self.ProgressingMode = "Configure"
            if "@@@ Performing" in textStr:
                self.ProgressingMode = "Perform"
                self.ConsoleView.appendHtml(
                    '<b><span style="color:#ff0000;"> Performing the {} test </span></b>'
                    .format(self.currentTest))
            if "Readout chip error report" in textStr:
                self.ProgressingMode = "Summary"
            if self.ProgressingMode == "Perform":
                if ">>>> Progress :" in textStr:
                    try:
                        index = textStr.split().index("Progress") + 2
                        self.ProgressValue = float(
                            textStr.split()[index].rstrip("%"))
                        self.ResultWidget.ProgressBar[
                            self.testIndexTracker].setValue(self.ProgressValue)
                    except:
                        pass
                continue
            text = textStr.encode('ascii')
            numUpAnchor, text = parseANSI(text)
            #if numUpAnchor > 0:
            #	textCursor = self.ConsoleView.textCursor()
            #	textCursor.beginEditBlock()
            #	textCursor.movePosition(QTextCursor.End, QTextCursor.MoveAnchor)
            #	textCursor.movePosition(QTextCursor.StartOfLine, QTextCursor.KeepAnchor)
            #	for numUp in range(numUpAnchor):
            #		textCursor.movePosition(QTextCursor.Up, QTextCursor.KeepAnchor)
            #	textCursor.removeSelectedText()
            #	textCursor.deletePreviousChar()
            #	textCursor.endEditBlock()
            #	self.ConsoleView.setTextCursor(textCursor)
            textCursor = self.ConsoleView.textCursor()
            self.ConsoleView.setTextCursor(textCursor)
            self.ConsoleView.appendHtml(text.decode("utf-8"))
        self.readingOutput = False

    @QtCore.pyqtSlot()
    def on_readyReadStandardOutput_info(self):
        if os.path.exists(self.outputFile):
            outputfile = open(self.outputFile, "a")
        else:
            outputfile = open(self.outputFile, "w")

        alltext = self.info_process.readAllStandardOutput().data().decode()
        outputfile.write(alltext)
        outputfile.close()
        textline = alltext.split('\n')

        for textStr in textline:
            self.ConsoleView.appendHtml(textStr)

    @QtCore.pyqtSlot()
    def on_finish(self):
        self.RunButton.setDisabled(True)
        self.RunButton.setText("&Continue")
        self.finishSingal = True

        #To be removed
        #if isCompositeTest(self.info[1]):
        #	self.ListWidget.insertItem(self.listWidgetIndex, "{}_Module_0_Chip_0".format(CompositeList[self.info[1]][self.testIndexTracker-1]))
        #if isSingleTest(self.info[1]):
        #	self.ListWidget.insertItem(self.listWidgetIndex, "{}_Module_0_Chip_0".format(self.info[1]))

        self.testIndexTracker += 1
        self.saveConfigs()

        if isCompositeTest(self.info[1]):
            if self.testIndexTracker == len(CompositeList[self.info[1]]):
                self.RunButton.setText("&Re-run")
                self.RunButton.setDisabled(False)
        if isSingleTest(self.info[1]):
            #self.RunButton.setText("&Finish")
            #self.RunButton.setDisabled(True)
            self.RunButton.setText("&Re-run")
            self.RunButton.setDisabled(False)

        # Save the output ROOT file to output_dir
        self.saveTest()

        # validate the results
        self.validateTest()

        # show the score of test
        self.refreshHistory()

        # For test
        # self.ResultWidget.updateResult("/Users/czkaiweb/Research/data")
        self.ResultWidget.updateResult(self.output_dir)

        if self.autoSave:
            self.saveTestToDB()
        self.update()

        if isCompositeTest(self.info[1]):
            self.runTest()

    #######################################################################
    ##  For real-time terminal display
    #######################################################################

    def refresh(self):
        self.destroyHeadLine()
        self.createHeadLine()
        self.destroyApp()
        self.createApp()

    def resizeEvent(self, event):
        self.resized.emit()
        return super(QtRunWindow, self).resizeEvent(event)

    def rescaleImage(self):
        self.DisplayH = self.height() * 3. / 7
        self.DisplayW = self.width() * 3. / 7
        self.ResultWidget.resizeImage(self.DisplayW, self.DisplayH)

    def setAutoSave(self):
        if self.autoSave:
            self.autoSave = False
        else:
            self.autoSave = True
        self.saveCheckBox.setChecked(self.autoSave)

    def closeEvent(self, event):
        if self.processingFlag == True:
            event.ignore()

        else:
            reply = QMessageBox.question(
                self, 'Window Close',
                'Are you sure you want to quit the test?',
                QMessageBox.No | QMessageBox.Yes, QMessageBox.No)

            if reply == QMessageBox.Yes:
                self.release()
                self.master.powersupply.TurnOff()
                event.accept()
            else:
                self.backSignal = False
                event.ignore()
Example #11
0
class WorkWidget(QWidget):

    work_started = pyqtSignal()
    work_ended = pyqtSignal()

    def __init__(self, work="", title_style=Style.h2()):
        QWidget.__init__(self)
        self.ctrl = QApplication.instance().ctrl
        self.ctrl.switch_language.connect(self.on_switch_language)
        self.ctrl.switch_configuration.connect(self.on_switch_configuration)
        self.paras = self.ctrl.paras
        self.conf = GuiConf()
        # work should be a verb: Execute, Transport ...
        self.work = work
        self.title_style = title_style
        self.setWindowTitle(_(self.work))
        self.executor_thread = None
        self.splitter_event_moved = False
        self.splitter_title_moved = False
        self.init_ui()
        self.show()

    def init_ui(self):
        vbox = QVBoxLayout()

        self.label_title = QLabel("%s %s" % (_(self.work), self.paras.configuration_name()))
        font = QFont()
        font.setPointSize(18)
        font.setBold(True)
        self.label_title.setFont(font)
        self.label_title.setContentsMargins(10, 5, 5, 7)
        self.label_title.setStyleSheet(self.title_style)
        hbox1 = QHBoxLayout()
        hbox1.addWidget(self.label_title, 1)
        vbox.addLayout(hbox1)

        self.splitter_title = QSplitter(Qt.Vertical)
        self.splitter_event = QSplitter(Qt.Vertical)
        self.splitter_title.splitterMoved.connect(self.on_splitter_title_moved)
        self.splitter_event.splitterMoved.connect(self.on_splitter_event_moved)

        self.lbl_events_title1 = QLabel(_("Main events"))
        self.splitter_title.addWidget(self.lbl_events_title1)

        self.lbl_events_title2 = QLabel(_("Resources"))
        self.splitter_title.addWidget(self.lbl_events_title2)

        self.lbl_events_title3 = QLabel(_("Errors"))
        self.splitter_title.addWidget(self.lbl_events_title3)

        self.pte_events1 = QTextBrowser()
        self.pte_events1.setOpenExternalLinks(True)
        self.pte_events1.setOpenLinks(False)
        self.pte_events1.anchorClicked.connect(self.on_anchor_clicked)
        self.pte_events1.setLineWrapMode(QTextEdit.NoWrap)
        self.splitter_event.addWidget(self.pte_events1)

        self.pte_events2 = QTextBrowser()
        self.pte_events2.setOpenExternalLinks(True)
        self.pte_events2.setOpenLinks(False)
        self.pte_events2.anchorClicked.connect(self.on_anchor_clicked)
        self.pte_events2.setLineWrapMode(QTextEdit.NoWrap)
        self.splitter_event.addWidget(self.pte_events2)

        self.pte_events3 = QPlainTextEdit()
        self.pte_events3.setReadOnly(True)
        self.pte_events3.setLineWrapMode(QPlainTextEdit.NoWrap)
        self.pte_events3.setStyleSheet(Style.red_text())
        self.pte_events3.setCenterOnScroll(True)
        self.splitter_event.addWidget(self.pte_events3)

        self.splitter_title.setStretchFactor(0, 5)
        self.splitter_title.setStretchFactor(1, 3)
        self.splitter_title.setStretchFactor(2, 1)
        self.splitter_event.setStretchFactor(0, 5)
        self.splitter_event.setStretchFactor(1, 3)
        self.splitter_event.setStretchFactor(2, 1)
        hbox_splitters = QHBoxLayout()
        hbox_splitters.addWidget(self.splitter_title, 0)
        hbox_splitters.addWidget(self.splitter_event, 5)
        vbox.addLayout(hbox_splitters)

        lbl_box = QHBoxLayout()
        self.lbl_processing = QLabel(_("Processing:"))
        self.lbl_processing_file = QLabel("")
        font = QFontDatabase.systemFont(QFontDatabase.FixedFont)
        self.lbl_processing_file.setFont(font)
        self.lbl_processing.setFont(font)
        self.lbl_processing.setVisible(False)
        self.lbl_processing_file.setVisible(False)
        lbl_box.addWidget(self.lbl_processing)
        lbl_box.addWidget(self.lbl_processing_file)
        lbl_box.addStretch(1)
        vbox.addLayout(lbl_box)

        btn_box = QHBoxLayout()
        btn_box.addStretch(1)
        self.chk_trial_run = QCheckBox(_("Trial run"))
        self.chk_trial_run.setChecked(not self.paras.is_saving_sitemaps)
        btn_box.addWidget(self.chk_trial_run)
        self.btn_run = QPushButton(_("Run"))
        self.btn_run.clicked.connect(self.on_btn_run_clicked)
        btn_box.addWidget(self.btn_run)
        self.btn_stop = QPushButton(_("Stop"))
        self.btn_stop.clicked.connect(self.on_btn_stop_clicked)
        self.normal_style = self.btn_stop.styleSheet()
        self.btn_stop.setEnabled(False)
        btn_box.addWidget(self.btn_stop)
        self.btn_close = QPushButton(_("Close"))
        self.btn_close.clicked.connect(self.on_btn_close_clicked)
        btn_box.addWidget(self.btn_close)
        vbox.addLayout(btn_box)

        self.setLayout(vbox)
        self.resize(self.conf.work_widget_width(self.work), self.conf.work_widget_height(self.work))

    def on_splitter_title_moved(self, pos, index):
        self.splitter_title_moved = True
        if not self.splitter_event_moved:
            self.splitter_event.moveSplitter(pos, index)
        self.splitter_title_moved = False

    def on_splitter_event_moved(self, pos, index):
        self.splitter_event_moved = True
        if not self.splitter_title_moved:
            self.splitter_title.moveSplitter(pos, index)
        self.splitter_event_moved = False

    def on_switch_language(self):
        self.setWindowTitle(_(self.work))
        self.label_title.setText("%s %s" % (_(self.work), self.paras.configuration_name()))
        self.lbl_events_title1.setText(_("Main events"))
        self.lbl_events_title2.setText(_("Resources"))
        self.lbl_events_title3.setText(_("Errors"))
        self.lbl_processing.setText(_("Processing:"))
        self.chk_trial_run.setText(_("Trial run"))
        self.btn_run.setText(_("Run"))
        self.btn_stop.setText(_("Stop"))
        self.btn_close.setText(_("Close"))

    def on_switch_configuration(self, name=None):
        LOG.debug("Switch configuration: %s" % name)
        self.paras = self.ctrl.paras
        self.label_title.setText("%s %s" % (_(self.work), self.paras.configuration_name()))

    def on_anchor_clicked(self, url):
        QDesktopServices.openUrl(QUrl(url))

    def on_btn_run_clicked(self):
        self.work_started.emit()
        self.pte_events1.setPlainText("")
        self.pte_events2.setPlainText("")
        self.pte_events3.setPlainText("")
        self.lbl_processing.setVisible(True)
        self.lbl_processing_file.setVisible(True)

        self.btn_close.setEnabled(False)
        self.btn_run.setEnabled(False)
        self.chk_trial_run.setEnabled(False)
        self.btn_stop.setEnabled(True)
        self.btn_stop.setStyleSheet(Style.alarm())

    def on_btn_stop_clicked(self):
        self.btn_stop.setStyleSheet(self.normal_style)
        self.btn_stop.setEnabled(False)
        self.on_signal_main_event(_("Interrupt requested..."))
        if self.executor_thread:
            self.executor_thread.requestInterruption()

    def on_signal_exception(self, msg):
        self.pte_events3.appendHtml(msg)
        self.update()

    def on_ask_confirmation(self, text, i_text, answer):
        msg_box = QMessageBox()
        msg_box.setWindowTitle(_("MPT"))
        msg_box.setText(text)
        i_text += "\n\n"
        i_text += _("Ok to proceed?")
        msg_box.setInformativeText(i_text)
        msg_box.setIcon(QMessageBox.Question)
        msg_box.setStandardButtons(QMessageBox.No | QMessageBox.Yes)
        msg_box.setDefaultButton(QMessageBox.Yes)
        exe = msg_box.exec()
        if exe == QMessageBox.No:
            answer.answer = False
        else:
            answer.answer = True
        answer.answered = True

    def on_signal_main_event(self, msg):
        self.pte_events1.append(msg)
        self.update()

    def on_signal_minor_event(self, msg):
        self.pte_events2.append(msg)
        self.update()

    def on_signal_next_file(self, filename):
        lfn = len(filename)
        ww = (self.width() - 150)/7
        www = int(ww/2)
        if lfn > ww:
            filename = filename[:www] + "..." + filename[lfn - www:]
        self.lbl_processing_file.setText(filename)
        self.update()

    def on_signal_end_processing(self, paras):
        self.ctrl.update_configuration(paras)

    def on_executor_thread_finished(self):
        self.btn_stop.setStyleSheet(self.normal_style)
        self.btn_stop.setEnabled(False)
        self.btn_close.setEnabled(True)
        self.btn_run.setEnabled(True)
        self.chk_trial_run.setEnabled(True)
        self.lbl_processing.setVisible(False)
        self.lbl_processing_file.setVisible(False)
        self.update()
        self.work_ended.emit()

    def on_btn_close_clicked(self):
        if self.windowState() & Qt.WindowFullScreen:
            self.setWindowState(Qt.WindowMaximized)
        else:
            self.close()
            self.destroy()

    def closeEvent(self, event):
        LOG.debug("Closing event on work window %s" % self.work)
        if self.executor_thread:
            self.executor_thread.requestInterruption()
        if self.windowState() & Qt.WindowFullScreen:
            self.setWindowState(Qt.WindowMaximized)
            event.ignore()
        else:
            self.save_dimensions()
            self.exit_if_last()
            event.accept()

    def save_dimensions(self):
        LOG.debug("Saving dimensions for work-window %s" % self.work)
        self.conf.set_work_widget_width(self.work, self.width())
        self.conf.set_work_widget_height(self.work, self.height())
        self.conf.persist()

    def exit_if_last(self):
        count_windows = 0
        for window in QApplication.instance().topLevelWindows():
            if window.type() == 1 or window.type() == 15:
                count_windows += 1

        LOG.debug("Testing for last window. window_count=%d" % count_windows)
        if count_windows == 1:
            LOG.debug("Closing last window")
            QApplication.instance().quit()
Example #12
0
class Window(QWidget):

    def __init__(self, *args, **kwargs):
        super(Window, self).__init__(*args, **kwargs)
        layout = QHBoxLayout(self)

        self.listWidget = QListWidget(self)
        self.listWidget.setAlternatingRowColors(True)
        self.listWidget.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.listWidget.setMovement(QListView.Free)
        self.listWidget.setMouseTracking(True)  # 用于itemEntered信号

        self.resultView = QPlainTextEdit(self)
        self.resultView.setReadOnly(True)

        layout.addWidget(self.listWidget)
        layout.addWidget(self.resultView)

        self.initData()
        self.initSignals()

    def initData(self):
        # 初始化模拟数据
        for i in range(100):
            item = QListWidgetItem('Item {0}'.format(i), self.listWidget)
            if i % 3 == 0:
                item.setFlags(item.flags() | Qt.ItemIsEditable)

    def initSignals(self):
        # 初始化信号
        self.listWidget.currentItemChanged.connect(self.onCurrentItemChanged)
        self.listWidget.currentRowChanged.connect(self.onCurrentRowChanged)
        self.listWidget.currentTextChanged.connect(self.onCurrentTextChanged)
        self.listWidget.itemActivated.connect(self.onItemActivated)
        self.listWidget.itemChanged.connect(self.onItemChanged)
        self.listWidget.itemClicked.connect(self.onItemClicked)
        self.listWidget.itemDoubleClicked.connect(self.onItemDoubleClicked)
        self.listWidget.itemEntered.connect(self.onItemEntered)
        self.listWidget.itemPressed.connect(self.onItemPressed)
        self.listWidget.itemSelectionChanged.connect(
            self.onItemSelectionChanged)

    def onCurrentItemChanged(self, current, previous):
        current = current.text() if current else ''
        previous = previous.text() if previous else ''
        self.resultView.appendHtml(
            '{0}: [{1}] -> [{2}]'.format(
                formatColor('currentItemChanged', QColor(Qt.red)),
                current, previous))

    def onCurrentRowChanged(self, currentRow):
        self.resultView.appendHtml(
            '{0}: {1}'.format(
                formatColor('currentRowChanged', QColor(Qt.green)),
                currentRow))

    def onCurrentTextChanged(self, currentText):
        self.resultView.appendHtml(
            '{0}: {1}'.format(
                formatColor('currentTextChanged', QColor(Qt.yellow)), currentText))

    def onItemActivated(self, item):
        self.resultView.appendHtml(
            '{0}: {1}'.format(
                formatColor('itemActivated', QColor(Qt.blue)), item.text()))

    def onItemChanged(self, item):
        self.resultView.appendHtml(
            '{0}: {1}'.format(
                formatColor('itemChanged', QColor(Qt.cyan)), item.text()))

    def onItemClicked(self, item):
        self.resultView.appendHtml(
            '{0}: {1}'.format(formatColor('itemClicked', QColor(Qt.magenta)), item.text()))

    def onItemDoubleClicked(self, item):
        self.resultView.appendHtml(
            '{0}: {1}'.format(formatColor('itemDoubleClicked', QColor(Qt.darkGreen)), item.text()))

    def onItemEntered(self, item):
        self.resultView.appendHtml(
            '{0}: {1}'.format(formatColor('itemEntered', QColor(Qt.darkCyan)), item.text()))

    def onItemPressed(self, item):
        print(item)
        self.resultView.appendHtml(
            '{0}: {1}'.format(formatColor('itemPressed', QColor(Qt.darkYellow)), item.text()))

    def onItemSelectionChanged(self):
        self.resultView.appendPlainText('itemSelectionChanged')
Example #13
0
class AppWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.setWindowTitle(progname)
        self.init_ui()

        self.network = None
        self.isDataLoaded = False
        self.training_data = None
        self.test_data = None
        self.predict_y = None
        self.networkSettings = None

    def init_ui(self):
        self.init_menus()
        self.init_toolbar(QSize(40, 40))

        mainWidget = QWidget(self)
        mainbox = QHBoxLayout(mainWidget)
        # self.setLayout(mainbox)

        topFrame = QFrame(mainWidget)
        topFrame.setFrameShape(QFrame.StyledPanel)
        btmFrame = QFrame(mainWidget)
        btmFrame.setFrameShape(QFrame.StyledPanel)

        splitter = QSplitter(Qt.Vertical)
        splitter.addWidget(topFrame)
        splitter.addWidget(btmFrame)
        # logText 30%, Plot 70%
        splitter.setStretchFactor(0, 4)
        splitter.setStretchFactor(1, 1)

        mainbox.addWidget(splitter)

        self.init_plot_area(topFrame)

        vboxLog = QVBoxLayout(btmFrame)
        self.logTextEdit = QPlainTextEdit("")
        self.logTextEdit.appendHtml("""<font size='4'>欢迎使用{}</font><p>""".format(progname))
        self.logTextEdit.setReadOnly(True)
        vboxLog.addWidget(self.logTextEdit)

        mainWidget.setFocus()
        self.setCentralWidget(mainWidget)
        self.statusBar().showMessage("Ready")
        self.setWindowIcon(QIcon('res/load_network.png'))
        self.show()

    def init_plot_area(self, parent):
        hboxPlot = QHBoxLayout(parent)
        # errplot_labels = {'t': u'供水温度预测误差', 'x': u'时间', 'y': u'误差百分比(%)'}
        # predplot_labels = {'t': u'供水温度预测值', 'x': u'时间', 'y': u'供水温度(℃)'}
        errplot_labels = {'t': 'Prediction Errors', 'x': 'Time', 'y': 'Error Percent(%)'}
        predplot_labels = {'t': 'Predicted Temperature', 'x': 'Time', 'y': 'Temperature(℃)'}
        self.errPlot = StaticMplotCanvas(parent, labels=errplot_labels)
        self.predPlot = StaticMplotCanvas(parent, labels=predplot_labels)
        hboxPlot.addWidget(self.errPlot)
        hboxPlot.addWidget(self.predPlot)

    def init_toolbar(self, iconSize):
        # data file
        self.loadDataAct = QAction(QIcon('res/load_data.png'), 'Import Training Data', self)
        self.loadDataAct.setShortcut('Ctrl+L')
        self.loadDataAct.triggered.connect(self.loadTrainingDataFile)
        self.saveDataAct = QAction(QIcon('res/save_data.png'), 'Export Predicted Data', self)
        self.saveDataAct.setShortcut('Ctrl+E')
        self.saveDataAct.triggered.connect(self.savePredictDataToFile)
        self.saveDataAct.setEnabled(False)
        # network
        self.loadNetworkAct = QAction(QIcon('res/load_network.png'), 'Load Trained Network', self)
        self.loadNetworkAct.setShortcut('Ctrl+N')
        self.loadNetworkAct.triggered.connect(self.restoreNeuralNetwork)
        self.loadNetworkAct.setEnabled(False)
        self.saveNetworkAct = QAction(QIcon('res/save_network.png'), 'Save Trained Network', self)
        self.saveNetworkAct.setShortcut('Ctrl+S')
        self.saveNetworkAct.triggered.connect(self.saveNeuralNetwork)
        self.saveNetworkAct.setEnabled(False)
        # run & predict
        self.runTrainingAct = QAction(QIcon('res/train_network.png'), 'Train Network', self)
        self.runTrainingAct.setShortcut('Ctrl+R')
        self.runTrainingAct.triggered.connect(self.runNetworkTraining)
        self.runTrainingAct.setEnabled(False)
        self.predictDatakAct = QAction(QIcon('res/predict.png'), 'Predict Data', self)
        self.predictDatakAct.setShortcut('Ctrl+P')
        self.predictDatakAct.triggered.connect(self.predictData)
        self.predictDatakAct.setEnabled(False)
        # clear
        self.resetAct = QAction(QIcon('res/clear.png'), 'Clear data and network', self)
        self.resetAct.setEnabled(False)
        self.resetAct.triggered.connect(self.clearDataAndNetwork)

        dataToolbar = self.addToolBar('Data ToolBar')
        dataToolbar.addAction(self.loadDataAct)
        dataToolbar.addAction(self.saveDataAct)
        dataToolbar.setIconSize(iconSize)

        networkToolbar = self.addToolBar('Network ToolBar')
        networkToolbar.addAction(self.loadNetworkAct)
        networkToolbar.addAction(self.runTrainingAct)
        networkToolbar.addAction(self.predictDatakAct)
        networkToolbar.addAction(self.saveNetworkAct)
        networkToolbar.setIconSize(iconSize)

        resetToolbar = self.addToolBar('Reset ToolBar')
        resetToolbar.addAction(self.resetAct)
        resetToolbar.setIconSize(iconSize)

    def init_menus(self):
        # File
        settingAct = QAction(QIcon('res/settings.png'), '设置', self)
        settingAct.triggered.connect(self.showSettingDialog)

        exitAct = QAction(QIcon('exit.png'), '退出', self)
        exitAct.setShortcut('Ctrl+Q')
        exitAct.triggered.connect(self.fileQuit)

        menubar = self.menuBar()
        fileMenu = menubar.addMenu('&File')
        fileMenu.addAction(settingAct)
        fileMenu.addSeparator()
        fileMenu.addAction(exitAct)

        # Help
        helpMenu = QMenu('&Help', self)
        self.menuBar().addMenu(helpMenu)
        helpMenu.addAction('使用说明', self.usage)
        helpMenu.addAction('关于', self.about)

    @pyqtSlot()
    def loadTrainingDataFile(self):
        fname, _ = QFileDialog.getOpenFileName(self, 'Open Training Data', '.', 'Data File(*.csv)')
        if not fname:
            # self.logStatus("加载数据文件{}失败!".format(fname), 'red', 'E')
            return

        dl = DataHandler(fname)
        self.training_data, self.test_data = dl.load(cb=self.logStatus)
        self.isDataLoaded = True
        self.runTrainingAct.setEnabled(True)
        self.loadNetworkAct.setEnabled(True)
        self.resetAct.setEnabled(True)
        self.logStatus("加载数据文件{}成功".format(fname))
        self.logStatus('请训练神经网络或者加载已经训练的神经网络模型', '#FF8C00', 'T')

    @pyqtSlot()
    def savePredictDataToFile(self):
        if self.predict_y is None:
            # self.logStatus('没有未保存的预测数据, 请先进行数据预测!', 'red', 'E')
            return

        fname, _ = QFileDialog.getSaveFileName(self, 'Save Predicted Data', '.', 'Data File(*.csv)')
        if not fname:
            self.logStatus('保存预测数据文件{}失败!'.format(fname), 'red', 'E')
            return

        test_x, _ = DataHandler.split_xy(self.test_data)
        status = DataHandler.save(np.concatenate((test_x, self.predict_y), axis=0), fname)
        if status:
            self.logStatus('保存预测数据文件{}成功'.format(fname))

    @pyqtSlot()
    def restoreNeuralNetwork(self):
        fname, _ = QFileDialog.getOpenFileName(self, 'Open Network File', '.', 'Network File(*.nf)')
        if not fname:
            # self.logStatus('打开神经网络文件{}失败!'.format(fname), 'red', 'E')
            return

        # clear previous plots
        self.clearPlots()

        training_x, training_y = DataHandler.split_xy(self.training_data)
        self.network = NeuralNetwork(training_x, training_y)
        try:
            self.network.load(fname)
        except ShapeError as e:
            self.logStatus('加载神经网络文件{}失败!'.format(fname), 'red', 'E')
            QMessageBox.warning(self, '警告', '加载神经网络文件失败, 请检查文件格式是否正确!')
            return

        self.logStatus('神经网络文件{}加载成功'.format(fname))
        self.predictDatakAct.setEnabled(True)
        self.logStatus('请执行数据预测', '#FF8C00', 'T')

    @pyqtSlot()
    def saveNeuralNetwork(self):
        fname, _ = QFileDialog.getSaveFileName(self, 'Save Network File', '.', 'Network File(*.nf)')
        if not fname:
            # self.logStatus('保存神经网络文件{}失败!'.format(fname), 'red', 'E')
            return

        self.network.dump(fname)
        self.logStatus('保存神经网络文件{}成功'.format(fname))

    @pyqtSlot()
    def runNetworkTraining(self):
        if self.network is not None:
            ans = QMessageBox.question(self, '警告',
                                 '系统中已存在训练好的神经网络,请问您需要重新训练神经网络吗?')
            if ans == QMessageBox.No:
                return

        # clear previous plots
        self.clearPlots()

        self.logStatus("正在初始化神经网络结构...", 'blue', 'I')
        training_x, training_y = DataHandler.split_xy(self.training_data)
        # retrieve settings
        epoch0 = 2000
        tol0 = 0.1
        retry_num = 3
        h0size = 4
        h1size = 4
        if self.networkSettings:
            epoch0 = self.networkSettings['epoch']
            tol0 = self.networkSettings['tol']
            retry_num = self.networkSettings['retry']
            h0size = self.networkSettings['h0size']
            h1size = self.networkSettings['h1size']

        self.logStatus("神经网络信息:layer1={}, layer2={}, epoch0={}, retry={}, tol0={}"
                       .format(h0size, h1size, epoch0, retry_num, tol0), 'blue', 'I')

        net = [(training_x.shape[0], ''), (h0size, 'sigmoid'), (h1size, 'sigmoid'), (1, 'feedthrough')]
        try:
            self.network = NeuralNetwork(training_x, training_y, sizes=net)
        except ShapeError as e:
            self.logStatus('初始化神经网络结构失败!')
            QMessageBox.warning(self, '警告', '初始化神经网络结构失败, 请重试!')
            return

        # training
        mu0 = 0.1
        beta = 10
        retry = 0
        self.logStatus("使用LM算法开始训练神经网络...", 'blue', 'I')
        while retry < retry_num:
            residual, mu, citer, msg = \
                self.network.train(retry=retry, epoch=epoch0, mu0=mu0,
                                   beta=beta, tol=tol0, cb=self.logStatus)
            if residual is None:
                if retry == (retry_num - 1):
                    self.logStatus("训练失败!".format(msg), 'red', 'E')
                    return
                else:
                    self.logStatus("训练失败:{}, 重试中...".format(msg), '#FFA07A', 'I')
                    self.network.randomize_wb()
                    # continue
            elif residual > tol0:
                if retry == (retry_num - 1):
                    self.logStatus("训练失败!".format(msg), 'red', 'E')
                    return
                else:
                    self.logStatus("训练失败: 运算未能收敛, 重试中...", '#FFA07A', 'I')
                    self.network.randomize_wb()
                    # continue
            else:
                self.logStatus("神经网络训练完成, 迭代次数={1}, 最终残差={0}"
                               .format(residual, citer+retry*epoch0), 'blue', 'I')
                break
            retry += 1
        self.predictDatakAct.setEnabled(True)
        self.saveNetworkAct.setEnabled(True)
        self.logStatus('请执行数据预测', '#FF8C00', 'T')

    @pyqtSlot()
    def predictData(self):
        # don't forget to clear previous plots
        self.clearPlots()

        self.logStatus("开始进行数据预测...", 'blue', 'I')
        test_x, test_y = DataHandler.split_xy(self.test_data, False)
        self.predict_y = self.network.predict(test_x)
        self.logStatus("开始计算误差...", 'blue', 'I')
        self.predPlot.update_plot(x=np.arange(len(self.predict_y[0])),
                                  y=self.predict_y[0])
        # error plot
        err_percent = (self.predict_y - test_y) * 100.0 / test_y
        self.errPlot.error_plot(x=np.arange(len(err_percent[0])),
                                y=err_percent[0])
        abs_err = np.abs(err_percent)
        self.logStatus("数据预测完成, 最大绝对值误差={}%, 平均绝对值误差={}%"
                       .format(abs_err.max(), abs_err.mean()), 'blue', 'I')
        self.saveDataAct.setEnabled(True)

    @pyqtSlot()
    def clearDataAndNetwork(self):
        ans = QMessageBox.question(self, '警告',
                                   '您希望删除所有的数据和已经训练好的神经网络吗?')
        if ans == QMessageBox.No:
            return

        # reset
        self.network = None
        self.isDataLoaded = False
        self.training_data = None
        self.test_data = None
        # update UI
        self.loadNetworkAct.setEnabled(False)
        self.runTrainingAct.setEnabled(False)
        self.saveDataAct.setEnabled(False)
        self.saveNetworkAct.setEnabled(False)
        self.predictDatakAct.setEnabled(False)
        self.resetAct.setEnabled(False)
        self.logTextEdit.clear()
        # clear plots
        self.clearPlots()

    @pyqtSlot(dict)
    def updateSettings(self, settings):
        self.networkSettings = settings

    @pyqtSlot()
    def showSettingDialog(self):
        dlg = SettingDialog(self, self.networkSettings)
        dlg.show()

    @pyqtSlot()
    def fileQuit(self):
        self.close()

    @pyqtSlot()
    def about(self):
        QMessageBox.about(self, "关于",
                          """<b>{}</b><p>版本号: {}""".format(progname, progversion)
                          )

    @pyqtSlot()
    def usage(self):
        dlg = UsageDialog(self)
        dlg.show()

    def closeEvent(self, ce):
        self.fileQuit()

    def logStatus(self, text, color='green', tag='S'):
        self.logTextEdit.appendHtml("<p><font color='{0}'><b>[{1}]:</b> {2}"
                                "</font></p>".format(color, tag, text))
        # force UI update. An alternative is to use QThread
        QApplication.processEvents()

    def clearPlots(self):
        self.predPlot.clear()
        self.errPlot.clear()
        errplot_labels = {'t': 'Prediction Errors', 'x': 'Time', 'y': 'Error Percent(%)'}
        predplot_labels = {'t': 'Predicted Temperature', 'x': 'Time', 'y': 'Temperature(℃)'}
        self.errPlot.initial_figure(errplot_labels)
        self.predPlot.initial_figure(predplot_labels)
Example #14
0
class View(QWidget):

    # Send data to port
    send_data = pyqtSignal(object)
    # Chage baudrate
    baudrate_changed = pyqtSignal(object)
    # Change end of line
    eol_changed = pyqtSignal(object)
    # Change port
    port_changed = pyqtSignal(object)
    # Pause model
    pause_m = pyqtSignal(object)
    # Continue model
    start_m = pyqtSignal(object)

    def __init__(self):
        QWidget.__init__(self)

        self.queue = None
        self.end_cmd = None
        self.autoscroll = False
        self.msg_sent = False

        self.timer = QTimer()
        self.timer.timeout.connect(self.update_gui)
        self.timer.start(100)

        self.__initUI()

    def __initUI(self):
        vbox = QVBoxLayout(self)
        # vbox.setSpacing(0)
        vbox.setContentsMargins(3, 3, 3, 3)

        # Add window's menu bar
        self.menubar = QMenuBar()
        file_menu = self.menubar.addMenu('File')
        file_menu.addAction('Save', self.save_to_file)
        vbox.addWidget(self.menubar)

        # Command box
        cmd_hbox = QHBoxLayout()

        self.cmd_edit = QLineEdit()
        cmd_hbox.addWidget(self.cmd_edit)

        cmd_btn = QPushButton('Send')
        cmd_btn.clicked.connect(self.emit_send_data)
        cmd_hbox.addWidget(cmd_btn)

        cmd_btn = QPushButton('Start')
        cmd_btn.clicked.connect(self.start_m.emit)
        cmd_hbox.addWidget(cmd_btn)

        cmd_btn = QPushButton('Stop')
        cmd_btn.clicked.connect(self.pause_m.emit)
        cmd_hbox.addWidget(cmd_btn)

        vbox.addLayout(cmd_hbox)

        # Editors pair box
        editor_hbox = QHBoxLayout()

        # Text edit area
        self.editer = QPlainTextEdit()
        self.editer.scrollContentsBy = self.ModScrollContentsBy
        editor_hbox.addWidget(self.editer)

        # HEX edit area
        self.editor_hex = QPlainTextEdit()
        self.editor_hex.scrollContentsBy = self.ModScrollContentsBy
        editor_hbox.addWidget(self.editor_hex)

        vbox.addLayout(editor_hbox)

        # Settings area
        stng_hbox = QHBoxLayout()

        # - Autoscroll
        chk_btn = QCheckBox('Autoscroll')
        chk_btn.stateChanged.connect(self.set_autoscroll)
        stng_hbox.addWidget(chk_btn)

        cmd_btn = QPushButton('Clear')
        cmd_btn.clicked.connect(self.editer.clear)
        cmd_btn.clicked.connect(self.editor_hex.clear)
        stng_hbox.addWidget(cmd_btn)

        stng_hbox.addStretch(1)

        # - Ending of line
        self.eol_menu = QComboBox()
        self.eol_menu.addItem('No line ending')
        self.eol_menu.addItem('Newline')
        self.eol_menu.addItem('Carriage return')
        self.eol_menu.addItem('Both NL + CR')
        self.eol_menu.setCurrentIndex(0)
        self.eol_menu.currentIndexChanged.connect(self.emit_eol_changed)
        stng_hbox.addWidget(self.eol_menu)

        # - Baudrate select
        self.br_menu = QComboBox()
        self.br_menu.addItem('300 baud')
        self.br_menu.addItem('1200 baud')
        self.br_menu.addItem('2400 baud')
        self.br_menu.addItem('4800 baud')
        self.br_menu.addItem('9600 baud')
        self.br_menu.addItem('19200 baud')
        self.br_menu.addItem('38400 baud')
        self.br_menu.addItem('57600 baud')
        self.br_menu.addItem('115200 baud')
        self.br_menu.addItem('230400 baud')
        self.br_menu.addItem('460800 baud')
        self.br_menu.currentIndexChanged.connect(self.emit_br_changed)
        # Set default baudrate 9600
        self.br_menu.setCurrentIndex(4)

        stng_hbox.addWidget(self.br_menu)

        vbox.addLayout(stng_hbox)

        # Port editing form
        port_hbox = QHBoxLayout()
        port_lbl = QLabel('Port: ')
        port_hbox.addWidget(port_lbl)

        self.port_edit = QLineEdit()

        self.port_edit.editingFinished.connect(self.changePort)
        port_hbox.addWidget(self.port_edit)

        vbox.addLayout(port_hbox)

        # Status Bar
        self.status_bar = QStatusBar()

        self.status_label = QLabel()
        self.status_label.setAlignment(Qt.AlignRight)

        self.status_bar.addWidget(self.status_label, 1)
        vbox.addWidget(self.status_bar)

        self.setLayout(vbox)

    def show_error(self, value):
        msg = QMessageBox(QMessageBox.NoIcon, 'Error occured.', value,
                          QMessageBox.Ok)
        msg.exec()

#==============================================================================
# Get, set
#==============================================================================

    def set_queue(self, queue):
        self.queue = queue

    def set_end_cmd(self, end_cmd):
        self.end_cmd = end_cmd

    def set_autoscroll(self, value):
        self.autoscroll = value

    def set_port(self, value):
        self.port_edit.insert(value)

    def get_cmd(self):
        return self.cmd_edit.text()

    def set_eol(self, value):
        self.eol_menu.setCurrentIndex(value)

    def update_status_bar(self, data_set):
        '''
        Update GUI status bar.
        Args:
            data_set: Dictionary with port configurations: port, baudrate,
            number of bits, parity, number of stop bits.
        '''
        string = '{} {}-{}-{}'.format(data_set['baudrate'],
                                      data_set['num_of_bits'],
                                      data_set['parity'],
                                      data_set['num_of_stop'])

        self.status_label.setText(string)

    def update_gui(self):
        self.process_incoming()
        self.update()

    def appendText(self, data):
        # pos = QPoint(self.editer.textCursor().position(), 0)
        # self.editer.moveCursor(QTextCursor.End)
        # self.editer.insertPlainText(text)
        self.editer.appendPlainText(data[0])
        # self.editer.cursorForPosition(pos)

        self.editor_hex.appendHtml(data[1])

    def process_incoming(self):
        while self.queue.qsize():
            try:
                msg = self.queue.get(0)
                # Check contents of message and do what it says
                # As a test, we simply print it
                # print(bytes(msg, 'ASCII'), end='')
                # self.editer.appendPlainText(msg)
                self.appendText(msg)
                if self.autoscroll:
                    self.editer.ensureCursorVisible()
                    self.editor_hex.ensureCursorVisible()
                    self.scroll_down()
            except Queue.empty:
                pass

    def scroll_down(self):
        for editor in [self.editer, self.editor_hex]:
            sb = editor.verticalScrollBar()
            sb.setValue(sb.maximum())
            editor.moveCursor(QTextCursor.End)

    def changePort(self):
        if not self.msg_sent:
            self.msg_sent = True
            self.emit_port_changed()
        else:
            self.msg_sent = False
            return None
#==============================================================================
# Utils
#==============================================================================

    def save_to_file(self):
        _file = QFileDialog.getSaveFileName()
        if _file[0]:
            with open(_file[0], 'w+') as fn:
                fn.write(self.editer.toPlainText())

#==============================================================================
# Signals
#==============================================================================

    def emit_send_data(self):
        self.send_data.emit(self.get_cmd())
        self.cmd_edit.clear()

    def emit_br_changed(self, value):
        baudrate = self.br_menu.itemText(value)[:-5]
        self.baudrate_changed.emit(baudrate)

    def emit_eol_changed(self, value):
        self.eol_changed.emit(value)

    def emit_port_changed(self):
        self.port_edit.clearFocus()
        self.port_changed.emit(self.port_edit.text())

#==============================================================================
# Events
#==============================================================================

    def ModScrollContentsBy(self, dx, dy):
        # print('as: {}. dx: {}. dy: {}.'.format(self.autoscroll, dx, dy))
        for editor in [self.editer, self.editor_hex]:
            if self.autoscroll:
                editor.ensureCursorVisible()
            else:
                QPlainTextEdit.scrollContentsBy(editor, dx, dy)

    def closeEvent(self, event):
        self.end_cmd()
        QWidget.closeEvent(self, event)
        print('exit')
Example #15
0
    class IcerikOkuma(QDialog):
        def __init__(self, ctx, sayfalar, Kaydet):
            super(QDialog, self).__init__()
            self.ctx = ctx
            self.title = 'İçerik Okuma'
            self.initUI()
            self.sayfalar = sayfalar
            self.Kaydet = Kaydet
            self.sayfano = 0
            self.toplamsayfa = 0
            self.saniye = 0
            self.toplamsure = 0
            self.run = False
            if Kaydet: self.otomatikZamanla(1, self.IcerikKaydet)
            elif self.ctx.IcerikOto:
                self.otomatikZamanla(
                    10, self.otomatikBasla)  # 10 saniye sonra otomatik başla
            self.exec_()

        def initUI(self):
            self.setWindowTitle(self.title)
            self.lblDersAd = QLabel('Ders Adı', self)
            self.txtLink = QLineEdit('Ders Bağlantısı', self)
            self.btnBar = QHBoxLayout()
            self.lblStatus = QLabel('<Status>', self)
            self.btnGeri = QPushButton('Geri', self)
            self.btnBaslat = QPushButton('Başlat', self)
            self.btnIleri = QPushButton('İleri', self)
            self.btnBar.addWidget(self.lblStatus)
            self.btnBar.addWidget(self.btnGeri)
            self.btnBar.addWidget(self.btnBaslat)
            self.btnBar.addWidget(self.btnIleri)
            self.txtDers = QPlainTextEdit('İçerik', self)
            self.vlayout = QVBoxLayout()
            self.vlayout.addWidget(self.lblDersAd)
            self.vlayout.addWidget(self.txtLink)
            self.vlayout.addLayout(self.btnBar)
            self.btnGeri.clicked.connect(self.GeriClicked)
            self.btnBaslat.clicked.connect(self.BaslatClicked)
            self.btnIleri.clicked.connect(self.IleriClicked)
            self.vlayout.addWidget(self.txtDers)
            self.setLayout(self.vlayout)
            self.setGeometry(115, 115, 800, 511)

        @pyqtSlot()
        def closeEvent(self, event):
            if self.run:
                self.BaslatClicked()
            event.accept()

        def otomatikZamanla(self, sure, func):
            self.timerX = QTimer()
            self.timerX.timeout.connect(func)
            self.timerX.start(1000 * sure)

        def otomatikBasla(self):
            self.timerX.stop()
            self.timerX = None
            self.BaslatClicked()

        @pyqtSlot()
        def BaslatClicked(self):
            if self.timerX is not None:
                self.timerX.stop()
                self.timerX = None
            if not self.run:
                self.run = True
                self.timer = QTimer()
                self.timer.timeout.connect(self.IcerikOkuTimer)
                self.timer.start(1000)
                self.IcerikOkuTimer()
            else:
                self.run = False
                self.timer.stop()
                self.timer = None
                self.btnBaslat.setText(
                    f"({(self.saniye % self.ctx.SureArtim) + 1})..{self.ctx.SureArtim} Devam Et"
                )
                self.ctx.logYaz(
                    f"BaslaClicked: {self.toplamsayfa} adet sayfa toplam {self.toplamsure} saniye  okundu."
                )
                self.otomatikZamanla(
                    180, self.close
                )  #oturum kapanabilir, 3 dakika sonra pencereyi kapat

        def sayacSifirla(self):
            self.saniye -= (self.saniye % self.ctx.SureArtim)

        def GeriClicked(self):
            if self.run:
                if self.sayfano > 1:
                    self.sayfano -= 2
                    self.sayacSifirla()

        def IleriClicked(self):
            if self.run:
                self.sayfano += 0
                self.sayacSifirla()

        def IcerikOkuTimer(self):
            if (self.saniye % self.ctx.SureArtim) == 0:
                if self.sayfano < len(self.sayfalar):
                    self.IcerikOku(self.sayfano)
                self.sayfano += 1
                self.toplamsayfa += 1
                if self.sayfano > len(self.sayfalar):
                    self.sayfano = 0
                    self.BaslatClicked()
                    self.btnBaslat.setText('Tekrar Başlat')
                    if self.ctx.IcerikOto: self.close()
                    self.sayacSifirla()
            else:
                self.btnBaslat.setText(
                    f"({(self.saniye % self.ctx.SureArtim) + 1})..{self.ctx.SureArtim} Durdur"
                )
                self.setWindowTitle(
                    f"{self.title} Geçen süre: {self.toplamsure+1}")
            self.saniye += 1
            self.toplamsure += 1

        def IcerikKaydet(self):
            klasor = f"{self.ctx.anaKlasor}{ICERIKKLASOR}"
            self.Belge = Document()
            self.Belge.add_heading(f"{self.ctx.IcerikDers} Ders İçeriği", 0)
            self.btnBar.setEnabled(False)
            self.timerX.stop()
            for no in range(len(self.sayfalar)):
                self.IcerikOku(no)
                if no % 10 == 0: QCoreApplication.processEvents()
            dosya = f"{klasor}\\{self.ctx.IcerikDers[:8]}.docx"
            self.Belge.save(dosya)
            self.close()
            os.system(f'"{dosya}"')

        def IcerikOku(self, no):
            sayfalar = self.sayfalar
            yanit = self.ctx.getSession().get(sayfalar[no]['link'],
                                              cookies=self.ctx.cerezler)
            yanit.encoding = 'utf-8'
            durum = yanit.status_code
            if durum == 200:
                sonuc = yanit.text
                bolumadi = sayfalar[no]['ad']
                self.lblDersAd.setText(f"{bolumadi} {no+1}/{len(sayfalar)}")
                self.txtLink.setText(sayfalar[no]['link'])
                self.lblStatus.setText(f"Durum= HTTP<{durum}>")
                self.txtDers.clear()
                if self.Kaydet:
                    paragraf = self.Belge.add_heading(bolumadi, level=1)
                    self.Paragraf = paragraf.add_run()
                soup = BeautifulSoup(sonuc, features='html.parser')
                # div = soup.find('div', {'id': 'sound'})
                # div = soup.select('div#iceriksayfa,div.icerik_sayfasi') # iki attribute'dan birini aramak için
                div = soup.find('div', {'class': 'icerik_sayfasi'})
                if not div:
                    div = soup.find('div', {'id': 'iceriksayfa'})
                if not div:
                    div = soup.find('div', {'id': 'icerik'})
                if not div:
                    div = soup.find('table', {'class': 'content'})
                if div:
                    metin = str(div.text)
                    metin = metin.replace('\n', ' ')
                    metin = metin.replace('\0', ' ')
                    self.txtDers.setPlainText(metin)
                    if self.Kaydet:
                        paragraf = self.Belge.add_paragraph(metin)
                        self.Paragraf = paragraf.add_run()
                imgs = soup.find_all('img')
                if imgs:
                    self.txtDers.appendHtml(self.imgGetir(imgs))
                if not div:
                    div = soup.find('div', {'id': 'guizno'})
                    if div:
                        html, metin = self.degerlendirmeSorulariGetir(
                            soup, div.text)
                        self.txtDers.appendHtml(html)
                        if self.Kaydet: self.Belge.add_paragraph(metin)
                    else:
                        self.ctx.responseYaz(
                            self.ctx.anaKlasor +
                            f"{ICERIKKLASOR}\\oys-{self.ctx.IcerikDers[5:8]}no-{no}.html",
                            sonuc)
                        div = soup.find('table')
                        if div:
                            sonuc = div.text
                        self.txtDers.appendHtml(sonuc)
                        if self.Kaydet:
                            self.Belge.add_paragraph(
                                '<HTML OKUNAMADI (flash vs olabilir)>')
                if debug:
                    print(
                        f"IcerikOku: ders={bolumadi} status={durum} link={sayfalar[no]['link']}"
                    )
                return self.txtDers.toPlainText()

        def imgGetir(self, imgs, baseurl=''):
            html = ''
            for img in imgs:
                kaynak = baseurl + img['src']
                dosyaadi = kaynak.split('/')[-1]
                kaynak = self.ctx.adres + '/' + kaynak  #Viewer/temp/AYU/176/72/LRN//pics/2/bolum2.PNG
                new_kaynak = self.resimIndir(kaynak, dosyaadi)
                if debug:
                    print(
                        f"imgGetir img={img,img['src']} new_kaynak={new_kaynak}"
                    )
                new_img = img.text.replace(kaynak, new_kaynak)
                html += new_img
                if self.Kaydet:
                    en = int(img.get('width', '200')) - 20
                    boy = int(img.get('height', '150'))  #- 10
                    if debug: print(f"imgGetir en={en} boy={boy}")
                    try:
                        self.Paragraf.add_picture(new_kaynak,
                                                  width=Pt(en),
                                                  height=Pt(boy))
                    except:
                        self.Paragraf.add_text('<IMG hatalı>')
            return html

        def resimIndir(self, kaynak, dosyaadi):
            klasor = f"{self.ctx.anaKlasor}{ICERIKKLASOR}\\img"
            os.makedirs(klasor, exist_ok=True)
            new_kaynak = f"{klasor}\\{dosyaadi}"
            #print("new_kaynak=",new_kaynak)
            yanit = self.ctx.getSession().get(kaynak,
                                              cookies=self.ctx.cerezler,
                                              stream=True)
            with open(new_kaynak, 'wb') as dosya:
                for chunk in yanit.iter_content(chunk_size=512):
                    dosya.write(chunk)
            return new_kaynak

        def degerlendirmeSorulariGetir(self, soup, quizname):
            sorular = []
            scripts = soup.find_all('script')
            kaynak = ''
            for script in scripts:
                script = script.text
                if 'var deger' in script:
                    kaynak = re.findall('deger=\"(.*?)\"', script)
                    if kaynak == []:
                        kaynak = ''
                    else:
                        kaynak = kaynak[0]
            baseurl = '/' + kaynak[:-9]
            kaynak = self.ctx.adres + '/' + kaynak + quizname  #Viewer/temp/AYU/700/912/LRN//sorular/quiz1.txt
            if debug:
                print(
                    f"degerlendirmeSorulariGetir: baseurl={baseurl} kaynak={kaynak}"
                )
            yanit = self.ctx.getSession().get(kaynak,
                                              cookies=self.ctx.cerezler)
            # if debug: print(f"degerlendirmeSorulariGetir:", yanit.encoding)
            yanit.encoding = 'utf-8'
            if yanit.status_code != 200: return yanit.status_code
            try:
                sonuc = yanit.json()
            except:  #json.decoder.JSONDecodeError
                if debug:
                    print(
                        f"degerlendirmeSorulariGetir: yanit={yanit} kaynak={kaynak} status={yanit.status_code} text=\n{yanit.text}"
                    )
                return yanit.text
            if debug: print("degerlendirmeSorulariGetir: sonuc=", sonuc)
            quiz = sonuc['quiz']
            i = 0
            for soru in quiz:
                if soru['question'].find("img") > 0:
                    imgs = []
                    soruimg = BeautifulSoup(soru['question'],
                                            features='html.parser').find('img')
                    if debug:
                        print(f"degerlendirmeSorulariGetir: soruimg={soruimg}")
                    if soruimg:
                        imgs.append(soruimg)
                        self.imgGetir(imgs, baseurl)
                sorular.append({'soru': soru['question']})
                sorular[i]['siklar'] = soru['option']
                sorular[i]['cevap'] = soru['ans']
                i += 1
            html = ''
            metin = ''
            i = 0
            for soru in sorular:
                html += f"<b>Soru {i+1} : </b>" + soru['soru'] + '<br><ul>'
                metin += f"Soru {i+1} : " + soru['soru'] + '\n'
                # if debug: print(f"degerlendirmeSorulariGetir: siklar=", soru['siklar'])
                for opt, deger in soru['siklar'].items():
                    # if debug: print (f"degerlendirmeSorulariGetir: opt=", opt,"deger=",deger)
                    if deger.find("img") > 0:
                        imgs = []
                        soruimg = BeautifulSoup(
                            deger, features='html.parser').find('img')
                        if debug:
                            print(
                                f"degerlendirmeSorulariGetir: şık.soruimg={soruimg}"
                            )
                        if soruimg:
                            imgs.append(soruimg)
                            self.imgGetir(imgs, baseurl)
                    if opt == soru['cevap']:
                        html += '<li><b>' + opt + ')' + deger + '</b></li>'
                        metin += '\n(' + opt + ') ' + deger
                    else:
                        html += '<li>' + opt + ')' + deger + '</li>'
                        metin += '\n ' + opt + ') ' + deger
                html += '</ul><br><br>'
                metin += '\n\n'
                i += 1
                # html += soru['cevap']
            return html, metin
Example #16
0
class App(QDialog):
    def __init__(self):
        super().__init__()
        self.title = 'Voice Control User Interface'
        self.left = 20
        self.top = 20
        self.width = 800
        self.height = 480

        self.frame = None
        self.text = ''

        self.initUI()

    def initUI(self):

        try:
            with open('config.local.json') as f:
                conf.JSON_DATA = json.load(f)
        except FileNotFoundError:
            with open('config.json') as f:
                conf.JSON_DATA = json.load(f)

        # {'task': [...], 'english': [....], 'chinese': [....], 'security': [....], 'custom': [....]}
        self.all_commands = sql.run_query(query='SELECT * FROM COMMANDS')
        self.profile_info = sql.run_query(query="SELECT * from USER")

        conf.USER_NAME = self.profile_info['name'][0]

        languages = ["english", "chinese"]
        query_result = sql.run_query(
            query="select language from user")  # Return type: Dictionary
        print(query_result)

        selected_lang = query_result['language'][0]
        index_no = languages.index(
            selected_lang.lower())  # Raise : Valuerror unless item is found
        """ ------------------- Design Main Window -------------------- """
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)
        """ ------------------- Combo Box -------------------- """
        self.combo = QComboBox()
        self.combo.addItems(languages)
        self.combo.setCurrentIndex(index_no)
        """ ------------------- Label -------------------- """
        self.language_label = QLabel('Dialog Box', self)
        self.language_label.setGeometry(50, 50, 900, 100)
        pixmap = QPixmap(
            "/Users/duzheng/Desktop/VCUI/icons/voice1.png").scaled(
                self.language_label.width(), self.language_label.height())
        self.language_label.setPixmap(pixmap)
        """ ------------------- Text -------------------- """
        # Create Chat Box
        self.chat = QPlainTextEdit()
        self.chat.setFixedSize(600, 300)
        self.chat.setReadOnly(True)
        """ ---------------- Button ---------------------- """
        # voice_btn not triggered when enter key pressed
        self.voice_btn = QPushButton(default=False, autoDefault=False)
        self.voice_btn.setFixedSize(80, 80)
        self.voice_btn.setStyleSheet("background-color:#3399FF;")

        pixmap2 = QPixmap("/Users/duzheng/Desktop/VCUI/icons/voice2.jpg")
        self.voice_btn.setIcon(QIcon(pixmap2))
        self.voice_btn.setIconSize(PyQt5.QtCore.QSize(75, 75))

        self.commands_btn = QPushButton('Commands',
                                        default=False,
                                        autoDefault=False)
        #self.folder_btn = QPushButton("Folder", default=False, autoDefault=False)
        #self.forget_pass_btn = QPushButton('Forget Password', default=False, autoDefault=False)
        """ -------------------- Layout -------------------------- """
        # Add vertical layout
        self.v_layout = QVBoxLayout()
        self.setLayout(self.v_layout)

        self.dia_h_layout = QHBoxLayout()
        self.dia_h_layout.addWidget(self.language_label)

        # Add horizontal layout
        self.h_layout = QHBoxLayout()
        self.h_layout.addWidget(self.chat)
        self.h_layout.addWidget(self.voice_btn)

        # Tools horizontal layout
        self.tools_h_layout = QHBoxLayout()
        self.tools_h_layout.addWidget(self.combo)
        self.tools_h_layout.addWidget(self.commands_btn)

        self.v_layout.addLayout(self.dia_h_layout)
        self.v_layout.addLayout(self.h_layout)
        self.v_layout.addLayout(self.tools_h_layout)
        """ ------------------ triggered button ---------------"""
        self.voice_btn.clicked.connect(self.voice_text)
        self.commands_btn.clicked.connect(self.show_table)

        self.show()

    @pyqtSlot()
    def voice_text(self):
        """ Convert voice to text and appear the text into chatbox """

        current_lang = self.combo.currentText(
        )  # collect selected language from combo box

        if conf.NEW_COMMAND:
            update_commands = sql.run_query(query='SELECT * FROM COMMANDS')
            self.all_commands = update_commands
            conf.NEW_COMMAND = False

        self.text = stt.stt_func(selected_lang=current_lang)

        self.text_append(self.text)

        self.send_to_command(current_lang)

    def send_to_command(self, current_lang):

        command_control.input_parser(
            commands_list=self.all_commands,
            selected_lang=current_lang,
            command=self.text,
            profile_info=self.profile_info,
        )

        self.text = ''

    def text_append(self, text):
        """ Show USER speech_to_text into window """

        textFormatted = '<b>{}: </b><span style=" font-size:12pt; font-weight:600; color:#33c4ff;">{}</span>'.format(
            conf.USER_NAME.capitalize(), text)
        self.chat.appendHtml(textFormatted)
        # Make pulic of chat object so that we can use this object from other file by conf.CHAT_OBJ
        conf.CHAT_OBJ = self.chat

    @pyqtSlot()
    def show_table(self):
        """ Show all command list into table """

        if conf.NEW_COMMAND:
            update_commands = sql.run_query(query='SELECT * FROM COMMANDS')
            self.all_commands = update_commands
            conf.NEW_COMMAND = False

        column_names = tuple(self.all_commands.keys())
        row_values = tuple(self.all_commands.values())
        col_size = len(column_names)
        row_size = len((row_values)[0])

        ab = TableWindow(row_values, column_names, row_size, col_size)
        if ab.exec_():
            ab.quit()
Example #17
0
class PlayWidget(QWidget):
    def __init__(self, get_selector, title=None, max_lines=1000000):
        QWidget.__init__(self)
        self.ctrl = QApplication.instance().ctrl
        self.ctrl.switch_language.connect(self.on_switch_language)
        self.get_selector = get_selector
        self.window_title = title
        self.setWindowTitle(_(self.window_title))
        self.max_lines = max_lines
        self.conf = GuiConf()

        self.player = None
        self.resource_count = 0
        self.excluded_resource_count = 0
        self.exception_count = 0

        self.__init_ui__()
        self.show()

    def __init_ui__(self):
        vbox = QVBoxLayout()

        self.lbl_explanation = QLabel(_("Selected resources"))
        vbox.addWidget(self.lbl_explanation)

        splitter = QSplitter()
        splitter.setOrientation(Qt.Vertical)

        self.pte_output = QPlainTextEdit()
        self.pte_output.setReadOnly(True)
        self.pte_output.setMaximumBlockCount(self.max_lines)
        self.pte_output.setLineWrapMode(QPlainTextEdit.NoWrap)
        self.pte_output.setCenterOnScroll(True)
        splitter.addWidget(self.pte_output)

        self.pte_exceptions = QPlainTextEdit()
        self.pte_exceptions.setReadOnly(True)
        self.pte_exceptions.setMaximumBlockCount(5000)
        self.pte_exceptions.setLineWrapMode(QPlainTextEdit.NoWrap)
        self.pte_exceptions.setCenterOnScroll(True)
        self.pte_exceptions.setStyleSheet(Style.blue_text())
        splitter.addWidget(self.pte_exceptions)
        splitter.setStretchFactor(0, 2)
        splitter.setStretchFactor(1, 1)
        vbox.addWidget(splitter)

        btn_box = QHBoxLayout()

        count_grid = QGridLayout()
        count_grid.setSpacing(3)
        self.lbl_recources_count = QLabel(_("Selected resources:"))
        count_grid.addWidget(self.lbl_recources_count, 1, 1)
        self.lbl_resources_counter = QLabel("0")
        self.lbl_resources_counter.setTextInteractionFlags(
            Qt.TextSelectableByMouse)
        count_grid.addWidget(self.lbl_resources_counter, 1, 2)

        self.lbl_excluded_recources_count = QLabel(_("Excluded resources:"))
        count_grid.addWidget(self.lbl_excluded_recources_count, 2, 1)
        self.lbl_excluded_resources_counter = QLabel("0")
        self.lbl_excluded_resources_counter.setTextInteractionFlags(
            Qt.TextSelectableByMouse)
        count_grid.addWidget(self.lbl_excluded_resources_counter, 2, 2)

        btn_box.addLayout(count_grid)
        btn_box.addStretch(1)

        self.btn_play = QPushButton(_("Play"))
        self.btn_play.clicked.connect(self.on_btn_play_clicked)
        btn_box.addWidget(self.btn_play)

        self.btn_stop = QPushButton(_("Stop"))
        self.normal_style = self.btn_stop.styleSheet()
        self.btn_stop.clicked.connect(self.on_btn_stop_clicked)
        self.btn_stop.setEnabled(False)
        btn_box.addWidget(self.btn_stop)

        self.btn_close = QPushButton(_("Close"))
        self.btn_close.clicked.connect(self.on_btn_close_clicked)
        btn_box.addWidget(self.btn_close)

        vbox.addLayout(btn_box)
        self.setLayout(vbox)
        self.resize(self.conf.play_widget_width(),
                    self.conf.play_widget_height())

    def on_switch_language(self):
        self.setWindowTitle(_(self.window_title))
        self.lbl_explanation.setText(_("Selected resources"))
        self.lbl_recources_count.setText(_("Selected resources:"))
        self.lbl_excluded_recources_count.setText(_("Excluded resources:"))
        self.btn_play.setText(_("Play"))
        self.btn_stop.setText(_("Stop"))
        self.btn_close.setText(_("Close"))

    def on_btn_play_clicked(self):
        self.pte_output.setPlainText("")
        self.pte_exceptions.setPlainText("")
        selector = self.get_selector()
        self.player = PlayerThread(selector, self)
        self.player.yield_resource.connect(self.on_yield_resource)
        self.player.signal_exception.connect(self.on_signal_exception)
        self.player.signal_excluded_resource.connect(
            self.on_signal_excluded_resource)
        self.player.finished.connect(self.on_player_finished)
        # counters
        self.resource_count = 0
        self.excluded_resource_count = 0
        self.exception_count = 0
        self.lbl_resources_counter.setText(str(self.resource_count))
        self.lbl_excluded_resources_counter.setText(
            str(self.excluded_resource_count))
        # buttons
        self.btn_close.setEnabled(False)
        self.btn_play.setEnabled(False)
        self.btn_stop.setEnabled(True)
        self.btn_stop.setStyleSheet(Style.alarm())
        self.player.start()
        self.update()

    def on_yield_resource(self, file):
        self.resource_count += 1
        self.lbl_resources_counter.setText(str(self.resource_count))
        self.pte_output.appendPlainText(file)
        self.update()

    def on_signal_exception(self, msg):
        self.exception_count += 1
        self.pte_exceptions.appendHtml("<span style=color:red;>" + msg +
                                       "</span>")

    def on_signal_excluded_resource(self, msg):
        self.excluded_resource_count += 1
        self.lbl_excluded_resources_counter.setText(
            str(self.excluded_resource_count))
        self.pte_exceptions.appendPlainText(msg)
        self.update()

    def on_btn_stop_clicked(self):
        self.btn_stop.setStyleSheet(self.normal_style)
        self.btn_stop.setEnabled(False)
        self.player.requestInterruption()

    def on_player_finished(self):
        self.btn_stop.setStyleSheet(self.normal_style)
        self.btn_stop.setEnabled(False)
        self.btn_play.setEnabled(True)
        self.btn_close.setEnabled(True)

    def on_btn_close_clicked(self):
        if self.windowState() & Qt.WindowFullScreen:
            self.setWindowState(Qt.WindowMaximized)
        else:
            self.close()

    def closeEvent(self, event):
        if self.player:
            self.player.requestInterruption()
        if self.windowState() & Qt.WindowFullScreen:
            self.setWindowState(Qt.WindowMaximized)
            event.ignore()
        else:
            self.conf.set_play_widget_width(self.width())
            self.conf.set_play_widget_height(self.height())
            self.conf.persist()
            event.accept()
Example #18
0
class DwarfConsoleWidget(QWidget):

    onCommandExecute = pyqtSignal(str, name='onCommandExecute')

    def __init__(self, parent=None, input_placeholder='', function_box=False, has_input=True):
        super().__init__(parent=parent)

        self.app_window = parent

        layout = QVBoxLayout()

        self.function_content = ''

        self.setContentsMargins(QMargins(0, 0, 0, 0))
        layout.setContentsMargins(QMargins(0, 0, 0, 0))

        # use textedit to allow copy contents
        self.output = QPlainTextEdit()
        self.output.setReadOnly(True)
        self.output.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

        layout.addWidget(self.output)

        if has_input:
            box = QHBoxLayout()
            box.setContentsMargins(QMargins(3, 3, 3, 3))

            self.input = DwarfConsoleInput(self)
            self.input.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
            self.input.setPlaceholderText(input_placeholder)
            self.input.onEnterKeyPressed.connect(self._enter_pressed)
            box.addWidget(self.input)

            if function_box:
                function_btn = QPushButton('ƒ')
                function_btn.setMinimumWidth(25)
                function_btn.clicked.connect(self.js_function_box)
                box.addWidget(function_btn)

            box_widget = QWidget()
            box_widget.setLayout(box)
            layout.addWidget(box_widget)

        self.setLayout(layout)

    def _enter_pressed(self, cmd):
        if cmd == 'clear':
            self.clear()
        else:
            self.onCommandExecute.emit(cmd)

    def log(self, what, clear=False):
        if clear:
            self.clear()

        what = str(what)

        # color up stuff
        if 'error:' in what.lower():
            html_text = '<font color="crimson">' + what + '</font>'
        else:
            html_text = what
        time_stamp = datetime.datetime.now().strftime("%H:%M:%S.%f")
        self.output.appendHtml(
            '<p><font color="yellowgreen" size="2" style="font-style:italic">'
            + time_stamp + '</font>&nbsp;&nbsp;' + html_text + '</p>')
        self.output.verticalScrollBar().setValue(
            self.output.verticalScrollBar().maximum())

    def clear(self):
        self.output.setPlainText('')

    def js_function_box(self):
        accept, what = JsEditorDialog(
            self.app_window,
            def_text=self.function_content,
            placeholder_text='// js script with both frida and dwarf api.\n'
            '// note that it\'s evaluated. Which means, if you define a variable\n'
            '// or attach an Interceptor, it won\'t be removed by '
            'just deleting the script content').show()
        if accept:
            self.function_content = what
            if what:
                self.app_window.session_manager.session.dwarf.dwarf_api(
                    'evaluateFunction', what)

    def get_js_script_text(self):
        return self.function_content

    def set_js_script_text(self, script):
        self.function_content = script
class LogWindow(QMainWindow):
    emit_msg = pyqtSignal(str)

    def __init__(self,
                 level: int = logging.INFO,
                 window_title: str = "Python log",
                 logger: logging.Logger = None,
                 min_width: int = 800,
                 min_height: int = 400,
                 maximum_block_count: int = 1000) -> None:
        super().__init__()
        self.setStyleSheet(LOGEDIT_STYLESHEET)

        self.handler = HtmlColorHandler(self.log_message, level)
        self.may_close = False
        self.set_may_close(self.may_close)

        self.setWindowTitle(window_title)
        if min_width:
            self.setMinimumWidth(min_width)
        if min_height:
            self.setMinimumHeight(min_height)

        log_group = StyledQGroupBox("Log")
        log_layout_1 = QVBoxLayout()
        log_layout_2 = QHBoxLayout()
        self.log = QPlainTextEdit()
        # QPlainTextEdit better than QTextEdit because it supports
        # maximumBlockCount while still allowing HTML (via appendHtml,
        # not insertHtml).
        self.log.setReadOnly(True)
        self.log.setLineWrapMode(QPlainTextEdit.NoWrap)
        self.log.setMaximumBlockCount(maximum_block_count)
        log_clear_button = QPushButton('Clear log')
        log_clear_button.clicked.connect(self.log.clear)
        log_copy_button = QPushButton('Copy to clipboard')
        log_copy_button.clicked.connect(self.copy_whole_log)
        log_layout_2.addWidget(log_clear_button)
        log_layout_2.addWidget(log_copy_button)
        log_layout_2.addStretch()
        log_layout_1.addWidget(self.log)
        log_layout_1.addLayout(log_layout_2)
        log_group.setLayout(log_layout_1)

        main_widget = QWidget(self)
        self.setCentralWidget(main_widget)
        main_layout = QVBoxLayout(main_widget)
        main_layout.addWidget(log_group)

        self.emit_msg.connect(self.log_internal)

        if logger:
            logger.addHandler(self.get_handler())

    def get_handler(self) -> logging.Handler:
        return self.handler

    def set_may_close(self, may_close: bool) -> None:
        # log.debug("LogWindow: may_close({})".format(may_close))
        self.may_close = may_close
        # return
        if may_close:
            self.setWindowFlags(self.windowFlags() | Qt.WindowCloseButtonHint)
        else:
            self.setWindowFlags(self.windowFlags() & ~Qt.WindowCloseButtonHint)
        self.show()
        # ... or it will be hidden (in a logical not a real way!) by
        # setWindowFlags(), and thus mess up the logic for the whole Qt app
        # exiting (since qt_app.exec_() runs until there are no more windows
        # being shown).

    def copy_whole_log(self) -> None:
        # Ctrl-C will copy the selected parts.
        # log.copy() will copy the selected parts.
        self.log.selectAll()
        self.log.copy()
        self.log.moveCursor(QTextCursor.End)
        self.scroll_to_end_of_log()

    def scroll_to_end_of_log(self) -> None:
        vsb = self.log.verticalScrollBar()
        vsb.setValue(vsb.maximum())
        hsb = self.log.horizontalScrollBar()
        hsb.setValue(0)

    # noinspection PyPep8Naming
    def closeEvent(self, event: QCloseEvent) -> None:
        """Trap exit."""
        if not self.may_close:
            # log.debug("LogWindow: ignore closeEvent")
            event.ignore()
        else:
            # log.debug("LogWindow: accept closeEvent")
            event.accept()

    def log_message(self, html: str) -> None:
        # Jump threads via a signal
        self.emit_msg.emit(html)

    @pyqtSlot(str)
    def log_internal(self, html: str) -> None:
        # self.log.moveCursor(QTextCursor.End)
        # self.log.insertHtml(html)
        self.log.appendHtml(html)
        # self.scroll_to_end_of_log()
        # ... unnecessary; if you're at the end, it scrolls, and if you're at
        # the top, it doesn't bug you.

    @pyqtSlot()
    def exit(self) -> None:
        # log.debug("LogWindow: exit")
        self.may_close = True
        # closed = QMainWindow.close(self)
        # log.debug("closed: {}".format(closed))
        QMainWindow.close(self)

    @pyqtSlot()
    def may_exit(self) -> None:
        # log.debug("LogWindow: may_exit")
        self.set_may_close(True)