class ClientDialog(QDialog): """a simple popup dialog for asking the player what he wants to do""" def __init__(self, client, parent=None): QDialog.__init__(self, parent) self.setWindowTitle(m18n('Choose') + ' - Kajongg') self.setObjectName('ClientDialog') self.client = client self.layout = QGridLayout(self) self.progressBar = QProgressBar() self.timer = QTimer() if not client.game.autoPlay: self.timer.timeout.connect(self.timeout) self.deferred = None self.buttons = [] self.setWindowFlags(Qt.SubWindow | Qt.WindowStaysOnTopHint) self.setModal(False) self.btnHeight = 0 self.answered = False def keyPressEvent(self, event): """ESC selects default answer""" if not self.client.game or self.client.game.autoPlay: return if event.key() in [Qt.Key_Escape, Qt.Key_Space]: self.selectButton() event.accept() else: for btn in self.buttons: if str(event.text()).upper() == btn.message.shortcut: self.selectButton(btn) event.accept() return QDialog.keyPressEvent(self, event) def __declareButton(self, message): """define a button""" maySay = self.client.sayable[message] if Preferences.showOnlyPossibleActions and not maySay: return btn = DlgButton(message, self) btn.setAutoDefault(True) btn.clicked.connect(self.selectedAnswer) self.buttons.append(btn) def focusTileChanged(self): """update icon and tooltip for the discard button""" if not self.client.game: return for button in self.buttons: button.decorate(self.client.game.myself.handBoard.focusTile) for tile in self.client.game.myself.handBoard.lowerHalfTiles(): txt = [] for button in self.buttons: _, _, tileTxt = button.message.toolTip(button, tile) if tileTxt: txt.append(tileTxt) txt = '<br><br>'.join(txt) tile.graphics.setToolTip(txt) if self.client.game.activePlayer == self.client.game.myself: Internal.field.handSelectorChanged(self.client.game.myself.handBoard) def checkTiles(self): """does the logical state match the displayed tiles?""" for player in self.client.game.players: logExposed = list() physExposed = list() physConcealed = list() for tile in player.bonusTiles: logExposed.append(tile.element) for tile in player.handBoard.tiles: if tile.yoffset == 0 or tile.element[0] in 'fy': physExposed.append(tile.element) else: physConcealed.append(tile.element) for meld in player.exposedMelds: logExposed.extend(meld.pairs) logConcealed = sorted(player.concealedTileNames) logExposed.sort() physExposed.sort() physConcealed.sort() assert logExposed == physExposed, '%s != %s' % (logExposed, physExposed) assert logConcealed == physConcealed, '%s != %s' % (logConcealed, physConcealed) def messages(self): """a list of all messages returned by the declared buttons""" return list(x.message for x in self.buttons) def proposeAction(self): """either intelligently or first button by default. May also focus a proposed tile depending on the action.""" result = self.buttons[0] game = self.client.game if game.autoPlay or Preferences.propose: answer, parameter = self.client.intelligence.selectAnswer( self.messages()) result = [x for x in self.buttons if x.message == answer][0] result.setFocus() if answer in [Message.Discard, Message.OriginalCall]: for tile in game.myself.handBoard.tiles: if tile.element == parameter: game.myself.handBoard.focusTile = tile return result def askHuman(self, move, answers, deferred): """make buttons specified by answers visible. The first answer is default. The default button only appears with blue border when this dialog has focus but we always want it to be recognizable. Hence setBackgroundRole.""" self.move = move self.deferred = deferred for answer in answers: self.__declareButton(answer) self.focusTileChanged() self.show() self.checkTiles() game = self.client.game myTurn = game.activePlayer == game.myself prefButton = self.proposeAction() if game.autoPlay: self.selectButton(prefButton) return prefButton.setFocus() self.progressBar.setVisible(not myTurn) if not myTurn: msecs = 50 self.progressBar.setMinimum(0) self.progressBar.setMaximum(game.ruleset.claimTimeout * 1000 // msecs) self.progressBar.reset() self.timer.start(msecs) def placeInField(self): """place the dialog at bottom or to the right depending on space.""" field = Internal.field cwi = field.centralWidget() view = field.centralView geometry = self.geometry() if not self.btnHeight: self.btnHeight = self.buttons[0].height() vertical = view.width() > view.height() * 1.2 if vertical: height = (len(self.buttons) + 1) * self.btnHeight * 1.2 width = (cwi.width() - cwi.height() ) // 2 geometry.setX(cwi.width() - width) geometry.setY(min(cwi.height()//3, cwi.height() - height)) else: handBoard = self.client.game.myself.handBoard if not handBoard: # we are in the progress of logging out return hbLeftTop = view.mapFromScene(handBoard.mapToScene(handBoard.rect().topLeft())) hbRightBottom = view.mapFromScene(handBoard.mapToScene(handBoard.rect().bottomRight())) width = hbRightBottom.x() - hbLeftTop.x() height = self.btnHeight geometry.setY(cwi.height() - height) geometry.setX(hbLeftTop.x()) for idx, btn in enumerate(self.buttons + [self.progressBar]): self.layout.addWidget(btn, idx+1 if vertical else 0, idx+1 if not vertical else 0) idx = len(self.buttons) + 2 spacer = QSpacerItem(20, 20, QSizePolicy.Expanding, QSizePolicy.Expanding) self.layout.addItem(spacer, idx if vertical else 0, idx if not vertical else 0) geometry.setWidth(width) geometry.setHeight(height) self.setGeometry(geometry) def showEvent(self, dummyEvent): """try to place the dialog such that it does not cover interesting information""" self.placeInField() def timeout(self): """the progressboard wants an update""" pBar = self.progressBar if isAlive(pBar): pBar.setValue(pBar.value()+1) pBar.setVisible(True) if pBar.value() == pBar.maximum(): # timeout: we always return the original default answer, not the one with focus self.selectButton() pBar.setVisible(False) def selectButton(self, button=None): """select default answer. button may also be of type Message.""" self.timer.stop() self.answered = True if button is None: button = self.focusWidget() if isinstance(button, Message): assert any(x.message == button for x in self.buttons) answer = button else: answer = button.message if not self.client.sayable[answer]: Sorry(m18n('You cannot say %1', answer.i18nName)) return Internal.field.clientDialog = None self.deferred.callback(answer) def selectedAnswer(self, dummyChecked): """the user clicked one of the buttons""" game = self.client.game if game and not game.autoPlay: self.selectButton(self.sender())
class Ui_Form (QWidget): ''' Ui class. Generated with pyuic4. ''' def __init__ (self, parent=None): QWidget.__init__(self, parent) self.timer = QTimer (self) self.image = QImage (720, 450, QImage.Format_RGB888) self.__engine = REngineThread () self.__model = Model () self.__fsm = DebugStateMachine (self) def wireEngineUp (self): ''' this method connects the REngine's signals. ''' self.connect (self.__engine, SIGNAL ("update (float)"), self.updateImage) self.connect (self.__engine, SIGNAL ("thread_completed()"), self.__fsm.finaliseRender) self.connect (self.__engine, SIGNAL ("inters_created (PyQt_PyObject, PyQt_PyObject)"), self.widget.addIntersection) self.connect (self.__engine, SIGNAL ("vector_created (PyQt_PyObject, PyQt_PyObject, QString)"), self.widget.addArrow) self.connect (self.__engine, SIGNAL ("line_created (PyQt_PyObject, PyQt_PyObject, QString)"), self.widget.addLine) def wireOGLViewerUp (self): ''' this method connects the REngine's signals. ''' self.widget.setModel (self.__model) QObject.connect (self.widget, SIGNAL ("MatrixChanged (PyQt_PyObject)"), self.displayGLMatrix) # call the function run regularly when the focus is on it. (60 FPS if interval = 20) QObject.connect (self.timer, SIGNAL ('timeout()'), self.widget.run) def freezeGLFrameRate (self): self.timer.stop () def speedUpGLFrameRate (self): self.timer.start () self.timer.setInterval (20) def setupUi (self, Form): font = QFont () font.setPointSize (9) font.setBold (False) font.setWeight (50) sizePolicy = QSizePolicy (QSizePolicy.Fixed, QSizePolicy.Fixed) sizePolicy.setHorizontalStretch (0) sizePolicy.setVerticalStretch (0) Form.setObjectName (_fromUtf8("Form")) Form.resize (971, 930) self.plainTextEdit = QPlainTextEdit (Form) self.plainTextEdit.setGeometry (QRect (740, 280, 221, 611)) self.plainTextEdit.setObjectName (_fromUtf8 ("plainTextEdit")) self.plainTextEdit.setFont (font) self.widget = OGLViewer (Form) self.widget.setUi_Form (self) self.widget.setGeometry (QRect (10, 10, 720, 450)) sizePolicy.setHeightForWidth (self.widget.sizePolicy().hasHeightForWidth()) self.widget.setSizePolicy (sizePolicy) self.widget.setObjectName (_fromUtf8 ("widget")) self.wireOGLViewerUp () self.wireEngineUp () self.label_view = QLabel (Form) self.label_view.setGeometry (QRect (10, 470, 720, 450)) sizePolicy.setHeightForWidth (self.label_view.sizePolicy().hasHeightForWidth()) self.label_view.setSizePolicy (sizePolicy) self.label_view.setObjectName (_fromUtf8 ("label_view")) self.initLabel () self.pixmap_item = self.label_view.setPixmap (QPixmap.fromImage (self.image)) # buttons definitions self.renderBtn = QPushButton (Form) self.pauseBtn = QPushButton (Form) self.stopBtn = QPushButton (Form) self.upBtn = QPushButton (Form) self.downBtn = QPushButton (Form) self.moreDownBtn = QPushButton (Form) self.moreUpBtn = QPushButton (Form) self.rightBtn = QPushButton (Form) self.moreRightBtn = QPushButton (Form) self.leftBtn = QPushButton (Form) self.furtherLeft = QPushButton (Form) self.grid_switch = QPushButton (Form) buttons_properties_list = [[self.renderBtn, True, QRect (740, 140, 61, 21), "render"], [self.pauseBtn, False, QRect (740, 120, 61, 21), "pause"], [self.stopBtn, False, QRect (740, 100, 61, 21), "stop"], [self.upBtn, False, QRect (820, 120, 21, 21), "one_row_up"], [self.downBtn, False, QRect (820, 140, 21, 21), "one_row_down"], [self.moreDownBtn, False, QRect (820, 160, 21, 21), "ten_rows_down"], [self.moreUpBtn, False, QRect (820, 100, 21, 21), "ten_rows_up"], [self.rightBtn, False, QRect (780, 180, 21, 21), "one_column_right"], [self.moreRightBtn, False, QRect (800, 180, 21, 21), "ten_columns_right"], [self.leftBtn, False, QRect (760, 180, 21, 21), "one_column_left"], [self.furtherLeft, False, QRect (740, 180, 21, 21), "ten_columns_left"], [self.grid_switch, False, QRect (870, 230, 91, 31), "grid_switch"]] for button in buttons_properties_list: button[0].setEnabled (button[1]) button[0].setGeometry (button[2]) button[0].setFont (font) button[0].setObjectName (_fromUtf8 (button[3])) # other UI elements self.progressBar = QProgressBar (Form) self.progressBar.setGeometry (QRect (740, 901, 221, 20)) self.progressBar.setProperty ("value", 0) self.progressBar.setObjectName (_fromUtf8("progressBar")) self.progressBar.setMinimum (0) self.progressBar.setMaximum (100) self.slider_label = QLabel (Form) self.slider_label.setGeometry (QRect(900, 260, 61, 16)) self.slider_label.setFont(font) self.slider_label.setObjectName (_fromUtf8("slider_label")) self.slider_label.setEnabled (True) self.arrowSizeSlider = QSlider (Form) self.arrowSizeSlider.setGeometry (QRect (740, 258, 151, 22)) self.arrowSizeSlider.setMinimum (2) self.arrowSizeSlider.setMaximum (40) self.arrowSizeSlider.setSingleStep (1) self.arrowSizeSlider.setProperty ("value", 4) self.arrowSizeSlider.setOrientation (Qt.Horizontal) self.arrowSizeSlider.setObjectName (_fromUtf8("arrowSizeSlider")) self.retranslateUi (Form) QMetaObject.connectSlotsByName (Form) def retranslateUi (self, Form): Form.setWindowTitle (_translate ("Form", "RayTracing Debugging Tool", None)) self.renderBtn.setText (_translate ("Form", "Render", None)) self.pauseBtn.setText (_translate ("Form", "Pause", None)) self.stopBtn.setText (_translate ("Form", "Stop", None)) self.upBtn.setText (_translate ("Form", "^", None)) self.downBtn.setText (_translate ("Form", "v", None)) self.moreDownBtn.setText (_translate ("Form", "+", None)) self.moreUpBtn.setText (_translate ("Form", "-", None)) self.rightBtn.setText (_translate ("Form", ">", None)) self.moreRightBtn.setText (_translate ("Form", "+", None)) self.leftBtn.setText (_translate ("Form", "<", None)) self.furtherLeft.setText (_translate ("Form", "-", None)) self.grid_switch.setText (_translate ("Form", "Grid on/off", None)) self.slider_label.setText (_translate ("Form", "Arrows size", None)) self.connect (self.renderBtn, SIGNAL ("clicked()"), self.__fsm.startRendering) self.connect (self.pauseBtn, SIGNAL ("clicked()"), self.__fsm.pauseRendering) self.connect (self.stopBtn, SIGNAL ("clicked()"), self.__fsm.stopRendering) self.connect (self.arrowSizeSlider, SIGNAL ("sliderMoved(int)"), self.resizeArrows) def initLabel (self): color = QColor (250, 250, 250) self.image.fill (color) ''' # test init for i in range (0, 200, 20): for x in range (i, i+20): for y in range (i, i+20): self.image.setPixel (x,y, qRgb (0, 0, 0)) ''' def enableUIButtons (self, boolean_dict): ''' method used by the debug state machine to manage the greyed-out state of buttons. ''' b_dict = dict (boolean_dict) self.renderBtn.setEnabled (b_dict['renderBtn']) self.pauseBtn.setEnabled (b_dict['pauseBtn']) self.stopBtn.setEnabled (b_dict['stopBtn']) self.upBtn.setEnabled (b_dict['upBtn']) self.downBtn.setEnabled (b_dict['downBtn']) self.moreDownBtn.setEnabled (b_dict['moreDownBtn']) self.moreUpBtn.setEnabled (b_dict['moreUpBtn']) self.rightBtn.setEnabled (b_dict['rightBtn']) self.moreRightBtn.setEnabled (b_dict['moreRightBtn']) self.leftBtn.setEnabled (b_dict['leftBtn']) self.furtherLeft.setEnabled (b_dict['furtherLeft']) if b_dict['progressBar'] == 'completed': self.progressBar.setValue (100) elif b_dict['progressBar'] == 'reset': self.progressBar.reset () def addScreenshot (self): ''' it grabs a screenshot of OpenGL viewer and add it into the QImage instance. ''' self.image = self.widget.grabFrameBuffer () self.pixmap_item = self.label_view.setPixmap (QPixmap.fromImage (self.image)) def prepAndStartEngineUp (self): ''' it preps the engine and start it up. ''' self.__engine.setImage (self.image) self.__engine.setCameraNormalMatrix (self.widget.getNormalMatrix(), self.widget.getFovy ()) self.__engine.setModel (self.__model) self.__engine.start () def addCamera (self): self.widget.addCamera () def setIsStoppedFlag (self, boo): self.__engine.setIsStoppedFlag (boo) def setIsPausedFlag (self, boo): self.__engine.setIsPausedFlag (boo) def changeRenderBtnName (self, title): self.renderBtn.setText (_translate ("Form", title, None)) # listeners - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - def resizeArrows (self, e): self.widget.changeArrowsSize (e*0.5) def updateImage (self, e): self.pixmap_item = self.label_view.setPixmap (QPixmap.fromImage (self.image)) self.progressBar.setValue (int(100*e)) def displayGLMatrix (self, e): ''' this method is registered to the MatrixChanged event fired from the OGLViewer component. It prints the model matrix. Only for debugging purposes. ''' pass
class HomeWidget(BaseWidget): def __init__(self, parent=0, *args, **kwargs): super(HomeWidget, self).__init__(parent=parent, *args, **kwargs) # inputs self._json_fpath = None self._destination_folder = self.guess_destination() self._from_date, self._to_date = self.guess_dates() self.nb_instances = 0 self.setup_ui() @property def json_fpath(self): return self._json_fpath @json_fpath.setter def json_fpath(self, value): self._json_fpath = value self.json_select_button.setText( value if value else "Sélection fichier JSON ODK Aggregate ...") self.update_start_button_state() @property def destination_folder(self): return self._destination_folder @destination_folder.setter def destination_folder(self, value): self._destination_folder = value self.destination_select_button.setText(self.destination_folder) self.update_start_button_state() @property def from_date(self): return self._from_date @from_date.setter def from_date(self, value): self._from_date = value self.from_date_selector.setPythonDate(self.from_date) self.update_start_button_state() @property def to_date(self): return self._to_date @to_date.setter def to_date(self, value): self._to_date = value self.to_date_selector.setPythonDate(self.to_date) self.update_start_button_state() def setup_ui(self): # json file selector self.json_groupbox = QGroupBox("Export ODK Aggregate") layout = QGridLayout() self.json_select_button = PushButton("", self) self.json_select_button.clicked.connect(self.json_selector_clicked) layout.addWidget(self.json_select_button, 1, 0) self.json_groupbox.setLayout(layout) # destination folder selector self.destination_groupbox = QGroupBox("Destination") layout = QGridLayout() self.destination_select_button = PushButton(self.destination_folder, self) self.destination_select_button.clicked.connect( self.destination_selector_clicked) layout.addWidget(self.destination_select_button, 1, 0) self.destination_groupbox.setLayout(layout) # period calendars today = datetime.date.today() self.period_groupbox = QGroupBox("Période") layout = QGridLayout() self.from_date_selector = DateTimeEdit(QDate(self.from_date)) self.from_date_selector.dateChanged.connect(self.from_date_changed) self.from_date_selector.setMaximumDate(self.to_date) self.to_date_selector = DateTimeEdit(QDate(self.to_date)) self.to_date_selector.dateChanged.connect(self.to_date_changed) self.to_date_selector.setMinimumDate(self.from_date) self.to_date_selector.setMaximumDate(today) layout.addWidget(Label("Du"), 2, 0) layout.addWidget(self.from_date_selector, 3, 0) layout.addWidget(Label("Au"), 2, 1) layout.addWidget(self.to_date_selector, 3, 1) self.period_groupbox.setLayout(layout) # start button self.start_button = PushButton("Démarrer") self.start_button.setEnabled(False) self.start_button.setDefault(True) self.start_button.clicked.connect(self.export_requested) # cancel button self.cancel_button = CancelPushButton("Annuler") self.cancel_button.setEnabled(False) self.cancel_button.clicked.connect(self.cancel_export) # grid self.gridBox = QGridLayout() self.gridBox.addWidget(self.json_groupbox, 0, 0, 1, 2) self.gridBox.addWidget(self.destination_groupbox, 1, 0, 1, 2) self.gridBox.addWidget(self.period_groupbox, 2, 0, 1, 2) self.gridBox.addWidget(self.start_button, 3, 0) self.gridBox.addWidget(self.cancel_button, 3, 1) vBox = QVBoxLayout() vBox.addLayout(self.gridBox) self.setLayout(vBox) self.json_fpath = None def guess_destination(self, root_only=False): start_folder = os.path.join(os.path.expanduser("~"), 'Desktop') if root_only: return start_folder return os.path.join(start_folder, Constants.DEFAULT_FOLDER_NAME) def guess_dates(self): to = datetime.date.today() start = to - datetime.timedelta(days=15) return start, to def update_start_button_state(self): self.start_button.setEnabled( all([ self.json_fpath, self.destination_folder, self.from_date, self.to_date ])) def json_selector_clicked(self): # self.file_name_field.setText("Aucun fichier") self.start_button.setEnabled(False) fpath = QFileDialog.getOpenFileName( self, "Choisir le fichier d'export JSON", self.json_fpath or self.guess_destination(root_only=True), "Fichiers JSON (*.json)") self.json_fpath = os.path.abspath(fpath) if fpath else self.json_fpath def destination_selector_clicked(self): path = QFileDialog.getExistingDirectory(self, "Sélectionner le dossier", self.destination_folder) if path: self.destination_folder = os.path.abspath(path) def from_date_changed(self, new_date): self.from_date = new_date.toPyDate() def to_date_changed(self, new_date): self.to_date = new_date.toPyDate() def export_requested(self): logger.debug("export_requested") self.parentWidget().exporter.check_aggregate_presence() def display_missing_aggregate_confirmation(self): if MissingODKConfirmationWidget().exec_() == QDialog.Accepted: self.start_export() def start_export(self): logger.debug("Lancement ...") self.add_progressbar() self.start_button.setEnabled(False) self.cancel_button.setEnabled(True) self.parentWidget().exporter.parse( destination_folder=self.destination_folder, fname=self.json_fpath, from_date=self.from_date, to_date=self.to_date) def cancel_export(self): logger.debug("cancel") self.parentWidget().exporter.cancel() def update_progress_label(self, index): progression_label = "Export en cours... {index}/{total}" \ .format(index=index, total=self.nb_instances) self.progression_groupbox.setTitle(progression_label) def add_progressbar(self): self.progressbar = QProgressBar() self.progressbar.setMinimum(0) self.progressbar.setMaximum(100) self.progressbar.reset() self.progressbar.setTextVisible(False) self.progression_groupbox = QGroupBox("...") progress_layout = QHBoxLayout() progress_layout.addWidget(self.progressbar) self.progression_groupbox.setLayout(progress_layout) self.gridBox.addWidget(self.progression_groupbox, 4, 0, 1, 2) def remove_progressbar(self): self.progression_groupbox.deleteLater() self.progression_groupbox = None @pyqtSlot(bool, int, str) def parsing_ended(self, succeeded, nb_instances, error_message): if succeeded: self.nb_instances = nb_instances @pyqtSlot(str, int) def exporting_instance(self, ident, index): logger.debug("exporting_instance") self.update_progress_label(index) @pyqtSlot(bool, int) def instance_completed(self, succeeded, index): logger.debug("instance_completed") pc = index * 100 // self.nb_instances self.progressbar.setValue(pc) @pyqtSlot(int, int, int, int) def export_ended(self, nb_instances_successful, nb_instances_failed, nb_medias_successful, nb_medias_failed): self.cancel_button.setEnabled(False) self.start_button.setEnabled(True) @pyqtSlot() def export_canceled(self): self.remove_progressbar() self.cancel_button.setEnabled(False) self.start_button.setEnabled(True) QMessageBox.warning( self, "Export annulé !", "L'export en cours a été annulé.\n" "Tous les fichiers créés ont été supprimés", QMessageBox.Ok, QMessageBox.NoButton) if self.parentWidget().is_exiting: self.parentWidget().do_close()
class CompileWidget(QWidget): progress = pyqtSignal(int) def __init__(self, parent=None): QWidget.__init__(self, parent) self._options = None self._conf = None self._go = QPushButton('Go') self._go.setMaximumWidth(100) font = self._go.font() font.setPointSizeF(font.pointSizeF() * 2.0) font.setWeight(QFont.Bold) self._go.setFont(font) self._go.clicked.connect(self.go) self._saveLog = QPushButton('Save') saveLogLabel = QLabel('Log File:') saveLogBrowse = QPushButton('&Browse') saveLogBrowse.clicked.connect(self.browseSaveLog) self._saveLogEdit = QLineEdit('') gLayout = QGridLayout() gLayout.addWidget(saveLogLabel, 0, 0, 1, 1, Qt.AlignRight) gLayout.addWidget(self._saveLogEdit, 0, 1, 1, 6) gLayout.addWidget(saveLogBrowse, 0, 7, 1, 1) self._console = QTextEdit() self._console.setLineWrapMode(QTextEdit.NoWrap) self._console.setMinimumSize(800, 400) palette = self._console.palette() palette.setColor(QPalette.Base, QColor.fromRgb(255, 255, 221)) # ffffdd. self._console.setPalette(palette) font = QFont('Bitstream Vera Sans Mono', self._console.font().pointSize()) self._console.setFont(font) self._highlighter = Highlighter(self._console.document()) self._progressBar = QProgressBar() self._progressBar.setRange(0, 100) self._progressBar.setTextVisible(True) hLayout = QHBoxLayout() hLayout.addStretch() hLayout.addWidget(self._go) hLayout.addStretch() hLayout.addWidget(self._saveLog) hLayout.addStretch() vLayout = QVBoxLayout() vLayout.addLayout(hLayout) vLayout.addLayout(gLayout) vLayout.addWidget(self._progressBar) vLayout.addWidget(self._console) self.setLayout(vLayout) self.progress.connect(self._progressBar.setValue) self._saveLog.clicked.connect(self.saveLog) self.readSettings() return def _setOptions(self, options): self._options = options def _setConf(self, conf): self._conf = conf def _getOptions(self): return self._options def _getConf(self): return self._conf options = property(_getOptions, _setOptions) conf = property(_getConf, _setConf) def browseSaveLog(self): self._saveLogEdit.setText( QFileDialog.getSaveFileName(self, 'Select Log File Report', self._saveLogEdit.text(), 'Report Files (*.log *.txt)')) return def saveLog(self): if self._saveLogEdit.text(): fd = open(self._saveLogEdit.text(), 'w+') fd.write(self._console.toPlainText()) fd.close() return def shellCommand(self): command = [self.conf.bootstrapDir + '/ccb.py'] for project in self.options.projects: for tool in project.actives: command += ['--tool=' + tool] toolsCount = len(command) - 1 if self.conf.rootDir: command += ['--root=%s' % self.conf.rootDir] #if self.options.svnUpdate: command += [ '--svn-update' ] #if self.options.svnStatus: command += [ '--svn-update' ] if self.options.enableDoc: command += ['--doc'] if self.options.devtoolset2: command += ['--devtoolset-2'] if self.options.qt5: command += ['--qt5'] if self.options.noCache: command += ['--no-cache'] if self.options.rmBuild: command += ['--rm-build'] if self.options.verbose: command += ['--verbose'] if self.options.make: makeArguments = 'install ' + self.options.threads command += ['--make=%s' % makeArguments] if self.options.buildMode == 'Debug': command += ['--debug'] return toolsCount, command def go(self): rePercentage = re.compile(r'^\[\s*(?P<percent>\d+)%\].*') reProcessTool = re.compile(r'^Processing tool:\s*"(?P<tool>.+)"') if not self.options or not self.conf: return toolsCount, command = self.shellCommand() if not toolsCount: return self._progressBar.reset() self._progressBar.setRange(0, toolsCount * 100) strCommand = command[0] for arg in command[1:]: strCommand += ' ' + arg strCommand += '\n\n' self._console.setFontItalic(True) self._console.insertPlainText(strCommand) self._console.setFontItalic(False) toolsDone = -1 builderProcess = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) while True: line = builderProcess.stdout.readline() if line == '': break m = rePercentage.match(line) if m: self.progress.emit(toolsDone * 100 + int(m.group('percent'))) else: m = reProcessTool.match(line) if m: toolsDone += 1 self._console.insertPlainText(line) scrollBar = self._console.verticalScrollBar() scrollBar.setValue(scrollBar.maximum()) QApplication.processEvents() builderProcess.wait() if builderProcess.returncode == None: pass return def readSettings(self): settings = QSettings() self._saveLogEdit.setText(settings.value('compile/saveLog').toString()) return def saveSettings(self): settings = QSettings() settings.setValue('compile/saveLog', self._saveLogEdit.text()) return
class FileItem(QTreeWidgetItem): cols = ['Filepath', 'Renamed Filepath'] invalid_char_map = {':': ''} re_filename_episodes = [ re.compile( r'.*[Ss](?P<season>\d{1,2})[\s\-_]*[Ee](?P<episode>\d{1,2}).*', re.I), re.compile( r'.*season\s*(?P<season>\d{1,2})[\s\-_]*episode\s*[Ee](?P<episode>\d{1,2}).*', re.I), ] re_filepath_episodes = [ re.compile( r'.*season\s*(?P<season>\d{1,2})\\(?:season\s*\d{1,2}[\s\-_]*)?(?:episode|ep\.?|e)\s*(?P<episode>\d{1,2}).*', re.I), ] def __init__(self, parent, filepath, mediaman): super(FileItem, self).__init__(parent) self._renamed = False self.mediaman = mediaman self.filepath = filepath self.renamed_filepath = None self.media_info = None self.episode_info = None self.thread = None self.pbar = None self.setText(0, self.filepath) self.send_thread() def send_thread(self): if self.thread is None: self.thread = FilepathSearchThread(self.treeWidget()) self.thread.finished.connect(self.receive_thread) thread_ready = self.thread.wait(10000) if thread_ready: tree = self.treeWidget() self.pbar = QProgressBar(tree) self.pbar.setRange(0, 0) tree.setItemWidget(self, 1, self.pbar) self.thread.filepath = self.filepath self.thread.start() def receive_thread(self): self.media_info = self.thread.result self.pbar.reset() tree = self.treeWidget() tree.removeItemWidget(self, 1) self.pbar.deleteLater() self.refresh() def _replace_invalid_chars(self, text): for k, v in FileItem.invalid_char_map.items(): if k in text: text = text.replace(k, v) return text def refresh(self): moviedir = self.mediaman.moviedir() tvdir = self.mediaman.tvdir() if self.media_info: title = self.media_info.get('title') title = self._replace_invalid_chars(title) year = self.media_info.get('year') filename, ext = os.path.splitext(os.path.basename(self.filepath)) if self.episode_info or self.media_info.get('episodes'): if not self.episode_info: episodes = self.media_info['episodes'] ep_match = None for reg in FileItem.re_filename_episodes: m = reg.search(filename) if m: ep_match = m.groupdict() break if not ep_match: for reg in FileItem.re_filepath_episodes: m = reg.search(self.filepath) if m: ep_match = m.groupdict() if ep_match: season, episode = int(ep_match['season']), int( ep_match['episode']) for episode_info in episodes: if (episode_info['season'], episode_info['episode']) == (season, episode): self.episode_info = episode_info break if self.episode_info: self.renamed_filepath = ur'{tvdir}\{title}\Season{season:02d}\{title}.S{season:02d}E{episode:02d}.{episode_title}{ext}'.format( tvdir=tvdir, title=title, year=year, season=self.episode_info['season'], episode=self.episode_info['episode'], episode_title=self._replace_invalid_chars( self.episode_info['title']), ext=ext, ) else: self.renamed_filepath = ur'{tvdir}\{title}\Season00\{title}.S00E00.XXX{ext}'.format( tvdir=tvdir, title=title, year=year, ext=ext, ) else: self.renamed_filepath = ur'{moviedir}\{title} ({year})\{title}{ext}'.format( moviedir=moviedir, title=title, year=year, ext=ext) self.setText(1, self.renamed_filepath) if os.path.exists(self.renamed_filepath): self.setBackgroundColor( FileItem.cols.index('Renamed Filepath'), QColor(0, 255, 0, 100))
class OWDatabasesPack(OWWidget.OWWidget): settingsList = ["fileslist", "downloadurl", "downloadmessage"] def __init__(self, parent=None, signalManager=None, title="Databases Pack"): super(OWDatabasesPack, self).__init__( parent, signalManager, title, wantMainArea=False) self.fileslist = [("Taxonomy", "ncbi_taxonomy.tar.gz")] self.downloadurl = "https://dl.dropboxusercontent.com/u/100248799/sf_pack.tar.gz" self.downloadmessage = ( "Downloading a subset of available databases for a smoother " + "ride through the workshop" ) self.loadSettings() self._tmpfile = None self.reply = None # Locks held on server files self.locks = [] self.net_manager = QNetworkAccessManager() # Lock all files in files list so any other (in process) atempt to # download the files waits) box = OWGUI.widgetBox(self.controlArea, "Info") self.info = OWGUI.widgetLabel(box, self.downloadmessage) self.info.setWordWrap(True) box = OWGUI.widgetBox(self.controlArea, "Status") self.statusinfo = OWGUI.widgetLabel(box, "Please wait") self.statusinfo.setWordWrap(True) self.progressbar = QProgressBar() box.layout().addWidget(self.progressbar) self.setMinimumWidth(250) already_available = [(domain, filename) for domain in serverfiles.listdomains() for filename in serverfiles.listfiles(domain)] if set(self.fileslist) <= set(already_available): # All files are already downloaded self.statusinfo.setText("All files already available") self.setStatusMessage("Done") else: for domain, filename in self.fileslist + [("_tmp_cache_", "pack")]: manager = serverfiles._lock_file(domain, filename) try: manager.__enter__() except Exception: warnings.warn("Could not acquire lock for {0} {0}" .format(domain, filename)) self.warning(0, "...") else: self.locks.append(manager) QTimer.singleShot(0, self.show) QTimer.singleShot(0, self.run) def run(self): self.setStatusMessage("Downloading") self.info.setText(self.downloadmessage) target_dir = os.path.join(environ.buffer_dir, "serverfiles_pack_cache") try: os.makedirs(target_dir) except OSError: pass self.progressBarInit() req = QNetworkRequest(QUrl(self.downloadurl)) self.reply = self.net_manager.get(req) self.reply.downloadProgress.connect(self._progress) self.reply.error.connect(self._error) self.reply.finished.connect(self._finished) self.reply.readyRead.connect(self._read) url = urlparse.urlsplit(self.downloadurl) self._tmpfilename = posixpath.basename(url.path) self._tmpfile = open( os.path.join(target_dir, self._tmpfilename), "wb+" ) def _progress(self, rec, total): if rec == 0 and total == 0: self.progressbar.setRange(0, 0) self.progressBarValue = 1.0 else: self.progressbar.setRange(0, total) self.progressbar.setValue(rec) self.progressBarValue = 100.0 * rec / (total or 1) def _error(self, error): self.statusinfo.setText( u"Error: {0}".format(error.errorString()) ) self.error(0, "Error: {0}".format(error.errorString())) self.setStatusMessage("Error") self._releaselocks() self._removetmp() self.progressBarFinshed() def _read(self): contents = str(self.reply.readAll()) self._tmpfile.write(contents) def _finished(self): self.progressbar.reset() if self.reply.error() != QNetworkReply.NoError: self._releaselocks() self._removetmp() return self.statusinfo.setText("Extracting") self.setStatusMessage("Extracting") try: self._extract() except Exception: # Permission errors, ... ?? pass self.statusinfo.setText("Done") self.setStatusMessage("Done") self.progressbar.reset() self.progressBarFinished() self._releaselocks() self._removetmp() def _extract(self): self._tmpfile.seek(0, 0) archive = tarfile.open(fileobj=self._tmpfile) target_dir = serverfiles.localpath() archive.extractall(target_dir) def onDeleteWidget(self): super(OWDatabasesPack, self).onDeleteWidget() self._releaselocks() if self.reply is not None and self.reply.isOpen(): self.reply.finished.disconnect(self._finished) self.reply.error.disconnect(self._error) self.reply.close() if self._tmpfile is not None: self._tmpfile.close() self._tmpfile = None def _releaselocks(self): for lock in self.locks: lock.__exit__(None, None, None) self.locks = [] def _removetmp(self): if self._tmpfile is not None: self._tmpfile.close() self._tmpfile = None tmp_file = os.path.join( environ.buffer_dir, "serverfiles_pack_cache", self._tmpfilename ) os.remove(tmp_file)
class _CancellableProgressEditor(Editor): title = Unicode # synced from factory.title_name message = Unicode # synced from factory.message_name min = Int#Float # synced from factory.min_name max = Int#Float # synced from factory.max_name show_time = Bool # synced from factory.show_time_name def _title_changed(self): if self._title is not None: self._title.setText('<B>%s</B>' % self.title) self._title.setVisible(len(self.title) > 0) def _message_changed(self): if self._message is not None: self._message.setText(self.message) self._message.setVisible(len(self.message) > 0) if self.factory.prefix_message: self.set_text() def _show_time_changed(self): if self._time_widget is not None: self._time_widget.setVisible(self.show_time) @on_trait_change('min, max') def change_range(self): if self._progress_bar is not None: self._progress_bar.setRange(self.min, self.max) self.update_editor() def init(self, parent): self.title = self.factory.title if len(self.factory.title_name) > 0: self.sync_value(self.factory.title_name, 'title')#, 'from') self.message = self.factory.message if len(self.factory.message_name) > 0: self.sync_value(self.factory.message_name, 'message')#, 'from') self.min = self.factory.min if len(self.factory.min_name) > 0: self.sync_value(self.factory.min_name, 'min')#, 'from') self.max = self.factory.max if len(self.factory.max_name) > 0: self.sync_value(self.factory.max_name, 'max')#, 'from') self.show_time = self.factory.show_time if len(self.factory.show_time_name) > 0: self.sync_value(self.factory.show_time_name, 'show_time')#, 'from') self.control = self._create_control(parent) self.can_cancel = self.factory.can_cancel if len(self.factory.can_cancel_name) > 0: self.sync_value(self.factory.can_cancel_name, 'can_cancel')#, 'from') self.set_tooltip() self.reset() can_cancel = Bool(False) from traits.api import Any buttons = Any @on_trait_change('can_cancel') def set_buttons_visible(self, value): self.buttons.setVisible(value) def _create_control(self, parent): layout = QVBoxLayout() if len(self.title) > 0: self._title = QLabel('<B>%s</B>' % self.title) layout.addWidget(self._title) if not self.factory.prefix_message and len(self.message) > 0: self._message = QLabel(self.message) # self._message = QTextEdit(self.message) # self._message.setReadOnly(True) layout.addWidget(self._message) self._progress_bar = QProgressBar() self._progress_bar.setRange(self.min, self.max) if not self.factory.can_cancel: layout.addWidget(self._progress_bar) else: self.buttons = QDialogButtonBox() self.buttons.addButton(u'Cancel', QDialogButtonBox.RejectRole) self.buttons.connect(self.buttons, SIGNAL('rejected()'), self.factory.cancelled) grid_layout = QGridLayout() grid_layout.addWidget(self._progress_bar, 0, 0) grid_layout.setColumnStretch(0, 1) grid_layout.addWidget(self.buttons, 0, 1) layout.addLayout(grid_layout) if self.factory.show_time: elapsed_label = QLabel('Elapsed time:') estimated_label = QLabel('Estimated time:') remaining_label = QLabel('Remaining time:') self._elapsed_control = QLabel('unknown') self._estimated_control = QLabel('unknown') self._remaining_control = QLabel('unknown') grid_layout = QGridLayout() grid_layout.addWidget(elapsed_label, 0, 0) grid_layout.addWidget(self._elapsed_control, 0, 1) grid_layout.addWidget(estimated_label, 1, 0) grid_layout.addWidget(self._estimated_control, 1, 1) grid_layout.addWidget(remaining_label, 2, 0) grid_layout.addWidget(self._remaining_control, 2, 1) grid_layout.setColumnStretch(1, 1) self._time_widget = QWidget() self._time_widget.setLayout(grid_layout) layout.addWidget(self._time_widget) if self.factory.show_text: self.set_text() else: self._progress_bar.setTextVisible(False) widget = QWidget() widget.setLayout(layout) return widget def set_text(self): if self._progress_bar is None: return if self.factory.prefix_message: format = self.message + ' ' else: format = '' if self.factory.show_value: format += '%v' if self.factory.show_max: if self.factory.show_value: format += '/' format += '%m' if self.factory.show_value or self.factory.show_max: format += ' ' if self.factory.show_percent: if self.factory.show_value or self.factory.show_max: format += '(%p%)' else: format += '%p%' self._progress_bar.setFormat(format) def _set_time_label(self, value, control): hours = value / 3600 minutes = (value % 3600) / 60 seconds = value % 60 label = "%1u:%02u:%02u" % (hours, minutes, seconds) control.setText(control.text()[:-7] + label) def update_editor(self): if self.factory.min <= self.value <= self.factory.max: if self.value == self.factory.min: self.reset() self._progress_bar.setValue(self.value) if self.factory.show_time: if (self.factory.max != self.factory.min): percent = (float(self.value) - self.factory.min) / (self.factory.max - self.factory.min) # if float(<undefined>) raises an error here then probably the # name of the trait this is editing is mispelled or owned by # the handler, e.g. 'handler.progress' instead of 'progress'. else: percent = 1.0 if self.factory.show_time and (percent != 0): current_time = time.time() elapsed = current_time - self._start_time estimated = elapsed / percent remaining = estimated - elapsed self._set_time_label(elapsed, self._elapsed_control) self._set_time_label(estimated, self._estimated_control) self._set_time_label(remaining, self._remaining_control) def reset(self): self._progress_bar.reset() self._start_time = time.time()
class OWDatabasesPack(OWWidget.OWWidget): settingsList = ["fileslist", "downloadurl", "downloadmessage"] def __init__(self, parent=None, signalManager=None, title="Databases Pack"): super(OWDatabasesPack, self).__init__(parent, signalManager, title, wantMainArea=False) self.fileslist = [("Taxonomy", "ncbi_taxonomy.tar.gz")] self.downloadurl = "https://dl.dropboxusercontent.com/u/100248799/sf_pack.tar.gz" self.downloadmessage = ( "Downloading a subset of available databases for a smoother " + "ride through the workshop" ) self.loadSettings() self._tmpfile = None self.reply = None # Locks held on server files self.locks = [] self.net_manager = QNetworkAccessManager() # Lock all files in files list so any other (in process) atempt to # download the files waits) box = OWGUI.widgetBox(self.controlArea, "Info") self.info = OWGUI.widgetLabel(box, self.downloadmessage) self.info.setWordWrap(True) box = OWGUI.widgetBox(self.controlArea, "Status") self.statusinfo = OWGUI.widgetLabel(box, "Please wait") self.statusinfo.setWordWrap(True) self.progressbar = QProgressBar() box.layout().addWidget(self.progressbar) self.setMinimumWidth(250) already_available = [ (domain, filename) for domain in serverfiles.listdomains() for filename in serverfiles.listfiles(domain) ] if set(self.fileslist) <= set(already_available): # All files are already downloaded self.statusinfo.setText("All files already available") self.setStatusMessage("Done") else: for domain, filename in self.fileslist + [("_tmp_cache_", "pack")]: manager = serverfiles._lock_file(domain, filename) try: manager.__enter__() except Exception: warnings.warn("Could not acquire lock for {0} {0}".format(domain, filename)) self.warning(0, "...") else: self.locks.append(manager) QTimer.singleShot(0, self.show) QTimer.singleShot(0, self.run) def run(self): self.setStatusMessage("Downloading") self.info.setText(self.downloadmessage) target_dir = os.path.join(environ.buffer_dir, "serverfiles_pack_cache") try: os.makedirs(target_dir) except OSError: pass self.progressBarInit() req = QNetworkRequest(QUrl(self.downloadurl)) self.reply = self.net_manager.get(req) self.reply.downloadProgress.connect(self._progress) self.reply.error.connect(self._error) self.reply.finished.connect(self._finished) self.reply.readyRead.connect(self._read) url = urlparse.urlsplit(self.downloadurl) self._tmpfilename = posixpath.basename(url.path) self._tmpfile = open(os.path.join(target_dir, self._tmpfilename), "wb+") def _progress(self, rec, total): if rec == 0 and total == 0: self.progressbar.setRange(0, 0) self.progressBarValue = 1.0 else: self.progressbar.setRange(0, total) self.progressbar.setValue(rec) self.progressBarValue = 100.0 * rec / (total or 1) def _error(self, error): self.statusinfo.setText(u"Error: {0}".format(error.errorString())) self.error(0, "Error: {0}".format(error.errorString())) self.setStatusMessage("Error") self._releaselocks() self._removetmp() self.progressBarFinshed() def _read(self): contents = str(self.reply.readAll()) self._tmpfile.write(contents) def _finished(self): self.progressbar.reset() if self.reply.error() != QNetworkReply.NoError: self._releaselocks() self._removetmp() return self.statusinfo.setText("Extracting") self.setStatusMessage("Extracting") try: self._extract() except Exception: # Permission errors, ... ?? pass self.statusinfo.setText("Done") self.setStatusMessage("Done") self.progressbar.reset() self.progressBarFinished() self._releaselocks() self._removetmp() def _extract(self): self._tmpfile.seek(0, 0) archive = tarfile.open(fileobj=self._tmpfile) target_dir = serverfiles.localpath() archive.extractall(target_dir) def onDeleteWidget(self): super(OWDatabasesPack, self).onDeleteWidget() self._releaselocks() if self.reply is not None and self.reply.isOpen(): self.reply.finished.disconnect(self._finished) self.reply.error.disconnect(self._error) self.reply.close() if self._tmpfile is not None: self._tmpfile.close() self._tmpfile = None def _releaselocks(self): for lock in self.locks: lock.__exit__(None, None, None) self.locks = [] def _removetmp(self): if self._tmpfile is not None: self._tmpfile.close() self._tmpfile = None tmp_file = os.path.join(environ.buffer_dir, "serverfiles_pack_cache", self._tmpfilename) os.remove(tmp_file)
class CANMonitor(QMainWindow): def __init__(self, app, test, parent): QMainWindow.__init__(self, parent) self.app = app self.lcdDict=dict() self.logBuffer=deque("", 1000) self.logBuffer2=dict() self.logBufferInit=dict() self.logEntryCount=1 self.maxLogEntryCount=65353 self.tableUpdateCouter=0 self.logFile=None self.test=test self.logFileName="/tmp/candash.log" self.connectCANEnable=True self.connectGPSEnable=True self.replayMode=False self.canIdList=[0x621, 0x353, 0x351, 0x635, 0x271, 0x371, 0x623, 0x571, 0x3e5, 0x591, 0x5d1] self.updateGPSThread=None self.config=Config("candash.cfg") self.log=Log(False) font = self.font() font.setPointSize(14) self.setFont(font) self.ampelGruen=QIcon("images/ampel-gruen.png") self.ampelRot=QIcon("images/ampel-rot.png") self.ampelGelb=QIcon("images/ampel-gelb.png") self.lastCANState=canStoppedState self.lastGPSState=gpsStoppedState self.canDecoder=CANDecoder(self) self.initUI(parent) def clearAllLCD(self): for lcdList in self.lcdDict.values(): for lcd in lcdList: if lcd.mode()==QLCDNumber.Bin: lcd.display("00000000") else: lcd.display(0) self.rpmGauge.setValue(0) self.velGauge.setValue(0) def createLCD(self, mode): lcd = QLCDNumber(self) lcd.setMode(mode) lcd.setMinimumHeight(50) lcd.setMinimumWidth(160) lcd.setDigitCount(8) if mode==QLCDNumber.Bin: lcd.display("00000000") else: lcd.display(0) lcd.setSegmentStyle(QLCDNumber.Flat) lcd.setAutoFillBackground(True) palette = lcd.palette() palette.setColor(QPalette.Normal, QPalette.Foreground, Qt.blue) palette.setColor(QPalette.Normal, QPalette.Background, Qt.lightGray) lcd.setPalette(palette); return lcd def addLCD(self, lcd, canId, subItem): if not hex(canId)+":"+subItem in self.lcdDict.keys(): lcdItemList=list() lcdItemList.append(lcd) self.lcdDict[hex(canId)+":"+subItem]=lcdItemList else: self.lcdDict[hex(canId)+":"+subItem].append(lcd) def createCANIdValueEntry(self, vbox, canId, subId, mode): lcd = self.createLCD(mode) self.addLCD(lcd, canId, subId) vbox.addWidget(lcd) def createCANIdEntry(self, form, canId, subId, label, mode): # hbox = QHBoxLayout() lbl = QLabel(self) lbl.setMinimumHeight(50) font = lbl.font() font.setPointSize(14) lbl.setFont(font) if label!=None: lbl.setText(label) # hbox.addWidget(lbl) lcd = self.createLCD(mode) # hbox.addWidget(lcd) form.addRow(lbl, lcd) self.addLCD(lcd, canId, subId) # vbox.addLayout(hbox) def createCANIdEntrySingleLine(self, form, canId, subIdList, label, mode): # hbox = QHBoxLayout() lbl = QLabel(self) lbl.setMinimumHeight(50) font = lbl.font() font.setPointSize(14) lbl.setFont(font) if label!=None: lbl.setText(label) # hbox.addWidget(lbl) hbox2=QHBoxLayout(); for i in range(len(subIdList)): subId=subIdList[i] lcd = self.createLCD(mode) hbox2.addWidget(lcd) self.addLCD(lcd, canId, subId) form.addRow(lbl, hbox2) # hbox.addLayout(hbox2) # vbox.addLayout(hbox) def createLogView(self, vbox): self.logView=QTableView(self) vbox.addWidget(self.logView) self.logViewModel=CANLogViewTableModel(self.logBuffer, self.canIdList, self) self.logView.setModel(self.logViewModel) header=QHeaderView(Qt.Horizontal, self.logView) header.setStretchLastSection(True) header.setResizeMode(NUMBER_COL, QHeaderView.Fixed) header.setResizeMode(TIME_COL, QHeaderView.Fixed) header.setResizeMode(ID_COL, QHeaderView.Fixed) self.logView.setHorizontalHeader(header) self.logView.setColumnWidth(NUMBER_COL, 80) self.logView.setColumnWidth(TIME_COL, 150) self.logView.setColumnWidth(ID_COL, 80) self.logView.setColumnWidth(SIZE_COL, 50) def createLogView2(self, vbox): self.logView2=QTableView(self) vbox.addWidget(self.logView2) self.logViewModel2=CANLogViewTableModel2(self.canIdList, self) self.logView2.setModel(self.logViewModel2) # self.logView2.setSortingEnabled(True) header=QHeaderView(Qt.Horizontal, self.logView2) # header.setStretchLastSection(True) # header.setClickable(True) header.setResizeMode(0, QHeaderView.Fixed) header.setResizeMode(1, QHeaderView.Fixed) self.logView2.setHorizontalHeader(header) self.logView2.setColumnWidth(0, 80) self.logView2.setColumnWidth(1, 50) for i in range(2, 10): self.logView2.setColumnWidth(i, 60) def initUI(self, parent): # exitAction = QAction(QIcon(), 'Exit', self) # exitAction.setShortcut('Ctrl+Q') # exitAction.setStatusTip('Exit application') # exitAction.triggered.connectToCAN(self.close) self.statusbar=self.statusBar() self.progress=QProgressBar() self.statusbar.addPermanentWidget(self.progress) # menubar = self.menuBar() # fileMenu = menubar.addMenu('&File') # fileMenu.addAction(exitAction) # toolbar = self.addToolBar('Exit') ## toolbar.addAction(exitAction) # # self.connectedIcon=QIcon("images/network-connect.png") # self.disconnectIcon=QIcon("images/network-disconnect.png") # self.connectAction = QAction(self.disconnectIcon , 'Connect', self) # self.connectAction.triggered.connect(self._connect1) # self.connectAction.setCheckable(True) # # toolbar.addAction(self.connectAction) # # self.gpsIcon=QIcon("images/icon_gps.gif") # self.reconnectGPSAction = QAction(self.gpsIcon, 'Reconnect GPS', self) # self.reconnectGPSAction.triggered.connect(self._reconnectGPS) # toolbar.addAction(self.reconnectGPSAction) mainWidget=QWidget(self) # mainWidget.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) mainWidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.setCentralWidget(mainWidget) top=QVBoxLayout(mainWidget) self.tabs = MyTabWidget(self) top.addWidget(self.tabs) mainTab = QWidget(self) miscTab = QWidget(self) misc2Tab=QWidget(self) canLogTab=QWidget(self) dashTab=QWidget(self) gpsTab=QWidget(self) osmTab=QWidget(self) debugLogTab=QWidget(self) mainTabLayout = QFormLayout(mainTab) miscTabLayout = QFormLayout(miscTab) misc2TabLayout = QFormLayout(misc2Tab) canLogTabLayout = QVBoxLayout(canLogTab) dashTabLayout = QHBoxLayout(dashTab) gpsTabLayout=QHBoxLayout(gpsTab) osmTabLayout=QVBoxLayout(osmTab) debugLogTabLayout=QVBoxLayout(debugLogTab) self.tabs.addTab(dashTab, "Dash") self.tabs.addTab(mainTab, "Main") self.tabs.addTab(miscTab, "Misc 1") self.tabs.addTab(misc2Tab, "Misc 2") self.tabs.addTab(canLogTab, "CAN") self.tabs.addTab(gpsTab, "GPS") self.tabs.addTab(osmTab, "OSM") self.tabs.addTab(debugLogTab, "Log") self.tabs.setCurrentIndex(0) self.debugLogWidget=DebugLogWidget(self) debugLogTabLayout.setAlignment(Qt.AlignLeft|Qt.AlignTop) self.debugLogWidget.addToWidget(debugLogTabLayout) # self.debugLogWidget.addLine("test") self.createCANIdEntry(mainTabLayout, 0x353, "0", "Drehzahl", QLCDNumber.Dec) # self.createCANIdEntry(mainTabLayout, 0x353, "1", "Öltemperatur", QLCDNumber.Dec) self.createCANIdEntry(mainTabLayout, 0x353, "1", "Kuehlwasser Temperatur", QLCDNumber.Dec) self.createCANIdEntry(mainTabLayout, 0x351, "0", "Geschwindigkeit", QLCDNumber.Dec) self.createCANIdEntry(mainTabLayout, 0x351, "1", "Außentemperatur", QLCDNumber.Dec) self.createCANIdEntry(miscTabLayout, 0x635, "0", "Licht, Klemme 58d", QLCDNumber.Dec) self.createCANIdEntry(miscTabLayout, 0x271, "0", "Zuendung", QLCDNumber.Dec) self.createCANIdEntrySingleLine(miscTabLayout, 0x371, ["0", "1"], "Tuerstatus", QLCDNumber.Bin) self.createCANIdEntry(miscTabLayout, 0x371, "2", "Blinker, Retoursgang", QLCDNumber.Bin) self.createCANIdEntrySingleLine(mainTabLayout, 0x623, ["0", "1", "2"], "Uhrzeit (Stunden)", QLCDNumber.Dec) self.createCANIdEntry(mainTabLayout, 0x571, "0", "Batteriespannung", QLCDNumber.Dec) self.createCANIdEntry(misc2TabLayout, 0x3e5, "0", "Zuheizer 1", QLCDNumber.Bin) self.createCANIdEntry(misc2TabLayout, 0x3e5, "1", "Zuheizer 2", QLCDNumber.Bin) self.createCANIdEntry(misc2TabLayout, 0x3e5, "2", "Zuheizer 3", QLCDNumber.Bin) self.createCANIdEntry(misc2TabLayout, 0x3e5, "3", "Zuheizer 4", QLCDNumber.Bin) self.createCANIdEntry(misc2TabLayout, 0x3e5, "4", "Zuheizer 5", QLCDNumber.Bin) self.createCANIdEntry(miscTabLayout, 0x3e5, "5", "Zuheizer", QLCDNumber.Dec) self.createCANIdEntry(miscTabLayout, 0x591, "0", "ZV", QLCDNumber.Dec) self.createCANIdEntry(miscTabLayout, 0x5d1, "0", "Scheibenwischer", QLCDNumber.Dec) self.createCANIdEntrySingleLine(mainTabLayout, 0x351, ["2", "3"], "Wegstreckenimpuls", QLCDNumber.Dec) self.createCANIdEntrySingleLine(misc2TabLayout, 0x621, ["0"], "0x621", QLCDNumber.Bin) self.createCANIdEntrySingleLine(misc2TabLayout, 0x621, ["1", "2"], "0x621", QLCDNumber.Dec) logTabs = MyTabWidget(self) canLogTabLayout.addWidget(logTabs) logTabWidget1=QWidget() logTabWidget2=QWidget() logTabs.addTab(logTabWidget1, "Time") logTabs.addTab(logTabWidget2, "Change") logTab1Layout = QVBoxLayout(logTabWidget1) self.createLogView(logTab1Layout) self.logViewFilerBox1=CANFilterBox(False, self) self.logViewFilerBox1.addFilterBox(logTab1Layout) self.logViewTableBox1=CANLogTableBox(self.logViewModel, self.logBuffer, None, self) self.logViewTableBox1.addTableBox(logTab1Layout) logTab2Layout = QVBoxLayout(logTabWidget2) self.createLogView2(logTab2Layout) self.logViewFilterBox2=CANFilterBox(True, self) self.logViewFilterBox2.addFilterBox(logTab2Layout) self.logViewTableBox2=CANLogTableBox(self.logViewModel2, self.logBuffer2, self.logBufferInit, self) self.logViewTableBox2.addTableBox(logTab2Layout) logButtonBox = QHBoxLayout() canLogTabLayout.addLayout(logButtonBox) self.logFileButton=QCheckBox("Log to File", self) self.logFileButton.setToolTip('Enable file logging') self.logFileButton.resize(self.logFileButton.sizeHint()) self.logFileButton.setDisabled(self.replayMode==True) self.logFileButton.clicked.connect(self._enableLogFile) logButtonBox.addWidget(self.logFileButton) self.clearLogButton = QPushButton('Clear Log', self) self.clearLogButton.resize(self.clearLogButton.sizeHint()) self.clearLogButton.setDisabled(not self.logFileAvailable() or self.replayMode==True) self.clearLogButton.clicked.connect(self._clearLogFile) logButtonBox.addWidget(self.clearLogButton) self.replayButton = QPushButton('Replay Log', self) self.replayButton.resize(self.replayButton.sizeHint()) self.replayButton.setDisabled(not self.logFileAvailable() or self.connectCANEnable==True) self.replayButton.clicked.connect(self._startReplayMode) logButtonBox.addWidget(self.replayButton) self.stopReplayButton = QPushButton('Stop Replay', self) self.stopReplayButton.resize(self.stopReplayButton.sizeHint()) self.stopReplayButton.setDisabled(self.replayMode==False) self.stopReplayButton.clicked.connect(self._stopReplayMode) logButtonBox.addWidget(self.stopReplayButton) velBox = QVBoxLayout() dashTabLayout.addLayout(velBox) dashTabLayout.setAlignment(Qt.AlignCenter|Qt.AlignBottom) self.velGauge=QtPngDialGauge(self, "tacho3", "tacho31.png") self.velGauge.setMinimum(20) self.velGauge.setMaximum(220) self.velGauge.setStartAngle(135) self.velGauge.setValue(0) # self.velGauge.setMaximumSize(400, 400) velBox.addWidget(self.velGauge) self.createCANIdValueEntry(velBox, 0x351, "0", QLCDNumber.Dec) valuesBox = QVBoxLayout() valuesBox.setAlignment(Qt.AlignCenter|Qt.AlignBottom) dashTabLayout.addLayout(valuesBox) self.createCANIdValueEntry(valuesBox, 0x353, "1", QLCDNumber.Dec) self.createCANIdValueEntry(valuesBox, 0x351, "1", QLCDNumber.Dec) self.createCANIdValueEntry(valuesBox, 0x571, "0", QLCDNumber.Dec) rpmBox = QVBoxLayout() dashTabLayout.addLayout(rpmBox) # rpmBox.setAlignment(Qt.AlignCenter|Qt.AlignHCenter) self.rpmGauge=QtPngDialGauge(self, "rpm", "rpm1.png") self.rpmGauge.setMinimum(0) self.rpmGauge.setMaximum(8000) self.rpmGauge.setStartAngle(125) self.rpmGauge.setValue(2500) # self.rpmGauge.setMaximumSize(400, 400) rpmBox.addWidget(self.rpmGauge) self.createCANIdValueEntry(rpmBox, 0x353, "0", QLCDNumber.Dec) self.gpsBox=GPSMonitor(self) gpsTabLayout.setAlignment(Qt.AlignLeft|Qt.AlignTop) self.gpsBox.loadConfig(self.config) self.gpsBox.addToWidget(gpsTabLayout) self.osmWidget=OSMWidget(self) osmTabLayout.setAlignment(Qt.AlignLeft|Qt.AlignTop) self.osmWidget.addToWidget(osmTabLayout) self.osmWidget.loadConfig(self.config) self.osmWidget.initWorkers() self.connect(self.osmWidget.mapWidgetQt, SIGNAL("updateStatus(QString)"), self.updateStatusBarLabel) self.connect(self.osmWidget.downloadThread, SIGNAL("updateStatus(QString)"), self.updateStatusBarLabel) self.connect(self.osmWidget, SIGNAL("updateStatus(QString)"), self.updateStatusBarLabel) self.connect(self.osmWidget, SIGNAL("startProgress()"), self.startProgress) self.connect(self.osmWidget, SIGNAL("stopProgress()"), self.stopProgress) self.osmWidget.initHome() connectBox=QHBoxLayout() top.addLayout(connectBox) section="connect" self.connectCANButton = QCheckBox('CAN Connect', self) self.connectCANButton.clicked.connect(self._connectCAN) self.connectCANEnable=self.config.getSection(section).getboolean("CANconnect", False) self.connectCANButton.setChecked(self.connectCANEnable) self.connectCANButton.setIcon(self.ampelRot) connectBox.addWidget(self.connectCANButton) self.connectGPSButton = QCheckBox('GPS Connect', self) self.connectGPSButton.clicked.connect(self._connectGPS) self.connectGPSEnable=self.config.getSection(section).getboolean("GPSconnect", False) self.connectGPSButton.setChecked(self.connectGPSEnable) self.connectGPSButton.setIcon(self.ampelRot) connectBox.addWidget(self.connectGPSButton) self.setGeometry(0, 0, 900, 600) self.setWindowTitle("candash") self.show() self.updateCANThread = CANSocketWorker(self) self.connect(self.updateCANThread, SIGNAL("updateStatus(QString)"), self.updateStatusBarLabel) self.connect(self.updateCANThread, SIGNAL("updateCANThreadState(QString)"), self.updateCANThreadState) self.connect(self.updateCANThread, SIGNAL("clearAllLCD()"), self.clearAllLCD) self.connect(self.updateCANThread, SIGNAL("connectCANFailed()"), self.connectCANFailed) self.connect(self.updateCANThread, SIGNAL("replayModeDone()"), self.replayModeDone) self.connect(self.updateCANThread, SIGNAL("processCANData(PyQt_PyObject)"), self.canDecoder.scan_can_frame) self._connectCAN() self.updateGPSThread=getGPSUpdateThread(self) if self.updateGPSThread!=None: self.connect(self.updateGPSThread, SIGNAL("updateStatus(QString)"), self.updateStatusBarLabel) self.connect(self.updateGPSThread, SIGNAL("updateGPSThreadState(QString)"), self.updateGPSThreadState) self.connect(self.updateGPSThread, SIGNAL("connectGPSFailed()"), self.connectGPSFailed) self.connect(self.updateGPSThread, SIGNAL("updateGPSDisplay(PyQt_PyObject)"), self.updateGPSDisplay) self._connectGPS() #def mousePressEvent(self, event): # print("CANMonitor mousePressEvent") # self.tabs.mousePressEvent(event) def getWidget(self, canId, subId): try: return self.lcdDict[hex(canId)+":"+subId] except KeyError: return list() def setValueDashDisplayRPM(self, value): if value!=self.rpmGauge.value(): self.rpmGauge.setValue(value) def setValueDashDisplayVel(self, value): if value!=self.velGauge.value(): self.velGauge.setValue(value) def displayIntValue(self, canId, subId, value, formatString): lcdList=self.getWidget(canId, subId) for lcdItem in lcdList: if lcdItem.intValue()!=int(value): lcdItem.display(formatString % value) def displayBinValue(self, canId, subId, value, formatString): lcdList=self.getWidget(canId, subId) for lcdItem in lcdList: if lcdItem.intValue()!=int(value): lcdItem.display(formatString % value) def displayFloatValue(self, canId, subId, value, formatString): lcdList=self.getWidget(canId, subId) for lcdItem in lcdList: if lcdItem.value()!=value: lcdItem.display(formatString % value) def addToLogView(self, line): tableEntry=[self.logEntryCount, self.createTimeStamp()] tableEntry.extend(line[0:]) if self.logFile!=None: self.addToLogFile(tableEntry) if self.logViewTableBox1.update==True: canId=line[0] if not self.logViewFilerBox1.matchFilter(canId, line, None): return self.logBuffer.appendleft(tableEntry) self.logEntryCount=self.logEntryCount+1 if self.logEntryCount==self.maxLogEntryCount: self.logEntryCount=1 self.tableUpdateCouter=self.tableUpdateCouter+1 if self.tableUpdateCouter==10: self.logViewModel.update(self.logBuffer, None) self.tableUpdateCouter=0 def addToLogView2(self, line): if self.logViewTableBox2.update==True: canId=line[0] if not canId in self.logBufferInit.keys(): self.logBufferInit[canId]=line if not self.logViewFilterBox2.matchFilter(canId, line, self.logBufferInit[canId]): try: del self.logBuffer2[canId] except KeyError: None else: self.logBuffer2[canId]=line self.logViewModel2.update(list(self.logBuffer2.values()), self.logBufferInit) def createTimeStamp(self): stamp=datetime.fromtimestamp(time.time()) return "".join(["%02d:%02d:%02d.%06d"%(stamp.hour, stamp.minute, stamp.second, stamp.microsecond)]) def dumpLogLine(self, line): logLine="%s %s %s"% (line[1], hex(line[2]), line[3]) dataLine="".join(["0x%02X " % x for x in line[4]]) return logLine+" "+dataLine def addToLogFile(self, line): if self.logFile!=None: self.logFile.write(self.dumpLogLine(line)+"\n") def setupLogFile(self, filePath): if self.logFile==None: self.logFile=open(filePath,"a") self.logFile.write("# Log started: "+time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.localtime())+"\n") self.logFile.write("# -------------------------------------------------------\n") def closeLogFile(self): if self.logFile!=None: self.logFile.write("# Log stoped: "+time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.localtime())+"\n") self.logFile.write("# -------------------------------------------------------\n") self.logFile.close() self.logFile=None def logFileAvailable(self): try: open(self.logFileName, "r") return True except IOError: return False def readLogFile(self): try: logFileEntries=list() readLogFile=open(self.logFileName, "r") while True: line=readLogFile.readline() if not line: break if line[0]=="#": continue lineParts=line.split(" ") strippedLen=len(lineParts)-1 neededLineParts=lineParts[1:strippedLen] addLen=8-len(neededLineParts[2:]) for _ in range(addLen): neededLineParts.append("%s" % "0x00") logFileEntries.append(struct.pack("IIBBBBBBBB", int(neededLineParts[0], 16), int(neededLineParts[1], 10), int(neededLineParts[2], 16), int(neededLineParts[3], 16),int(neededLineParts[4], 16),int(neededLineParts[5], 16),int(neededLineParts[6], 16), int(neededLineParts[7], 16), int(neededLineParts[8], 16), int(neededLineParts[9], 16))) return logFileEntries except IOError: return list() @pyqtSlot() def _enableFilter(self): self.filter=self.filterButton.isChecked() self.filterEdit.setDisabled(self.filter==False) self.applyFilterButton.setDisabled(self.filter==False) self.filterAll.setDisabled(self.filter==False) self.filterKnown.setDisabled(self.filter==False) self.filterUnknown.setDisabled(self.filter==False) self.filterRingIdsButton.setDisabled(self.filter==False) @pyqtSlot() def _applyFilter(self): if self.filter==True: self.filterValue=self.filterEdit.text() else: self.filterValue="" @pyqtSlot() def _cleanup(self): self.osmWidget._cleanup() self.gpsBox._cleanup() if self.updateCANThread.isRunning(): self.updateCANThread.stop() if self.updateGPSThread.isRunning(): self.updateGPSThread.stop() self.closeLogFile() section="connect" self.config.removeSection(section) self.config.addSection(section) self.config.getSection(section)["canConnect"]=str(self.connectCANEnable) self.config.getSection(section)["gpsConnect"]=str(self.connectGPSEnable) self.osmWidget.saveConfig(self.config) self.gpsBox.saveConfig(self.config) self.config.writeConfig() self.log.writeLogtoFile() @pyqtSlot() def _enableLogFile(self): if self.logFileButton.isChecked()==True: if self.logFile==None: self.setupLogFile(self.logFileName) else: self.closeLogFile() self.replayButton.setDisabled(not self.logFileAvailable() or self.connectCANEnable==True) self.clearLogButton.setDisabled(not self.logFileAvailable() or self.replayMode==True) @pyqtSlot() def _startReplayMode(self): self.closeLogFile() self.logFileButton.setChecked(False) self.replayButton.setDisabled(True) self.replayMode=True self.stopReplayButton.setDisabled(self.replayMode==False) self.logFileButton.setDisabled(self.replayMode==True) self.clearLogButton.setDisabled(not self.logFileAvailable() or self.replayMode==True) replayLines=self.readLogFile() self.logViewTableBox1._clearTable() self.logViewTableBox2._clearTable() self.updateCANThread.setup(self.app, self, self.test, True, replayLines) @pyqtSlot() def _stopReplayMode(self): self.updateCANThread.stop() self.replayModeDone() def replayModeDone(self): self.replayMode=False self.stopReplayButton.setDisabled(self.replayMode==False) self.replayButton.setDisabled(not self.logFileAvailable() or self.connectCANEnable==True) self.logFileButton.setDisabled(self.replayMode==True) self.clearLogButton.setDisabled(not self.logFileAvailable() or self.replayMode==True) @pyqtSlot() def _clearLogFile(self): if self.logFileAvailable(): self.closeLogFile() os.remove(self.logFileName) self.logFile=None self._enableLogFile() @pyqtSlot() def _connectCAN(self): self.connectCANEnable=self.connectCANButton.isChecked() if self.replayMode==True: self._stopReplayMode() if self.connectCANEnable==True: # self.connectCANButton.setDisabled(True) self.logViewTableBox1._clearTable() self.logViewTableBox2._clearTable() self.updateCANThread.setup(self.app, self.connectCANEnable, self.test, False, None) else: if self.updateCANThread.isRunning(): # self.connectCANButton.setDisabled(True) self.updateCANThread.stop() self.replayButton.setDisabled(not self.logFileAvailable() or self.connectCANEnable==True) self.connectCANButton.setChecked(self.connectCANEnable==True) @pyqtSlot() def _connectGPS(self): self.connectGPSEnable=self.connectGPSButton.isChecked() if self.connectGPSEnable==True: # self.connectGPSButton.setDisabled(True) self.updateGPSThread.setup(self.connectGPSEnable) else: if self.updateGPSThread.isRunning(): # self.connectGPSButton.setDisabled(True) self.updateGPSThread.stop() self.connectGPSButton.setChecked(self.connectGPSEnable==True) @pyqtSlot() def _enableFilterRingIds(self): self.filterRingIds=self.filterRingIdsButton.isChecked() def connectCANEnabled(self): return self.connectCANEnable def connectCANFailed(self): self.connectCANEnable=False self.connectCANButton.setChecked(False) # self.connectCANButton.setDisabled(False) self.connectCANButton.setIcon(self.ampelRot) def updateStatusBarLabel(self, text): self.statusbar.showMessage(text) logLine=self.log.addLineToLog(text) self.debugLogWidget.addLineToLog(logLine) def stopProgress(self): self.progress.setMinimum(0) self.progress.setMaximum(1) self.progress.reset() def startProgress(self): self.progress.setMinimum(0) self.progress.setMaximum(0) # def connectCANSuccessful(self): # self.connectCANEnable=True ## self.connectCANButton.setChecked(True) ## self.connectCANButton.setDisabled(False) # self.connectCANButton.setIcon(self.ampelGruen) # def stopCANSuccesful(self): # self.connectCANButton.setDisabled(False) # self.connectCANButton.setIcon(self.ampelRot) def canIdIsInKnownList(self, canId): return canId in self.canIdList def updateGPSDisplay(self, gpsData): self.gpsBox.update(gpsData) def showError(self, title, text): msgBox=QMessageBox(QMessageBox.Warning, title, text, QMessageBox.Ok, self) font = self.font() font.setPointSize(14) msgBox.setFont(font) msgBox.exec() def connectGPSFailed(self): self.connectGPSButton.setChecked(False) self.connectGPSEnable=False # self.connectGPSButton.setDisabled(False) self.connectGPSButton.setIcon(self.ampelRot) self.showError("GPS Error", "Error connecing to GPS") # def connectGPSSuccesful(self): ## self.connectGPSButton.setChecked(True) # self.connectGPSEnable=True ## self.connectGPSButton.setDisabled(False) # self.connectGPSButton.setIcon(self.ampelGruen) def connectGPSEnabled(self): return self.connectGPSEnable # def stopGPSSuccesful(self): # self.connectGPSButton.setDisabled(False) # self.connectGPSButton.setIcon(self.ampelRot) def updateCANThreadState(self, state): if state!=self.lastCANState: if state==canIdleState: self.connectCANButton.setIcon(self.ampelGelb) elif state==canRunState: self.connectCANButton.setIcon(self.ampelGruen) elif state==canStoppedState: self.connectCANButton.setIcon(self.ampelRot) self.connectCANEnable=False self.lastCANState=state def updateGPSThreadState(self, state): if state!=self.lastGPSState: if state==gpsRunState: self.connectGPSButton.setIcon(self.ampelGruen) elif state==gpsStoppedState: self.connectGPSButton.setIcon(self.ampelRot) self.connectGPSnable=False self.lastGPSState=state def hasOSMWidget(self): return self.osmWidget!=None
class HomeWidget(BaseWidget): def __init__(self, parent=0, *args, **kwargs): super(HomeWidget, self).__init__(parent=parent, *args, **kwargs) # inputs self._json_fpath = None self._destination_folder = self.guess_destination() self._from_date, self._to_date = self.guess_dates() self.nb_instances = 0 self.setup_ui() @property def json_fpath(self): return self._json_fpath @json_fpath.setter def json_fpath(self, value): self._json_fpath = value self.json_select_button.setText( value if value else "Sélection fichier JSON ODK Aggregate ...") self.update_start_button_state() @property def destination_folder(self): return self._destination_folder @destination_folder.setter def destination_folder(self, value): self._destination_folder = value self.destination_select_button.setText(self.destination_folder) self.update_start_button_state() @property def from_date(self): return self._from_date @from_date.setter def from_date(self, value): self._from_date = value self.from_date_selector.setPythonDate(self.from_date) self.update_start_button_state() @property def to_date(self): return self._to_date @to_date.setter def to_date(self, value): self._to_date = value self.to_date_selector.setPythonDate(self.to_date) self.update_start_button_state() def setup_ui(self): # json file selector self.json_groupbox = QGroupBox("Export ODK Aggregate") layout = QGridLayout() self.json_select_button = PushButton("", self) self.json_select_button.clicked.connect(self.json_selector_clicked) layout.addWidget(self.json_select_button, 1, 0) self.json_groupbox.setLayout(layout) # destination folder selector self.destination_groupbox = QGroupBox("Destination") layout = QGridLayout() self.destination_select_button = PushButton( self.destination_folder, self) self.destination_select_button.clicked.connect( self.destination_selector_clicked) layout.addWidget(self.destination_select_button, 1, 0) self.destination_groupbox.setLayout(layout) # period calendars today = datetime.date.today() self.period_groupbox = QGroupBox("Période") layout = QGridLayout() self.from_date_selector = DateTimeEdit(QDate(self.from_date)) self.from_date_selector.dateChanged.connect(self.from_date_changed) self.from_date_selector.setMaximumDate(self.to_date) self.to_date_selector = DateTimeEdit(QDate(self.to_date)) self.to_date_selector.dateChanged.connect(self.to_date_changed) self.to_date_selector.setMinimumDate(self.from_date) self.to_date_selector.setMaximumDate(today) layout.addWidget(Label("Du"), 2, 0) layout.addWidget(self.from_date_selector, 3, 0) layout.addWidget(Label("Au"), 2, 1) layout.addWidget(self.to_date_selector, 3, 1) self.period_groupbox.setLayout(layout) # start button self.start_button = PushButton("Démarrer") self.start_button.setEnabled(False) self.start_button.setDefault(True) self.start_button.clicked.connect(self.export_requested) # cancel button self.cancel_button = CancelPushButton("Annuler") self.cancel_button.setEnabled(False) self.cancel_button.clicked.connect(self.cancel_export) # grid self.gridBox = QGridLayout() self.gridBox.addWidget(self.json_groupbox, 0, 0, 1, 2) self.gridBox.addWidget(self.destination_groupbox, 1, 0, 1, 2) self.gridBox.addWidget(self.period_groupbox, 2, 0, 1, 2) self.gridBox.addWidget(self.start_button, 3, 0) self.gridBox.addWidget(self.cancel_button, 3, 1) vBox = QVBoxLayout() vBox.addLayout(self.gridBox) self.setLayout(vBox) self.json_fpath = None def guess_destination(self, root_only=False): start_folder = os.path.join(os.path.expanduser("~"), 'Desktop') if root_only: return start_folder return os.path.join(start_folder, Constants.DEFAULT_FOLDER_NAME) def guess_dates(self): to = datetime.date.today() start = to - datetime.timedelta(days=15) return start, to def update_start_button_state(self): self.start_button.setEnabled( all([self.json_fpath, self.destination_folder, self.from_date, self.to_date])) def json_selector_clicked(self): # self.file_name_field.setText("Aucun fichier") self.start_button.setEnabled(False) fpath = QFileDialog.getOpenFileName( self, "Choisir le fichier d'export JSON", self.json_fpath or self.guess_destination(root_only=True), "Fichiers JSON (*.json)") self.json_fpath = os.path.abspath(fpath) if fpath else self.json_fpath def destination_selector_clicked(self): path = QFileDialog.getExistingDirectory( self, "Sélectionner le dossier", self.destination_folder) if path: self.destination_folder = os.path.abspath(path) def from_date_changed(self, new_date): self.from_date = new_date.toPyDate() def to_date_changed(self, new_date): self.to_date = new_date.toPyDate() def export_requested(self): logger.debug("export_requested") self.parentWidget().exporter.check_aggregate_presence() def display_missing_aggregate_confirmation(self): if MissingODKConfirmationWidget().exec_() == QDialog.Accepted: self.start_export() def start_export(self): logger.debug("Lancement ...") self.add_progressbar() self.start_button.setEnabled(False) self.cancel_button.setEnabled(True) self.parentWidget().exporter.parse( destination_folder=self.destination_folder, fname=self.json_fpath, from_date=self.from_date, to_date=self.to_date) def cancel_export(self): logger.debug("cancel") self.parentWidget().exporter.cancel() def update_progress_label(self, index): progression_label = "Export en cours... {index}/{total}" \ .format(index=index, total=self.nb_instances) self.progression_groupbox.setTitle(progression_label) def add_progressbar(self): self.progressbar = QProgressBar() self.progressbar.setMinimum(0) self.progressbar.setMaximum(100) self.progressbar.reset() self.progressbar.setTextVisible(False) self.progression_groupbox = QGroupBox("...") progress_layout = QHBoxLayout() progress_layout.addWidget(self.progressbar) self.progression_groupbox.setLayout(progress_layout) self.gridBox.addWidget(self.progression_groupbox, 4, 0, 1, 2) def remove_progressbar(self): self.progression_groupbox.deleteLater() self.progression_groupbox = None @pyqtSlot(bool, int, str) def parsing_ended(self, succeeded, nb_instances, error_message): if succeeded: self.nb_instances = nb_instances @pyqtSlot(str, int) def exporting_instance(self, ident, index): logger.debug("exporting_instance") self.update_progress_label(index) @pyqtSlot(bool, int) def instance_completed(self, succeeded, index): logger.debug("instance_completed") pc = index * 100 // self.nb_instances self.progressbar.setValue(pc) @pyqtSlot(int, int, int, int) def export_ended(self, nb_instances_successful, nb_instances_failed, nb_medias_successful, nb_medias_failed): self.cancel_button.setEnabled(False) self.start_button.setEnabled(True) @pyqtSlot() def export_canceled(self): self.remove_progressbar() self.cancel_button.setEnabled(False) self.start_button.setEnabled(True) QMessageBox.warning(self, "Export annulé !", "L'export en cours a été annulé.\n" "Tous les fichiers créés ont été supprimés", QMessageBox.Ok, QMessageBox.NoButton) if self.parentWidget().is_exiting: self.parentWidget().do_close()
class FileItem(QTreeWidgetItem): cols = ['Filepath', 'Renamed Filepath'] invalid_char_map = {':': ''} re_filename_episodes = [ re.compile(r'.*[Ss](?P<season>\d{1,2})[\s\-_]*[Ee](?P<episode>\d{1,2}).*', re.I), re.compile(r'.*season\s*(?P<season>\d{1,2})[\s\-_]*episode\s*[Ee](?P<episode>\d{1,2}).*', re.I), ] re_filepath_episodes = [ re.compile(r'.*season\s*(?P<season>\d{1,2})\\(?:season\s*\d{1,2}[\s\-_]*)?(?:episode|ep\.?|e)\s*(?P<episode>\d{1,2}).*', re.I), ] def __init__(self, parent, filepath, mediaman): super(FileItem, self).__init__(parent) self._renamed = False self.mediaman = mediaman self.filepath = filepath self.renamed_filepath = None self.media_info = None self.episode_info = None self.thread = None self.pbar = None self.setText(0, self.filepath) self.send_thread() def send_thread(self): if self.thread is None: self.thread = FilepathSearchThread(self.treeWidget()) self.thread.finished.connect(self.receive_thread) thread_ready = self.thread.wait(10000) if thread_ready: tree = self.treeWidget() self.pbar = QProgressBar(tree) self.pbar.setRange(0, 0) tree.setItemWidget(self, 1, self.pbar) self.thread.filepath = self.filepath self.thread.start() def receive_thread(self): self.media_info = self.thread.result self.pbar.reset() tree = self.treeWidget() tree.removeItemWidget(self, 1) self.pbar.deleteLater() self.refresh() def _replace_invalid_chars(self, text): for k, v in FileItem.invalid_char_map.items(): if k in text: text = text.replace(k, v) return text def refresh(self): moviedir = self.mediaman.moviedir() tvdir = self.mediaman.tvdir() if self.media_info: title = self.media_info.get('title') title = self._replace_invalid_chars(title) year = self.media_info.get('year') filename, ext = os.path.splitext(os.path.basename(self.filepath)) if self.episode_info or self.media_info.get('episodes'): if not self.episode_info: episodes = self.media_info['episodes'] ep_match = None for reg in FileItem.re_filename_episodes: m = reg.search(filename) if m: ep_match = m.groupdict() break if not ep_match: for reg in FileItem.re_filepath_episodes: m = reg.search(self.filepath) if m: ep_match = m.groupdict() if ep_match: season, episode = int(ep_match['season']), int(ep_match['episode']) for episode_info in episodes: if (episode_info['season'], episode_info['episode']) == (season, episode): self.episode_info = episode_info break if self.episode_info: self.renamed_filepath = ur'{tvdir}\{title}\Season{season:02d}\{title}.S{season:02d}E{episode:02d}.{episode_title}{ext}'.format( tvdir=tvdir, title=title, year=year, season=self.episode_info['season'] , episode=self.episode_info['episode'], episode_title=self._replace_invalid_chars(self.episode_info['title']), ext=ext, ) else: self.renamed_filepath = ur'{tvdir}\{title}\Season00\{title}.S00E00.XXX{ext}'.format( tvdir=tvdir, title=title, year=year, ext=ext, ) else: self.renamed_filepath = ur'{moviedir}\{title} ({year})\{title}{ext}'.format( moviedir=moviedir, title=title, year=year, ext=ext ) self.setText(1, self.renamed_filepath) if os.path.exists(self.renamed_filepath): self.setBackgroundColor(FileItem.cols.index('Renamed Filepath'), QColor(0, 255, 0, 100))