class STMainWindow(QMainWindow): def __init__(self): super(STMainWindow, self).__init__() self.createActions() self.createMenus() layout = QVBoxLayout() self.tabs = QTabWidget() self.tabs.setTabPosition(QTabWidget.West) self.tabs.currentChanged[int].connect(self.tabChanged) self.sessiontab = sweattrails.qt.sessiontab.SessionTab(self) self.tabs.addTab(self.sessiontab, "Sessions") self.tabs.addTab(sweattrails.qt.fitnesstab.FitnessTab(self), "Fitness") self.tabs.addTab(sweattrails.qt.profiletab.ProfileTab(self), "Profile") self.usertab = sweattrails.qt.usertab.UserTab(self) self.tabs.addTab(self.usertab, "Users") self.usertab.hide() layout.addWidget(self.tabs) w = QWidget(self) w.setLayout(layout) self.setCentralWidget(w) self.statusmessage = QLabel() self.statusmessage.setMinimumWidth(200) self.statusBar().addPermanentWidget(self.statusmessage) self.progressbar = QProgressBar() self.progressbar.setMinimumWidth(100) self.progressbar.setMinimum(0) self.progressbar.setMaximum(100) self.statusBar().addPermanentWidget(self.progressbar) self.setWindowTitle("SweatTrails") self.setWindowIconText("SweatTrails") icon = QPixmap("image/sweatdrops.png") self.setWindowIcon(QIcon(icon)) QCoreApplication.instance().refresh.connect(self.userSet) def createActions(self): self.switchUserAct = QAction("&Switch User", self, shortcut = "Ctrl+U", statusTip = "Switch User", triggered = self.switch_user) self.importFileAct = QAction("&Import", self, shortcut = "Ctrl+I", statusTip = "Import Session", triggered = self.file_import) self.downloadAct = QAction("&Download", self, shortcut = "Ctrl+D", statusTip = "Download activities from device", triggered = QCoreApplication.instance().download) self.downloadAct = QAction("&Withings", self, statusTip = "Download Withings data", triggered = QCoreApplication.instance().withings) self.exitAct = QAction("E&xit", self, shortcut = "Ctrl+Q", statusTip = "Exit SweatTrails", triggered = self.close) self.aboutAct = QAction("&About", self, triggered = self.about) self.aboutQtAct = QAction("About &Qt", self, triggered = QApplication.aboutQt) def createMenus(self): self.fileMenu = self.menuBar().addMenu(self.tr("&File")) self.fileMenu.addAction(self.switchUserAct) self.fileMenu.addSeparator() self.fileMenu.addAction(self.importFileAct) self.fileMenu.addAction(self.downloadAct) self.fileMenu.addSeparator() self.fileMenu.addAction(self.exitAct) self.menuBar().addSeparator() self.helpMenu = self.menuBar().addMenu("&Help") self.helpMenu.addAction(self.aboutAct) self.helpMenu.addAction(self.aboutQtAct) def show(self): super(QMainWindow, self).show() if self.select_user(): t = sweattrails.qt.imports.BackgroundThread.get_thread() t.jobStarted.connect(self.status_message) t.jobFinished.connect(self.status_message) t.jobError.connect(self.status_message) else: self.close() def switch_user(self): pass def select_user(self): ret = False if QCoreApplication.instance().user: return True elif QCoreApplication.instance().has_users(): dialog = SelectUser(self) dialog.select() ret = QCoreApplication.instance().is_authenticated() if ret: self.refresh() else: dialog = CreateUser(self) dialog.exec_() ret = QCoreApplication.instance().is_authenticated() if ret: self.refresh() return ret # # FILE IMPORT # def file_import(self): (fileNames, _) = QFileDialog.getOpenFileNames(self, "Open Activity File", "", "Activity Files (*.tcx *.fit *.csv)") if fileNames: QCoreApplication.instance().import_files(*fileNames) def file_import_started(self, filename): self.switchUserAct.setEnabled(False) def file_imported(self, filename): self.switchUserAct.setEnabled(True) self.refresh() def file_import_error(self, filename, msg): self.switchUserAct.setEnabled(True) self.refresh() # # END FILE IMPORT # # ===================================================================== # S I G N A L H A N D L E R S # ===================================================================== def refresh(self): QCoreApplication.instance().refresh.emit() self.status_message("") def tabChanged(self, tabix): w = self.tabs.currentWidget() if hasattr(w, "activate"): w.activate(0) if hasattr(w, "setValues"): w.setValues() def setSession(self, session): self.tabs.setCurrentIndex(0) self.sessiontab.setSession(session) def setTab(self, tab): t = self.tabs.currentWidget() if t and hasattr(t, "setTab"): t.setTab(tab) def userSet(self): user = QCoreApplication.instance().user if user.is_admin(): self.usertab.show() def status_message(self, msg, *args): self.statusmessage.setText(msg.format(*args)) def progress_init(self, msg, *args): self.progressbar.setValue(0) self.status_message(msg, *args) def progress(self, percentage): self.progressbar.setValue(percentage) def progress_done(self): self.progressbar.reset() def about(self): QMessageBox.about(self, "About SweatTrails", "SweatTrails is a training log application")
class MyMDIApp(QMainWindow): def __init__(self): super(MyMDIApp, self).__init__() self.setGeometry(50, 40, 1200, 960) # self.setAcceptDrops(True) self.filelist = [] # ==================================== # UIの生成 # ==================================== # :: MDIワークスペースのインスタンス化 self.workspace = QWorkspace() self.workspace.setWindowTitle("Simple WorkSpace Exmaple") # :: lite_listview self.lite_listview = LiteListView() self.lite_listview.addItems(["abc", "def", "ghi", "jkl"]) self.lite_listview.resize(150, 300) self.workspace.addWindow(self.lite_listview) # :: ボタンs layout = QHBoxLayout() self.btn_ana = QPushButton("Analysis") self.btn_plot = QPushButton("Plot") self.btn_save = QPushButton("Save") layout.addWidget(self.btn_ana) layout.addWidget(self.btn_plot) layout.addWidget(self.btn_save) self.btn_wid = QWidget() self.btn_wid.setWindowTitle("Feauters Analysis") self.btn_wid.setLayout(layout) self.workspace.addWindow(self.btn_wid) # :: Figure.FigureImapctAndHarmonic # : FIH用FigureCanvasグラフオブジェクト self.fig_impandharm = FigureImapctAndHarmonic() self.fig_impandharm.setWindowTitle("Figure Impact & Harmonic") self.workspace.addWindow(self.fig_impandharm) self.fig_impandharm.close() # : 以下コントローラ layout2 = QHBoxLayout() self.fih_label = QLabel() self.fih_label.setText(self.tr("Impact & Harm")) self.fih_btn_ana = QPushButton("Analysis") self.fih_btn_plot = QPushButton("Plot") self.fih_btn_save = QPushButton("Save") layout2.addWidget(self.fih_label) layout2.addWidget(self.fih_btn_ana) layout2.addWidget(self.fih_btn_plot) layout2.addWidget(self.fih_btn_save) self.btn_wid2 = QWidget() self.btn_wid2.setWindowTitle("Imapct & Harm") self.btn_wid2.setLayout(layout2) self.workspace.addWindow(self.btn_wid2) self.fih_btn_ana.clicked.connect(self.run_FIH_analysis) self.fih_btn_plot.clicked.connect(self.run_FIH_plot) self.fih_btn_save.clicked.connect(self.fig_impandharm.save) # :: キャンバス self.sig_canvas = SignalDataCanvas() # self.sig_canvas.setWindowTitle(self.tr(u"特徴量グラフ")) self.sig_canvas.setWindowTitle(u"特徴量グラフ") self.workspace.addWindow(self.sig_canvas) self.sig_canvas.close() # :: ステータスバー self.myStatusBar = QStatusBar() self.setStatusBar(self.myStatusBar) self.myStatusBar.showMessage('Ready', 1000) # :: プログラスバー self.progressBar = QProgressBar() self.myStatusBar.addPermanentWidget(self.progressBar) self.progressBar.reset() self.progressBar.setVisible(False) self.progressBar.setValue(0) # :: MainWidgetに追加 # self.workspace.tile() self.setCentralWidget(self.workspace) self.setCSS() # ==================================== # シグナルスロットのコネクト # ==================================== # lite_listview self.lite_listview.fileDropped.connect(self.set_filelist) self.lite_listview.clicked.connect(self.file_selected) # Analysisボタンで解析実行 self.btn_ana.clicked.connect(self.run_analysis) self.btn_plot.clicked.connect(self.run_plot) # ***************************************************** # ファイルインプット用スロット # ***************************************************** @Slot() def file_selected(self, index): """listviewがクリックされたときの動作""" selected_stritem = index.data(Qt.DisplayRole) s = "Clicked[%d] : %r" % (index.row(), selected_stritem) self.flush(s) @Slot() def set_filelist(self, file_list): self.filelist = file_list self.flush("Load File List") # ***************************************************** # Window2: インパクト音&自由振動音解析用 # ***************************************************** @Slot() def run_FIH_analysis(self): """解析データの受け渡し用中間スロット""" self.flush("> (QThread) run analysis") self.p2 = thread_analysis_imp_and_hrm() self.p2.set_option(self.filelist) self.p2.progress.connect(self.progress) self.p2.start() @Slot() def run_FIH_plot(self): res = self.p2.get_result() self.fig_impandharm.plot(resuls=res) pass # ***************************************************** # Window1: 全データ統計解析用 # ***************************************************** @Slot() def run_analysis(self): """特徴量算出アルゴリズム実行""" self.flush("> (QThread) run analysis") self.p = thProcess() self.p.set_option(self.filelist) self.p.progress.connect(self.progress) self.p.start() @Slot() def run_plot(self): """特徴量マップの表示""" # ; データ取得 # res = array(self.result) res = array(self.p.get_result()) # : canvas = BubleChartCanvas() self.workspace.addWindow(canvas) canvas.show() # グラフプロット names = [os.path.basename(p) for p in self.filelist] canvas.bubbleplot(xdata=res[:, 0], data=res[:, 1], names=names) canvas.set_xlabel("Katasa").set_ylabel("Matomari").canvas_update() # ***************************************************** # 共通関数 # ***************************************************** def flush(self, s, ms=1): maxlen = 50 if len(s) > maxlen: s = s[:10] + "..." + s[-maxlen:] self.myStatusBar.showMessage(s, ms * 1000) def progress(self, value=None): if value < 0: self.progressBar.reset() self.progressBar.setVisible(False) if value >= 0: self.progressBar.setVisible(True) self.progressBar.setValue(value) def setCSS(self): """cssを読みこんでセット """ with open(CSS_PATH, "r") as f: self.setStyleSheet("".join(f.readlines()))
class CompareSequences(QMainWindow): def __init__(self): super(CompareSequences, self).__init__() self.setWindowTitle('Compare Sequences') self.setFixedHeight(125) self._ledit1 = QLineEdit() self._btn_pick_1 = QPushButton('...') self._ledit2 = QLineEdit() self._btn_pick_2 = QPushButton('...') self._btn_compare = QPushButton('Compare') self._progress_bar = QProgressBar(self.statusBar()) self._setup_ui() self._set_connections() def _compare(self): self._toggle_ui() src_one = self._ledit1.text() src_two = self._ledit2.text() # TODO: put in some checks for valid sequences if not (src_one and src_two): msg = 'Please pick proper sequences.' print msg nuke.message(msg) else: read1 = nuke.createNode('Read', inpanel=False) read1.knob('file').fromUserText(src_one) read2 = nuke.createNode('Read', inpanel=False) read2.knob('file').fromUserText(src_two) read2.setXYpos(read1.xpos() + 100, read1.ypos()) if not (read1.width() == read2.width() and read1.height() == read2.height()): msg = 'Sequences are not the same resolution.' print msg nuke.message(msg) else: # TODO: check for same resolution m = nuke.createNode('Merge2', inpanel=False) m.knob('operation').setValue(6) m.setXYpos(read1.xpos(), read2.ypos() + 100) m.setInput(0, read1) m.setInput(1, read2) c = nuke.createNode('CurveTool', inpanel=False) c.knob('operation').setValue(3) c.knob('ROI').fromDict( { 'x': 0, 'y': 0, 'r': read1.width(), 't': read1.height() } ) v = nuke.createNode('Viewer') check = False frame = None first = read1.knob('first').value() last = read1.knob('last').value() self._progress_bar.setRange(first, last) for i in range(first, last + 1): self._progress_bar.setValue(i) nuke.execute(c, i, i) data = c.knob('maxlumapixvalue').animations() check = False for curve in data: if not curve.constant(): check = True frame = i break if check: break if not check: msg = 'There is no difference.' else: msg = 'There is a difference at frame %d.' % frame nuke.message(msg) self._progress_bar.reset() self._toggle_ui() def _pick_sequence(self): btn = self.sender() le_btn_pair = { self._btn_pick_1: self._ledit1, self._btn_pick_2: self._ledit2 } clip_path = nuke.getClipname('Pick Sequence to compare') le_btn_pair[btn].setText(clip_path) def _set_connections(self): self._btn_pick_1.released.connect(self._pick_sequence) self._btn_pick_2.released.connect(self._pick_sequence) self._btn_compare.released.connect(self._compare) def _setup_ui(self): self._btn_pick_1.setFixedWidth(25) self._btn_pick_1.setToolTip('Pick first sequence.') self._btn_pick_2.setFixedWidth(25) self._btn_pick_2.setToolTip('Pick second sequence.') self._btn_compare.setToolTip('Compare sequences.') self._progress_bar.setFixedHeight(10) lyt_seq1 = QHBoxLayout() lyt_seq1.addWidget(self._ledit1) lyt_seq1.addWidget(self._btn_pick_1) lyt_seq2 = QHBoxLayout() lyt_seq2.addWidget(self._ledit2) lyt_seq2.addWidget(self._btn_pick_2) lyt_main = QVBoxLayout() lyt_main.addLayout(lyt_seq1) lyt_main.addLayout(lyt_seq2) lyt_main.addWidget(self._btn_compare) main_widget = QWidget() main_widget.setLayout(lyt_main) self.setCentralWidget(main_widget) def _toggle_ui(self): self._ledit1.setEnabled(not self._ledit1.isEnabled()) self._ledit2.setEnabled(not self._ledit2.isEnabled()) self._btn_pick_1.setEnabled(not self._btn_pick_1.isEnabled()) self._btn_pick_2.setEnabled(not self._btn_pick_2.isEnabled()) self._btn_compare.setEnabled(not self._btn_compare.isEnabled())
class TransferPanel(QWidget): ''' Transfer Panel This Panel is the main dialog box for the Dive Computer Transfer GUI ''' def __init__(self, parent=None): super(TransferPanel, self).__init__(parent) self._logbook = None self._logbookName = 'None' self._logbookPath = None self._createLayout() self._readSettings() self.setWindowTitle(self.tr('DC Transfer - %s') % self._logbookName) def _createLayout(self): 'Create the Widget Layout' self._txtLogbook = QLineEdit() self._txtLogbook.setReadOnly(True) self._lblLogbook = QLabel(self.tr('&Logbook File:')) self._lblLogbook.setBuddy(self._txtLogbook) self._btnBrowse = QPushButton('...') self._btnBrowse.clicked.connect(self._btnBrowseClicked) self._btnBrowse.setStyleSheet('QPushButton { min-width: 24px; max-width: 24px; }') self._btnBrowse.setToolTip(self.tr('Browse for a Logbook')) self._cbxComputer = QComboBox() self._lblComputer = QLabel(self.tr('Dive &Computer:')) self._lblComputer.setBuddy(self._cbxComputer) self._btnAddComputer = QPushButton(QPixmap(':/icons/list-add.png'), self.tr('')) self._btnAddComputer.setStyleSheet('QPushButton { min-width: 24px; min-height: 24; max-width: 24px; max-height: 24; }') self._btnAddComputer.clicked.connect(self._btnAddComputerClicked) self._btnRemoveComputer = QPushButton(QPixmap(':/icons/list-remove.png'), self.tr('')) self._btnRemoveComputer.setStyleSheet('QPushButton { min-width: 24px; min-height: 24; max-width: 24px; max-height: 24; }') self._btnRemoveComputer.clicked.connect(self._btnRemoveComputerClicked) hbox = QHBoxLayout() hbox.addWidget(self._btnAddComputer) hbox.addWidget(self._btnRemoveComputer) gbox = QGridLayout() gbox.addWidget(self._lblLogbook, 0, 0) gbox.addWidget(self._txtLogbook, 0, 1) gbox.addWidget(self._btnBrowse, 0, 2) gbox.addWidget(self._lblComputer, 1, 0) gbox.addWidget(self._cbxComputer, 1, 1) gbox.addLayout(hbox, 1, 2) gbox.setColumnStretch(1, 1) self._pbTransfer = QProgressBar() self._pbTransfer.reset() self._txtStatus = QTextEdit() self._txtStatus.setReadOnly(True) self._btnTransfer = QPushButton(self.tr('&Transfer Dives')) self._btnTransfer.clicked.connect(self._btnTransferClicked) self._btnExit = QPushButton(self.tr('E&xit')) self._btnExit.clicked.connect(self.close) hbox = QHBoxLayout() hbox.addWidget(self._btnTransfer) hbox.addStretch() hbox.addWidget(self._btnExit) vbox = QVBoxLayout() vbox.addLayout(gbox) vbox.addWidget(self._pbTransfer) vbox.addWidget(self._txtStatus) vbox.addLayout(hbox) self.setLayout(vbox) def _closeLogbook(self): 'Close the current Logbook' if self._logbook is None: return self._logbook = None self._logbookName = 'None' self._logbookPath = None self._txtLogbook.clear() self._cbxComputer.setModel(None) self._writeSettings() self.setWindowTitle(self.tr('DC Transfer - %s') % self._logbookName) def _openLogbook(self, path): 'Open an existing Logbook' if self._logbook is not None: self._closeLogbook() if not os.path.exists(path): QMessageBox.critical(self, self.tr('Missing Logbook'), self.tr('Logbook File "%s" does not exist.') % path) return #TODO: Handle a Schema Upgrade in a user-friendly manner self._logbook = Logbook(path, auto_update=False) self._logbookName = os.path.basename(path) self._logbookPath = path self._txtLogbook.setText(self._logbookPath) self._cbxComputer.setModel(DiveComputersModel(self._logbook)) self._writeSettings() self.setWindowTitle(self.tr('DC Transfer - %s') % self._logbookName) def _readSettings(self): 'Read main window settings from the configuration' settings = QSettings() settings.beginGroup('MainWindow') max = settings.value('max') size = settings.value('size') pos = settings.value('pos') file = settings.value('file') settings.endGroup() # Size and Position the Main Window if size is not None: self.resize(size) if pos is not None: self.move(pos) # HAX because QVariant is not exposed in PySide and the default # coercion to string is just stupid if max is not None and (max == 'true'): self.showMaximized() # Open the Logbook if file is not None: self._openLogbook(file) def _writeSettings(self): 'Write settings to the configuration' settings = QSettings() settings.beginGroup('MainWindow') settings.setValue('pos', self.pos()) settings.setValue('size', self.size()) settings.setValue('max', self.isMaximized()) settings.setValue('file', self._logbookPath) settings.endGroup() def closeEvent(self, e): 'Intercept an OnClose event' self._writeSettings() e.accept() #-------------------------------------------------------------------------- # Slots @QtCore.Slot() def _btnAddComputerClicked(self): 'Add a Dive Computer' dc = AddDiveComputerWizard.RunWizard(self) if dc is not None: self._logbook.session.add(dc) self._logbook.session.commit() self._cbxComputer.model().reload() self._cbxComputer.setCurrentIndex(self._cbxComputer.findText(dc.name)) @QtCore.Slot() def _btnRemoveComputerClicked(self): 'Remove a Dive Computer' idx = self._cbxComputer.currentIndex() dc = self._cbxComputer.itemData(idx, Qt.UserRole+0) if QMessageBox.question(self, self.tr('Delete Dive Computer?'), self.tr('Are you sure you want to delete "%s"?') % dc.name, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) == QMessageBox.Yes: self._logbook.session.delete(dc) self._logbook.session.commit() self._cbxComputer.model().reload() @QtCore.Slot() def _btnBrowseClicked(self): 'Browse for a Logbook File' if self._logbook is not None: dir = os.path.dirname(self._logbookPath) else: dir = os.path.expanduser('~') fn = QFileDialog.getOpenFileName(self, caption=self.tr('Select a Logbook file'), dir=dir, filter='Logbook Files (*.lbk);;All Files(*.*)')[0] if fn == '': return if not os.path.exists(fn): if QMessageBox.question(self, self.tr('Create new Logbook?'), self.tr('Logbook "%s" does not exist. Would you like to create it?') % os.path.basename(fn), QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) != QMessageBox.Yes: return Logbook.Create(fn) self._openLogbook(fn) @QtCore.Slot() def _btnTransferClicked(self): 'Transfer Dives' idx = self._cbxComputer.currentIndex() dc = self._cbxComputer.itemData(idx, Qt.UserRole+0) if self._logbook.session.dirty: print "Flushing dirty session" self._logbook.rollback() self._txtLogbook.setEnabled(False) self._btnBrowse.setEnabled(False) self._cbxComputer.setEnabled(False) self._btnAddComputer.setEnabled(False) self._btnRemoveComputer.setEnabled(False) self._btnTransfer.setEnabled(False) self._btnExit.setEnabled(False) self._txtStatus.clear() thread = QThread(self) #FIXME: ZOMG HAX: Garbage Collector will eat TransferWorker when moveToThread is called #NOTE: Qt.QueuedConnection is important... self.worker = None self.worker = TransferWorker(dc) thread.started.connect(self.worker.start, Qt.QueuedConnection) self.worker.moveToThread(thread) self.worker.finished.connect(self._transferFinished, Qt.QueuedConnection) self.worker.finished.connect(self.worker.deleteLater, Qt.QueuedConnection) self.worker.finished.connect(thread.deleteLater, Qt.QueuedConnection) self.worker.progress.connect(self._transferProgress, Qt.QueuedConnection) self.worker.started.connect(self._transferStart, Qt.QueuedConnection) self.worker.status.connect(self._transferStatus, Qt.QueuedConnection) thread.start() @QtCore.Slot(str) def _transferStatus(self, msg): 'Transfer Status Message' self._txtStatus.append(msg) @QtCore.Slot(int) def _transferStart(self, nBytes): 'Transfer Thread Stated' if nBytes > 0: self._pbTransfer.setMaximum(nBytes) else: self._pbTransfer.setMaximum(100) self._pbTransfer.reset() @QtCore.Slot(int) def _transferProgress(self, nTransferred): 'Transfer Thread Progress Event' self._pbTransfer.setValue(nTransferred) @QtCore.Slot(models.Dive) def _transferParsed(self, dive): 'Transfer Thread Parsed Dive' self._logbook.session.add(dive) @QtCore.Slot() def _transferFinished(self): 'Transfer Thread Finished' self._logbook.session.commit() self._txtLogbook.setEnabled(True) self._btnBrowse.setEnabled(True) self._cbxComputer.setEnabled(True) self._btnAddComputer.setEnabled(True) self._btnRemoveComputer.setEnabled(True) self._btnTransfer.setEnabled(True) self._btnExit.setEnabled(True)