Beispiel #1
0
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")
Beispiel #2
0
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()))
Beispiel #3
0
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())
Beispiel #4
0
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)