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)
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)
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)
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>   ') 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>   ') 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> </sub> ') 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>   ') 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>   ') 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   <sub> </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   <sub> </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>  ') 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'])
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)
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()
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)
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(' ', ' ') 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)
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()
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()
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()
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')
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)
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')
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
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()
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()
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> ' + 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