Example #1
0
    def __init__(self, config):
        # Initialize the object as a QWidget and
        # set its title and minimum width

        QWidget.__init__(self)

        self.config = config
        self.peerList = config.peerList
        self.setWindowTitle('BlastShare')
        self.setMinimumSize(320, 480)
        self.setMaximumWidth(320)
        self.prefw = None
        
        # connects the signals!
        self.connect(self.peerList,
                     SIGNAL("initTransfer"), self.sendFileToPeer)

        ''' Will add feature in future version '''
        '''
        shareFilesAction = QAction(QIcon('exit.png'), '&Share File(s)', self)
        shareFilesAction.setShortcut('Ctrl+O')
        shareFilesAction.setStatusTip('Share File(s)')
        shareFilesAction.triggered.connect(quitApp)
        '''
        
        preferencesAction = QAction(QIcon('exit.png'), '&Preferences', self)
        preferencesAction.setShortcut('Ctrl+P')
        preferencesAction.setStatusTip('Preferences')
        preferencesAction.triggered.connect(self.editPreferences)

        exitAction = QAction(QIcon('exit.png'), '&Exit', self)
        exitAction.setShortcut('Ctrl+Q')
        exitAction.setStatusTip('Exit application')
        exitAction.triggered.connect(quitApp)

        menubar = QMenuBar()
        fileMenu = menubar.addMenu('&File')
        
        ''' Will enable in future versions '''
        # fileMenu.addAction(shareFilesAction)
        
        fileMenu.addAction(preferencesAction)
        fileMenu.addAction(exitAction)

        layout = QVBoxLayout()
        layout.setContentsMargins(QMargins(0, 0, 0, 0))
        self.setLayout(layout)
        
        statusBar = QStatusBar()
        statusBar.showMessage('Ready')
        
        layout.addWidget(menubar)
        layout.addWidget(self.peerList)
        layout.addWidget(statusBar)
Example #2
0
class Tab(QWidget):
    def __init__(self, parent, configuration):
        QWidget.__init__(self, parent)
        self._configuration = configuration

        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        main_widget = QWidget(self)
        self._layout_main = QVBoxLayout()
        self._layout_main.setContentsMargins(0, 0, 0, 0)
        self._layout_main.setSpacing(6)
        main_widget.setLayout(self._layout_main)
        layout.addWidget(main_widget)
        self._status_bar = QStatusBar()
        layout.addWidget(self._status_bar)
        self.setLayout(layout)

    def _add_widget(self, widget):
        self._layout_main.addWidget(widget)

    def set_error_message(self, msg):
        self._status_bar.showMessage(msg)
class MainWindow(QWidget):
    progress = pyqtSignal(int)
    status = pyqtSignal(str)
    """Main window of the MTurKClient application"""
    def __init__(self, projFile):
        QWidget.__init__(self)
        self.projPath = os.path.split(projFile)[0]
        self.projName = os.path.split(projFile)[1]
        self.setFixedSize(500, 680)
        self.setWindowIcon(QIcon('icon.png'))
        self.setWindowIconText("MTurk Client")
        if os.path.exists(os.path.join(self.projPath, 'mturk_segmentation.ini')):
            self.task = SegmentationTask(projFile)
            self.segmentation_mode = True
        elif os.path.exists(os.path.join(self.projPath, 'mturk_features.ini')):
            self.task = CorrespondenceTask(projFile)
            self.segmentation_mode = False
        else:
            raise Exception('No configuration file found!')
        self.task.setParent(self)
        self.setWindowFlags(Qt.WindowCloseButtonHint)
        self.setTitle()

        ###Tab widget###
        self.tabWidget = QTabWidget()
        self.initUploadTab()
        self.initDownloadTab()
        self.initManageTab()
        self.initSettingTab()
        self.initStatusBar()

        # Layout management
        vbox = QVBoxLayout()
        vbox.addWidget(self.tabWidget)
        vbox.addWidget(self.statusBar)
        self.setLayout(vbox)
        self.show()

        self.task.loadVideoLabelFile()
        if not self.task.connect():
            self.status.emit("MTurk connection failed.")
        else:
            self.getBalance()

    def setTitle(self):
        if self.task.sandbox:
            self.setWindowTitle("MTurk Client - Sandbox Mode")
        else:
            self.setWindowTitle("MTurk Client")

    def getTotalNumberOfHITs(self):
        if self.segmentation_mode:
            return len(self.task.videoLabelHandler.objects) * int(self.task.assignments)
        else:
            if self.tabWidget.widget(0).findChildren(QCheckBox)[0].isChecked():
                shift = 25
            else:
                shift = 50
            xN = len(range(0, self.task.videoLabelHandler.imageWidth - shift, shift))
            yN = len(range(0, self.task.videoLabelHandler.imageHeight - shift, shift))
            return xN * yN * int(self.task.assignments)

    def getBalance(self):
        """Get current account balance and compute costs"""
        self.balance = self.task.connection.get_account_balance()[0]
        self.credit_label.setText("Your current account balance is:\t\t\t{0}".format(self.balance))

        costs = self.task.reward * self.getTotalNumberOfHITs()
        self.costLabel.setText("Costs per frame:\t\t\t\t\t${0}".format(costs))

    def updateTable(self):
        appDict, rejDict = self.task.reviewTool.updateTable()

        for i in range(self.table_turk.rowCount()):
            # Get workerID
            workerID = str(self.table_turk.item(i, 0).text())

            # Set rejected or approved
            if workerID in rejDict.keys():
                self.table_turk.item(i, 2).setText(str(rejDict[workerID]))
            if workerID in appDict.keys():
                self.table_turk.item(i, 3).setText(str(appDict[workerID]))

    def getStatus(self):
        """
        Get current status of the HITs that are being worked on and current account balance.
        """
        # Get Status as dictionary {WorkerID:23; ...}
        hitStatus, assignments = self.task.status()
        totalAssignments = self.getTotalNumberOfHITs()
        self.status.emit("Finished HITs: ({0}/{1})".format(assignments, totalAssignments))

        # Update Table
        for i, entry in enumerate(hitStatus):
            self.table_turk.insertRow(i)
            turker = QTableWidgetItem(entry[0])
            assignments = QTableWidgetItem(str(entry[1]))
            rejected = QTableWidgetItem(str(0))
            approved = QTableWidgetItem(str(0))

            self.table_turk.setItem(i, 0, turker)
            self.table_turk.setItem(i, 1, assignments)
            self.table_turk.setItem(i, 2, rejected)
            self.table_turk.setItem(i, 3, approved)

    def initSettingTab(self):
        # ##Setting Tab###
        setting_tab = QWidget()
        setting_tab_layout = QVBoxLayout()
        self.tabWidget.addTab(setting_tab, "Settings")

        # Task Box
        task_box = QGroupBox()
        task_box.setTitle(QString("Task properties"))
        task_layout = QGridLayout()

        # Name
        name = QLabel("Name:")
        name_value = QLineEdit() 
        name_value.setText(self.task.hittypename)
        name_value.setEnabled(False)
        clickable(name_value).connect(self.enable)
        task_layout.addWidget(name, 0, 1)
        task_layout.addWidget(name_value, 0, 2, 1, 3)

        # Description
        description = QLabel("Description:")
        description_value = QLineEdit() 
        description_value.setText(self.task.description)
        description_value.setEnabled(False)
        clickable(description_value).connect(self.enable)
        task_layout.addWidget(description, 1, 1)
        task_layout.addWidget(description_value, 1, 2, 1, 3)

        # Keywords
        keywords = QLabel("Keywords:")
        keywords_value = QLineEdit()
        keywords_value.setText(','.join(self.task.keywords))
        keywords_value.setEnabled(False)
        clickable(keywords_value).connect(self.enable)
        task_layout.addWidget(keywords, 2, 1)
        task_layout.addWidget(keywords_value, 2, 2, 1, 3)

        # Qualification
        qualification = QLabel("Qualification [%]:")
        qualification_value = QSpinBox()
        qualification_value.setSuffix('%')
        qualification_value.setValue(int(self.task.qualification))
        qualification_value.setEnabled(False)
        clickable(qualification_value).connect(self.enable)
        task_layout.addWidget(qualification, 3, 1)
        task_layout.addWidget(qualification_value, 3, 4)

        # Assignments
        assignments = QLabel("Assignments:")
        assignments_value = QSpinBox()
        assignments_value.setSuffix('')
        assignments_value.setValue(int(self.task.assignments))
        assignments_value.setEnabled(False)
        clickable(assignments_value).connect(self.enable)
        task_layout.addWidget(assignments, 4, 1)
        task_layout.addWidget(assignments_value, 4, 4)

        # Duration
        duration = QLabel("Duration [min]:") 
        duration_value = QSpinBox()
        duration_value.setSuffix('min')
        duration_value.setValue(int(self.task.duration))
        duration_value.setEnabled(False)
        clickable(duration_value).connect(self.enable)
        task_layout.addWidget(duration, 5, 1)
        task_layout.addWidget(duration_value, 5, 4)

        # Reward
        reward = QLabel("Reward [0.01$]:")
        reward_value = QDoubleSpinBox()
        reward_value.setRange(0.01, 0.5)
        reward_value.setSingleStep(0.01)
        reward_value.setSuffix('$')
        reward_value.setValue(self.task.reward)
        reward_value.setEnabled(False)
        clickable(reward_value).connect(self.enable)
        task_layout.addWidget(reward, 6, 1)
        task_layout.addWidget(reward_value, 6, 4)

        # Lifetime
        lifetime = QLabel("Lifetime [d]:")
        lifetime_value = QSpinBox()
        lifetime_value.setSuffix('d')
        lifetime_value.setValue(self.task.lifetime)
        lifetime_value.setEnabled(False)
        clickable(lifetime_value).connect(self.enable)
        task_layout.addWidget(lifetime, 7, 1)
        task_layout.addWidget(lifetime_value, 7, 4)

        # sandbox
        sandbox = QCheckBox("Sandbox")
        sandbox.setChecked(self.task.sandbox)
        task_layout.addWidget(sandbox, 8, 1)
        task_box.setLayout(task_layout)
        task_layout.setColumnMinimumWidth(1, 120)

        # Image Storage Box
        storage_box = QGroupBox()
        storage_box.setTitle(QString("Image Storage"))
        storage_layout = QGridLayout()

        # Host URL
        host_url = QLabel("Host-URL:")
        host_url_value = QLineEdit()
        host_url_value.setText(self.task.host_url)
        host_url_value.setEnabled(False)
        clickable(host_url_value).connect(self.enable)

        # Dropbox Path
        dropbox_path = QLabel("Dropbox-Path:")
        dropbox_path_value = QLineEdit()
        dropbox_path_value.setText(self.task.dropbox_path)
        dropbox_path_value.setEnabled(False)
        clickable(dropbox_path_value).connect(self.enable)

        # Dropbox or S3
        usingS3 = QRadioButton("S3")
        usingS3.setChecked(self.task.usingS3)
        usingS3.setEnabled(False)
        usingDropbox = QRadioButton("Dropbox")
        usingDropbox.setChecked(self.task.usingDropbox)

        storage_layout.addWidget(host_url, 0, 1)
        storage_layout.addWidget(host_url_value, 0, 2, 1, 3)
        storage_layout.addWidget(dropbox_path, 1, 1)
        storage_layout.addWidget(dropbox_path_value, 1, 2, 1, 3)

        # Add Layouts
        save_button = QPushButton("Save Settings")
        setting_tab_layout.addWidget(task_box)
        setting_tab_layout.addWidget(storage_box)
        setting_tab.setLayout(setting_tab_layout)
        save_button.clicked.connect(self.SaveSettings)

        storage_layout.addWidget(usingS3, 2, 1)
        storage_layout.addWidget(usingDropbox, 3, 1)
        storage_layout.addWidget(save_button, 3, 4)

        # storage_layout.addStretch(1)
        storage_box.setLayout(storage_layout)

    def initListView(self):
        """Init status table"""
        # List Box model
        model = QStandardItemModel()

        # Init list objects
        for i, frame in enumerate(self.task.videoLabelHandler.files):
            item = QStandardItem(frame + "\t{0} Objects".format(len(self.task.videoLabelHandler.objects)))
            item.setCheckState(Qt.Checked)
            item.setCheckable(True)
            model.appendRow(item)

        if self.segmentation_mode:
            model.item(0).setEnabled(False)
        else:
            model.item(i).setCheckState(Qt.Unchecked)
            model.item(i).setEnabled(False)

        # Set model
        self.view.setModel(model)

    def initUploadTab(self):
        ###Upload Tab###
        upload_tab = QWidget()
        upload_tab_layout = QVBoxLayout()
        self.tabWidget.addTab(upload_tab, "Upload HITs")

        # Frames Box
        frames_box = QGroupBox()
        frames_box.setTitle("Frame selection")
        frames_layout = QVBoxLayout()

        frames_label = QLabel("Select which frames to upload:")
        frames_layout.addWidget(frames_label)

        # Init list view
        self.view = QListView()
        frames_layout.addWidget(self.view)
        upload_button = QPushButton("Upload HITs")
        upload_button.clicked.connect(self.upload)

        frames_layout.addWidget(upload_button)
        frames_box.setLayout(frames_layout)

        # MotionOptions Box
        motionOptionsBox = QGroupBox()
        motionOptionsBox.setTitle("Motion Annotation Options")
        patchLabel = QLabel("Patch size: 50x50 Pixels")
        motionOptionsBoxLayout = QHBoxLayout()
        overlapping = QCheckBox("Overlapping Patches")
        overlapping.clicked.connect(self.getBalance)
        motionOptionsBoxLayout.addWidget(overlapping)
        motionOptionsBoxLayout.addWidget(patchLabel)
        motionOptionsBox.setLayout(motionOptionsBoxLayout)

        # LayerOptions Box
        options_box = QGroupBox()
        options_box.setTitle('Layer Annotation Options')
        blurLabel = QLabel("Amount: 4 Pixels")
        options_box_layout = QHBoxLayout()
        blur = QCheckBox("Blur Outlines")
        options_box_layout.addWidget(blur)
        options_box_layout.addWidget(blurLabel)
        options_box.setLayout(options_box_layout)

        # Disable not needed options
        if self.segmentation_mode:
            motionOptionsBox.setEnabled(False)
        else:
            options_box.setEnabled(False)

        # Costs
        costs_box = QGroupBox()
        costs_box.setTitle('Costs')
        costs_box_layout = QVBoxLayout()
        self.credit_label = QLabel("Your current account balance is:")
        self.costLabel = QLabel("Costs per frame:")
        costs_box_layout.addWidget(self.credit_label)
        costs_box_layout.addWidget(self.costLabel)
        costs_box.setLayout(costs_box_layout)

        # Upload Box
        upload_tab_layout.addWidget(frames_box)
        upload_tab_layout.addWidget(motionOptionsBox)
        upload_tab_layout.addWidget(options_box)
        upload_tab_layout.addWidget(costs_box)
        upload_tab.setLayout(upload_tab_layout)

    def initStatusBar(self):
        """Init status bar"""
        # Status bar
        self.progress.connect(self.updateProgressBar)
        self.status.connect(self.updateStatusBar)
        self.statusBar = QStatusBar()
        progress = QProgressBar()
        self.statusBar.addPermanentWidget(progress)

    def initDownloadTab(self):
        # # # Download Tab# # # 
        download_tab = QWidget()
        download_tab_layout = QVBoxLayout()
        self.tabWidget.addTab(download_tab, "Download HITs")

        # Status
        status_box = QGroupBox()
        status_box.setTitle("Status")
        status_layout = QGridLayout()
        self.table_turk = ContextTable(0, 4, self)
        self.table_turk.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.table_turk.setColumnWidth(0, 160)
        self.table_turk.setColumnWidth(1, 50)
        self.table_turk.setColumnWidth(2, 90)
        self.table_turk.setColumnWidth(3, 90)
        # self.table_turk.verticalHeader().setVisible(False)

        # Set Headers
        header_0 = QTableWidgetItem()
        header_0.setText("Turker")
        self.table_turk.setHorizontalHeaderItem(0, header_0)

        header_1 = QTableWidgetItem()
        header_1.setText("HITs")
        self.table_turk.setHorizontalHeaderItem(1, header_1)

        header_2 = QTableWidgetItem()
        header_2.setText("Rejected")
        self.table_turk.setHorizontalHeaderItem(2, header_2)

        header_3 = QTableWidgetItem()
        header_3.setText("Approved")
        self.table_turk.setHorizontalHeaderItem(3, header_3)

        status_layout.addWidget(self.table_turk, 0, 0, 1, 2)

        # Status Button
        status_button = QPushButton('Update Status')
        status_button.clicked.connect(self.getStatus)

        status_layout.addWidget(status_button, 1, 1)
        status_box.setLayout(status_layout)
        download_tab_layout.addWidget(status_box) 

        # Download Button
        download_button = QPushButton("Download Results")
        download_button.clicked.connect(self.download)
        status_layout.addWidget(download_button, 1, 0)

        # Options Box
        options_box = QGroupBox()
        options_box.setTitle("Import results")
        options_box_layout = QGridLayout()
        matching = QRadioButton("Choose best matching Outlines")
        matching.setEnabled(False)
        review = QRadioButton("Review by hand")
        review.setChecked(True)
        review_button = QPushButton("Review Results")
        review_button.clicked.connect(self.review)

        # Import Button
        import_button = QPushButton("Import results")
        import_button.clicked.connect(self.importResults)

        options_box_layout.addWidget(review, 0, 0)
        options_box_layout.addWidget(review_button, 0, 1)
        options_box_layout.addWidget(matching, 1, 0)
        options_box_layout.addWidget(import_button, 2, 0, 1, 2)

        options_box.setLayout(options_box_layout)
        download_tab_layout.addWidget(options_box)
        download_tab.setLayout(download_tab_layout)

    def initManageTab(self):
        ###Manage Tab###
        manage_tab = QWidget()
        manage_tab_layout = QVBoxLayout()
        self.tabWidget.addTab(manage_tab, "Manage HITs")

        # Send Box
        send_box_layout = QVBoxLayout()
        subject = QLineEdit()
        subject_label = QLabel("Subject:")

        send_text = QTextEdit()
        send_button = QPushButton("Send Message")
        send_button.setMinimumWidth(135)

        send_button.clicked.connect(self.sendMessage)

        allTurkers = QRadioButton("Send message to all Turkers")
        allTurkers.setChecked(True)
        singleTurker = QRadioButton("Send message to single Turker")

        workerIDLabel = QLabel('Worker-ID:')
        workerID = QLineEdit()

        def checkState():
            # Set enabled if checked
            if allTurkers.isChecked():
                workerIDLabel.setEnabled(False)
                workerID.setEnabled(False)
            else:
                workerIDLabel.setEnabled(True)
                workerID.setEnabled(True)

        # Connect to check state
        allTurkers.clicked.connect(checkState)
        singleTurker.clicked.connect(checkState)
        checkState()

        # Choose if single or all turkers receive message
        chooseSendLayout = QHBoxLayout()
        chooseSendLayout.addWidget(singleTurker)
        chooseSendLayout.addWidget(workerIDLabel)
        chooseSendLayout.addWidget(workerID)

        # Send box layout
        send_box = QGroupBox()
        send_box_layout.addWidget(allTurkers)
        send_box_layout.addLayout(chooseSendLayout)
        send_box_layout.addWidget(subject_label)
        send_box_layout.addWidget(subject)
        send_box_layout.addWidget(send_text)
        send_box_layout.addWidget(send_button)
        send_box_layout.setAlignment(send_button, Qt.AlignRight)
        send_box.setTitle("Notify Workers")
        send_box.setLayout(send_box_layout)
        manage_tab_layout.addWidget(send_box)

        # Pay box
        payBox = QGroupBox()
        payBox.setTitle("Pay Workers")
        payBox_layout = QGridLayout()

        approveFeedbackLabel = QLabel("Approve Feedback:")
        approveFeedback = QTextEdit()
        approveFeedback.setText("Thank you for your work.")

        rejectFeedback = QTextEdit()
        rejectFeedback.setText("We are sorry, but we cannot accept your work because you did not follow the instructions or submitted careless work.")

        payBox_layout.addWidget(approveFeedbackLabel, 0, 0)
        payBox_layout.addWidget(approveFeedback , 1, 0, 1, 0)
        reject_label = QLabel("{0} HITs will be rejected".format(0))
        approve_label = QLabel("{0} HITs will be approved".format(0))
        pay_button = QPushButton("Pay Turkers")
        pay_button.clicked.connect(self.pay)

        payBox_layout.addWidget(reject_label, 2, 0)
        payBox_layout.addWidget(approve_label, 3, 0)
        payBox_layout.addWidget(pay_button, 4, 0)
        payBox.setLayout(payBox_layout)
        manage_tab_layout.addWidget(payBox)

        # Delete Box
        deleteBox = QGroupBox()
        deleteBox.setTitle("Clean up finished HITs")
        deleteBox_layout = QHBoxLayout()
        delete_label = QLabel("{0} HITs are finished and can be deleted".format(0))
        delete_button = QPushButton("Delete HITs")
        delete_button.clicked.connect(self.delete)
        deleteBox_layout.addWidget(delete_label)
        deleteBox_layout.addWidget(delete_button)
        deleteBox.setLayout(deleteBox_layout)
        manage_tab_layout.addWidget(deleteBox)

        # Evaluation Button
        evalButton = QPushButton("Evaluate")
        evalButton.clicked.connect(self.evaluate)
        manage_tab_layout.addWidget(evalButton)

        # Add layouts to tab
        manage_tab.setLayout(manage_tab_layout)

    def SaveSettings(self):
        """Save settings to config file"""
        # Line edits
        lineEdits = self.tabWidget.widget(3).findChildren(QLineEdit)
        self.task._conf.set('Task', 'name', lineEdits[0].text())
        self.task._conf.set('Task', 'Description', lineEdits[1].text())
        self.task._conf.set('Task', 'Keywords', lineEdits[2].text())
        self.task._conf.set('Image storage', 'host-url', lineEdits[8].text())
        self.task._conf.set('Image storage', 'dropbox-path', lineEdits[9].text())

        # Spin boxes
        spinBoxes = self.tabWidget.widget(3).findChildren(QSpinBox)
        self.task._conf.set('Task', 'assignments', spinBoxes[1].value())
        self.task._conf.set('Task', 'qualification', spinBoxes[0].value())
        self.task._conf.set('Task', 'duration', spinBoxes[2].value())
        self.task._conf.set('Task', 'lifetime', spinBoxes[3].value())
        self.task._conf.set('Task', 'reward', self.tabWidget.widget(3).findChild(QDoubleSpinBox).value())

        # Dropbox usage
        radioButtons = self.tabWidget.widget(3).findChildren(QRadioButton)
        self.task._conf.set('Task', 'usings3', radioButtons[0].isChecked())
        self.task._conf.set('Task', 'usingdropbox', radioButtons[1].isChecked())

        # Sandbox
        self.task._conf.set('Task', 'sandbox', self.tabWidget.widget(3).findChild(QCheckBox).isChecked())

        # Write config file
        self.task.saveConfigFile()

        # Disable objects
        for obj in lineEdits:
            obj.setEnabled(False)
        for obj in spinBoxes:
            obj.setEnabled(False)
        self.tabWidget.widget(3).findChild(QDoubleSpinBox).setEnabled(False)

        # Reload Config File
        self.task.loadConfigFile()
        self.task.connect()
        self.setTitle()
        self.getBalance()
        self.status.emit('Settings saved')

    def pay(self):
        self.status.emit("Paying turkers...")
        feedback = self.tabWidget.widget(2).findChildren(QTextEdit)[1].document()
        self.task.pay(feedback.toPlainText())
        self.status.emit("Done")

    def enable(self):
        """Does nothing. But is required for the clickable widget option for now. Yeah!!"""
        pass

    def review(self):
        self.task.reviewHITs()
        self.updateTable()

    def sendMessage(self):
        """Send message to turkers"""
        # Goes the message to all or a single turker?
        if self.tabWidget.widget(2).findChildren(QRadioButton)[1].isChecked():
            workerID = self.tabWidget.widget(2).findChildren(QLineEdit)[0].text()
            if workerID == "":
                self.status.emit('Please provide Worker-ID')
                return False
        else:
            workerID = ""

        # Get subject and message
        subject = self.tabWidget.widget(2).findChildren(QLineEdit)[1].text()
        message = self.tabWidget.widget(2).findChildren(QTextEdit)[0].document()
        self.task.sendMessage(subject, message.toPlainText(), workerID=workerID)
        return True

    def upload(self):
        """Upload HITs"""
        message_box = QMessageBox()
        reply = message_box.question(self, 'Upload HITs',
                "Are you sure you want to upload the HITs?", QMessageBox.Yes, QMessageBox.No)

        if reply == QMessageBox.Yes:
            if self.segmentation_mode:
                if self.tabWidget.widget(0).findChildren(QCheckBox)[1].isChecked():
                    self.task.videoLabelHandler.blurOutlines()
                self.task.upload()
            else:
                if self.tabWidget.widget(0).findChildren(QCheckBox)[0].isChecked():
                    overlapping = True
                else:
                    overlapping = False
                self.task.upload(overlapping)

    def updateProgressBar(self, value):
        self.statusBar.children()[2].setValue(value)

    def updateStatusBar(self, mesg):
        self.statusBar.showMessage(mesg)

    def importResults(self):
        self.task.getTurked()

    def delete(self):
        self.task.deleteHit()

    def download(self):
        self.task.harvest()

    def evaluate(self):
        """
        Evaluate additional data from MTurk.

        The following statistics are computed:
            * Working Time
            * Feedback
            * Hit statistics
            * Working time per worker
            * Worker statistics
        """
        evaluation = Evaluation(self.task)
        evaluation.workingTime()
        evaluation.extractFeedback()
        evaluation.HITStatistics()
        evaluation.workingTimePerWorker()
        evaluation.workerStatistics()
class UniFileSyncUI(QMainWindow):
    """UniFileSyncUI class"""
    def __init__(self, name=None):
        super(UniFileSyncUI, self).__init__()

        self.ui = Ui_UniFileSyncPop()
        self.ui.setupUi(self)

        self.setFixedSize(self.size())

        self.server = UServer('UServer')
        self.server.regSelfToBus()

        if name:
            self.setName(name)

        self.createActions()
        self.createTrayIcon()
        self.createStatusBar()

        qApp.setQuitOnLastWindowClosed(False)

        #connect the signal with slot
        self.connectUISlots(self.ui)
        #set UI label
        username = ConfManager.getManager().getValue('UI', 'username')
        self.ui.nameLabel.setText(username)

        #Start server immediately
        self.server.start()
        #self.server.getHandler('start')({'name': 'all'})
        msg = self.server.initMsg('start', None, MSG_UNIQUE_ID_T_CONTROLLER, False, {'name': 'all'})
        UMsgBus.getBus().send(msg)

        self.server.addCallBack(self.statusupdate)
        self.server.addCallBack(self.cloudinfoupdate)

        #setup list
        self.setupFolderList(self.ui.folderList)
        self.setupPluginList(self.ui.pluginList)
        self.setupNetworkConf()

        #Init status bar
        stBarConf = ConfManager.getManager().getValue('UI', 'statusbar')
        self.statusbar.showMessage(stBarConf['messages']['init'])

        #Init system icon
        self.trayIcon.show()
        self.showTrayIconMessage()


    def setupNetworkConf(self):
        """setup network configuration into UI"""
        conf = ConfManager.getManager().getValue('common', 'network')
        self.ui.proxyLineEdit.setText(conf['proxy'])
        self.ui.portLineEdit.setText("%s" % conf['port'])

    def closeEvent(self, event):
        """override close event"""
        if self.trayIcon.isVisible():
            self.hide()
            event.ignore()

        ConfManager.getManager().save()
        logging.debug('[%s] is closed, window is hide, configuration is saved', self.getName())

    def createActions(self):
        """create tray icon menu action"""
        self.configAction = QAction("&ShowConfig", self, triggered=self.show)
        self.exitAction = QAction("&Exit", self)
        self.exitAction.triggered.connect(lambda: UMsgBus.getBus().send(self.server.initMsg('stop', None, MSG_UNIQUE_ID_T_CONTROLLER, False, {'name': 'all'})))
        self.exitAction.triggered.connect(qApp.quit)

    def createTrayIcon(self):
        """create system tray icon"""
        self.trayIconMenu = QMenu(self)
        es = self.trayIconMenu.addAction(self.configAction)

        self.trayIconMenu.addSeparator()

        ea = self.trayIconMenu.addAction(self.exitAction)

        self.trayIcon = QSystemTrayIcon(self)
        self.trayIcon.setContextMenu(self.trayIconMenu)
        self.trayIcon.setIcon(QIcon('icon/tray.png'))

    def showTrayIconMessage(self):
        """show tray icon message"""
        conf = ConfManager.getManager().getValue('UI', 'trayicon')
        popup = conf['popup']

        self.trayIcon.showMessage(popup['title'], popup['message'])

    def setupFolderList(self, folderList):
        """setup folder list for showing"""
        fts = ConfManager.getManager().getValue('common', 'folders')
        i = 0
        for ft in fts:
            flistItem = QListWidgetItem(QIcon('icon/folder.png'), ft, folderList)
            folderList.insertItem(i, flistItem)
            i += 1

    def setupPluginList(self, pluginList):
        """setup plugin list from configuration file"""
        fts = ConfManager.getManager().getValue('common', 'plugins')
        i = 0
        for ft in fts:
            flistItem = QListWidgetItem(QIcon('icon/plugin.png'), ft['name'], pluginList)
            pluginList.insertItem(i, flistItem)
            i += 1

    def show(self):
        """ovrride parent show method"""
        super(UniFileSyncUI, self).show()

        #Init status bar
        stBarConf = ConfManager.getManager().getValue('UI', 'statusbar')
        self.statusbar.showMessage(stBarConf['messages']['init'])

        #set UI label
        username = ConfManager.getManager().getValue('UI', 'username')
        self.ui.nameLabel.setText(username)

    def connectUISlots(self, ui):
        """connect ui component with slots"""
        ui.connBtn.clicked.connect(lambda : self.connBtnSlots(ui.connBtn))
        ui.addFolderBtn.clicked.connect(lambda: self.connBtnSlots(ui.addFolderBtn))
        ui.rmFolderBtn.clicked.connect(lambda: self.connBtnSlots(ui.rmFolderBtn))
        ui.saveBtn.clicked.connect(lambda: self.connBtnSlots(ui.saveBtn))
        ui.unloadBtn.clicked.connect(lambda: self.connBtnSlots(ui.unloadBtn))
        ui.reloadBtn.clicked.connect(lambda: self.connBtnSlots(ui.reloadBtn))
        ui.resetBtn.clicked.connect(lambda: self.connBtnSlots(ui.resetBtn))
        ui.addPluginBtn.clicked.connect(lambda: self.connBtnSlots(ui.addPluginBtn))
        ui.syncFolderBtn.clicked.connect(lambda: self.connBtnSlots(ui.syncFolderBtn))

        self.connect(self, SIGNAL('statusupdate'), self.statusbarUpdate)
        self.connect(self, SIGNAL('cloudinfoupdate'), self.infoLabelUpdate)

    def connBtnSlots(self, btn):
        """docstring for connBtnSlots"""
        if btn is self.ui.connBtn:
            if btn.text() == 'Connect':
                msg = self.server.initMsg('info', None, MSG_UNIQUE_ID_T_CONTROLLER, True, {'name': 'all'})
                UMsgBus.getBus().send(msg)
                #res, data = self.server.getHandler('info')({'name': 'all'})
                btn.setText('Connecting')
                #self.ui.infoLabel.setText(data)
                logging.debug('[%s]: Press Connect to getCloudInfo', self.getName())
            elif btn.text() == 'Disconnect':
                #self.server.getHandler('stop')({'name': 'all'})
                btn.setText('Connect')
                self.ui.infoLabel.setText('Cloud Disk is disconnected')

        elif btn is self.ui.addFolderBtn:
            fileDialog = QFileDialog(self)
            fileDialog.setWindowTitle('Select Folder')
            folderPath = fileDialog.getExistingDirectory()
            if folderPath != "":
                listItem = QListWidgetItem(QIcon('icon/folder.png'), folderPath, self.ui.folderList)
                pyStr = '%s' % folderPath
                logging.debug('[%s]: add folder path %s', self.getName(), pyStr)
                self.ui.folderList.insertItem(self.ui.folderList.count(), listItem)
                folderList = ConfManager.getManager().getValue('common', 'folders')
                folderList.append(pyStr)
                ConfManager.getManager().setValue('common', 'folders', folderList)
                #send watch dir request
                msg =  self.server.initMsg('watch', None, MSG_UNIQUE_ID_T_CONTROLLER, True, {'path': pyStr, 'action': 'add'})
                UMsgBus.getBus().send(msg)
                self.statusbar.showMessage('Adding watch path %s' % folderPath)

        elif btn is self.ui.rmFolderBtn:
            row = self.ui.folderList.currentRow()
            item = self.ui.folderList.currentItem()
            folderList = ConfManager.getManager().getValue('common', 'folders')
            self.statusbar.showMessage('Removing watch path %s' % item.text())
            pyStr = '%s' % item.text()
            folderList.remove(pyStr)
            ConfManager.getManager().setValue('common', 'folders', folderList)
            logging.debug('[%s]: remove item %d %s', self.getName(), row, item.text())
            msg =  self.server.initMsg('watch', None, MSG_UNIQUE_ID_T_CONTROLLER, True, {'path': pyStr, 'action': 'rm'})
            UMsgBus.getBus().send(msg)
            self.ui.folderList.takeItem(row)

        elif btn is self.ui.saveBtn:
            proxyConf = ConfManager.getManager().getValue('common', 'network')
            server = str(self.ui.proxyLineEdit.text())

            if server != "" and server != None:
                user = str(self.ui.proxyNameLineEdit.text())
                password = str(self.ui.proxyPwdLineEdit.text())
                logging.debug('[%s]: save server=>%s user=>%s password=>%s into configuration',
                              self.getName(), server, user, password)
                proxyConf['proxy'] = server
                proxyConf['user'] = user
                proxyConf['password'] = password
                ConfManager.getManager().setValue('common', 'network', proxyConf)
                msg =  self.server.initMsg('proxy', None, MSG_UNIQUE_ID_T_CONTROLLER, True,
                            {'http': 'http://%s' % server, 'https': 'https://%s' % server})
                UMsgBus.getBus().send(msg)
                self.statusbar.showMessage('Applying proxy %s' % server)

                ConfManager.getManager().save()

        elif btn is self.ui.resetBtn:
            proxyConf = ConfManager.getManager().getValue('common', 'network')
            server = proxyConf['proxy']
            user = proxyConf['user']
            password = proxyConf['password']
            port = proxyConf['port']

            self.ui.proxyLineEdit.setText(server)
            self.ui.proxyNameLineEdit.setText(user)
            self.ui.proxyPwdLineEdit.setText(password)
            self.ui.portLineEdit.setText(str(port))
            self.ui.serverEnableCheckBox.setCheckState(0)

        elif btn is self.ui.unloadBtn:
            row = self.ui.pluginList.currentRow()
            it = str(self.ui.pluginList.currentItem().text())
            logging.debug('[%s]: unload plugin name %s', self.getName(), it)
            self.statusbar.showMessage('Unloading plugin %s' % it)
            PluginManager.getManager().unload(it)
            self.ui.pluginList.takeItem(row)
            conf = ConfManager.getManager().getValue('common', 'plugins')
            for pc in conf:
                if pc['name'] == it:
                    conf.remove(pc)
            ConfManager.getManager().setValue('common', 'plugins', conf)

        elif btn is self.ui.reloadBtn:
            row = self.ui.pluginList.currentRow()
            it = str(self.ui.pluginList.currentItem().text())
            logging.debug('[%s]: reload plugin name %s', self.getName(), it)
            self.statusbar.showMessage('Reloading plugin %s' % it)
            PluginManager.getManager().reload(it)

        elif btn is self.ui.addPluginBtn:
            path = QFileDialog.getOpenFileName(self)
            PluginManager.getManager().loadPluginFromPath(str(path))

        elif btn is self.ui.syncFolderBtn:
            folder = str(self.ui.folderList.currentItem().text())
            msg =  self.server.initMsg('sync', None, MSG_UNIQUE_ID_T_CONTROLLER, True, {'path': str(folderPath), 'action': 'add'})
            UMsgBus.getBus().send(msg)
            self.statusbar.showMessage('Sync %s with cloud' % folder)

    def createStatusBar(self):
        """create status bar"""

        self.statusbar = QStatusBar(self)
        self.setStatusBar(self.statusbar)

    def setName(self, name):
        """set server name"""
        self.name = name

    def getName(self):
        """get server name"""
        return self.name

    def statusupdate(self, param):
        """call back for status update"""
        self.emit(SIGNAL('statusupdate'), param['result'])

    def statusbarUpdate(self, res):
        """statusbar update callback"""
        self.statusbar.showMessage(ERR_STR_TABLE[res])

    def cloudinfoupdate(self, param):
        """cloud infor update callback"""
        self.emit(SIGNAL('cloudinfoupdate'), param['data'])

    def infoLabelUpdate(self, res):
        """infoLabelUpdate"""
        if res:
            self.ui.infoLabel.setText(res)
            self.ui.connBtn.setText('Disconnect')
Example #5
0
class OWWidget(QDialog, metaclass=WidgetMetaClass):
    # Global widget count
    widget_id = 0

    # Widget description
    name = None
    id = None
    category = None
    version = None
    description = None
    long_description = None
    icon = "icons/Unknown.png"
    priority = sys.maxsize
    author = None
    author_email = None
    maintainer = None
    maintainer_email = None
    help = None
    help_ref = None
    url = None
    keywords = []
    background = None
    replaces = None
    inputs = []
    outputs = []

    # Default widget layout settings
    want_basic_layout = True
    want_main_area = True
    want_control_area = True
    want_graph = False
    show_save_graph = True
    want_status_bar = False
    no_report = False

    save_position = True
    resizing_enabled = True

    widgetStateChanged = Signal(str, int, str)
    blockingStateChanged = Signal(bool)
    asyncCallsStateChange = Signal()
    progressBarValueChanged = Signal(float)
    processingStateChanged = Signal(int)

    settingsHandler = None
    """:type: SettingsHandler"""

    savedWidgetGeometry = settings.Setting(None)

    def __new__(cls, parent=None, *args, **kwargs):
        self = super().__new__(cls, None, cls.get_flags())
        QDialog.__init__(self, None, self.get_flags())

        stored_settings = kwargs.get('stored_settings', None)
        if self.settingsHandler:
            self.settingsHandler.initialize(self, stored_settings)

        self.signalManager = kwargs.get('signal_manager', None)

        setattr(self, gui.CONTROLLED_ATTRIBUTES, ControlledAttributesDict(self))
        self._guiElements = []      # used for automatic widget debugging
        self.__reportData = None

        # TODO: position used to be saved like this. Reimplement.
        #if save_position:
        #    self.settingsList = getattr(self, "settingsList", []) + \
        #                        ["widgetShown", "savedWidgetGeometry"]

        OWWidget.widget_id += 1
        self.widget_id = OWWidget.widget_id

        if self.name:
            self.setCaption(self.name)

        self.setFocusPolicy(Qt.StrongFocus)

        self.startTime = time.time()    # used in progressbar

        self.widgetState = {"Info": {}, "Warning": {}, "Error": {}}

        self.__blocking = False
        # flag indicating if the widget's position was already restored
        self.__was_restored = False

        self.__progressBarValue = -1
        self.__progressState = 0
        self.__statusMessage = ""

        if self.want_basic_layout:
            self.insertLayout()

        return self

    def __init__(self, *args, **kwargs):
        """QDialog __init__ was already called in __new__,
        please do not call it here."""

    @classmethod
    def get_flags(cls):
        return (Qt.Window if cls.resizing_enabled
                else Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint)

    # noinspection PyAttributeOutsideInit
    def insertLayout(self):
        def createPixmapWidget(self, parent, iconName):
            w = QLabel(parent)
            parent.layout().addWidget(w)
            w.setFixedSize(16, 16)
            w.hide()
            if os.path.exists(iconName):
                w.setPixmap(QPixmap(iconName))
            return w

        self.setLayout(QVBoxLayout())
        self.layout().setMargin(2)

        self.warning_bar = gui.widgetBox(self, orientation="horizontal",
                                         margin=0, spacing=0)
        self.warning_icon = gui.widgetLabel(self.warning_bar, "")
        self.warning_label = gui.widgetLabel(self.warning_bar, "")
        self.warning_label.setStyleSheet("padding-top: 5px")
        self.warning_bar.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Maximum)
        gui.rubber(self.warning_bar)
        self.warning_bar.setVisible(False)

        self.topWidgetPart = gui.widgetBox(self,
                                           orientation="horizontal", margin=0)
        self.leftWidgetPart = gui.widgetBox(self.topWidgetPart,
                                            orientation="vertical", margin=0)
        if self.want_main_area:
            self.leftWidgetPart.setSizePolicy(
                QSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding))
            self.leftWidgetPart.updateGeometry()
            self.mainArea = gui.widgetBox(self.topWidgetPart,
                                          orientation="vertical",
                                          sizePolicy=QSizePolicy(QSizePolicy.Expanding,
                                                                 QSizePolicy.Expanding),
                                          margin=0)
            self.mainArea.layout().setMargin(4)
            self.mainArea.updateGeometry()

        if self.want_control_area:
            self.controlArea = gui.widgetBox(self.leftWidgetPart,
                                             orientation="vertical", margin=4)

        if self.want_graph and self.show_save_graph:
            graphButtonBackground = gui.widgetBox(self.leftWidgetPart,
                                                  orientation="horizontal", margin=4)
            self.graphButton = gui.button(graphButtonBackground,
                                          self, "&Save Graph")
            self.graphButton.setAutoDefault(0)

        if self.want_status_bar:
            self.widgetStatusArea = QFrame(self)
            self.statusBarIconArea = QFrame(self)
            self.widgetStatusBar = QStatusBar(self)

            self.layout().addWidget(self.widgetStatusArea)

            self.widgetStatusArea.setLayout(QHBoxLayout(self.widgetStatusArea))
            self.widgetStatusArea.layout().addWidget(self.statusBarIconArea)
            self.widgetStatusArea.layout().addWidget(self.widgetStatusBar)
            self.widgetStatusArea.layout().setMargin(0)
            self.widgetStatusArea.setFrameShape(QFrame.StyledPanel)

            self.statusBarIconArea.setLayout(QHBoxLayout())
            self.widgetStatusBar.setSizeGripEnabled(0)

            self.statusBarIconArea.hide()

            self._warningWidget = createPixmapWidget(
                self.statusBarIconArea,
                os.path.join(environ.widget_install_dir,
                             "icons/triangle-orange.png"))
            self._errorWidget = createPixmapWidget(
                self.statusBarIconArea,
                os.path.join(environ.widget_install_dir,
                             "icons/triangle-red.png"))

    # status bar handler functions
    def setState(self, stateType, id, text):
        stateChanged = super().setState(stateType, id, text)
        if not stateChanged or not hasattr(self, "widgetStatusArea"):
            return

        iconsShown = 0
        warnings = [("Warning", self._warningWidget, self._owWarning),
                    ("Error", self._errorWidget, self._owError)]
        for state, widget, use in warnings:
            if not widget:
                continue
            if use and self.widgetState[state]:
                widget.setToolTip("\n".join(self.widgetState[state].values()))
                widget.show()
                iconsShown = 1
            else:
                widget.setToolTip("")
                widget.hide()

        if iconsShown:
            self.statusBarIconArea.show()
        else:
            self.statusBarIconArea.hide()

        if (stateType == "Warning" and self._owWarning) or \
                (stateType == "Error" and self._owError):
            if text:
                self.setStatusBarText(stateType + ": " + text)
            else:
                self.setStatusBarText("")
        self.updateStatusBarState()

    def updateWidgetStateInfo(self, stateType, id, text):
        html = self.widgetStateToHtml(self._owInfo, self._owWarning,
                                      self._owError)
        if html:
            self.widgetStateInfoBox.show()
            self.widgetStateInfo.setText(html)
            self.widgetStateInfo.setToolTip(html)
        else:
            if not self.widgetStateInfoBox.isVisible():
                dHeight = - self.widgetStateInfoBox.height()
            else:
                dHeight = 0
            self.widgetStateInfoBox.hide()
            self.widgetStateInfo.setText("")
            self.widgetStateInfo.setToolTip("")
            width, height = self.width(), self.height() + dHeight
            self.resize(width, height)

    def updateStatusBarState(self):
        if not hasattr(self, "widgetStatusArea"):
            return
        if self.widgetState["Warning"] or self.widgetState["Error"]:
            self.widgetStatusArea.show()
        else:
            self.widgetStatusArea.hide()

    def setStatusBarText(self, text, timeout=5000):
        if hasattr(self, "widgetStatusBar"):
            self.widgetStatusBar.showMessage(" " + text, timeout)

    # TODO add!
    def prepareDataReport(self, data):
        pass


    # ##############################################
    """
    def isDataWithClass(self, data, wantedVarType=None, checkMissing=False):
        self.error([1234, 1235, 1236])
        if not data:
            return 0
        if not data.domain.classVar:
            self.error(1234, "A data set with a class attribute is required.")
            return 0
        if wantedVarType and data.domain.classVar.varType != wantedVarType:
            self.error(1235, "Unable to handle %s class." %
                             str(data.domain.class_var.var_type).lower())
            return 0
        if checkMissing and not orange.Preprocessor_dropMissingClasses(data):
            self.error(1236, "Unable to handle data set with no known classes")
            return 0
        return 1
    """

    def restoreWidgetPosition(self):
        restored = False
        if self.save_position:
            geometry = self.savedWidgetGeometry
            if geometry is not None:
                restored = self.restoreGeometry(QByteArray(geometry))

            if restored:
                space = qApp.desktop().availableGeometry(self)
                frame, geometry = self.frameGeometry(), self.geometry()

                #Fix the widget size to fit inside the available space
                width = space.width() - (frame.width() - geometry.width())
                width = min(width, geometry.width())
                height = space.height() - (frame.height() - geometry.height())
                height = min(height, geometry.height())
                self.resize(width, height)

                # Move the widget to the center of available space if it is
                # currently outside it
                if not space.contains(self.frameGeometry()):
                    x = max(0, space.width() / 2 - width / 2)
                    y = max(0, space.height() / 2 - height / 2)

                    self.move(x, y)
        return restored

    def __updateSavedGeometry(self):
        if self.__was_restored:
            # Update the saved geometry only between explicit show/hide
            # events (i.e. changes initiated by the user not by Qt's default
            # window management).
            self.savedWidgetGeometry = self.saveGeometry()

    # when widget is resized, save the new width and height
    def resizeEvent(self, ev):
        QDialog.resizeEvent(self, ev)
        # Don't store geometry if the widget is not visible
        # (the widget receives a resizeEvent (with the default sizeHint)
        # before showEvent and we must not overwrite the the savedGeometry
        # with it)
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()

    def moveEvent(self, ev):
        QDialog.moveEvent(self, ev)
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()

    # set widget state to hidden
    def hideEvent(self, ev):
        if self.save_position:
            self.__updateSavedGeometry()
        self.__was_restored = False
        QDialog.hideEvent(self, ev)

    def closeEvent(self, ev):
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()
        self.__was_restored = False
        QDialog.closeEvent(self, ev)

    def showEvent(self, ev):
        QDialog.showEvent(self, ev)
        if self.save_position:
            # Restore saved geometry on show
            self.restoreWidgetPosition()
        self.__was_restored = True

    def wheelEvent(self, event):
        """ Silently accept the wheel event. This is to ensure combo boxes
        and other controls that have focus don't receive this event unless
        the cursor is over them.
        """
        event.accept()

    def setCaption(self, caption):
        # we have to save caption title in case progressbar will change it
        self.captionTitle = str(caption)
        self.setWindowTitle(caption)

    # put this widget on top of all windows
    def reshow(self):
        self.show()
        self.raise_()
        self.activateWindow()

    def send(self, signalName, value, id=None):
        if self.signalManager is not None:
            self.signalManager.send(self, signalName, value, id)

    def __setattr__(self, name, value):
        """Set value to members of this instance or any of its members.

        If member is used in a gui control, notify the control about the change.

        name: name of the member, dot is used for nesting ("graph.point.size").
        value: value to set to the member.

        """

        names = name.rsplit(".")
        field_name = names.pop()
        obj = reduce(lambda o, n: getattr(o, n, None), names, self)
        if obj is None:
            raise AttributeError("Cannot set '{}' to {} ".format(name, value))

        if obj is self:
            super().__setattr__(field_name, value)
        else:
            setattr(obj, field_name, value)

        notify_changed(obj, field_name, value)

        if self.settingsHandler:
            self.settingsHandler.fast_save(self, name, value)

    def openContext(self, *a):
        self.settingsHandler.open_context(self, *a)

    def closeContext(self):
        self.settingsHandler.close_context(self)

    def retrieveSpecificSettings(self):
        pass

    def storeSpecificSettings(self):
        pass

    def saveSettings(self):
        self.settingsHandler.update_defaults(self)

    # this function is only intended for derived classes to send appropriate
    # signals when all settings are loaded
    def activate_loaded_settings(self):
        pass

    # reimplemented in other widgets
    def onDeleteWidget(self):
        pass

    def handleNewSignals(self):
        # this is called after all new signals have been handled
        # implement this in your widget if you want to process something only
        # after you received multiple signals
        pass

    # ############################################
    # PROGRESS BAR FUNCTIONS

    def progressBarInit(self):
        self.startTime = time.time()
        self.setWindowTitle(self.captionTitle + " (0% complete)")

        if self.__progressState != 1:
            self.__progressState = 1
            self.processingStateChanged.emit(1)

        self.progressBarValue = 0

    def progressBarSet(self, value):
        old = self.__progressBarValue
        self.__progressBarValue = value

        if value > 0:
            if self.__progressState != 1:
                warnings.warn("progressBarSet() called without a "
                              "preceding progressBarInit()",
                              stacklevel=2)
                self.__progressState = 1
                self.processingStateChanged.emit(1)

            usedTime = max(1, time.time() - self.startTime)
            totalTime = (100.0 * usedTime) / float(value)
            remainingTime = max(0, totalTime - usedTime)
            h = int(remainingTime / 3600)
            min = int((remainingTime - h * 3600) / 60)
            sec = int(remainingTime - h * 3600 - min * 60)
            if h > 0:
                text = "%(h)d:%(min)02d:%(sec)02d" % vars()
            else:
                text = "%(min)d:%(sec)02d" % vars()
            self.setWindowTitle(self.captionTitle +
                                " (%(value).2f%% complete, remaining time: %(text)s)" % vars())
        else:
            self.setWindowTitle(self.captionTitle + " (0% complete)")

        self.progressBarValueChanged.emit(value)

        if old != value:
            self.progressBarValueChanged.emit(value)

        qApp.processEvents()

    def progressBarValue(self):
        return self.__progressBarValue

    progressBarValue = pyqtProperty(float, fset=progressBarSet,
                                    fget=progressBarValue)

    processingState = pyqtProperty(int, fget=lambda self: self.__progressState)

    def progressBarAdvance(self, value):
        self.progressBarSet(self.progressBarValue + value)

    def progressBarFinished(self):
        self.setWindowTitle(self.captionTitle)
        if self.__progressState != 0:
            self.__progressState = 0
            self.processingStateChanged.emit(0)

    #: Widget's status message has changed.
    statusMessageChanged = Signal(str)

    def setStatusMessage(self, text):
        if self.__statusMessage != text:
            self.__statusMessage = text
            self.statusMessageChanged.emit(text)

    def statusMessage(self):
        return self.__statusMessage

    def keyPressEvent(self, e):
        if (int(e.modifiers()), e.key()) in OWWidget.defaultKeyActions:
            OWWidget.defaultKeyActions[int(e.modifiers()), e.key()](self)
        else:
            QDialog.keyPressEvent(self, e)

    def information(self, id=0, text=None):
        self.setState("Info", id, text)

    def warning(self, id=0, text=""):
        self.setState("Warning", id, text)

    def error(self, id=0, text=""):
        self.setState("Error", id, text)

    def setState(self, state_type, id, text):
        changed = 0
        if type(id) == list:
            for val in id:
                if val in self.widgetState[state_type]:
                    self.widgetState[state_type].pop(val)
                    changed = 1
        else:
            if type(id) == str:
                text = id
                id = 0
            if not text:
                if id in self.widgetState[state_type]:
                    self.widgetState[state_type].pop(id)
                    changed = 1
            else:
                self.widgetState[state_type][id] = text
                changed = 1

        if changed:
            if type(id) == list:
                for i in id:
                    self.widgetStateChanged.emit(state_type, i, "")
            else:
                self.widgetStateChanged.emit(state_type, id, text or "")

        tooltip_lines = []
        highest_type = None
        for a_type in ("Error", "Warning", "Info"):
            msgs_for_ids = self.widgetState.get(a_type)
            if not msgs_for_ids:
                continue
            msgs_for_ids = list(msgs_for_ids.values())
            if not msgs_for_ids:
                continue
            tooltip_lines += msgs_for_ids
            if highest_type is None:
                highest_type = a_type

        if highest_type is None:
            self.set_warning_bar(None)
        elif len(tooltip_lines) == 1:
            msg = tooltip_lines[0]
            if "\n" in msg:
                self.set_warning_bar(
                    highest_type, msg[:msg.index("\n")] + " (...)", msg)
            else:
                self.set_warning_bar(
                    highest_type, tooltip_lines[0], tooltip_lines[0])
        else:
            self.set_warning_bar(
                highest_type,
                "{} problems during execution".format(len(tooltip_lines)),
                "\n".join(tooltip_lines))

        return changed

    def set_warning_bar(self, state_type, text=None, tooltip=None):
        colors = {"Error": ("#ffc6c6", "black", QStyle.SP_MessageBoxCritical),
                  "Warning": ("#ffffc9", "black", QStyle.SP_MessageBoxWarning),
                  "Info": ("#ceceff", "black", QStyle.SP_MessageBoxInformation)}
        current_height = self.height()
        if state_type is None:
            if not self.warning_bar.isHidden():
                new_height = current_height - self.warning_bar.height()
                self.warning_bar.setVisible(False)
                self.resize(self.width(), new_height)
            return
        background, foreground, icon = colors[state_type]
        style = QApplication.instance().style()
        self.warning_icon.setPixmap(style.standardIcon(icon).pixmap(14, 14))

        self.warning_bar.setStyleSheet(
            "background-color: {}; color: {};"
            "padding: 3px; padding-left: 6px; vertical-align: center".
            format(background, foreground))
        self.warning_label.setText(text)
        self.warning_label.setToolTip(tooltip)
        if self.warning_bar.isHidden():
            self.warning_bar.setVisible(True)
            new_height = current_height + self.warning_bar.height()
            self.resize(self.width(), new_height)

    def widgetStateToHtml(self, info=True, warning=True, error=True):
        pixmaps = self.getWidgetStateIcons()
        items = []
        iconPath = {"Info": "canvasIcons:information.png",
                    "Warning": "canvasIcons:warning.png",
                    "Error": "canvasIcons:error.png"}
        for show, what in [(info, "Info"), (warning, "Warning"),
                           (error, "Error")]:
            if show and self.widgetState[what]:
                items.append('<img src="%s" style="float: left;"> %s' %
                             (iconPath[what],
                              "\n".join(self.widgetState[what].values())))
        return "<br>".join(items)

    @classmethod
    def getWidgetStateIcons(cls):
        if not hasattr(cls, "_cached__widget_state_icons"):
            iconsDir = os.path.join(environ.canvas_install_dir, "icons")
            QDir.addSearchPath("canvasIcons",
                               os.path.join(environ.canvas_install_dir,
                                            "icons/"))
            info = QPixmap("canvasIcons:information.png")
            warning = QPixmap("canvasIcons:warning.png")
            error = QPixmap("canvasIcons:error.png")
            cls._cached__widget_state_icons = \
                {"Info": info, "Warning": warning, "Error": error}
        return cls._cached__widget_state_icons

    defaultKeyActions = {}

    if sys.platform == "darwin":
        defaultKeyActions = {
            (Qt.ControlModifier, Qt.Key_M):
                lambda self: self.showMaximized
                if self.isMinimized() else self.showMinimized(),
            (Qt.ControlModifier, Qt.Key_W):
                lambda self: self.setVisible(not self.isVisible())}

    def setBlocking(self, state=True):
        """ Set blocking flag for this widget. While this flag is set this
        widget and all its descendants will not receive any new signals from
        the signal manager
        """
        if self.__blocking != state:
            self.__blocking = state
            self.blockingStateChanged.emit(state)

    def isBlocking(self):
        """ Is this widget blocking signal processing.
        """
        return self.__blocking

    def resetSettings(self):
        self.settingsHandler.reset_settings(self)
Example #6
0
 def showMessage(self, message, timeout):
     if settings.SHOW_STATUS_NOTIFICATIONS:
         self._widgetStatus.hide()
         self._replaceWidget.setVisible(False)
         self.show()
         QStatusBar.showMessage(self, message, timeout)
Example #7
0
class MainWindow(QMainWindow):
	def __init__(self):
		QMainWindow.__init__(self)
		
		self.sync_delay = 5
		self.sync_active = False
		self.verbose = False

		self.timePattern = re.compile('\.[0-9]+$')
		
		self.setWindowTitle('%s %s' % (QApplication.applicationName(), QApplication.applicationVersion()));
		
		self.widget = QWidget()
		self.setCentralWidget(self.widget)

		self.statusBar = QStatusBar(self)
		self.setStatusBar(self.statusBar)

		self.mAction = self.menuBar().addMenu(self.tr("&Action"))
		#self.mAction.addAction(self.tr("&update"), self.updateTplTable(), QKeySequence('F5'))
		self.mAction.addAction(self.tr('&import records'), self.onImport, QKeySequence('F6'))
		self.mAction.addAction(self.tr('edit &settings'), self.onSettings, QKeySequence('F8'))
		self.mAction.addAction(self.tr("e&xit"), self.onExit, 'Ctrl+Q')

		self.mAbout = self.menuBar().addMenu(self.tr("&about"))
		self.mAbout.addAction(QApplication.applicationName(), self.onAboutAppAction)
		self.mAbout.addAction("Qt", self.onAboutQtAction)
		
		self.pageForwardButton = QPushButton(self)
		self.pageForwardButton.setText('>')
		self.connect(self.pageForwardButton, SIGNAL('clicked()'), self.pageForward)

		self.pageBackwardButton = QPushButton(self)
		self.pageBackwardButton.setText('<')
		self.connect(self.pageBackwardButton, SIGNAL('clicked()'), self.pageBackward)
		
		self.timer = QTimer(self)
		self.timer.setInterval(1000)
		self.connect(self.timer, SIGNAL('timeout()'), self, SLOT('onTimer()'))
		self.time_begin = datetime.now()
		self.time_end = datetime.now()

		self.db_path = os.path.join(os.path.dirname(sys.argv[0]) if os.name != 'posix' else os.path.expanduser('~'), '.tt2.db')
		self.db = sqlite3.connect(self.db_path)
		self.cursor = self.db.cursor()
		
		try:
			self.cursor.execute('SELECT id FROM tt LIMIT 1')
		except:
			self.createDb()
		
		self.settings = self.fetchSettings()
		self.syncer = Syncer(self.db_path, self)
		self.connect( self.syncer, SIGNAL('active'), self.setSyncerActive )
		self.connect( self.syncer, SIGNAL('message'), self.msg )
		self.connect( self.syncer, SIGNAL('newSettings'), self.fetchSettings )
		
		self.layout = QGridLayout(self.widget)
		
		self.descriptionLabel = QLabel(self.widget)
		self.descriptionLabel.setText('Beschreibung')
		self.descriptionLabel.setMaximumHeight( self.font().pointSize() * 2 )
		self.descriptionInput = QLineEdit(self.widget)
		self.updateDescriptionEditCompleter()				
		self.noteLabel = QLabel(self.widget)
		self.noteLabel.setText('Notiz')
		self.noteLabel.setMaximumHeight( self.font().pointSize() * 2 )
		self.noteInput = QLineEdit(self.widget)
		self.startStopButton = QPushButton(self.widget)
		self.startStopButton.setText('Start')
		
		self.tableView = TplTable(self, int( self.getSetting('displayrows', DEFAULTROWS) ) )

		self.pageForwardAction = QAction(self)
		self.pageForwardAction.setShortcut(QKeySequence('Right'))
		self.connect(self.pageForwardAction, SIGNAL('triggered()'), self.pageForward);
		self.pageForwardButton.addAction(self.pageForwardAction)

		self.pageBackwardAction = QAction(self)
		self.pageBackwardAction.setShortcut(QKeySequence('Left'))
		self.connect(self.pageBackwardAction, SIGNAL('triggered()'), self.pageBackward);
		self.pageBackwardButton.addAction(self.pageBackwardAction)

		self.updateTplTable()
			
		self.layout.addWidget(self.descriptionLabel, 0, 0, 1, 1)
		self.layout.addWidget(self.descriptionInput, 1, 0, 1, 1)
		self.layout.addWidget(self.noteLabel, 0, 1, 1, 1)
		self.layout.addWidget(self.noteInput, 1, 1, 1, 1)
		self.layout.addWidget(self.startStopButton, 2, 0, 1, 2)
		self.layout.addWidget(self.tableView, 3,0,1,2)
		self.layout.addWidget(self.pageBackwardButton, 4, 0, 1, 1)
		self.layout.addWidget(self.pageForwardButton, 4, 1, 1, 1)

		self.connect(self.descriptionInput, SIGNAL('returnPressed ()'), self.onStartStop )
		self.connect(self.noteInput, SIGNAL('returnPressed ()'), self.onStartStop )
		self.connect(self.startStopButton, SIGNAL('clicked()'), self.onStartStop )
		self.connect(self.tableView, SIGNAL('valueChanged(int)'), self.onValueChanged )
		self.connect(self.tableView, SIGNAL('del(int)'), self.onDelete )

		self.last_sync = datetime.now()
		self.sync()
	
	def __del__(self):
		pass

	def setSyncerActive(self, active):
		if not active:
			self.last_sync = datetime.now()
		self.sync_active = active

	def msg(self, msg, timeout = 0):
		#print(msg)
		self.statusBar.showMessage(msg, timeout)

	def sync(self):
		if self.getSetting('syncEnabled','False') == 'False':
			return

		# reset delay if still active
		if self.sync_active:
			self.last_sync = datetime.now()

		if datetime.now() < ( self.last_sync + timedelta( seconds = self.sync_delay ) ):
			try:
				#print 'cancel'
				self.t.cancel()
			except:
				pass
			#print 'start +',( self.last_sync + timedelta( seconds = self.sync_delay ) - datetime.now() ).seconds + 1
			self.t = Timer( ( self.last_sync + timedelta( seconds = self.sync_delay ) - datetime.now() ).seconds + 1 ,self.sync)
			self.t.start()
		else:
		#	print 'start syncer instance'
			Thread(target=self.syncer.do_sync).start()


	def createDb(self):
		try:
			self.q('''CREATE TABLE tt (
			id INTEGER PRIMARY KEY AUTOINCREMENT,
			remote_id INTEGER,
			time_begin INTEGER,
			time_end INTEGER,
			description STRING,
			note STRING DEFAULT "",
			is_new INTEGER DEFAULT 1,
			need_update INTEGER DEFAULT 0,
			is_delete INTEGER DEFAULT 0
			)''')
			self.q('''CREATE TABLE settings (
			key STRING UNIQUE,
			value STRING
			)''')
			self.q('CREATE INDEX idx_time_begin ON tt (time_begin)')
		except:
			self.statusBar.showMessage('error creating Database!')
		else:
			self.statusBar.showMessage('Table tt created successfully')
			
	def q(self, query):
		try:
			self.cursor.execute(query)
		except Exception as e:
			print( e )
			self.statusBar.showMessage('query execution failed "%s"' % query)
		else:
			self.db.commit()
	
	def updateTplTable(self):
		self.q('SELECT id,time_begin,time_end,description,note FROM tt WHERE is_delete != 1 ORDER BY time_begin DESC LIMIT %d' % ( int( self.getSetting( 'displayrows', DEFAULTROWS ) ) ) )
		self.tableView.set(self.cursor.fetchall())
		
	def updateDescriptionEditCompleter(self):
		self.q('SELECT DISTINCT description FROM tt WHERE is_delete != 1')
		#words = QStringList()
		words = []
		for word in self.cursor.fetchall():
			words.append(str(word[0]))
		self.descriptionInput.setCompleter(QCompleter(words, self))

	@pyqtSlot()
	def pageForward(self):
		self.q('SELECT MIN(time_begin) FROM tt')
		if not self.tableView.getLastTime() == self.cursor.fetchone()[0]:
			sql = 'SELECT id,time_begin,time_end,description,note FROM tt WHERE is_delete != 1 AND time_begin < %d ORDER BY time_begin DESC LIMIT %s' % ( self.tableView.getLastTime(), int( self.getSetting( 'displayrows', DEFAULTROWS ) ) )
			if self.verbose:
				print( sql )
			self.q( sql )
			self.tableView.set(self.cursor.fetchall())

	@pyqtSlot()
	def pageBackward(self):
		self.q('SELECT MAX(time_begin) FROM tt')
		if not self.tableView.getFirstTime() == self.cursor.fetchone()[0]:
			sql = 'SELECT * FROM ( SELECT id,time_begin,time_end,description,note FROM tt WHERE is_delete != 1 AND time_begin > %d ORDER BY time_begin LIMIT %s ) as tbl ORDER BY time_begin DESC' % ( self.tableView.getFirstTime(), int( self.getSetting( 'displayrows', DEFAULTROWS ) ) )
			if self.verbose:
				print( sql )
			self.q( sql )
			self.tableView.set(self.cursor.fetchall())
		
	@pyqtSlot()
	def onExit(self):
		QApplication.exit();

	@pyqtSlot()
	def onValueChanged(self, _id):
		if self.verbose:
			print('changed:', _id)
			print(self.tableView.get(_id))
		data = self.tableView.get(_id)
		self.q('''
				UPDATE tt
				SET time_begin = %d,
				time_end = %d,
				description = '%s',
				note = '%s',
				need_update = 1
				WHERE
				id = %d
				'''	% ( data[1], data[2], data[3], data[4], data[0] ))
		self.updateDescriptionEditCompleter()
		self.sync()

	@pyqtSlot()
	def onDelete(self, _id):
		if self.verbose:
			print('del:', _id,self.tableView.get(_id)[0])
		self.q('UPDATE tt SET is_delete = 1 WHERE id = %d' % self.tableView.get(_id)[0])
		self.updateTplTable()
		self.updateDescriptionEditCompleter()				
		self.sync()
		
	@pyqtSlot()
	def onTimer(self):
		self.startStopButton.setText('Stop (%s)' % self.timePattern.sub( '', str( datetime.now() - self.time_begin ) ) )

	@pyqtSlot()
	def onStartStop(self):
		if self.timer.isActive():
			self.timer.stop()
			self.time_end = datetime.now()
			self.q('''
				INSERT INTO tt
				(time_begin,time_end,description,note)
				VALUES
				('%d','%d','%s','%s')
				'''	% ( int(mktime(self.time_begin.timetuple())), int(mktime(self.time_end.timetuple())), self.descriptionInput.text(), self.noteInput.text() ))
			self.noteInput.clear()
			self.updateTplTable()
			self.updateDescriptionEditCompleter()				
			self.startStopButton.setText('Start')
			self.sync()
		else:
			self.time_begin = datetime.now()
			self.timer.start()
			self.onTimer()

	def onAboutAppAction(self):
		QMessageBox.about(self, self.tr("&about"), self.tr("%1 version %2").arg(QApplication.applicationName()).arg(QApplication.applicationVersion()))
		
	def onAboutQtAction(self):
		QMessageBox.aboutQt(self, self.tr("&about"))

	def checkSync(self):
		if self.sync_active:
			QMessageBox.information(self, 'Information', '''Sync is currently active. Please wait until it's finished''')
			return False
		else:
			return True

	def onSettings(self):
		if not self.checkSync():
			return
		settings = self.fetchSettings()

		inp = SettingsWidget(settings, self)
		if inp.exec_():
			for key in inp.data():
				if type( inp.data()[key] ) == bytes:
					value = inp.data()[key].decode( 'UTF-8' )
				else:
					value = inp.data()[key]
				self.q( '''REPLACE INTO settings VALUES ('%s','%s')''' % ( key, value ) )
			try: dr = settings['displayrows']
			except: dr = None
			if dr != inp.data()['displayrows']:
				QMessageBox.information(self, 'displayrows changed...', 'exiting now.')
				sys.exit(0)
		self.settings = self.fetchSettings()

	def onImport(self):
		if not self.checkSync():
			return
		ImportWidget( self.db_path, self ).exec_()
		self.updateTplTable()
		self.updateDescriptionEditCompleter()				

	def fetchSettings(self):
		s = {}
		self.q( 'SELECT key,value FROM settings' )
		for key,value in self.cursor.fetchall():
			s[key] = value
		return s

	def getSetting(self,key,default = None):
		try:
			v = self.settings[key]
		except:
			if default != None:
				v = default
			else:
				v = ''
		return v
Example #8
0
class QTSeedEditor(QDialog):
    """
    DICOM viewer.
    """
    @staticmethod
    def get_line(mode='h'):
        line = QFrame()
        if mode == 'h':
            line.setFrameStyle(QFrame.HLine)
        elif mode == 'v':
            line.setFrameStyle(QFrame.VLine)

        line.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding)

        return line

    def initUI(self, shape, vscale, height=600, mode='seed'):
        """
        Initialize UI.

        Parameters
        ----------
        shape : (int, int, int)
            Shape of data matrix.
        vscale : (float, float, float)
            Voxel scaling.
        height : int
            Maximal slice height in pixels.
        mode : str
            Editor mode.
        """

        # picture
        grid = height / float(shape[1] * vscale[1])
        mgrid = (grid * vscale[0], grid * vscale[1])
        self.slice_box = SliceBox(shape[:-1], mgrid, mode)
        self.slice_box.setScrollFun(self.scrollSlices)
        self.connect(self.slice_box, SIGNAL('focus_slider'),
                     self.focusSliceSlider)

        # sliders
        self.allow_select_slice = True
        self.n_slices = shape[2]
        self.slider = QSlider(Qt.Vertical)
        self.slider.label = QLabel()
        self.slider.label.setText('Slice: %d / %d' %
                                  (self.actual_slice, self.n_slices))
        self.slider.setRange(1, self.n_slices)
        self.slider.valueChanged.connect(self.sliderSelectSlice)
        self.slider.setValue(self.actual_slice)

        self.slider_cw = {}
        self.slider_cw['c'] = QSlider(Qt.Horizontal)
        self.slider_cw['c'].valueChanged.connect(self.changeC)
        self.slider_cw['c'].label = QLabel()

        self.slider_cw['w'] = QSlider(Qt.Horizontal)
        self.slider_cw['w'].valueChanged.connect(self.changeW)
        self.slider_cw['w'].label = QLabel()

        self.view_label = QLabel('View size: %d x %d' %
                                 self.img_aview.shape[:-1])
        self.voxel_label = QLabel('Voxel size [mm]:\n  %.2f x %.2f x %.2f'\
                                      % tuple(self.voxel_size[np.array(self.act_transposition)]))

        combo_view_options = VIEW_TABLE.keys()
        combo_view = QComboBox(self)
        combo_view.activated[str].connect(self.setView)
        combo_view.addItems(combo_view_options)

        # buttons
        self.btn_quit = QPushButton("Return", self)
        self.btn_quit.clicked.connect(self.quit)

        combo_dmask = QComboBox(self)
        combo_dmask.activated.connect(self.changeMask)
        self.mask_points_tab, aux = self.init_draw_mask(DRAW_MASK, mgrid)
        for icon, label in aux:
            combo_dmask.addItem(icon, label)

        self.slice_box.setMaskPoints(
            self.mask_points_tab[combo_dmask.currentIndex()])

        self.status_bar = QStatusBar()

        self.seeds_copy = None
        vopts = []
        vmenu = []
        appmenu = []
        if mode == 'seed' and self.mode_fun is not None:
            btn_recalc = QPushButton("Recalculate", self)
            btn_recalc.clicked.connect(self.recalculate)
            self.btn_save = QPushButton("Save seeds", self)
            self.btn_save.clicked.connect(self.saveload_seeds)
            btn_s2b = QPushButton("Seg. to bckgr.", self)
            btn_s2b.clicked.connect(self.seg_to_background_seeds)
            btn_s2f = QPushButton("Seg. to forgr.", self)
            btn_s2f.clicked.connect(self.seg_to_foreground_seeds)
            appmenu.append(
                QLabel('<b>Segmentation mode</b><br><br><br>' +
                       'Select the region of interest<br>' +
                       'using the mouse buttons:<br><br>' +
                       '&nbsp;&nbsp;<i>left</i> - inner region<br>' +
                       '&nbsp;&nbsp;<i>right</i> - outer region<br><br>'))
            appmenu.append(btn_recalc)
            appmenu.append(self.btn_save)
            appmenu.append(btn_s2f)
            appmenu.append(btn_s2b)
            appmenu.append(QLabel())
            self.volume_label = QLabel('Volume:\n  unknown')
            appmenu.append(self.volume_label)

            # Set middle pencil as default (M. Jirik)
            combo_dmask.setCurrentIndex(1)
            self.slice_box.setMaskPoints(
                self.mask_points_tab[combo_dmask.currentIndex()])
            #  -----mjirik---end------

        if mode == 'seed' or mode == 'crop'\
                or mode == 'mask' or mode == 'draw':

            combo_seed_label_options = ['all', '1', '2', '3', '4']
            combo_seed_label = QComboBox(self)
            combo_seed_label.activated[str].connect(self.changeFocusedLabel)
            combo_seed_label.addItems(combo_seed_label_options)
            self.changeFocusedLabel(
                combo_seed_label_options[combo_seed_label.currentIndex()])
            # vopts.append(QLabel('Label to delete:'))
            # vopts.append(combo_seed_label)
            vmenu.append(QLabel('Label to delete:'))
            vmenu.append(combo_seed_label)

            btn_del = QPushButton("Del Slice Seeds", self)
            btn_del.clicked.connect(self.deleteSliceSeeds)
            vmenu.append(None)
            vmenu.append(btn_del)
            btn_del = QPushButton("Del All Seeds", self)
            btn_del.clicked.connect(self.deleteSeedsInAllImage)
            vmenu.append(None)
            vmenu.append(btn_del)

            combo_contour_options = ['fill', 'contours', 'off']
            combo_contour = QComboBox(self)
            combo_contour.activated[str].connect(self.changeContourMode)
            combo_contour.addItems(combo_contour_options)
            self.changeContourMode(
                combo_contour_options[combo_contour.currentIndex()])
            vopts.append(QLabel('Selection mode:'))
            vopts.append(combo_contour)

        if mode == 'mask':
            btn_recalc_mask = QPushButton("Recalculate mask", self)
            btn_recalc_mask.clicked.connect(self.updateMaskRegion_btn)
            btn_all = QPushButton("Select all", self)
            btn_all.clicked.connect(self.maskSelectAll)
            btn_reset = QPushButton("Reset selection", self)
            btn_reset.clicked.connect(self.resetSelection)
            btn_reset_seads = QPushButton("Reset seads", self)
            btn_reset_seads.clicked.connect(self.resetSeads)
            btn_add = QPushButton("Add selection", self)
            btn_add.clicked.connect(self.maskAddSelection)
            btn_rem = QPushButton("Remove selection", self)
            btn_rem.clicked.connect(self.maskRemoveSelection)
            btn_mask = QPushButton("Mask region", self)
            btn_mask.clicked.connect(self.maskRegion)
            appmenu.append(
                QLabel('<b>Mask mode</b><br><br><br>' +
                       'Select the region to mask<br>' +
                       'using the left mouse button<br><br>'))
            appmenu.append(self.get_line('h'))
            appmenu.append(btn_recalc_mask)
            appmenu.append(btn_all)
            appmenu.append(btn_reset)
            appmenu.append(btn_reset_seads)
            appmenu.append(self.get_line('h'))
            appmenu.append(btn_add)
            appmenu.append(btn_rem)
            appmenu.append(self.get_line('h'))
            appmenu.append(btn_mask)
            appmenu.append(self.get_line('h'))
            self.mask_qhull = None

        if mode == 'crop':
            btn_crop = QPushButton("Crop", self)
            btn_crop.clicked.connect(self.crop)
            appmenu.append(
                QLabel('<b>Crop mode</b><br><br><br>' +
                       'Select the crop region<br>' +
                       'using the left mouse button<br><br>'))
            appmenu.append(btn_crop)

        if mode == 'draw':
            appmenu.append(
                QLabel('<b>Manual segmentation<br> mode</b><br><br><br>' +
                       'Mark the region of interest<br>' +
                       'using the mouse buttons:<br><br>' +
                       '&nbsp;&nbsp;<i>left</i> - draw<br>' +
                       '&nbsp;&nbsp;<i>right</i> - erase<br>' +
                       '&nbsp;&nbsp;<i>middle</i> - vol. erase<br><br>'))

            btn_reset = QPushButton("Reset", self)
            btn_reset.clicked.connect(self.resetSliceDraw)
            vmenu.append(None)
            vmenu.append(btn_reset)

            combo_erase_options = ['inside', 'outside']
            combo_erase = QComboBox(self)
            combo_erase.activated[str].connect(self.changeEraseMode)
            combo_erase.addItems(combo_erase_options)
            self.changeEraseMode(
                combo_erase_options[combo_erase.currentIndex()])
            vopts.append(QLabel('Volume erase mode:'))
            vopts.append(combo_erase)

        hbox = QHBoxLayout()
        vbox = QVBoxLayout()
        vbox_left = QVBoxLayout()
        vbox_app = QVBoxLayout()

        hbox.addWidget(self.slice_box)
        hbox.addWidget(self.slider)
        vbox_left.addWidget(self.slider.label)
        vbox_left.addWidget(self.view_label)
        vbox_left.addWidget(self.voxel_label)
        vbox_left.addWidget(QLabel())
        vbox_left.addWidget(QLabel('View plane:'))
        vbox_left.addWidget(combo_view)
        vbox_left.addWidget(self.get_line())
        vbox_left.addWidget(self.slider_cw['c'].label)
        vbox_left.addWidget(self.slider_cw['c'])
        vbox_left.addWidget(self.slider_cw['w'].label)
        vbox_left.addWidget(self.slider_cw['w'])
        vbox_left.addWidget(self.get_line())
        vbox_left.addWidget(QLabel('Drawing mask:'))
        vbox_left.addWidget(combo_dmask)

        for ii in vopts:
            vbox_left.addWidget(ii)

        for ii in vmenu:
            if ii is None:
                vbox_left.addStretch(1)

            else:
                vbox_left.addWidget(ii)

        for ii in appmenu:
            if ii is None:
                vbox_app.addStretch(1)

            else:
                vbox_app.addWidget(ii)

        vbox_app.addStretch(1)
        vbox_app.addWidget(self.btn_quit)

        hbox.addLayout(vbox_left)
        hbox.addWidget(self.get_line('v'))
        hbox.addLayout(vbox_app)
        vbox.addLayout(hbox)
        vbox.addWidget(self.status_bar)
        self.my_layout = vbox
        self.setLayout(vbox)

        self.setWindowTitle('Segmentation Editor')
        self.show()

    def __init__(self,
                 img,
                 viewPositions=None,
                 seeds=None,
                 contours=None,
                 mode='seed',
                 modeFun=None,
                 voxelSize=[1, 1, 1],
                 volume_unit='mm3'):
        """
        Initiate Editor

        Parameters
        ----------
        img : array
            DICOM data matrix.
        actualSlice : int
            Index of actual slice.
        seeds : array
            Seeds, user defined regions of interest.
        contours : array
            Computed segmentation.
        mode : str
            Editor modes:
               'seed' - seed editor
               'crop' - manual crop
               'draw' - drawing
               'mask' - mask region
        modeFun : fun
            Mode function invoked by user button.
        voxelSize : tuple of float
            voxel size [mm]
        volume_unit : allow select output volume in mililiters or mm3
            [mm, ml]
        """

        QDialog.__init__(self)

        self.BACKGROUND_NOMODEL_SEED_LABEL = 4
        self.FOREGROUND_NOMODEL_SEED_LABEL = 3

        self.mode = mode
        self.mode_fun = modeFun

        self.actual_view = 'axial'
        self.act_transposition = VIEW_TABLE[self.actual_view]
        self.img = img
        self.img_aview = self.img.transpose(self.act_transposition)

        self.volume_unit = volume_unit

        self.last_view_position = {}
        for jj, ii in enumerate(VIEW_TABLE.keys()):
            if viewPositions is None:
                viewpos = img.shape[VIEW_TABLE[ii][-1]] / 2

            else:
                viewpos = viewPositions[jj]

            self.last_view_position[ii] =\
                img.shape[VIEW_TABLE[ii][-1]] - viewpos - 1

        self.actual_slice = self.last_view_position[self.actual_view]

        # set contours
        self.contours = contours
        if self.contours is None:
            self.contours_aview = None
        else:
            self.contours_aview = self.contours.transpose(
                self.act_transposition)

        # masked data - has information about which data were removed
        # 1 == enabled, 0 == deleted
        # How to return:
        #       editorDialog.exec_()
        #       masked_data = editorDialog.masked
        self.masked = np.ones(self.img.shape, np.int8)

        self.voxel_size = np.squeeze(np.asarray(voxelSize))
        self.voxel_scale = self.voxel_size / float(np.min(self.voxel_size))
        self.voxel_volume = np.prod(voxelSize)

        # set seeds
        if seeds is None:
            self.seeds = np.zeros(self.img.shape, np.int8)
        else:
            self.seeds = seeds

        self.seeds_aview = self.seeds.transpose(self.act_transposition)
        self.seeds_modified = False

        self.initUI(self.img_aview.shape,
                    self.voxel_scale[np.array(self.act_transposition)], 600,
                    mode)

        if mode == 'draw':
            self.seeds_orig = self.seeds.copy()
            self.slice_box.setEraseFun(self.eraseVolume)

        # set view window values C/W
        lb = np.min(img)
        self.img_min_val = lb
        ub = np.max(img)
        dul = np.double(ub) - np.double(lb)
        self.cw_range = {'c': [lb, ub], 'w': [1, dul]}
        self.slider_cw['c'].setRange(lb, ub)
        self.slider_cw['w'].setRange(1, dul)
        self.changeC(lb + dul / 2)
        self.changeW(dul)

        self.offset = np.zeros((3, ), dtype=np.int16)
        # set what labels will be deleted by 'delete seeds' button
        self.textFocusedLabel = "all"

    def showStatus(self, msg):
        self.status_bar.showMessage(QString(msg))
        QApplication.processEvents()

    def init_draw_mask(self, draw_mask, grid):
        mask_points = []
        mask_iconlabel = []
        for mask, label in draw_mask:
            w, h = mask.shape
            xx, yy = mask.nonzero()
            mask_points.append((xx - w / 2, yy - h / 2))

            img = QImage(w, h, QImage.Format_ARGB32)
            img.fill(qRgba(255, 255, 255, 0))
            for ii in range(xx.shape[0]):
                img.setPixel(xx[ii], yy[ii], qRgba(0, 0, 0, 255))

            img = img.scaled(QSize(w * grid[0], h * grid[1]))
            icon = QIcon(QPixmap.fromImage(img))
            mask_iconlabel.append((icon, label))

        return mask_points, mask_iconlabel

    def saveSliceSeeds(self):
        aux = self.slice_box.getSliceSeeds()
        if aux is not None:
            self.seeds_aview[..., self.actual_slice] = aux
            self.seeds_modified = True

        else:
            self.seeds_modified = False

    def updateMaskRegion_btn(self):
        self.saveSliceSeeds()
        self.updateMaskRegion()

    def updateMaskRegion(self):
        crp = self.getCropBounds(return_nzs=True)
        if crp is not None:
            off, cri, nzs = crp
            if nzs[0].shape[0] <= 5:
                self.showStatus("Not enough points (need >= 5)!")

            else:
                points = np.transpose(nzs)
                hull = Delaunay(points)
                X, Y, Z = np.mgrid[cri[0], cri[1], cri[2]]
                grid = np.vstack([X.ravel(), Y.ravel(), Z.ravel()]).T
                simplex = hull.find_simplex(grid)
                fill = grid[simplex >= 0, :]
                fill = (fill[:, 0], fill[:, 1], fill[:, 2])
                if self.contours is None or self.contours_old is None:
                    self.contours = np.zeros(self.img.shape, np.int8)
                    self.contours_old = self.contours.copy()
                else:
                    self.contours[self.contours != 2] = 0
                self.contours[fill] = 1
                self.contours_aview = self.contours.transpose(
                    self.act_transposition)

                self.selectSlice(self.actual_slice)

    def maskRegion(self):
        self.masked[self.contours == 0] = 0

        self.img[self.contours != 2] = self.img_min_val
        self.contours.fill(0)
        self.contours_old = self.contours.copy()
        self.seeds.fill(0)
        self.selectSlice(self.actual_slice)

    def maskAddSelection(self):
        self.updateMaskRegion()
        if self.contours is None:
            return

        self.contours[self.contours == 1] = 2
        self.contours_old = self.contours.copy()
        self.seeds.fill(0)
        self.selectSlice(self.actual_slice)

    def maskRemoveSelection(self):
        self.updateMaskRegion()
        if self.contours is None:
            return

        self.contours[self.contours == 1] = 0
        self.contours_old = self.contours.copy()
        self.seeds.fill(0)
        self.selectSlice(self.actual_slice)

    def maskSelectAll(self):
        self.updateMaskRegion()

        self.seeds[0][0][0] = 1
        self.seeds[0][0][-1] = 1
        self.seeds[0][-1][0] = 1
        self.seeds[0][-1][-1] = 1
        self.seeds[-1][0][0] = 1
        self.seeds[-1][0][-1] = 1
        self.seeds[-1][-1][0] = 1
        self.seeds[-1][-1][-1] = 1

        self.updateMaskRegion()
        self.selectSlice(self.actual_slice)

    def resetSelection(self):
        self.updateMaskRegion()
        if self.contours is None:
            return

        self.contours.fill(0)
        self.contours_old = self.contours.copy()
        self.seeds.fill(0)
        self.selectSlice(self.actual_slice)

    def resetSeads(self):
        self.seeds.fill(0)
        if self.contours is not None:
            self.contours = self.contours_old.copy()
            self.contours_aview = self.contours.transpose(
                self.act_transposition)
        self.updateMaskRegion()
        self.selectSlice(self.actual_slice)

    def updateCropBounds(self):
        crp = self.getCropBounds()
        if crp is not None:
            _, cri = crp
            self.contours = np.zeros(self.img.shape, np.int8)
            self.contours[cri].fill(1)
            self.contours_aview = self.contours.transpose(
                self.act_transposition)

    def focusSliceSlider(self):
        self.slider.setFocus(True)

    def sliderSelectSlice(self, value):
        self.selectSlice(self.n_slices - value)

    def scrollSlices(self, inc):
        if abs(inc) > 0:
            new = self.actual_slice + inc
            self.selectSlice(new)

    def selectSlice(self, value, force=False):
        if not (self.allow_select_slice):
            return

        if (value < 0) or (value >= self.n_slices):
            return

        if (value != self.actual_slice) or force:
            self.saveSliceSeeds()
            if self.seeds_modified:
                if self.mode == 'crop':
                    self.updateCropBounds()

                elif self.mode == 'mask':
                    self.updateMaskRegion()

        if self.contours is None:
            contours = None

        else:
            contours = self.contours_aview[..., value]

        slider_val = self.n_slices - value
        self.slider.setValue(slider_val)
        self.slider.label.setText('Slice: %d / %d' %
                                  (slider_val, self.n_slices))

        self.slice_box.setSlice(self.img_aview[..., value],
                                self.seeds_aview[..., value], contours)
        self.actual_slice = value

    def getSeeds(self):
        return self.seeds

    def getImg(self):
        return self.img

    def getOffset(self):
        return self.offset * self.voxel_size

    def getSeedsVal(self, label):
        return self.img[self.seeds == label]

    def getContours(self):
        return self.contours

    def setContours(self, contours):
        """
        store segmentation
        :param contours: segmentation
        :return: Nothing
        """
        """
        :param contours:
        :return:
        """
        self.contours = contours
        self.contours_aview = self.contours.transpose(self.act_transposition)

        self.selectSlice(self.actual_slice)

    def changeCW(self, value, key):
        rg = self.cw_range[key]
        if (value < rg[0]) or (value > rg[1]):
            return

        if (value != self.slice_box.getCW()[key]):
            self.slider_cw[key].setValue(value)
            self.slider_cw[key].label.setText('%s: %d' % (key.upper(), value))
            self.slice_box.setCW(value, key)
            self.slice_box.updateSliceCW(self.img_aview[...,
                                                        self.actual_slice])

    def changeC(self, value):
        self.changeCW(value, 'c')

    def changeW(self, value):
        self.changeCW(value, 'w')

    def setView(self, value):
        self.last_view_position[self.actual_view] = self.actual_slice
        # save seeds
        self.saveSliceSeeds()
        if self.seeds_modified:
            if self.mode == 'crop':
                self.updateCropBounds()

            elif self.mode == 'mask':
                self.updateMaskRegion()

        key = str(value)
        self.actual_view = key
        self.actual_slice = self.last_view_position[key]

        self.act_transposition = VIEW_TABLE[key]
        self.img_aview = self.img.transpose(self.act_transposition)
        self.seeds_aview = self.seeds.transpose(self.act_transposition)

        if self.contours is not None:
            self.contours_aview = self.contours.transpose(
                self.act_transposition)
            contours = self.contours_aview[..., self.actual_slice]

        else:
            contours = None

        vscale = self.voxel_scale[np.array(self.act_transposition)]
        height = self.slice_box.height()
        grid = height / float(self.img_aview.shape[1] * vscale[1])
        # width = (self.img_aview.shape[0] * vscale[0])[0]
        # if width > 800:
        #     height = 400
        #     grid = height / float(self.img_aview.shape[1] * vscale[1])

        mgrid = (grid * vscale[0], grid * vscale[1])

        self.slice_box.resizeSlice(new_slice_size=self.img_aview.shape[:-1],
                                   new_grid=mgrid)

        self.slice_box.setSlice(self.img_aview[..., self.actual_slice],
                                self.seeds_aview[...,
                                                 self.actual_slice], contours)

        self.allow_select_slice = False
        self.n_slices = self.img_aview.shape[2]
        slider_val = self.n_slices - self.actual_slice
        self.slider.setRange(1, self.n_slices)
        self.slider.setValue(slider_val)
        self.allow_select_slice = True

        self.slider.label.setText('Slice: %d / %d' %
                                  (slider_val, self.n_slices))
        self.view_label.setText('View size: %d x %d' %
                                self.img_aview.shape[:-1])

        self.adjustSize()
        self.adjustSize()

    def changeMask(self, val):
        self.slice_box.setMaskPoints(self.mask_points_tab[val])

    def changeContourMode(self, val):
        self.slice_box.contour_mode = str(val)
        self.slice_box.updateSlice()

    def changeEraseMode(self, val):
        self.slice_box.erase_mode = str(val)

    def eraseVolume(self, pos, mode):
        self.showStatus("Processing...")
        xyz = np.array(pos + (self.actual_slice, ))
        p = np.zeros_like(xyz)
        p[np.array(self.act_transposition)] = xyz
        p = tuple(p)

        if self.seeds[p] > 0:
            if mode == 'inside':
                erase_reg(self.seeds, p, val=0)

            elif mode == 'outside':
                erase_reg(self.seeds, p, val=-1)
                idxs = np.where(self.seeds < 0)
                self.seeds.fill(0)
                self.seeds[idxs] = 1

        if self.contours is None:
            contours = None

        else:
            contours = self.contours_aview[..., self.actual_slice]

        self.slice_box.setSlice(self.img_aview[..., self.actual_slice],
                                self.seeds_aview[...,
                                                 self.actual_slice], contours)

        self.showStatus("Done")

    def cropUpdate(self, img):

        for ii in VIEW_TABLE.keys():
            self.last_view_position[ii] = 0
        self.actual_slice = 0

        self.img = img
        self.img_aview = self.img.transpose(self.act_transposition)

        self.contours = None
        self.contours_aview = None

        self.seeds = np.zeros(self.img.shape, np.int8)
        self.seeds_aview = self.seeds.transpose(self.act_transposition)
        self.seeds_modified = False

        vscale = self.voxel_scale[np.array(self.act_transposition)]
        height = self.slice_box.height()
        grid = height / float(self.img_aview.shape[1] * vscale[1])
        mgrid = (grid * vscale[0], grid * vscale[1])

        self.slice_box.resizeSlice(new_slice_size=self.img_aview.shape[:-1],
                                   new_grid=mgrid)

        self.slice_box.setSlice(self.img_aview[..., self.actual_slice],
                                self.seeds_aview[..., self.actual_slice], None)

        self.allow_select_slice = False
        self.n_slices = self.img_aview.shape[2]
        self.slider.setValue(self.actual_slice + 1)
        self.slider.setRange(1, self.n_slices)
        self.allow_select_slice = True

        self.slider.label.setText('Slice: %d / %d' %
                                  (self.actual_slice + 1, self.n_slices))
        self.view_label.setText('View size: %d x %d' %
                                self.img_aview.shape[:-1])

    def getCropBounds(self, return_nzs=False, flat=False):

        nzs = self.seeds.nonzero()
        cri = []
        flag = True
        for ii in range(3):
            if nzs[ii].shape[0] == 0:
                flag = False
                break

            smin, smax = np.min(nzs[ii]), np.max(nzs[ii])
            if not (flat):
                if smin == smax:
                    flag = False
                    break

            cri.append((smin, smax))

        if flag:
            cri = np.array(cri)

            out = []
            offset = []
            for jj, ii in enumerate(cri):
                out.append(slice(ii[0], ii[1] + 1))
                offset.append(ii[0])

            if return_nzs:
                return np.array(offset), tuple(out), nzs

            else:
                return np.array(offset), tuple(out)

        else:
            return None

    def crop(self):
        self.showStatus("Processing...")

        crp = self.getCropBounds()

        if crp is not None:
            offset, cri = crp
            crop = self.img[cri]
            self.img = np.ascontiguousarray(crop)
            self.offset += offset

            self.showStatus('Done')

        else:
            self.showStatus('Region not selected!')

        self.cropUpdate(self.img)

    def seg_to_background_seeds(self, event):
        self.saveSliceSeeds()
        self.seeds[self.seeds < 3] = 0

        from PyQt4.QtCore import pyqtRemoveInputHook
        # pyqtRemoveInputHook()
        # import ipdb; ipdb.set_trace()
        self.seeds[(self.contours == 1)
                   & (self.seeds < 3)] = self.BACKGROUND_NOMODEL_SEED_LABEL
        self.contours[...] = 0

    def seg_to_foreground_seeds(self, event):
        self.saveSliceSeeds()
        self.seeds[self.seeds < 3] = 0
        # from PyQt4.QtCore import pyqtRemoveInputHook
        # pyqtRemoveInputHook()
        # import ipdb; ipdb.set_trace()
        self.seeds[(self.contours == 1)
                   & (self.seeds < 3)] = self.FOREGROUND_NOMODEL_SEED_LABEL
        self.contours[...] = 0

    def saveload_seeds(self, event):
        if self.seeds_copy is None:
            self.seeds_copy = self.seeds.copy()
            self.seeds[...] = 0
            # print "save"
            # from PyQt4.QtCore import pyqtRemoveInputHook
            # pyqtRemoveInputHook()
            # import ipdb; ipdb.set_trace()
            self.btn_save.setText("Load seeds")
        else:
            # from PyQt4.QtCore import pyqtRemoveInputHook
            # pyqtRemoveInputHook()
            # import ipdb; ipdb.set_trace()
            self.seeds[self.seeds_copy > 0] = self.seeds_copy[
                self.seeds_copy > 0]
            self.seeds_copy = None
            self.btn_save.setText("Save seeds")

    def recalculate(self, event):
        self.saveSliceSeeds()
        if np.abs(np.min(self.seeds) - np.max(self.seeds)) < 2:
            self.showStatus('Inner and outer regions not defined!')
            return

        self.showStatus("Processing...")
        self.mode_fun(self)
        self.selectSlice(self.actual_slice)
        self.updateVolume()
        self.showStatus("Done")

    def changeFocusedLabel(self, textlabel):
        self.textFocusedLabel = textlabel
        # logger
        # print " lakjlfkj ", textlabel
        logger.debug(self.textFocusedLabel)

    def deleteSliceSeeds(self, event):
        if self.textFocusedLabel == 'all':
            self.seeds_aview[..., self.actual_slice] = 0
        else:
            # delete only seeds with specific label
            self.seeds_aview[self.seeds_aview[..., self.actual_slice] ==
                             int(self.textFocusedLabel), self.actual_slice] = 0

        self.slice_box.setSlice(seeds=self.seeds_aview[..., self.actual_slice])
        self.slice_box.updateSlice()

    def deleteSeedsInAllImage(self, event):
        if self.textFocusedLabel == 'all':
            self.seeds_aview[...] = 0
        else:
            # delete only seeds with specific label
            self.seeds_aview[self.seeds_aview[...] == int(
                self.textFocusedLabel)] = 0

        self.slice_box.setSlice(seeds=self.seeds_aview[..., self.actual_slice])
        self.slice_box.updateSlice()

    def resetSliceDraw(self, event):
        seeds_orig_aview = self.seeds_orig.transpose(self.act_transposition)
        self.seeds_aview[..., self.actual_slice] = seeds_orig_aview[
            ..., self.actual_slice]
        self.slice_box.setSlice(seeds=self.seeds_aview[..., self.actual_slice])
        self.slice_box.updateSlice()

    def quit(self, event):
        self.close()

    def updateVolume(self):
        text = 'Volume:\n  unknown'
        if self.voxel_volume is not None:
            if self.mode == 'draw':
                vd = self.seeds

            else:
                vd = self.contours

            if vd is not None:
                nzs = vd.nonzero()
                nn = nzs[0].shape[0]
                if self.volume_unit == 'ml':
                    text = 'Volume [ml]:\n  %.2f' %\
                        (nn * self.voxel_volume / 1000)
                else:
                    text = 'Volume [mm3]:\n  %.2e' % (nn * self.voxel_volume)

        self.volume_label.setText(text)

    def getROI(self):
        crp = self.getCropBounds()
        if crp is not None:
            _, cri = crp

        else:
            cri = []
            for jj, ii in enumerate(self.img.shape):
                off = self.offset[jj]
                cri.append(slice(off, off + ii))

        return cri
Example #9
0
class OWWidget(QDialog, Report, metaclass=WidgetMetaClass):
    # Global widget count
    widget_id = 0

    # Widget Meta Description
    # -----------------------

    #: Widget name (:class:`str`) as presented in the Canvas
    name = None
    id = None
    category = None
    version = None
    #: Short widget description (:class:`str` optional), displayed in
    #: canvas help tooltips.
    description = None
    #: A longer widget description (:class:`str` optional)
    long_description = None
    #: Widget icon path relative to the defining module
    icon = "icons/Unknown.png"
    #: Widget priority used for sorting within a category
    #: (default ``sys.maxsize``).
    priority = sys.maxsize

    author = None
    author_email = None
    maintainer = None
    maintainer_email = None
    help = None
    help_ref = None
    url = None
    keywords = []
    background = None
    replaces = None

    #: A list of published input definitions
    inputs = []
    #: A list of published output definitions
    outputs = []

    # Default widget GUI layout settings
    # ----------------------------------

    #: Should the widget have basic layout
    #: (If this flag is false then the `want_main_area` and
    #: `want_control_area` are ignored).
    want_basic_layout = True
    #: Should the widget construct a `mainArea` (this is a resizable
    #: area to the right of the `controlArea`).
    want_main_area = True
    #: Should the widget construct a `controlArea`.
    want_control_area = True
    #: Widget painted by `Save graph" button
    graph_name = None
    graph_writers = FileFormat.img_writers
    want_status_bar = False

    save_position = True

    #: If false the widget will receive fixed size constraint
    #: (derived from it's layout). Use for widgets which have simple
    #: static size contents.
    resizing_enabled = True

    widgetStateChanged = Signal(str, int, str)
    blockingStateChanged = Signal(bool)
    progressBarValueChanged = Signal(float)
    processingStateChanged = Signal(int)

    settingsHandler = None
    """:type: SettingsHandler"""

    savedWidgetGeometry = settings.Setting(None)

    #: A list of advice messages (:class:`Message`) to display to the user.
    #: When a widget is first shown a message from this list is selected
    #: for display. If a user accepts (clicks 'Ok. Got it') the choice is
    #: recorded and the message is never shown again (closing the message
    #: will not mark it as seen). Messages can be displayed again by pressing
    #: Shift + F1
    #:
    #: :type: list of :class:`Message`
    UserAdviceMessages = []

    def __new__(cls, *args, **kwargs):
        self = super().__new__(cls, None, cls.get_flags())
        QDialog.__init__(self, None, self.get_flags())

        stored_settings = kwargs.get('stored_settings', None)
        if self.settingsHandler:
            self.settingsHandler.initialize(self, stored_settings)

        self.signalManager = kwargs.get('signal_manager', None)
        self.__env = _asmappingproxy(kwargs.get("env", {}))

        setattr(self, gui.CONTROLLED_ATTRIBUTES, ControlledAttributesDict(self))
        self.__reportData = None

        OWWidget.widget_id += 1
        self.widget_id = OWWidget.widget_id

        if self.name:
            self.setCaption(self.name)

        self.setFocusPolicy(Qt.StrongFocus)

        self.startTime = time.time()    # used in progressbar

        self.widgetState = {"Info": {}, "Warning": {}, "Error": {}}

        self.__blocking = False

        # flag indicating if the widget's position was already restored
        self.__was_restored = False

        self.__progressBarValue = -1
        self.__progressState = 0
        self.__statusMessage = ""

        self.__msgwidget = None
        self.__msgchoice = 0

        if self.want_basic_layout:
            self.insertLayout()

        sc = QShortcut(QKeySequence(Qt.ShiftModifier | Qt.Key_F1), self)
        sc.activated.connect(self.__quicktip)

        return self

    def __init__(self, *args, **kwargs):
        """QDialog __init__ was already called in __new__,
        please do not call it here."""

    def inline_graph_report(self):
        box = gui.widgetBox(self.controlArea, orientation="horizontal")
        box.layout().addWidget(self.graphButton)
        box.layout().addWidget(self.report_button)
#        self.report_button_background.hide()
#        self.graphButtonBackground.hide()

    @classmethod
    def get_flags(cls):
        return (Qt.Window if cls.resizing_enabled
                else Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint)

    class Splitter(QSplitter):
        def createHandle(self):
            return self.Handle(self.orientation(), self,
                                   cursor=Qt.PointingHandCursor)
        class Handle(QSplitterHandle):
            def mouseReleaseEvent(self, event):
                if event.button() == Qt.LeftButton:
                    splitter = self.splitter()
                    splitter.setSizes([int(splitter.sizes()[0] == 0), 1000])
                super().mouseReleaseEvent(event)
            def mouseMoveEvent(self, event):
                return  # Prevent moving; just show/hide

    # noinspection PyAttributeOutsideInit
    def insertLayout(self):
        def createPixmapWidget(self, parent, iconName):
            w = QLabel(parent)
            parent.layout().addWidget(w)
            w.setFixedSize(16, 16)
            w.hide()
            if os.path.exists(iconName):
                w.setPixmap(QPixmap(iconName))
            return w

        self.setLayout(QVBoxLayout())
        self.layout().setMargin(2)

        self.warning_bar = gui.widgetBox(self, orientation="horizontal",
                                         margin=0, spacing=0)
        self.warning_icon = gui.widgetLabel(self.warning_bar, "")
        self.warning_label = gui.widgetLabel(self.warning_bar, "")
        self.warning_label.setStyleSheet("padding-top: 5px")
        self.warning_bar.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Maximum)
        gui.rubber(self.warning_bar)
        self.warning_bar.setVisible(False)

        self.want_main_area = self.graph_name is not None or self.want_main_area

        splitter = self.Splitter(Qt.Horizontal, self)
        self.layout().addWidget(splitter)

        if self.want_control_area:
            self.controlArea = gui.widgetBox(splitter,
                                             orientation="vertical",
                                             margin=0)
            splitter.setSizes([1])  # Results in smallest size allowed by policy

            if self.graph_name is not None or hasattr(self, "send_report"):
                leftSide = self.controlArea
                self.controlArea = gui.widgetBox(leftSide, margin=0)
            if self.graph_name is not None:
                self.graphButton = gui.button(leftSide, None, "&Save Graph")
                self.graphButton.clicked.connect(self.save_graph)
                self.graphButton.setAutoDefault(0)
            if hasattr(self, "send_report"):
                self.report_button = gui.button(leftSide, None, "&Report",
                                                callback=self.show_report)
                self.report_button.setAutoDefault(0)

            if self.want_main_area:
                self.controlArea.setSizePolicy(QSizePolicy.Fixed,
                                               QSizePolicy.MinimumExpanding)
            self.controlArea.layout().setContentsMargins(4, 4, 0 if self.want_main_area else 4, 4)
        if self.want_main_area:
            self.mainArea = gui.widgetBox(splitter,
                                          orientation="vertical",
                                          margin=4,
                                          sizePolicy=QSizePolicy(QSizePolicy.Expanding,
                                                                 QSizePolicy.Expanding))
            splitter.setCollapsible(1, False)
            self.mainArea.layout().setContentsMargins(0 if self.want_control_area else 4, 4, 4, 4)

        if self.want_status_bar:
            self.widgetStatusArea = QFrame(self)
            self.statusBarIconArea = QFrame(self)
            self.widgetStatusBar = QStatusBar(self)

            self.layout().addWidget(self.widgetStatusArea)

            self.widgetStatusArea.setLayout(QHBoxLayout(self.widgetStatusArea))
            self.widgetStatusArea.layout().addWidget(self.statusBarIconArea)
            self.widgetStatusArea.layout().addWidget(self.widgetStatusBar)
            self.widgetStatusArea.layout().setMargin(0)
            self.widgetStatusArea.setFrameShape(QFrame.StyledPanel)

            self.statusBarIconArea.setLayout(QHBoxLayout())
            self.widgetStatusBar.setSizeGripEnabled(0)

            self.statusBarIconArea.hide()

            self._warningWidget = createPixmapWidget(
                self.statusBarIconArea,
                gui.resource_filename("icons/triangle-orange.png"))
            self._errorWidget = createPixmapWidget(
                self.statusBarIconArea,
                gui.resource_filename("icons/triangle-red.png"))

        if not self.resizing_enabled:
            self.layout().setSizeConstraint(QVBoxLayout.SetFixedSize)

    def save_graph(self):
        graph_obj = getdeepattr(self, self.graph_name, None)
        if graph_obj is None:
            return
        saveplot.save_plot(graph_obj, self.graph_writers)

    def updateStatusBarState(self):
        if not hasattr(self, "widgetStatusArea"):
            return
        if self.widgetState["Warning"] or self.widgetState["Error"]:
            self.widgetStatusArea.show()
        else:
            self.widgetStatusArea.hide()

    def setStatusBarText(self, text, timeout=5000):
        if hasattr(self, "widgetStatusBar"):
            self.widgetStatusBar.showMessage(" " + text, timeout)

    def __restoreWidgetGeometry(self):
        restored = False
        if self.save_position:
            geometry = self.savedWidgetGeometry
            if geometry is not None:
                restored = self.restoreGeometry(QByteArray(geometry))

            if restored and not self.windowState() & \
                    (Qt.WindowMaximized | Qt.WindowFullScreen):
                space = qApp.desktop().availableGeometry(self)
                frame, geometry = self.frameGeometry(), self.geometry()

                #Fix the widget size to fit inside the available space
                width = space.width() - (frame.width() - geometry.width())
                width = min(width, geometry.width())
                height = space.height() - (frame.height() - geometry.height())
                height = min(height, geometry.height())
                self.resize(width, height)

                # Move the widget to the center of available space if it is
                # currently outside it
                if not space.contains(self.frameGeometry()):
                    x = max(0, space.width() / 2 - width / 2)
                    y = max(0, space.height() / 2 - height / 2)

                    self.move(x, y)
        return restored

    def __updateSavedGeometry(self):
        if self.__was_restored and self.isVisible():
            # Update the saved geometry only between explicit show/hide
            # events (i.e. changes initiated by the user not by Qt's default
            # window management).
            self.savedWidgetGeometry = self.saveGeometry()

    # when widget is resized, save the new width and height
    def resizeEvent(self, ev):
        QDialog.resizeEvent(self, ev)
        # Don't store geometry if the widget is not visible
        # (the widget receives a resizeEvent (with the default sizeHint)
        # before first showEvent and we must not overwrite the the
        # savedGeometry with it)
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()

    def moveEvent(self, ev):
        QDialog.moveEvent(self, ev)
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()

    # set widget state to hidden
    def hideEvent(self, ev):
        if self.save_position:
            self.__updateSavedGeometry()
        QDialog.hideEvent(self, ev)

    def closeEvent(self, ev):
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()
        QDialog.closeEvent(self, ev)

    def showEvent(self, ev):
        QDialog.showEvent(self, ev)
        if self.save_position and not self.__was_restored:
            # Restore saved geometry on show
            self.__restoreWidgetGeometry()
            self.__was_restored = True
        self.__quicktipOnce()

    def wheelEvent(self, event):
        # Silently accept the wheel event. This is to ensure combo boxes
        # and other controls that have focus don't receive this event unless
        # the cursor is over them.
        event.accept()

    def setCaption(self, caption):
        # we have to save caption title in case progressbar will change it
        self.captionTitle = str(caption)
        self.setWindowTitle(caption)

    # put this widget on top of all windows
    def reshow(self):
        self.show()
        self.raise_()
        self.activateWindow()

    def send(self, signalName, value, id=None):
        """
        Send a `value` on the `signalName` widget output.

        An output with `signalName` must be defined in the class ``outputs``
        list.
        """
        if not any(s.name == signalName for s in self.outputs):
            raise ValueError('{} is not a valid output signal for widget {}'.format(
                signalName, self.name))
        if self.signalManager is not None:
            self.signalManager.send(self, signalName, value, id)

    def __setattr__(self, name, value):
        """Set value to members of this instance or any of its members.

        If member is used in a gui control, notify the control about the change.

        name: name of the member, dot is used for nesting ("graph.point.size").
        value: value to set to the member.

        """

        names = name.rsplit(".")
        field_name = names.pop()
        obj = reduce(lambda o, n: getattr(o, n, None), names, self)
        if obj is None:
            raise AttributeError("Cannot set '{}' to {} ".format(name, value))

        if obj is self:
            super().__setattr__(field_name, value)
        else:
            setattr(obj, field_name, value)

        notify_changed(obj, field_name, value)

        if self.settingsHandler:
            self.settingsHandler.fast_save(self, name, value)

    def openContext(self, *a):
        self.settingsHandler.open_context(self, *a)

    def closeContext(self):
        self.settingsHandler.close_context(self)

    def retrieveSpecificSettings(self):
        pass

    def storeSpecificSettings(self):
        pass

    def saveSettings(self):
        self.settingsHandler.update_defaults(self)

    def onDeleteWidget(self):
        """
        Invoked by the canvas to notify the widget it has been deleted
        from the workflow.

        If possible, subclasses should gracefully cancel any currently
        executing tasks.
        """
        pass

    def handleNewSignals(self):
        """
        Invoked by the workflow signal propagation manager after all
        signals handlers have been called.

        Reimplement this method in order to coalesce updates from
        multiple updated inputs.
        """
        pass

    # ############################################
    # PROGRESS BAR FUNCTIONS

    def progressBarInit(self, processEvents=QEventLoop.AllEvents):
        """
        Initialize the widget's progress (i.e show and set progress to 0%).

        .. note::
            This method will by default call `QApplication.processEvents`
            with `processEvents`. To suppress this behavior pass
            ``processEvents=None``.

        :param processEvents: Process events flag
        :type processEvents: `QEventLoop.ProcessEventsFlags` or `None`
        """
        self.startTime = time.time()
        self.setWindowTitle(self.captionTitle + " (0% complete)")

        if self.__progressState != 1:
            self.__progressState = 1
            self.processingStateChanged.emit(1)

        self.progressBarSet(0, processEvents)

    def progressBarSet(self, value, processEvents=QEventLoop.AllEvents):
        """
        Set the current progress bar to `value`.

        .. note::
            This method will by default call `QApplication.processEvents`
            with `processEvents`. To suppress this behavior pass
            ``processEvents=None``.

        :param float value: Progress value
        :param processEvents: Process events flag
        :type processEvents: `QEventLoop.ProcessEventsFlags` or `None`
        """
        old = self.__progressBarValue
        self.__progressBarValue = value

        if value > 0:
            if self.__progressState != 1:
                warnings.warn("progressBarSet() called without a "
                              "preceding progressBarInit()",
                              stacklevel=2)
                self.__progressState = 1
                self.processingStateChanged.emit(1)

            usedTime = max(1, time.time() - self.startTime)
            totalTime = (100.0 * usedTime) / float(value)
            remainingTime = max(0, totalTime - usedTime)
            h = int(remainingTime / 3600)
            min = int((remainingTime - h * 3600) / 60)
            sec = int(remainingTime - h * 3600 - min * 60)
            if h > 0:
                text = "%(h)d:%(min)02d:%(sec)02d" % vars()
            else:
                text = "%(min)d:%(sec)02d" % vars()
            self.setWindowTitle(self.captionTitle +
                                " (%(value).2f%% complete, remaining time: %(text)s)" % vars())
        else:
            self.setWindowTitle(self.captionTitle + " (0% complete)")

        if old != value:
            self.progressBarValueChanged.emit(value)

        if processEvents is not None and processEvents is not False:
            qApp.processEvents(processEvents)

    def progressBarValue(self):
        return self.__progressBarValue

    progressBarValue = pyqtProperty(float, fset=progressBarSet,
                                    fget=progressBarValue)

    processingState = pyqtProperty(int, fget=lambda self: self.__progressState)

    def progressBarAdvance(self, value, processEvents=QEventLoop.AllEvents):
        self.progressBarSet(self.progressBarValue + value, processEvents)

    def progressBarFinished(self, processEvents=QEventLoop.AllEvents):
        """
        Stop the widget's progress (i.e hide the progress bar).

        .. note::
            This method will by default call `QApplication.processEvents`
            with `processEvents`. To suppress this behavior pass
            ``processEvents=None``.

        :param processEvents: Process events flag
        :type processEvents: `QEventLoop.ProcessEventsFlags` or `None`
        """
        self.setWindowTitle(self.captionTitle)
        if self.__progressState != 0:
            self.__progressState = 0
            self.processingStateChanged.emit(0)

        if processEvents is not None and processEvents is not False:
            qApp.processEvents(processEvents)

    @contextlib.contextmanager
    def progressBar(self, iterations=0):
        """
        Context manager for progress bar.

        Using it ensures that the progress bar is removed at the end without
        needing the `finally` blocks.

        Usage:

            with self.progressBar(20) as progress:
                ...
                progress.advance()

        or

            with self.progressBar() as progress:
                ...
                progress.advance(0.15)

        or

            with self.progressBar():
                ...
                self.progressBarSet(50)

        :param iterations: the number of iterations (optional)
        :type iterations: int
        """
        progress_bar = gui.ProgressBar(self, iterations)
        yield progress_bar
        progress_bar.finish()  # Let us not rely on garbage collector

    #: Widget's status message has changed.
    statusMessageChanged = Signal(str)

    def setStatusMessage(self, text):
        """
        Set widget's status message.

        This is a short status string to be displayed inline next to
        the instantiated widget icon in the canvas.
        """
        if self.__statusMessage != text:
            self.__statusMessage = text
            self.statusMessageChanged.emit(text)

    def statusMessage(self):
        """
        Return the widget's status message.
        """
        return self.__statusMessage

    def keyPressEvent(self, e):
        if (int(e.modifiers()), e.key()) in OWWidget.defaultKeyActions:
            OWWidget.defaultKeyActions[int(e.modifiers()), e.key()](self)
        else:
            QDialog.keyPressEvent(self, e)

    def information(self, id=0, text=None):
        """
        Set/clear a widget information message (for `id`).

        Args:
            id (int or list): The id of the message
            text (str): Text of the message.
        """
        self.setState("Info", id, text)

    def warning(self, id=0, text=""):
        """
        Set/clear a widget warning message (for `id`).

        Args:
            id (int or list): The id of the message
            text (str): Text of the message.
        """
        self.setState("Warning", id, text)

    def error(self, id=0, text=""):
        """
        Set/clear a widget error message (for `id`).

        Args:
            id (int or list): The id of the message
            text (str): Text of the message.
        """
        self.setState("Error", id, text)

    def setState(self, state_type, id, text):
        changed = 0
        if type(id) == list:
            for val in id:
                if val in self.widgetState[state_type]:
                    self.widgetState[state_type].pop(val)
                    changed = 1
        else:
            if type(id) == str:
                text = id
                id = 0
            if not text:
                if id in self.widgetState[state_type]:
                    self.widgetState[state_type].pop(id)
                    changed = 1
            else:
                self.widgetState[state_type][id] = text
                changed = 1

        if changed:
            if type(id) == list:
                for i in id:
                    self.widgetStateChanged.emit(state_type, i, "")
            else:
                self.widgetStateChanged.emit(state_type, id, text or "")

        tooltip_lines = []
        highest_type = None
        for a_type in ("Error", "Warning", "Info"):
            msgs_for_ids = self.widgetState.get(a_type)
            if not msgs_for_ids:
                continue
            msgs_for_ids = list(msgs_for_ids.values())
            if not msgs_for_ids:
                continue
            tooltip_lines += msgs_for_ids
            if highest_type is None:
                highest_type = a_type

        if highest_type is None:
            self.set_warning_bar(None)
        elif len(tooltip_lines) == 1:
            msg = tooltip_lines[0]
            if "\n" in msg:
                self.set_warning_bar(
                    highest_type, msg[:msg.index("\n")] + " (...)", msg)
            else:
                self.set_warning_bar(
                    highest_type, tooltip_lines[0], tooltip_lines[0])
        else:
            self.set_warning_bar(
                highest_type,
                "{} problems during execution".format(len(tooltip_lines)),
                "\n".join(tooltip_lines))

        return changed

    def set_warning_bar(self, state_type, text=None, tooltip=None):
        colors = {"Error": ("#ffc6c6", "black", QStyle.SP_MessageBoxCritical),
                  "Warning": ("#ffffc9", "black", QStyle.SP_MessageBoxWarning),
                  "Info": ("#ceceff", "black", QStyle.SP_MessageBoxInformation)}
        current_height = self.height()
        if state_type is None:
            if not self.warning_bar.isHidden():
                new_height = current_height - self.warning_bar.height()
                self.warning_bar.setVisible(False)
                self.resize(self.width(), new_height)
            return
        background, foreground, icon = colors[state_type]
        style = QApplication.instance().style()
        self.warning_icon.setPixmap(style.standardIcon(icon).pixmap(14, 14))

        self.warning_bar.setStyleSheet(
            "background-color: {}; color: {};"
            "padding: 3px; padding-left: 6px; vertical-align: center".
            format(background, foreground))
        self.warning_label.setText(text)
        self.warning_bar.setToolTip(tooltip)
        if self.warning_bar.isHidden():
            self.warning_bar.setVisible(True)
            new_height = current_height + self.warning_bar.height()
            self.resize(self.width(), new_height)

    def widgetStateToHtml(self, info=True, warning=True, error=True):
        iconpaths = {
            "Info": gui.resource_filename("icons/information.png"),
            "Warning": gui.resource_filename("icons/warning.png"),
            "Error": gui.resource_filename("icons/error.png")
        }
        items = []

        for show, what in [(info, "Info"), (warning, "Warning"),
                           (error, "Error")]:
            if show and self.widgetState[what]:
                items.append('<img src="%s" style="float: left;"> %s' %
                             (iconpaths[what],
                              "\n".join(self.widgetState[what].values())))
        return "<br>".join(items)

    @classmethod
    def getWidgetStateIcons(cls):
        if not hasattr(cls, "_cached__widget_state_icons"):
            info = QPixmap(gui.resource_filename("icons/information.png"))
            warning = QPixmap(gui.resource_filename("icons/warning.png"))
            error = QPixmap(gui.resource_filename("icons/error.png"))
            cls._cached__widget_state_icons = \
                {"Info": info, "Warning": warning, "Error": error}
        return cls._cached__widget_state_icons

    defaultKeyActions = {}

    if sys.platform == "darwin":
        defaultKeyActions = {
            (Qt.ControlModifier, Qt.Key_M):
                lambda self: self.showMaximized
                if self.isMinimized() else self.showMinimized(),
            (Qt.ControlModifier, Qt.Key_W):
                lambda self: self.setVisible(not self.isVisible())}

    def setBlocking(self, state=True):
        """
        Set blocking flag for this widget.

        While this flag is set this widget and all its descendants
        will not receive any new signals from the workflow signal manager.

        This is useful for instance if the widget does it's work in a
        separate thread or schedules processing from the event queue.
        In this case it can set the blocking flag in it's processNewSignals
        method schedule the task and return immediately. After the task
        has completed the widget can clear the flag and send the updated
        outputs.

        .. note::
            Failure to clear this flag will block dependent nodes forever.
        """
        if self.__blocking != state:
            self.__blocking = state
            self.blockingStateChanged.emit(state)

    def isBlocking(self):
        """
        Is this widget blocking signal processing.
        """
        return self.__blocking

    def resetSettings(self):
        self.settingsHandler.reset_settings(self)

    def workflowEnv(self):
        """
        Return (a view to) the workflow runtime environment.

        Returns
        -------
        env : types.MappingProxyType
        """
        return self.__env

    def workflowEnvChanged(self, key, value, oldvalue):
        """
        A workflow environment variable `key` has changed to value.

        Called by the canvas framework to notify widget of a change
        in the workflow runtime environment.

        The default implementation does nothing.
        """
        pass

    def __showMessage(self, message):
        if self.__msgwidget is not None:
            self.__msgwidget.hide()
            self.__msgwidget.deleteLater()
            self.__msgwidget = None

        if message is None:
            return

        buttons = MessageOverlayWidget.Ok | MessageOverlayWidget.Close
        if message.moreurl is not None:
            buttons |= MessageOverlayWidget.Help

        if message.icon is not None:
            icon = message.icon
        else:
            icon = Message.Information

        self.__msgwidget = MessageOverlayWidget(
            parent=self, text=message.text, icon=icon, wordWrap=True,
            standardButtons=buttons)

        b = self.__msgwidget.button(MessageOverlayWidget.Ok)
        b.setText("Ok, got it")

        self.__msgwidget.setStyleSheet("""
            MessageOverlayWidget {
                background: qlineargradient(
                    x1: 0, y1: 0, x2: 0, y2: 1,
                    stop:0 #666, stop:0.3 #6D6D6D, stop:1 #666)
            }
            MessageOverlayWidget QLabel#text-label {
                color: white;
            }"""
        )

        if message.moreurl is not None:
            helpbutton = self.__msgwidget.button(MessageOverlayWidget.Help)
            helpbutton.setText("Learn more\N{HORIZONTAL ELLIPSIS}")
            self.__msgwidget.helpRequested.connect(
                lambda: QDesktopServices.openUrl(QUrl(message.moreurl)))

        self.__msgwidget.setWidget(self)
        self.__msgwidget.show()

    def __quicktip(self):
        messages = list(self.UserAdviceMessages)
        if messages:
            message = messages[self.__msgchoice % len(messages)]
            self.__msgchoice += 1
            self.__showMessage(message)

    def __quicktipOnce(self):
        filename = os.path.join(settings.widget_settings_dir(),
                               "user-session-state.ini")
        namespace = ("user-message-history/{0.__module__}.{0.__qualname__}"
                     .format(type(self)))
        session_hist = QSettings(filename, QSettings.IniFormat)
        session_hist.beginGroup(namespace)
        messages = self.UserAdviceMessages

        def ispending(msg):
            return not session_hist.value(
                "{}/confirmed".format(msg.persistent_id),
                defaultValue=False, type=bool)
        messages = list(filter(ispending, messages))

        if not messages:
            return

        message = messages[self.__msgchoice % len(messages)]
        self.__msgchoice += 1

        self.__showMessage(message)

        def userconfirmed():
            session_hist = QSettings(filename, QSettings.IniFormat)
            session_hist.beginGroup(namespace)
            session_hist.setValue(
                "{}/confirmed".format(message.persistent_id), True)
            session_hist.sync()

        self.__msgwidget.accepted.connect(userconfirmed)
class UniFileSyncUI(QMainWindow):
    """UniFileSyncUI class"""
    def __init__(self, name=None):
        super(UniFileSyncUI, self).__init__()

        self.ui = Ui_UniFileSyncPop()
        self.ui.setupUi(self)

        self.setFixedSize(self.size())

        self.server = UServer('UServer')
        self.server.regSelfToBus()

        if name:
            self.setName(name)

        self.createActions()
        self.createTrayIcon()
        self.createStatusBar()

        qApp.setQuitOnLastWindowClosed(False)

        #connect the signal with slot
        self.connectUISlots(self.ui)
        #set UI label
        username = ConfManager.getManager().getValue('UI', 'username')
        self.ui.nameLabel.setText(username)

        #Start server immediately
        self.server.start()
        #self.server.getHandler('start')({'name': 'all'})
        msg = self.server.initMsg('start', None, MSG_UNIQUE_ID_T_CONTROLLER,
                                  False, {'name': 'all'})
        UMsgBus.getBus().send(msg)

        self.server.addCallBack(self.statusupdate)
        self.server.addCallBack(self.cloudinfoupdate)

        #setup list
        self.setupFolderList(self.ui.folderList)
        self.setupPluginList(self.ui.pluginList)
        self.setupNetworkConf()

        #Init status bar
        stBarConf = ConfManager.getManager().getValue('UI', 'statusbar')
        self.statusbar.showMessage(stBarConf['messages']['init'])

        #Init system icon
        self.trayIcon.show()
        self.showTrayIconMessage()

    def setupNetworkConf(self):
        """setup network configuration into UI"""
        conf = ConfManager.getManager().getValue('common', 'network')
        self.ui.proxyLineEdit.setText(conf['proxy'])
        self.ui.portLineEdit.setText("%s" % conf['port'])

    def closeEvent(self, event):
        """override close event"""
        if self.trayIcon.isVisible():
            self.hide()
            event.ignore()

        ConfManager.getManager().save()
        logging.debug('[%s] is closed, window is hide, configuration is saved',
                      self.getName())

    def createActions(self):
        """create tray icon menu action"""
        self.configAction = QAction("&ShowConfig", self, triggered=self.show)
        self.exitAction = QAction("&Exit", self)
        self.exitAction.triggered.connect(lambda: UMsgBus.getBus().send(
            self.server.initMsg('stop', None, MSG_UNIQUE_ID_T_CONTROLLER,
                                False, {'name': 'all'})))
        self.exitAction.triggered.connect(qApp.quit)

    def createTrayIcon(self):
        """create system tray icon"""
        self.trayIconMenu = QMenu(self)
        es = self.trayIconMenu.addAction(self.configAction)

        self.trayIconMenu.addSeparator()

        ea = self.trayIconMenu.addAction(self.exitAction)

        self.trayIcon = QSystemTrayIcon(self)
        self.trayIcon.setContextMenu(self.trayIconMenu)
        self.trayIcon.setIcon(QIcon('icon/tray.png'))

    def showTrayIconMessage(self):
        """show tray icon message"""
        conf = ConfManager.getManager().getValue('UI', 'trayicon')
        popup = conf['popup']

        self.trayIcon.showMessage(popup['title'], popup['message'])

    def setupFolderList(self, folderList):
        """setup folder list for showing"""
        fts = ConfManager.getManager().getValue('common', 'folders')
        i = 0
        for ft in fts:
            flistItem = QListWidgetItem(QIcon('icon/folder.png'), ft,
                                        folderList)
            folderList.insertItem(i, flistItem)
            i += 1

    def setupPluginList(self, pluginList):
        """setup plugin list from configuration file"""
        fts = ConfManager.getManager().getValue('common', 'plugins')
        i = 0
        for ft in fts:
            flistItem = QListWidgetItem(QIcon('icon/plugin.png'), ft['name'],
                                        pluginList)
            pluginList.insertItem(i, flistItem)
            i += 1

    def show(self):
        """ovrride parent show method"""
        super(UniFileSyncUI, self).show()

        #Init status bar
        stBarConf = ConfManager.getManager().getValue('UI', 'statusbar')
        self.statusbar.showMessage(stBarConf['messages']['init'])

        #set UI label
        username = ConfManager.getManager().getValue('UI', 'username')
        self.ui.nameLabel.setText(username)

    def connectUISlots(self, ui):
        """connect ui component with slots"""
        ui.connBtn.clicked.connect(lambda: self.connBtnSlots(ui.connBtn))
        ui.addFolderBtn.clicked.connect(
            lambda: self.connBtnSlots(ui.addFolderBtn))
        ui.rmFolderBtn.clicked.connect(
            lambda: self.connBtnSlots(ui.rmFolderBtn))
        ui.saveBtn.clicked.connect(lambda: self.connBtnSlots(ui.saveBtn))
        ui.unloadBtn.clicked.connect(lambda: self.connBtnSlots(ui.unloadBtn))
        ui.reloadBtn.clicked.connect(lambda: self.connBtnSlots(ui.reloadBtn))
        ui.resetBtn.clicked.connect(lambda: self.connBtnSlots(ui.resetBtn))
        ui.addPluginBtn.clicked.connect(
            lambda: self.connBtnSlots(ui.addPluginBtn))
        ui.syncFolderBtn.clicked.connect(
            lambda: self.connBtnSlots(ui.syncFolderBtn))

        self.connect(self, SIGNAL('statusupdate'), self.statusbarUpdate)
        self.connect(self, SIGNAL('cloudinfoupdate'), self.infoLabelUpdate)

    def connBtnSlots(self, btn):
        """docstring for connBtnSlots"""
        if btn is self.ui.connBtn:
            if btn.text() == 'Connect':
                msg = self.server.initMsg('info', None,
                                          MSG_UNIQUE_ID_T_CONTROLLER, True,
                                          {'name': 'all'})
                UMsgBus.getBus().send(msg)
                #res, data = self.server.getHandler('info')({'name': 'all'})
                btn.setText('Connecting')
                #self.ui.infoLabel.setText(data)
                logging.debug('[%s]: Press Connect to getCloudInfo',
                              self.getName())
            elif btn.text() == 'Disconnect':
                #self.server.getHandler('stop')({'name': 'all'})
                btn.setText('Connect')
                self.ui.infoLabel.setText('Cloud Disk is disconnected')

        elif btn is self.ui.addFolderBtn:
            fileDialog = QFileDialog(self)
            fileDialog.setWindowTitle('Select Folder')
            folderPath = fileDialog.getExistingDirectory()
            if folderPath != "":
                listItem = QListWidgetItem(QIcon('icon/folder.png'),
                                           folderPath, self.ui.folderList)
                pyStr = '%s' % folderPath
                logging.debug('[%s]: add folder path %s', self.getName(),
                              pyStr)
                self.ui.folderList.insertItem(self.ui.folderList.count(),
                                              listItem)
                folderList = ConfManager.getManager().getValue(
                    'common', 'folders')
                folderList.append(pyStr)
                ConfManager.getManager().setValue('common', 'folders',
                                                  folderList)
                #send watch dir request
                msg = self.server.initMsg('watch', None,
                                          MSG_UNIQUE_ID_T_CONTROLLER, True, {
                                              'path': pyStr,
                                              'action': 'add'
                                          })
                UMsgBus.getBus().send(msg)
                self.statusbar.showMessage('Adding watch path %s' % folderPath)

        elif btn is self.ui.rmFolderBtn:
            row = self.ui.folderList.currentRow()
            item = self.ui.folderList.currentItem()
            folderList = ConfManager.getManager().getValue('common', 'folders')
            self.statusbar.showMessage('Removing watch path %s' % item.text())
            pyStr = '%s' % item.text()
            folderList.remove(pyStr)
            ConfManager.getManager().setValue('common', 'folders', folderList)
            logging.debug('[%s]: remove item %d %s', self.getName(), row,
                          item.text())
            msg = self.server.initMsg('watch', None,
                                      MSG_UNIQUE_ID_T_CONTROLLER, True, {
                                          'path': pyStr,
                                          'action': 'rm'
                                      })
            UMsgBus.getBus().send(msg)
            self.ui.folderList.takeItem(row)

        elif btn is self.ui.saveBtn:
            proxyConf = ConfManager.getManager().getValue('common', 'network')
            server = str(self.ui.proxyLineEdit.text())

            if server != "" and server != None:
                user = str(self.ui.proxyNameLineEdit.text())
                password = str(self.ui.proxyPwdLineEdit.text())
                logging.debug(
                    '[%s]: save server=>%s user=>%s password=>%s into configuration',
                    self.getName(), server, user, password)
                proxyConf['proxy'] = server
                proxyConf['user'] = user
                proxyConf['password'] = password
                ConfManager.getManager().setValue('common', 'network',
                                                  proxyConf)
                msg = self.server.initMsg('proxy', None,
                                          MSG_UNIQUE_ID_T_CONTROLLER, True, {
                                              'http': 'http://%s' % server,
                                              'https': 'https://%s' % server
                                          })
                UMsgBus.getBus().send(msg)
                self.statusbar.showMessage('Applying proxy %s' % server)

                ConfManager.getManager().save()

        elif btn is self.ui.resetBtn:
            proxyConf = ConfManager.getManager().getValue('common', 'network')
            server = proxyConf['proxy']
            user = proxyConf['user']
            password = proxyConf['password']
            port = proxyConf['port']

            self.ui.proxyLineEdit.setText(server)
            self.ui.proxyNameLineEdit.setText(user)
            self.ui.proxyPwdLineEdit.setText(password)
            self.ui.portLineEdit.setText(str(port))
            self.ui.serverEnableCheckBox.setCheckState(0)

        elif btn is self.ui.unloadBtn:
            row = self.ui.pluginList.currentRow()
            it = str(self.ui.pluginList.currentItem().text())
            logging.debug('[%s]: unload plugin name %s', self.getName(), it)
            self.statusbar.showMessage('Unloading plugin %s' % it)
            PluginManager.getManager().unload(it)
            self.ui.pluginList.takeItem(row)
            conf = ConfManager.getManager().getValue('common', 'plugins')
            for pc in conf:
                if pc['name'] == it:
                    conf.remove(pc)
            ConfManager.getManager().setValue('common', 'plugins', conf)

        elif btn is self.ui.reloadBtn:
            row = self.ui.pluginList.currentRow()
            it = str(self.ui.pluginList.currentItem().text())
            logging.debug('[%s]: reload plugin name %s', self.getName(), it)
            self.statusbar.showMessage('Reloading plugin %s' % it)
            PluginManager.getManager().reload(it)

        elif btn is self.ui.addPluginBtn:
            path = QFileDialog.getOpenFileName(self)
            PluginManager.getManager().loadPluginFromPath(str(path))

        elif btn is self.ui.syncFolderBtn:
            folder = str(self.ui.folderList.currentItem().text())
            msg = self.server.initMsg('sync', None, MSG_UNIQUE_ID_T_CONTROLLER,
                                      True, {
                                          'path': str(folderPath),
                                          'action': 'add'
                                      })
            UMsgBus.getBus().send(msg)
            self.statusbar.showMessage('Sync %s with cloud' % folder)

    def createStatusBar(self):
        """create status bar"""

        self.statusbar = QStatusBar(self)
        self.setStatusBar(self.statusbar)

    def setName(self, name):
        """set server name"""
        self.name = name

    def getName(self):
        """get server name"""
        return self.name

    def statusupdate(self, param):
        """call back for status update"""
        self.emit(SIGNAL('statusupdate'), param['result'])

    def statusbarUpdate(self, res):
        """statusbar update callback"""
        self.statusbar.showMessage(ERR_STR_TABLE[res])

    def cloudinfoupdate(self, param):
        """cloud infor update callback"""
        self.emit(SIGNAL('cloudinfoupdate'), param['data'])

    def infoLabelUpdate(self, res):
        """infoLabelUpdate"""
        if res:
            self.ui.infoLabel.setText(res)
            self.ui.connBtn.setText('Disconnect')
Example #11
0
class MainWindow(QMainWindow):
	def __init__(self):
		QMainWindow.__init__(self)
		
		self.listSize = 20
		self.verbose = False

		self.timePattern = re.compile('\.[0-9]+$')
		
		self.setWindowTitle('%s %s' % (QApplication.applicationName(), QApplication.applicationVersion()));
		
		self.widget = QWidget()
		self.setCentralWidget(self.widget)

		self.statusBar = QStatusBar(self)
		self.setStatusBar(self.statusBar)

		self.mAction = self.menuBar().addMenu(self.tr("&Action"))
		#self.mAction.addAction(self.tr("&update"), self.updateTplTable(), QKeySequence('F5'))
		self.mAction.addAction(self.tr("e&xit"), self.onExit, 'Ctrl+Q')

		self.mAbout = self.menuBar().addMenu(self.tr("&about"))
		self.mAbout.addAction(QApplication.applicationName(), self.onAboutAppAction)
		self.mAbout.addAction("Qt", self.onAboutQtAction)
		
		self.pageForwardButton = QPushButton(self)
		self.pageForwardButton.setText('>')
		self.connect(self.pageForwardButton, SIGNAL('clicked()'), self.pageForward)

		self.pageBackwardButton = QPushButton(self)
		self.pageBackwardButton.setText('<')
		self.connect(self.pageBackwardButton, SIGNAL('clicked()'), self.pageBackward)
		
		self.timer = QTimer(self)
		self.timer.setInterval(1000)
		self.connect(self.timer, SIGNAL('timeout()'), self, SLOT('onTimer()'))
		self.time_begin = datetime.now()
		self.time_end = datetime.now()

		db_path = os.path.join(os.path.dirname(sys.argv[0]) if os.name != 'posix' else os.path.expanduser('~'), '.tt.db')
		self.db = sqlite3.connect(db_path)
		self.cursor = self.db.cursor()
		
		try:
			self.cursor.execute('SELECT id FROM timebrowser_record LIMIT 1')
		except:
			try:
#				print 'migrate'
#				print 'try tt'
				self.cursor.execute('SELECT id FROM tt LIMIT 1')
#				print 'may drop timebrowser_record'
				self.cursor.execute('DROP TABLE IF EXISTS timebrowser_record')
#				print 'get create statements'
				self.cursor.execute('''SELECT sql FROM sqlite_master WHERE type='table' AND name='tt' ''')
				sql1 = self.cursor.fetchone()[0].replace( ' tt ', ' timebrowser_record ' )
				self.cursor.execute('''SELECT sql FROM sqlite_master WHERE type='index' AND tbl_name='tt' ''')
				sql2 = self.cursor.fetchone()[0].replace( ' tt ', ' timebrowser_record ' )
#				print 'drop index'
				self.cursor.execute('DROP INDEX IF EXISTS idx_time_begin')
#				print 'apply create statements'
				self.cursor.execute( sql1 )
				self.cursor.execute( sql2 )
#				print 'copy data'
				self.cursor.execute( 'INSERT INTO timebrowser_record SELECT * FROM tt' )
#				print 'drop tt'
				self.cursor.execute( 'DROP TABLE tt' )
				self.statusBar.showMessage('successfully migrated Database!')
#				print 'done'
			except:# Exception, e:
#				print e
				self.createDb()
		
		self.layout = QGridLayout(self.widget)
		
		self.descriptionLabel = QLabel(self.widget)
		self.descriptionLabel.setText('Beschreibung')
		self.descriptionLabel.setMaximumHeight( self.font().pointSize() * 2 )
		self.descriptionInput = QLineEdit(self.widget)
		self.updateDescriptionEditCompleter()				
		self.noteLabel = QLabel(self.widget)
		self.noteLabel.setText('Notiz')
		self.noteLabel.setMaximumHeight( self.font().pointSize() * 2 )
		self.noteInput = QLineEdit(self.widget)
		self.startStopButton = QPushButton(self.widget)
		self.startStopButton.setText('Start')
		
		self.tableView = TplTable(self, self.listSize)

		self.pageForwardAction = QAction(self)
		self.pageForwardAction.setShortcut(QKeySequence('Right'))
		self.connect(self.pageForwardAction, SIGNAL('triggered()'), self.pageForward);
		self.pageForwardButton.addAction(self.pageForwardAction)

		self.pageBackwardAction = QAction(self)
		self.pageBackwardAction.setShortcut(QKeySequence('Left'))
		self.connect(self.pageBackwardAction, SIGNAL('triggered()'), self.pageBackward);
		self.pageBackwardButton.addAction(self.pageBackwardAction)

		self.updateTplTable()
			
		self.layout.addWidget(self.descriptionLabel, 0, 0, 1, 1)
		self.layout.addWidget(self.descriptionInput, 1, 0, 1, 1)
		self.layout.addWidget(self.noteLabel, 0, 1, 1, 1)
		self.layout.addWidget(self.noteInput, 1, 1, 1, 1)
		self.layout.addWidget(self.startStopButton, 2, 0, 1, 2)
		self.layout.addWidget(self.tableView, 3,0,1,2)
		self.layout.addWidget(self.pageBackwardButton, 4, 0, 1, 1)
		self.layout.addWidget(self.pageForwardButton, 4, 1, 1, 1)

		self.connect(self.descriptionInput, SIGNAL('returnPressed ()'), self.onStartStop )
		self.connect(self.noteInput, SIGNAL('returnPressed ()'), self.onStartStop )
		self.connect(self.startStopButton, SIGNAL('clicked()'), self.onStartStop )
		self.connect(self.tableView, SIGNAL('valueChanged(int)'), self.onValueChanged )
		self.connect(self.tableView, SIGNAL('del(int)'), self.onDelete )
		
	def __del__(self):
		pass
	
	def createDb(self):
		try:
			self.q('''CREATE TABLE timebrowser_record (
			id INTEGER PRIMARY KEY AUTOINCREMENT,
			time_begin INTEGER,
			time_end INTEGER,
			description STRING,
			note STRING DEFAULT ""
			)''')
			self.q('CREATE INDEX idx_time_begin ON timebrowser_record (time_begin)')
		except:
			self.statusBar.showMessage('error creating Database!')
		else:
			self.statusBar.showMessage('Table timebrowser_record created successfully')
			
	def q(self, query):
		try:
			self.cursor.execute(query)
		except:
			self.statusBar.showMessage('query execution failed "%s"' % query)
		else:
			self.db.commit()
	
	def updateTplTable(self):
		self.q('SELECT * FROM timebrowser_record ORDER BY time_begin DESC LIMIT %d' % ( self.listSize ) )
		self.tableView.set(self.cursor.fetchall())
		
	def updateDescriptionEditCompleter(self):
		self.q('SELECT DISTINCT description FROM timebrowser_record')
		words = QStringList()
		for word in self.cursor.fetchall():
			words.append(word[0])
		self.descriptionInput.setCompleter(QCompleter(words, self))

	@pyqtSlot()
	def pageForward(self):
		self.q('SELECT MIN(time_begin) FROM timebrowser_record')
		if not self.tableView.getLastTime() == self.cursor.fetchone()[0]:
			sql = 'SELECT * FROM timebrowser_record WHERE time_begin < %d  ORDER BY time_begin DESC LIMIT %s' % ( self.tableView.getLastTime(), self.listSize)
			if self.verbose:
				print( sql )
			self.q( sql )
			self.tableView.set(self.cursor.fetchall())

	@pyqtSlot()
	def pageBackward(self):
		self.q('SELECT MAX(time_begin) FROM timebrowser_record')
		if not self.tableView.getFirstTime() == self.cursor.fetchone()[0]:
			sql = 'SELECT * FROM ( SELECT * FROM timebrowser_record WHERE time_begin > %d ORDER BY time_begin LIMIT %s ) as tbl ORDER BY time_begin DESC' % ( self.tableView.getFirstTime(), self.listSize)
			if self.verbose:
				print( sql )
			self.q( sql )
			self.tableView.set(self.cursor.fetchall())
		
	@pyqtSlot()
	def onExit(self):
		QApplication.exit();

	@pyqtSlot()
	def onValueChanged(self, _id):
		if self.verbose:
			print 'changed:', _id
			print self.tableView.get(_id)
		data = self.tableView.get(_id)
		self.q('''
				UPDATE timebrowser_record
				SET time_begin = %d,
				time_end = %d,
				description = '%s',
				note = '%s'
				WHERE
				id = %d
				'''	% ( data[1], data[2], data[3], data[4], data[0] ))
		self.updateDescriptionEditCompleter()				

	@pyqtSlot()
	def onDelete(self, _id):
		if self.verbose:
			print 'del:', _id,self.tableView.get(_id)[0]
		self.q('DELETE FROM timebrowser_record WHERE id = %d' % self.tableView.get(_id)[0])
		self.updateTplTable()
		self.updateDescriptionEditCompleter()				
		
	@pyqtSlot()
	def onTimer(self):
		self.startStopButton.setText('Stop (%s)' % self.timePattern.sub( '', str( datetime.now() - self.time_begin ) ) )

	@pyqtSlot()
	def onStartStop(self):
		if self.timer.isActive():
			self.timer.stop()
			self.time_end = datetime.now()
			self.q('''
				INSERT INTO timebrowser_record
				(time_begin,time_end,description,note)
				VALUES
				('%d','%d','%s','%s')
				'''	% ( int(mktime(self.time_begin.timetuple())), int(mktime(self.time_end.timetuple())), self.descriptionInput.text(), self.noteInput.text() ))
			self.noteInput.clear()
			self.updateTplTable()
			self.updateDescriptionEditCompleter()				
			self.startStopButton.setText('Start')
		else:
			self.time_begin = datetime.now()
			self.timer.start()
			self.onTimer()

	def onAboutAppAction(self):
		QMessageBox.about(self, self.tr("&about"), self.tr("%1 version %2").arg(QApplication.applicationName()).arg(QApplication.applicationVersion()))
		
	def onAboutQtAction(self):
		QMessageBox.aboutQt(self, self.tr("&about"))
Example #12
0
confManager = ConfManager.getManager()

fts = confManager.getValue('common', 'folders')

ui.nameLabel.setText(confManager.getValue('UI', 'username'))

i = 0
for ft in fts:
    flistItem = QListWidgetItem(QIcon('icon/folder.png'), ft, ui.folderList)
    ui.folderList.insertItem(i, flistItem)
    i += 1

statusBar = QStatusBar(d)
#print confManager.getValue('UI', 'window')
statusBar.showMessage(
    confManager.getValue('UI', 'statusbar')['messages']['init'])
d.setStatusBar(statusBar)


def connect(btn):
    """connect to UniFileSync Server"""
    req['param'] = {'name': 'all'}

    print btn.text()

    if btn.text() == 'Connect':
        req['action'] = 'start'
    else:
        req['action'] = 'stop'

    try:
Example #13
0
class QtStatusBar(QtWidget, ProxyStatusBar):
    """ A Qt implementation of an Enaml ProxyStatusBar.

    """
    #: A reference to the widget created by the proxy.
    widget = Typed(QStatusBar)

    #--------------------------------------------------------------------------
    # Initialization API
    #--------------------------------------------------------------------------
    def create_widget(self):
        """ Create the QStatusBar widget.

        """
        self.widget = QStatusBar(self.parent_widget())

    def init_widget(self):
        """ Initialize the widget.

        """
        super(QtStatusBar, self).init_widget()
        self.set_size_grip_enabled(self.declaration.size_grip_enabled)

    def init_layout(self):
        """ Initialize the layout for the widget.

        """
        super(QtStatusBar, self).init_layout()
        widget = self.widget
        for child in self.children():
            if isinstance(child, QtStatusItem):
                s_widget = child.status_widget()
                if s_widget is not None:
                    stretch = child.stretch()
                    if child.is_permanent():
                        widget.addPermanentWidget(s_widget, stretch)
                    else:
                        widget.addWidget(s_widget, stretch)

    #--------------------------------------------------------------------------
    # Utility Methods
    #--------------------------------------------------------------------------
    def refresh_item(self, item):
        """ A method invoked by a child status item.

        This method can be called when the widget for the item should
        be refreshed in the status bar.

        """
        w = self.widget
        s = item.status_widget()
        if s is not None:
            w.removeWidget(s)
            for index, child in enumerate(self.children()):
                if child is item:
                    stretch = item.stretch()
                    if item.is_permanent():
                        w.insertPermanentWidget(index, s, stretch)
                    else:
                        w.insertWidget(index, s, stretch)
                    s.show()
                    break

    #--------------------------------------------------------------------------
    # Child Events
    #--------------------------------------------------------------------------
    def child_added(self, child):
        """ Handle the child added event for a QtStatusBar.

        """
        super(QtStatusBar, self).child_added(child)
        if isinstance(child, QtStatusItem):
            w = self.widget
            s = child.status_widget()
            if s is not None:
                for index, item in enumerate(self.children()):
                    if child is item:
                        stretch = item.stretch()
                        if item.is_permanent():
                            w.insertPermanentWidget(index, s, stretch)
                        else:
                            w.insertWidget(index, s, stretch)
                        break

    def child_removed(self, child):
        """ Handle the child removed event for a QtStatusBar.

        """
        if isinstance(child, QtStatusItem):
            s = child.status_widget()
            if s is not None:
                self.widget.removeWidget(s)

    #--------------------------------------------------------------------------
    # ProxyStatusBar API
    #--------------------------------------------------------------------------
    def set_size_grip_enabled(self, enabled):
        """ Set the size grip enabled on the underlying widget.

        """
        self.widget.setSizeGripEnabled(enabled)

    def show_message(self, message, timeout=0):
        """ Show a temporary message in the status bar.

        """
        self.widget.showMessage(message, timeout)

    def clear_message(self):
        """ Clear any temporary message shown in the status bar.

        """
        self.widget.clearMessage()
Example #14
0
confManager = ConfManager.getManager()

fts = confManager.getValue("common", "folders")

ui.nameLabel.setText(confManager.getValue("UI", "username"))

i = 0
for ft in fts:
    flistItem = QListWidgetItem(QIcon("icon/folder.png"), ft, ui.folderList)
    ui.folderList.insertItem(i, flistItem)
    i += 1

statusBar = QStatusBar(d)
# print confManager.getValue('UI', 'window')
statusBar.showMessage(confManager.getValue("UI", "statusbar")["messages"]["init"])
d.setStatusBar(statusBar)


def connect(btn):
    """connect to UniFileSync Server"""
    req["param"] = {"name": "all"}

    print btn.text()

    if btn.text() == "Connect":
        req["action"] = "start"
    else:
        req["action"] = "stop"

    try:
Example #15
0
class OWWidget(QDialog, metaclass=WidgetMetaClass):
    # Global widget count
    widget_id = 0

    # Widget description
    name = None
    id = None
    category = None
    version = None
    description = None
    long_description = None
    icon = "icons/Unknown.png"
    priority = sys.maxsize
    author = None
    author_email = None
    maintainer = None
    maintainer_email = None
    help = None
    help_ref = None
    url = None
    keywords = []
    background = None
    replaces = None
    inputs = []
    outputs = []

    # Default widget layout settings
    want_basic_layout = True
    want_main_area = True
    want_control_area = True
    want_graph = False
    show_save_graph = True
    want_status_bar = False
    no_report = False

    save_position = True
    resizing_enabled = True

    widgetStateChanged = Signal(str, int, str)
    blockingStateChanged = Signal(bool)
    asyncCallsStateChange = Signal()
    progressBarValueChanged = Signal(float)
    processingStateChanged = Signal(int)

    settingsHandler = None
    """:type: SettingsHandler"""

    savedWidgetGeometry = settings.Setting(None)

    def __new__(cls, parent=None, *args, **kwargs):
        self = super().__new__(cls, None, cls.get_flags())
        QDialog.__init__(self, None, self.get_flags())

        stored_settings = kwargs.get('stored_settings', None)
        if self.settingsHandler:
            self.settingsHandler.initialize(self, stored_settings)

        self.signalManager = kwargs.get('signal_manager', None)

        setattr(self, gui.CONTROLLED_ATTRIBUTES,
                ControlledAttributesDict(self))
        self._guiElements = []  # used for automatic widget debugging
        self.__reportData = None

        # TODO: position used to be saved like this. Reimplement.
        #if save_position:
        #    self.settingsList = getattr(self, "settingsList", []) + \
        #                        ["widgetShown", "savedWidgetGeometry"]

        OWWidget.widget_id += 1
        self.widget_id = OWWidget.widget_id

        if self.name:
            self.setCaption(self.name)

        self.setFocusPolicy(Qt.StrongFocus)

        self.startTime = time.time()  # used in progressbar

        self.widgetState = {"Info": {}, "Warning": {}, "Error": {}}

        self.__blocking = False
        # flag indicating if the widget's position was already restored
        self.__was_restored = False

        self.__progressBarValue = -1
        self.__progressState = 0
        self.__statusMessage = ""

        if self.want_basic_layout:
            self.insertLayout()

        return self

    def __init__(self, *args, **kwargs):
        """QDialog __init__ was already called in __new__,
        please do not call it here."""

    @classmethod
    def get_flags(cls):
        return (Qt.Window if cls.resizing_enabled else Qt.Dialog
                | Qt.MSWindowsFixedSizeDialogHint)

    # noinspection PyAttributeOutsideInit
    def insertLayout(self):
        def createPixmapWidget(self, parent, iconName):
            w = QLabel(parent)
            parent.layout().addWidget(w)
            w.setFixedSize(16, 16)
            w.hide()
            if os.path.exists(iconName):
                w.setPixmap(QPixmap(iconName))
            return w

        self.setLayout(QVBoxLayout())
        self.layout().setMargin(2)

        self.warning_bar = gui.widgetBox(self,
                                         orientation="horizontal",
                                         margin=0,
                                         spacing=0)
        self.warning_icon = gui.widgetLabel(self.warning_bar, "")
        self.warning_label = gui.widgetLabel(self.warning_bar, "")
        self.warning_label.setStyleSheet("padding-top: 5px")
        self.warning_bar.setSizePolicy(QSizePolicy.Ignored,
                                       QSizePolicy.Maximum)
        gui.rubber(self.warning_bar)
        self.warning_bar.setVisible(False)

        self.topWidgetPart = gui.widgetBox(self,
                                           orientation="horizontal",
                                           margin=0)
        self.leftWidgetPart = gui.widgetBox(self.topWidgetPart,
                                            orientation="vertical",
                                            margin=0)
        if self.want_main_area:
            self.leftWidgetPart.setSizePolicy(
                QSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding))
            self.leftWidgetPart.updateGeometry()
            self.mainArea = gui.widgetBox(self.topWidgetPart,
                                          orientation="vertical",
                                          sizePolicy=QSizePolicy(
                                              QSizePolicy.Expanding,
                                              QSizePolicy.Expanding),
                                          margin=0)
            self.mainArea.layout().setMargin(4)
            self.mainArea.updateGeometry()

        if self.want_control_area:
            self.controlArea = gui.widgetBox(self.leftWidgetPart,
                                             orientation="vertical",
                                             margin=4)

        if self.want_graph and self.show_save_graph:
            graphButtonBackground = gui.widgetBox(self.leftWidgetPart,
                                                  orientation="horizontal",
                                                  margin=4)
            self.graphButton = gui.button(graphButtonBackground, self,
                                          "&Save Graph")
            self.graphButton.setAutoDefault(0)

        if self.want_status_bar:
            self.widgetStatusArea = QFrame(self)
            self.statusBarIconArea = QFrame(self)
            self.widgetStatusBar = QStatusBar(self)

            self.layout().addWidget(self.widgetStatusArea)

            self.widgetStatusArea.setLayout(QHBoxLayout(self.widgetStatusArea))
            self.widgetStatusArea.layout().addWidget(self.statusBarIconArea)
            self.widgetStatusArea.layout().addWidget(self.widgetStatusBar)
            self.widgetStatusArea.layout().setMargin(0)
            self.widgetStatusArea.setFrameShape(QFrame.StyledPanel)

            self.statusBarIconArea.setLayout(QHBoxLayout())
            self.widgetStatusBar.setSizeGripEnabled(0)

            self.statusBarIconArea.hide()

            self._warningWidget = createPixmapWidget(
                self.statusBarIconArea,
                os.path.join(environ.widget_install_dir,
                             "icons/triangle-orange.png"))
            self._errorWidget = createPixmapWidget(
                self.statusBarIconArea,
                os.path.join(environ.widget_install_dir,
                             "icons/triangle-red.png"))

    # status bar handler functions
    def setState(self, stateType, id, text):
        stateChanged = super().setState(stateType, id, text)
        if not stateChanged or not hasattr(self, "widgetStatusArea"):
            return

        iconsShown = 0
        warnings = [("Warning", self._warningWidget, self._owWarning),
                    ("Error", self._errorWidget, self._owError)]
        for state, widget, use in warnings:
            if not widget:
                continue
            if use and self.widgetState[state]:
                widget.setToolTip("\n".join(self.widgetState[state].values()))
                widget.show()
                iconsShown = 1
            else:
                widget.setToolTip("")
                widget.hide()

        if iconsShown:
            self.statusBarIconArea.show()
        else:
            self.statusBarIconArea.hide()

        if (stateType == "Warning" and self._owWarning) or \
                (stateType == "Error" and self._owError):
            if text:
                self.setStatusBarText(stateType + ": " + text)
            else:
                self.setStatusBarText("")
        self.updateStatusBarState()

    def updateWidgetStateInfo(self, stateType, id, text):
        html = self.widgetStateToHtml(self._owInfo, self._owWarning,
                                      self._owError)
        if html:
            self.widgetStateInfoBox.show()
            self.widgetStateInfo.setText(html)
            self.widgetStateInfo.setToolTip(html)
        else:
            if not self.widgetStateInfoBox.isVisible():
                dHeight = -self.widgetStateInfoBox.height()
            else:
                dHeight = 0
            self.widgetStateInfoBox.hide()
            self.widgetStateInfo.setText("")
            self.widgetStateInfo.setToolTip("")
            width, height = self.width(), self.height() + dHeight
            self.resize(width, height)

    def updateStatusBarState(self):
        if not hasattr(self, "widgetStatusArea"):
            return
        if self.widgetState["Warning"] or self.widgetState["Error"]:
            self.widgetStatusArea.show()
        else:
            self.widgetStatusArea.hide()

    def setStatusBarText(self, text, timeout=5000):
        if hasattr(self, "widgetStatusBar"):
            self.widgetStatusBar.showMessage(" " + text, timeout)

    # TODO add!
    def prepareDataReport(self, data):
        pass

    # ##############################################
    """
    def isDataWithClass(self, data, wantedVarType=None, checkMissing=False):
        self.error([1234, 1235, 1236])
        if not data:
            return 0
        if not data.domain.classVar:
            self.error(1234, "A data set with a class attribute is required.")
            return 0
        if wantedVarType and data.domain.classVar.varType != wantedVarType:
            self.error(1235, "Unable to handle %s class." %
                             str(data.domain.class_var.var_type).lower())
            return 0
        if checkMissing and not orange.Preprocessor_dropMissingClasses(data):
            self.error(1236, "Unable to handle data set with no known classes")
            return 0
        return 1
    """

    def restoreWidgetPosition(self):
        restored = False
        if self.save_position:
            geometry = self.savedWidgetGeometry
            if geometry is not None:
                restored = self.restoreGeometry(QByteArray(geometry))

            if restored:
                space = qApp.desktop().availableGeometry(self)
                frame, geometry = self.frameGeometry(), self.geometry()

                #Fix the widget size to fit inside the available space
                width = space.width() - (frame.width() - geometry.width())
                width = min(width, geometry.width())
                height = space.height() - (frame.height() - geometry.height())
                height = min(height, geometry.height())
                self.resize(width, height)

                # Move the widget to the center of available space if it is
                # currently outside it
                if not space.contains(self.frameGeometry()):
                    x = max(0, space.width() / 2 - width / 2)
                    y = max(0, space.height() / 2 - height / 2)

                    self.move(x, y)
        return restored

    def __updateSavedGeometry(self):
        if self.__was_restored:
            # Update the saved geometry only between explicit show/hide
            # events (i.e. changes initiated by the user not by Qt's default
            # window management).
            self.savedWidgetGeometry = self.saveGeometry()

    # when widget is resized, save the new width and height
    def resizeEvent(self, ev):
        QDialog.resizeEvent(self, ev)
        # Don't store geometry if the widget is not visible
        # (the widget receives a resizeEvent (with the default sizeHint)
        # before showEvent and we must not overwrite the the savedGeometry
        # with it)
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()

    def moveEvent(self, ev):
        QDialog.moveEvent(self, ev)
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()

    # set widget state to hidden
    def hideEvent(self, ev):
        if self.save_position:
            self.__updateSavedGeometry()
        self.__was_restored = False
        QDialog.hideEvent(self, ev)

    def closeEvent(self, ev):
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()
        self.__was_restored = False
        QDialog.closeEvent(self, ev)

    def showEvent(self, ev):
        QDialog.showEvent(self, ev)
        if self.save_position:
            # Restore saved geometry on show
            self.restoreWidgetPosition()
        self.__was_restored = True

    def wheelEvent(self, event):
        """ Silently accept the wheel event. This is to ensure combo boxes
        and other controls that have focus don't receive this event unless
        the cursor is over them.
        """
        event.accept()

    def setCaption(self, caption):
        # we have to save caption title in case progressbar will change it
        self.captionTitle = str(caption)
        self.setWindowTitle(caption)

    # put this widget on top of all windows
    def reshow(self):
        self.show()
        self.raise_()
        self.activateWindow()

    def send(self, signalName, value, id=None):
        if not any(s.name == signalName for s in self.outputs):
            raise ValueError(
                '{} is not a valid output signal for widget {}'.format(
                    signalName, self.name))
        if self.signalManager is not None:
            self.signalManager.send(self, signalName, value, id)

    def __setattr__(self, name, value):
        """Set value to members of this instance or any of its members.

        If member is used in a gui control, notify the control about the change.

        name: name of the member, dot is used for nesting ("graph.point.size").
        value: value to set to the member.

        """

        names = name.rsplit(".")
        field_name = names.pop()
        obj = reduce(lambda o, n: getattr(o, n, None), names, self)
        if obj is None:
            raise AttributeError("Cannot set '{}' to {} ".format(name, value))

        if obj is self:
            super().__setattr__(field_name, value)
        else:
            setattr(obj, field_name, value)

        notify_changed(obj, field_name, value)

        if self.settingsHandler:
            self.settingsHandler.fast_save(self, name, value)

    def openContext(self, *a):
        self.settingsHandler.open_context(self, *a)

    def closeContext(self):
        self.settingsHandler.close_context(self)

    def retrieveSpecificSettings(self):
        pass

    def storeSpecificSettings(self):
        pass

    def saveSettings(self):
        self.settingsHandler.update_defaults(self)

    # this function is only intended for derived classes to send appropriate
    # signals when all settings are loaded
    def activate_loaded_settings(self):
        pass

    # reimplemented in other widgets
    def onDeleteWidget(self):
        pass

    def handleNewSignals(self):
        # this is called after all new signals have been handled
        # implement this in your widget if you want to process something only
        # after you received multiple signals
        pass

    # ############################################
    # PROGRESS BAR FUNCTIONS

    def progressBarInit(self, processEvents=QEventLoop.AllEvents):
        """
        Initialize the widget's progress (i.e show and set progress to 0%).

        .. note::
            This method will by default call `QApplication.processEvents`
            with `processEvents`. To suppress this behavior pass
            ``processEvents=None``.

        :param processEvents: Process events flag
        :type processEvents: `QEventLoop.ProcessEventsFlags` or `None`
        """
        self.startTime = time.time()
        self.setWindowTitle(self.captionTitle + " (0% complete)")

        if self.__progressState != 1:
            self.__progressState = 1
            self.processingStateChanged.emit(1)

        self.progressBarSet(0, processEvents)

    def progressBarSet(self, value, processEvents=QEventLoop.AllEvents):
        """
        Set the current progress bar to `value`.

        .. note::
            This method will by default call `QApplication.processEvents`
            with `processEvents`. To suppress this behavior pass
            ``processEvents=None``.

        :param float value: Progress value
        :param processEvents: Process events flag
        :type processEvents: `QEventLoop.ProcessEventsFlags` or `None`
        """
        old = self.__progressBarValue
        self.__progressBarValue = value

        if value > 0:
            if self.__progressState != 1:
                warnings.warn(
                    "progressBarSet() called without a "
                    "preceding progressBarInit()",
                    stacklevel=2)
                self.__progressState = 1
                self.processingStateChanged.emit(1)

            usedTime = max(1, time.time() - self.startTime)
            totalTime = (100.0 * usedTime) / float(value)
            remainingTime = max(0, totalTime - usedTime)
            h = int(remainingTime / 3600)
            min = int((remainingTime - h * 3600) / 60)
            sec = int(remainingTime - h * 3600 - min * 60)
            if h > 0:
                text = "%(h)d:%(min)02d:%(sec)02d" % vars()
            else:
                text = "%(min)d:%(sec)02d" % vars()
            self.setWindowTitle(
                self.captionTitle +
                " (%(value).2f%% complete, remaining time: %(text)s)" % vars())
        else:
            self.setWindowTitle(self.captionTitle + " (0% complete)")

        if old != value:
            self.progressBarValueChanged.emit(value)

        if processEvents is not None and processEvents is not False:
            qApp.processEvents(processEvents)

    def progressBarValue(self):
        return self.__progressBarValue

    progressBarValue = pyqtProperty(float,
                                    fset=progressBarSet,
                                    fget=progressBarValue)

    processingState = pyqtProperty(int, fget=lambda self: self.__progressState)

    def progressBarAdvance(self, value, processEvents=QEventLoop.AllEvents):
        self.progressBarSet(self.progressBarValue + value, processEvents)

    def progressBarFinished(self, processEvents=QEventLoop.AllEvents):
        """
        Stop the widget's progress (i.e hide the progress bar).

        .. note::
            This method will by default call `QApplication.processEvents`
            with `processEvents`. To suppress this behavior pass
            ``processEvents=None``.

        :param processEvents: Process events flag
        :type processEvents: `QEventLoop.ProcessEventsFlags` or `None`
        """
        self.setWindowTitle(self.captionTitle)
        if self.__progressState != 0:
            self.__progressState = 0
            self.processingStateChanged.emit(0)

        if processEvents is not None and processEvents is not False:
            qApp.processEvents(processEvents)

    #: Widget's status message has changed.
    statusMessageChanged = Signal(str)

    def setStatusMessage(self, text):
        if self.__statusMessage != text:
            self.__statusMessage = text
            self.statusMessageChanged.emit(text)

    def statusMessage(self):
        return self.__statusMessage

    def keyPressEvent(self, e):
        if (int(e.modifiers()), e.key()) in OWWidget.defaultKeyActions:
            OWWidget.defaultKeyActions[int(e.modifiers()), e.key()](self)
        else:
            QDialog.keyPressEvent(self, e)

    def information(self, id=0, text=None):
        self.setState("Info", id, text)

    def warning(self, id=0, text=""):
        self.setState("Warning", id, text)

    def error(self, id=0, text=""):
        self.setState("Error", id, text)

    def setState(self, state_type, id, text):
        changed = 0
        if type(id) == list:
            for val in id:
                if val in self.widgetState[state_type]:
                    self.widgetState[state_type].pop(val)
                    changed = 1
        else:
            if type(id) == str:
                text = id
                id = 0
            if not text:
                if id in self.widgetState[state_type]:
                    self.widgetState[state_type].pop(id)
                    changed = 1
            else:
                self.widgetState[state_type][id] = text
                changed = 1

        if changed:
            if type(id) == list:
                for i in id:
                    self.widgetStateChanged.emit(state_type, i, "")
            else:
                self.widgetStateChanged.emit(state_type, id, text or "")

        tooltip_lines = []
        highest_type = None
        for a_type in ("Error", "Warning", "Info"):
            msgs_for_ids = self.widgetState.get(a_type)
            if not msgs_for_ids:
                continue
            msgs_for_ids = list(msgs_for_ids.values())
            if not msgs_for_ids:
                continue
            tooltip_lines += msgs_for_ids
            if highest_type is None:
                highest_type = a_type

        if highest_type is None:
            self.set_warning_bar(None)
        elif len(tooltip_lines) == 1:
            msg = tooltip_lines[0]
            if "\n" in msg:
                self.set_warning_bar(highest_type,
                                     msg[:msg.index("\n")] + " (...)", msg)
            else:
                self.set_warning_bar(highest_type, tooltip_lines[0],
                                     tooltip_lines[0])
        else:
            self.set_warning_bar(
                highest_type,
                "{} problems during execution".format(len(tooltip_lines)),
                "\n".join(tooltip_lines))

        return changed

    def set_warning_bar(self, state_type, text=None, tooltip=None):
        colors = {
            "Error": ("#ffc6c6", "black", QStyle.SP_MessageBoxCritical),
            "Warning": ("#ffffc9", "black", QStyle.SP_MessageBoxWarning),
            "Info": ("#ceceff", "black", QStyle.SP_MessageBoxInformation)
        }
        current_height = self.height()
        if state_type is None:
            if not self.warning_bar.isHidden():
                new_height = current_height - self.warning_bar.height()
                self.warning_bar.setVisible(False)
                self.resize(self.width(), new_height)
            return
        background, foreground, icon = colors[state_type]
        style = QApplication.instance().style()
        self.warning_icon.setPixmap(style.standardIcon(icon).pixmap(14, 14))

        self.warning_bar.setStyleSheet(
            "background-color: {}; color: {};"
            "padding: 3px; padding-left: 6px; vertical-align: center".format(
                background, foreground))
        self.warning_label.setText(text)
        self.warning_bar.setToolTip(tooltip)
        if self.warning_bar.isHidden():
            self.warning_bar.setVisible(True)
            new_height = current_height + self.warning_bar.height()
            self.resize(self.width(), new_height)

    def widgetStateToHtml(self, info=True, warning=True, error=True):
        pixmaps = self.getWidgetStateIcons()
        items = []
        iconPath = {
            "Info": "canvasIcons:information.png",
            "Warning": "canvasIcons:warning.png",
            "Error": "canvasIcons:error.png"
        }
        for show, what in [(info, "Info"), (warning, "Warning"),
                           (error, "Error")]:
            if show and self.widgetState[what]:
                items.append('<img src="%s" style="float: left;"> %s' %
                             (iconPath[what], "\n".join(
                                 self.widgetState[what].values())))
        return "<br>".join(items)

    @classmethod
    def getWidgetStateIcons(cls):
        if not hasattr(cls, "_cached__widget_state_icons"):
            iconsDir = os.path.join(environ.canvas_install_dir, "icons")
            QDir.addSearchPath(
                "canvasIcons",
                os.path.join(environ.canvas_install_dir, "icons/"))
            info = QPixmap("canvasIcons:information.png")
            warning = QPixmap("canvasIcons:warning.png")
            error = QPixmap("canvasIcons:error.png")
            cls._cached__widget_state_icons = \
                {"Info": info, "Warning": warning, "Error": error}
        return cls._cached__widget_state_icons

    defaultKeyActions = {}

    if sys.platform == "darwin":
        defaultKeyActions = {
            (Qt.ControlModifier, Qt.Key_M):
            lambda self: self.showMaximized
            if self.isMinimized() else self.showMinimized(),
            (Qt.ControlModifier, Qt.Key_W):
            lambda self: self.setVisible(not self.isVisible())
        }

    def setBlocking(self, state=True):
        """ Set blocking flag for this widget. While this flag is set this
        widget and all its descendants will not receive any new signals from
        the signal manager
        """
        if self.__blocking != state:
            self.__blocking = state
            self.blockingStateChanged.emit(state)

    def isBlocking(self):
        """ Is this widget blocking signal processing.
        """
        return self.__blocking

    def resetSettings(self):
        self.settingsHandler.reset_settings(self)
Example #16
0
class TestDlg(QDialog):
	"""docstring for TestDlg"""
	classDefChanged = pyqtSignal()
	def __init__(self, parent=None):
		super(TestDlg, self).__init__(parent)
		self.mymodel = None
		self.myscene = DragEnabledScene(QRectF(-400,-300,800,600))
		self.myview = QGraphicsView()
		self.myview.setScene(self.myscene)
		self.myfile = None
		layout = QVBoxLayout()
		layout.addWidget(self.myview)
		buttonLayout = QHBoxLayout()
		self.savebutton = QPushButton('Save')
		self.loadbutton = QPushButton('Load')
		self.renderbutton = QPushButton('Accept')
		buttonLayout.addWidget(self.savebutton)
		buttonLayout.addWidget(self.loadbutton)
		buttonLayout.addWidget(self.renderbutton)
		layout.addLayout(buttonLayout)
		self.statusbar =  QStatusBar()
		layout.addWidget(self.statusbar)
		self.statusbar.showMessage("Ready.",2000)
		self.setLayout(layout)

		self.loadfromInitData()

		self.savebutton.pressed.connect(self.saveMatrix)
		self.loadbutton.pressed.connect(self.loadMatrix)
		self.myscene.selectionChanged.connect(self.updateStatus)
		self.myscene.modelchanged.connect(self.changeModel)

		self.renderbutton.pressed.connect(self.testDistance)

	def testDistance(self):
		print "Toplogical Distance:",self.mymodel.toplogicalDistance(1,10)
		print "Matrix Distance:",self.mymodel.matrixDistance(1,10)
		print "Posibility:", self.mymodel.transactionPossibility(1,10)

	def changeModel(self):
		self.classDefChanged.emit()

	def updateStatus(self):
		items = self.myscene.selectedItems()
		message = " "
		if items and isinstance(items[0],NodeGraphicsItem):
			message = items[0].model.name
		self.statusbar.showMessage(message)

	def loadfromInitData(self): 
		rootNode = TreeNode(0,'root')
		tempid = 100
		for key in data:
			parentNode = TreeNode(tempid,key,parent=rootNode)
			for leaf in data[key]:
				leafNode = TreeNode(leaf[0],leaf[1],color=leaf[2],accu=leaf[3],parent=parentNode)
			tempid += 1
		self.mymodel = rootNode

		for node in self.mymodel.children:
			self.myscene.addItem(NodeGraphicsItem(node))

	def saveMatrix(self):
		path = '.'
		fname = QFileDialog.getSaveFileName(self,"Save class relation",path,'class relation file (*.crf)')
		if fname:
			if not "." in fname:
				fname+='.crf'
			self.myfile = fname
			with open(fname,'w') as savefile:
				savefile.write(json.dumps(self.mymodel.toJSON()))

	def loadMatrix(self):
		path = '.'
		fname = QFileDialog.getOpenFileName(self,"Load class relation",path,'class relation file (*.crf)')
		if fname:
			if not "." in fname:
				fname += '.crf'
			self.myfile = fname
			with open(fname,'r') as loadfile:
				rawobj = json.loads(loadfile.read())
				rootNode = TreeNode(rawobj['id'],rawobj['name'],rawobj['rect'],rawobj['color'],rawobj['pos'],rawobj['accu'])
				for child in rawobj['children']:
					parentNode = TreeNode(child['id'],child['name'],child['rect'],child['color'],child['pos'],child['accu'],rootNode)
					for leaf in child['children']:
						leafNode = TreeNode(leaf['id'],leaf['name'],leaf['rect'],leaf['color'],leaf['pos'],leaf['accu'],parentNode)
				self.mymodel = rootNode

				self.myscene.clear()
				for node in self.mymodel.children:
					self.myscene.addItem(NodeGraphicsItem(node))
		self.changeModel()
Example #17
0
class MainWindow(QMainWindow):

    def __init__(self):
        super(MainWindow, self).__init__()

        self.statusBar = QStatusBar()
        self.setStatusBar(self.statusBar)
        self.statusBar.showMessage('Welcom to the GUI')

        openFile = QAction('Open File', self)
        openFile.setShortcut('Ctrl+O')
        openFile.setStatusTip('Open new File (like gamess, Bunge Slater)')
        openFile.triggered.connect(self.showDialogFile)

        openEZFIO = QAction('Open EZFIO', self)
        openEZFIO.setShortcut('Ctrl+E')
        openEZFIO.setStatusTip('Open new EZFIO')
        openEZFIO.triggered.connect(self.showDialogFolderRead)

        saveToEZFIO = QAction('Save to EZFIO', self)
        saveToEZFIO.setShortcut('Ctrl+S')
        saveToEZFIO.setStatusTip('Save on folder EZFIO proof')
        saveToEZFIO.triggered.connect(self.showDialogFolderSave)

        inputCipsi = QAction('CIPSI', self)
        inputCipsi.setStatusTip('create/edit CIPSI input')
        inputCipsi.triggered.connect(self.creatCipsiInputTab)

        inputQMC = QAction('QMC', self)
        inputQMC.setStatusTip('create/edit QMC input')
        inputQMC.triggered.connect(self.creatQMCInputTab)

        input_menu = QMenu("Input", self)
        input_menu.addAction(inputQMC)
        input_menu.addAction(inputCipsi)

        outputCipsi = QAction('CIPSI', self)
        outputCipsi.setStatusTip('create/edit CIPSI output')
        outputCipsi.triggered.connect(self.emptySlot)

        outputQMC = QAction('QMC', self)
        outputQMC.setStatusTip('create/edit QMC output')
        outputQMC.triggered.connect(self.emptySlot)

        output_menu = QMenu("Output", self)
        output_menu.addAction(outputQMC)
        output_menu.addAction(outputCipsi)

        exitAction = QAction(QIcon('exit.png'), '&Exit', self)
        exitAction.setShortcut('Ctrl+Q')
        exitAction.setStatusTip('Exit application')
        exitAction.triggered.connect(QApplication.quit)

        menubar = self.menuBar()
        fileMenu = menubar.addMenu('&File')
        fileMenu.addAction(openFile)
        fileMenu.addAction(openEZFIO)
        fileMenu.addAction(saveToEZFIO)
        fileMenu.addMenu(input_menu)
        fileMenu.addMenu(output_menu)
        fileMenu.addAction(exitAction)

        helpMenu = menubar.addMenu('&Help')


        tab_widget = QTabWidget()
        tab_widget.setTabsClosable(True)
        tab_widget.tabCloseRequested.connect(tab_widget.removeTab)
        self.setCentralWidget(tab_widget)
        self.tab_widget=tab_widget

        self.setWindowTitle("Pig Is a Gui")
        self.setMinimumSize(160, 160)
        self.resize(800, 500)

    def showDialogFile(self):
        dialog = QFileDialog(self)
        dialog.setFileMode(QFileDialog.ExistingFile)
        dialog.setOption(QFileDialog.ShowDirsOnly, False)
        dialog.setOption(QFileDialog.ReadOnly, True)

        if dialog.exec_():
            for d in dialog.selectedFiles():

                global wrater

                wrater = slater_wraper(d)

                ot = self.findChild(orbitalTable)
                ot.setmydata({'Energy': wrater.get_mo_energy(),
                              'Occu': wrater.get_mo_occ(),
                              'Orbital': wrater.get_mo_name()})

                bt = self.findChild(basisTypeWidget)
                bt.setSlater()

                iw = self.findChild(informationWidget)
                iw.setName("He")
                iw.setNbElec("6")

    def showDialogFolderRead(self):
        dialog = QFileDialog(self)
        dialog.setFileMode(QFileDialog.Directory)
        dialog.setOption(QFileDialog.ShowDirsOnly, True)

        if dialog.exec_():
            for d in dialog.selectedFiles():
                print d

    def showDialogFolderSave(self):
        dialog = QFileDialog(self)
        dialog.setFileMode(QFileDialog.Directory)
        dialog.setOption(QFileDialog.ShowDirsOnly, True)

        for name in dic_stoping_criterum:
            d = self.findChild(mySpinBoxWidget, name)
            print name, d.getValue()

        if dialog.exec_():
            for d in dialog.selectedFiles():
                wrater.write_ezfio(str(d))

    def creatCipsiInputTab(self):
        tab = CipsiWidget()
        self.tab_widget.addTab(tab, "CIPSI input")

    def creatQMCInputTab(self):
        tab = QWidget()
        self.tab_widget.addTab(tab, "QMC input")

    def emptySlot(self):
        return
Example #18
0
class QTSeedEditor(QDialog):
    """
    DICOM viewer.
    """
    @staticmethod
    def get_line(mode='h'):
        line = QFrame()
        if mode == 'h':
            line.setFrameStyle(QFrame.HLine)
        elif mode == 'v':
            line.setFrameStyle(QFrame.VLine)

        line.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding)

        return line

    def initUI(self, shape, vscale, height=600, mode='seed'):
        """
        Initialize UI.

        Parameters
        ----------
        shape : (int, int, int)
            Shape of data matrix.
        vscale : (float, float, float)
            Voxel scaling.
        height : int
            Maximal slice height in pixels.
        mode : str
            Editor mode.
        """

        self.slab = {}

        # picture
        grid = height / float(shape[1] * vscale[1])
        mgrid = (grid * vscale[0], grid * vscale[1])
        self.slice_box = SliceBox(shape[:-1], mgrid, mode)
        self.slice_box.setScrollFun(self.scrollSlices)
        self.connect(self.slice_box, SIGNAL('focus_slider'),
                     self.focusSliceSlider)

        # sliders
        self.allow_select_slice = True
        self.n_slices = shape[2]
        self.slider = QSlider(Qt.Vertical)
        self.slider.valueChanged.connect(self.sliderSelectSlice)
        self.slider.label = QLabel()
        self.slider.setRange(1, self.n_slices)

        self.slider_cw = {}
        self.slider_cw['c'] = QSlider(Qt.Horizontal)
        self.slider_cw['c'].valueChanged.connect(self.changeC)
        self.slider_cw['c'].label = QLabel()

        self.slider_cw['w'] = QSlider(Qt.Horizontal)
        self.slider_cw['w'].valueChanged.connect(self.changeW)
        self.slider_cw['w'].label = QLabel()

        self.view_label = QLabel('View size: %d x %d' %
                                 self.img_aview.shape[:-1])
        self.voxel_label = QLabel('Voxel size [mm]:\n  %.2f x %.2f x %.2f'\
                                      % tuple(self.voxel_size[np.array(self.act_transposition)]))

        # combo_view_options = VIEW_TABLE.keys()
        # combo_view = QComboBox(self)
        # combo_view.activated[str].connect(self.setView)
        # combo_view.addItems(combo_view_options)

        #radio button group for choosing seed class ------------------------
        self.current_class = 1
        self.slice_box.seed_mark = self.current_class
        number_group = QGroupBox(QString('Class markers'))
        vbox_NG = QVBoxLayout()
        r1 = QRadioButton('class 1')
        r1.setStyleSheet('QRadioButton {color: red}')
        r1.setChecked(True)
        r2 = QRadioButton('class 2')
        r2.setStyleSheet('QRadioButton {color: green}')
        r3 = QRadioButton('class 3')
        r3.setStyleSheet('QRadioButton {color: blue}')
        r4 = QRadioButton('class 4')
        r4.setStyleSheet('QRadioButton {color: cyan}')
        r5 = QRadioButton('class 5')
        r5.setStyleSheet('QRadioButton {color: magenta}')

        vbox_NG.addWidget(r1)
        vbox_NG.addWidget(r2)
        vbox_NG.addWidget(r3)
        vbox_NG.addWidget(r4)
        vbox_NG.addWidget(r5)

        number_group.setLayout(vbox_NG)

        self.button_group = QButtonGroup()
        self.button_group.addButton(r1, 1)
        self.button_group.addButton(r2, 2)
        self.button_group.addButton(r3, 3)
        self.button_group.addButton(r4, 4)
        self.button_group.addButton(r5, 5)
        self.connect(self.button_group, SIGNAL("buttonClicked(int)"),
                     self.change_seed_class)
        #-------------------------------------------------------------------

        # buttons
        # btn_save = QPushButton('Save', self)
        # btn_save.clicked.connect(self.save)

        btn_quit = QPushButton("Quit", self)
        btn_quit.clicked.connect(self.quit)

        # btn_crop = QPushButton('Crop', self)
        # btn_crop.clicked.connect(self.crop)

        combo_dmask = QComboBox(self)
        combo_dmask.activated.connect(self.changeMask)
        self.mask_points_tab, aux = self.init_draw_mask(DRAW_MASK, mgrid)
        for icon, label in aux:
            combo_dmask.addItem(icon, label)

        self.slice_box.setMaskPoints(
            self.mask_points_tab[combo_dmask.currentIndex()])

        self.status_bar = QStatusBar()

        vopts = []
        vmenu = []
        appmenu = []

        # btn_recalc = QPushButton("Recalculate", self)
        # btn_recalc.clicked.connect(self.recalculate)
        # appmenu.append(QLabel('<b>Segmentation mode</b><br><br><br>' +
        #                       'Select the region of interest<br>' +
        #                       'using the mouse buttons.<br><br>'))
        # appmenu.append(btn_recalc)
        # appmenu.append(QLabel())
        # self.volume_label = QLabel('Volume [mm3]:\n  unknown')
        # appmenu.append(self.volume_label)

        # btn_crop = QPushButton("Crop", self)
        # btn_crop.clicked.connect(self.crop)
        # appmenu.append(btn_crop)

        btn_save = QPushButton("Save Seeds", self)
        btn_save.clicked.connect(self.saveSeeds)
        appmenu.append(btn_save)

        btn_del = QPushButton("Delete Seeds", self)
        btn_del.clicked.connect(self.deleteSliceSeeds)
        appmenu.append(btn_del)

        # combo_contour_options = ['fill', 'contours', 'off']
        # combo_contour = QComboBox(self)
        # combo_contour.activated[str].connect(self.changeContourMode)
        # combo_contour.addItems(combo_contour_options)
        # self.changeContourMode(combo_contour_options[combo_contour.currentIndex()])
        # vopts.append(QLabel('Selection mode:'))
        # vopts.append(combo_contour)

        # btn_reset = QPushButton("Reset Seeds", self)
        # btn_reset.clicked.connect(self.resetSliceDraw)
        # # appmenu.append(None)
        # appmenu.append(btn_reset)

        hbox = QHBoxLayout()
        vbox = QVBoxLayout()
        vbox_left = QVBoxLayout()
        vbox_app = QVBoxLayout()

        hbox.addWidget(self.slice_box)
        hbox.addWidget(self.slider)
        vbox_left.addWidget(self.slider.label)
        vbox_left.addWidget(self.view_label)
        vbox_left.addWidget(self.voxel_label)
        # vbox_left.addWidget(QLabel())
        # vbox_left.addWidget(QLabel('View plane:'))
        # vbox_left.addWidget(combo_view)
        vbox_left.addWidget(self.get_line())
        vbox_left.addWidget(self.slider_cw['c'].label)
        vbox_left.addWidget(self.slider_cw['c'])
        vbox_left.addWidget(self.slider_cw['w'].label)
        vbox_left.addWidget(self.slider_cw['w'])
        vbox_left.addWidget(self.get_line())
        vbox_left.addWidget(QLabel('Drawing mask:'))
        vbox_left.addWidget(combo_dmask)

        for ii in vopts:
            vbox_left.addWidget(ii)

        for ii in vmenu:
            if ii is None:
                vbox_left.addStretch(1)

            else:
                vbox_left.addWidget(ii)

        for ii in appmenu:
            if ii is None:
                vbox_app.addStretch(1)

            else:
                vbox_app.addWidget(ii)

        vbox_left.addWidget(self.get_line())
        vbox_left.addWidget(number_group)

        # vbox_app.addWidget(btn_crop)

        vbox_app.addStretch(1)
        # vbox_app.addWidget(btn_save)
        vbox_app.addWidget(btn_quit)

        hbox.addLayout(vbox_left)
        hbox.addWidget(self.get_line('v'))
        hbox.addLayout(vbox_app)
        vbox.addLayout(hbox)
        vbox.addWidget(self.status_bar)
        self.setLayout(vbox)

        self.setWindowTitle('Seed Editor')
        self.show()

    def __init__(self,
                 img,
                 seeds_fname='seeds.npy',
                 actualSlice=0,
                 seeds=None,
                 contours=None,
                 mode='seed',
                 modeFun=None,
                 voxelSize=[1, 1, 1]):
        """
        Initiate Editor

        Parameters
        ----------
        img : array
            DICOM data matrix.
        actualSlice : int
            Index of actual slice.
        seeds : array
            Seeds, user defined regions of interest.
        contours : array
            Computed segmentation.
        mode : str
            Editor modes:
               'seed' - seed editor
               'crop' - manual crop
               'draw' - drawing
        modeFun : fun
            Mode function invoked by user button.
        voxelSize : tuple of float
            voxel size [mm]
        """

        QDialog.__init__(self)

        self.mode = mode
        self.mode_fun = modeFun
        self.seeds_fname = seeds_fname
        # self.datapath = datapath

        self.actual_view = 'axial'
        self.act_transposition = VIEW_TABLE[self.actual_view]
        self.last_view_position = {}
        for ii in VIEW_TABLE.iterkeys():
            self.last_view_position[ii] = img.shape[VIEW_TABLE[ii][-1]] - 1

        self.img = img
        self.img_aview = self.img.transpose(self.act_transposition)
        self.actual_slice = self.img_aview.shape[-1] - actualSlice - 1
        self.last_view_position[self.actual_view] = self.actual_slice

        # set contours
        self.contours = contours
        if self.contours is None:
            self.contours_aview = None

        else:
            self.contours_aview = self.contours.transpose(
                self.act_transposition)

        self.voxel_size = np.array(voxelSize)
        self.voxel_scale = self.voxel_size / float(np.min(self.voxel_size))
        self.voxel_volume = np.prod(voxelSize)

        # set seeds
        if seeds is None:
            self.seeds = np.zeros(self.img.shape, np.int8)
        else:
            self.seeds = seeds

        self.seeds_aview = self.seeds.transpose(self.act_transposition)
        self.seeds_modified = False

        self.initUI(self.img_aview.shape,
                    self.voxel_scale[np.array(self.act_transposition)], 600,
                    mode)

        if mode == 'draw':
            self.seeds_orig = self.seeds.copy()
            self.slice_box.setEraseFun(self.eraseVolume)

        # set view window values C/W
        lb = np.min(img)
        ub = np.max(img)
        dul = ub - lb
        self.cw_range = {'c': [lb, ub], 'w': [1, dul]}
        self.slider_cw['c'].setRange(lb, ub)
        self.slider_cw['w'].setRange(1, dul)
        self.changeC(lb + dul / 2)
        self.changeW(dul)

        self.offset = np.zeros((3, ), dtype=np.int16)

    def change_seed_class(self, id):
        self.current_class = id
        self.slice_box.seed_mark = self.current_class
        # print 'Current seed class changed to ', id, '.'

    def showStatus(self, msg):
        self.status_bar.showMessage(QString(msg))
        QApplication.processEvents()

    def init_draw_mask(self, draw_mask, grid):
        mask_points = []
        mask_iconlabel = []
        for mask, label in draw_mask:
            w, h = mask.shape
            xx, yy = mask.nonzero()
            mask_points.append((xx - w / 2, yy - h / 2))

            img = QImage(w, h, QImage.Format_ARGB32)
            img.fill(qRgba(255, 255, 255, 0))
            for ii in range(xx.shape[0]):
                img.setPixel(xx[ii], yy[ii], qRgba(0, 0, 0, 255))

            img = img.scaled(QSize(w * grid[0], h * grid[1]))
            icon = QIcon(QPixmap.fromImage(img))
            mask_iconlabel.append((icon, label))

        return mask_points, mask_iconlabel

    def saveSliceSeeds(self):
        aux = self.slice_box.getSliceSeeds()
        if aux is not None:
            self.seeds_aview[..., self.actual_slice] = aux
            self.seeds_modified = True

        else:
            self.seeds_modified = False

    def saveSeeds(self):
        print 'Saving seeds array ... ',
        # aux = np.swapaxes(np.swapaxes(self.seeds_aview, 1, 2), 0, 1)
        aux = np.swapaxes(self.seeds_aview, 0, 2)
        np.save(self.seeds_fname, aux)
        print 'done'

    def updateCropBounds(self):
        crp = self.getCropBounds()
        if crp is not None:
            _, cri = crp
            self.contours = np.zeros(self.img.shape, np.int8)
            self.contours[cri].fill(1)
            self.contours_aview = self.contours.transpose(
                self.act_transposition)

    def focusSliceSlider(self):
        self.slider.setFocus(True)

    def sliderSelectSlice(self, value):
        self.selectSlice(self.n_slices - value)

    def scrollSlices(self, inc):
        if abs(inc) > 0:
            new = self.actual_slice + inc
            self.selectSlice(new)

    def selectSlice(self, value, force=False):
        if not (self.allow_select_slice):
            return

        if (value < 0) or (value >= self.n_slices):
            return

        if (value != self.actual_slice) or force:
            self.saveSliceSeeds()
            if self.seeds_modified and (self.mode == 'crop'):
                self.updateCropBounds()

        if self.contours is None:
            contours = None

        else:
            contours = self.contours_aview[..., value]

        slider_val = self.n_slices - value
        self.slider.setValue(slider_val)
        self.slider.label.setText('Slice: %d / %d' %
                                  (slider_val, self.n_slices))

        self.slice_box.setSlice(self.img_aview[..., value],
                                self.seeds_aview[..., value], contours)
        self.actual_slice = value

    def getSeeds(self):
        return self.seeds

    def getImg(self):
        return self.img

    def getOffset(self):
        return self.offset * self.voxel_size

    def getSeedsVal(self, label):
        return self.img[self.seeds == label]

    def getContours(self):
        return self.contours

    def setContours(self, contours):
        self.contours = contours
        self.contours_aview = self.contours.transpose(self.act_transposition)

        self.selectSlice(self.actual_slice)

    def changeCW(self, value, key):
        rg = self.cw_range[key]
        if (value < rg[0]) or (value > rg[1]):
            return

        if (value != self.slice_box.getCW()[key]):
            self.slider_cw[key].setValue(value)
            self.slider_cw[key].label.setText('%s: %d' % (key.upper(), value))
            self.slice_box.setCW(value, key)
            self.slice_box.updateSliceCW(self.img_aview[...,
                                                        self.actual_slice])

    def changeC(self, value):
        self.changeCW(value, 'c')

    def changeW(self, value):
        self.changeCW(value, 'w')

    def setView(self, value):
        self.last_view_position[self.actual_view] = self.actual_slice
        # save seeds
        self.saveSliceSeeds()
        if self.seeds_modified and (self.mode == 'crop'):
            self.updateCropBounds(self.seeds_aview[..., self.actual_slice])

        key = str(value)
        self.actual_view = key
        self.actual_slice = self.last_view_position[key]

        self.act_transposition = VIEW_TABLE[key]
        self.img_aview = self.img.transpose(self.act_transposition)
        self.seeds_aview = self.seeds.transpose(self.act_transposition)

        if self.contours is not None:
            self.contours_aview = self.contours.transpose(
                self.act_transposition)
            contours = self.contours_aview[..., self.actual_slice]

        else:
            contours = None

        vscale = self.voxel_scale[np.array(self.act_transposition)]
        height = self.slice_box.height()
        grid = height / float(self.img_aview.shape[1] * vscale[1])
        mgrid = (grid * vscale[0], grid * vscale[1])

        self.slice_box.resizeSlice(new_slice_size=self.img_aview.shape[:-1],
                                   new_grid=mgrid)

        self.slice_box.setSlice(self.img_aview[..., self.actual_slice],
                                self.seeds_aview[...,
                                                 self.actual_slice], contours)

        self.allow_select_slice = False
        self.n_slices = self.img_aview.shape[2]
        slider_val = self.n_slices - self.actual_slice
        self.slider.setValue(slider_val)
        self.slider.setRange(1, self.n_slices)
        self.allow_select_slice = True

        self.slider.label.setText('Slice: %d / %d' %
                                  (slider_val, self.n_slices))
        self.view_label.setText('View size: %d x %d' %
                                self.img_aview.shape[:-1])

    def changeMask(self, val):
        self.slice_box.setMaskPoints(self.mask_points_tab[val])

    def changeContourMode(self, val):
        self.slice_box.contour_mode = str(val)
        self.slice_box.updateSlice()

    def changeEraseMode(self, val):
        self.slice_box.erase_mode = str(val)

    def eraseVolume(self, pos, mode):
        self.showStatus("Processing...")
        xyz = pos + (self.actual_slice, )
        p = tuple(np.array(xyz)[np.array(self.act_transposition)])
        if self.seeds[p] > 0:
            if mode == 'inside':
                erase_reg(self.seeds, p, val=0)

            elif mode == 'outside':
                erase_reg(self.seeds, p, val=-1)
                idxs = np.where(self.seeds < 0)
                self.seeds.fill(0)
                self.seeds[idxs] = 1

        if self.contours is None:
            contours = None

        else:
            contours = self.contours_aview[..., self.actual_slice]

        self.slice_box.setSlice(self.img_aview[..., self.actual_slice],
                                self.seeds_aview[...,
                                                 self.actual_slice], contours)

        self.showStatus("Done")

    def cropUpdate(self, img):

        for ii in VIEW_TABLE.iterkeys():
            self.last_view_position[ii] = 0
        self.actual_slice = 0

        self.img = img
        self.img_aview = self.img.transpose(self.act_transposition)

        self.contours = None
        self.contours_aview = None

        self.seeds = np.zeros(self.img.shape, np.int8)
        self.seeds_aview = self.seeds.transpose(self.act_transposition)
        self.seeds_modified = False

        vscale = self.voxel_scale[np.array(self.act_transposition)]
        height = self.slice_box.height()
        grid = height / float(self.img_aview.shape[1] * vscale[1])
        mgrid = (grid * vscale[0], grid * vscale[1])

        self.slice_box.resizeSlice(new_slice_size=self.img_aview.shape[:-1],
                                   new_grid=mgrid)

        self.slice_box.setSlice(self.img_aview[..., self.actual_slice],
                                self.seeds_aview[..., self.actual_slice], None)

        self.allow_select_slice = False
        self.n_slices = self.img_aview.shape[2]
        self.slider.setValue(self.actual_slice + 1)
        self.slider.setRange(1, self.n_slices)
        self.allow_select_slice = True

        self.slider.label.setText('Slice: %d / %d' %
                                  (self.actual_slice + 1, self.n_slices))
        self.view_label.setText('View size: %d x %d' %
                                self.img_aview.shape[:-1])

    def getCropBounds(self):

        nzs = self.seeds.nonzero()
        cri = []
        flag = True
        for ii in range(3):
            if nzs[ii].shape[0] == 0:
                flag = False
                break

            smin, smax = np.min(nzs[ii]), np.max(nzs[ii])
            if smin == smax:
                flag = False
                break

            cri.append((smin, smax))

        if flag:
            cri = np.array(cri)

            out = []
            offset = []
            for jj, ii in enumerate(cri):
                out.append(slice(ii[0], ii[1] + 1))
                offset.append(ii[0])

            return np.array(offset), tuple(out)

        else:
            return None

    def crop(self):
        self.showStatus("Processing...")

        crp = self.getCropBounds()

        if crp is not None:
            offset, cri = crp
            crop = self.img[cri]
            self.img = np.ascontiguousarray(crop)
            self.offset += offset

            self.showStatus('Done')

        else:
            self.showStatus('Region not selected!')

        self.cropUpdate(self.img)

    def recalculate(self, event):
        self.saveSliceSeeds()
        if np.abs(np.min(self.seeds) - np.max(self.seeds)) < 2:
            self.showStatus('At least two regions must be marked!')
            return

        self.showStatus("Processing...")
        # idx = 3
        # s = random_walker(self.img[idx,:,:], self.seeds[idx,:,:])#, mode='cg_mg')
        # plt.figure()
        # plt.imshow(mark_boundaries(self.img[idx,:,:], s))
        # plt.show()
        # self.segmentation = np.zeros(self.img.shape)
        # self.segmentation[idx,:,:] = s
        self.segmentation = random_walker(self.img, self.seeds, mode='cg_mg')
        self.setContours(self.segmentation - 1)
        self.selectSlice(self.actual_slice)
        # self.updateVolume()
        self.showStatus("Done")

    def deleteSliceSeeds(self, event):
        self.seeds_aview[..., self.actual_slice] = 0
        self.slice_box.setSlice(seeds=self.seeds_aview[..., self.actual_slice])
        self.slice_box.updateSlice()

    def resetSliceDraw(self, event):
        seeds_orig_aview = self.seeds_orig.transpose(self.act_transposition)
        self.seeds_aview[..., self.actual_slice] = seeds_orig_aview[
            ..., self.actual_slice]
        self.slice_box.setSlice(seeds=self.seeds_aview[..., self.actual_slice])
        self.slice_box.updateSlice()

    def quit(self, event):
        self.close()

    # def save(self, event):
    #     odp = os.path.expanduser("~/lisa_data")
    #     if not op.exists(odp):
    #         os.makedirs(odp)
    #
    #     data = self.export()
    #     # data['version'] = self.version
    #     # data['experiment_caption'] = self.experiment_caption
    #     # data['lisa_operator_identifier'] = self.lisa_operator_identifier
    #     pth, filename = op.split(op.normpath(self.datapath))
    #     # filename += "-" + self.experiment_caption
    #     filepath = 'org-' + filename + '.pklz'
    #     filepath = op.join(odp, filepath)
    #     filepath = misc.suggest_filename(filepath)
    #     misc.obj_to_file(data, filepath, filetype='pklz')
    #
    #     filepath = 'organ_last.pklz'
    #     filepath = op.join(odp, filepath)
    #     misc.obj_to_file(data, filepath, filetype='pklz')

    # def export(self):
    #     slab = {}
    #     slab['none'] = 0
    #     slab['liver'] = 1
    #     slab['lesions'] = 6
    #     slab.update(self.slab)
    #
    #     data = {}
    #     data['version'] = (1, 0, 1)
    #     data['data3d'] = self.img
    #     # data['crinfo'] = self.crinfo
    #     data['segmentation'] = self.segmentation
    #     data['slab'] = slab
    #     # data['voxelsize_mm'] = self.voxelsize_mm
    #     # data['orig_shape'] = self.orig_shape
    #     # data['processing_time'] = self.processing_time
    #     return data

    def updateVolume(self):
        text = 'Volume [mm3]:\n  unknown'
        if self.voxel_volume is not None:
            if self.mode == 'draw':
                vd = self.seeds

            else:
                vd = self.contours

            if vd is not None:
                nzs = vd.nonzero()
                nn = nzs[0].shape[0]
                text = 'Volume [mm3]:\n  %.2e' % (nn * self.voxel_volume)

        self.volume_label.setText(text)

    def getROI(self):
        crp = self.getCropBounds()
        if crp is not None:
            _, cri = crp

        else:
            cri = []
            for jj, ii in enumerate(self.img.shape):
                off = self.offset[jj]
                cri.append(slice(off, off + ii))

        return cri
Example #19
0
class VocDialog(QDialog) :

    """This is the dialog which presents the interface and organise everything."""

    MAGICWORD = 'CHANGEME'
    findDight = reCompile(r'\d+')
    baseURL = 'http://www.gstatic.com/dictionary/static/sounds/de/0/CHANGEME.mp3'

    def __init__(self, autoProxy=False, parent=None) :
        super(VocDialog, self).__init__(parent)
        self.logger = getLogger('VocVoc.VocDialog')
        self.info = self.logger.info
        self.warn = self.logger.warn
        self.debug = self.logger.debug
        if autoProxy :
            self.info('Starting VocDialog with autoProxy.')
        else :
            self.info('Starting VocDialog without autoProxy.')
        self.mediaObeject = Phonon.createPlayer(Phonon.MusicCategory, Phonon.MediaSource(''))
        self.setupUi()
        self.connect()
        self.initCountWord()
        self.candidates = None
        self.autoProxy = autoProxy
        self.spellChecker = SpellChecker()
        self.correct = self.spellChecker.correct
        self.corpusDir = self.spellChecker.corpusDir
        self.info('VocDialog started.')

    def keyPressEvent(self, event) :
        self.debug('Key is {}.'.format(event.key()))
        super(VocDialog, self).keyPressEvent(event)

    def resizeEvent(self, event) :
        self.debug("Resized to {}.".format(self.size()))
        super(VocDialog, self).resizeEvent(event)

    def initCountWord(self) :
        """
        The first one is a count about how many time the input is wrong.
          WRONG : Not collected in or can be corrected by the wordModel.
        The second one is the last time's wrong input.
        """
        self.countWord = [0, '']

    def setupUi(self) :
        "Setup the UI."
        self.info('Seting up the UI.')
        self.fileDialog = QFileDialog()
        self.fileDialog.setFileMode(QFileDialog.AnyFile)
        self.fileDialog.setViewMode(QFileDialog.Detail)

        self.loadButton = QPushButton( r'Open/New :', self)
        self.loadButton.setAutoDefault(False)

        self.textList = QListWidget(self)

        self.inputLine = tabEnabledLineEdit(self)

        self.toggleButton = QPushButton(r'Show/Hide', self)
        self.toggleButton.setAutoDefault(False)
        self.toggleButton.setCheckable(True)

        self.textLabel = QLabel()

        self.hBox = QHBoxLayout()
        self.hBox.addWidget(self.inputLine)
        self.hBox.addWidget(self.toggleButton)

        self.statusBar = QStatusBar(self)
        msg = 'Hello World! I love YOU!!!'
        self.statusBar.showMessage(msg, 5000)

        vBox = QVBoxLayout()
        items = [self.loadButton, self.textList, self.hBox, self.statusBar]
        for item in items :
            try :
                vBox.addWidget(item)
            except :
                vBox.addLayout(item)

        self.textViewer = QTextEdit()
        self.textViewer.setHidden(True)
        self.textViewer.setReadOnly(True)

        HBox = QHBoxLayout()

        items = [vBox, self.textViewer]
        for item in items :
            try :
                HBox.addWidget(item)
            except :
                HBox.addLayout(item)
                
        self.setLayout(HBox)
        self.resize(350, 500)
        self.setWindowTitle("VocVoc -- Your Vocabulary Helper")
        self.info('UI is set up now.')

    def connect(self) :
        "Connect signals and slots in the UI."
        self.info('Connecting signals and slots.')
        self.loadButton.clicked.connect(self.loadFile)
        self.inputLine.returnPressed.connect(self.enteredText)
        self.inputLine.ctrlN.connect(self.completeHandler)
        self.inputLine.ctrlP.connect(lambda : self.completeHandler(False))
        self.textList.itemActivated.connect(self.itemActivated)
        self.toggleButton.clicked.connect(self.toggleViewer)
        if self.logger.isEnabledFor(DEBUG) :
            self.mediaObeject.stateChanged.connect( self.errorState )
        self.info('Signals and slots connected.')

    def errorState(self, state) :
        errorStates = {
                        0: 'Loading',
                        1: 'Stopped',
                        2: 'Playing',
                        3: 'Buffering',
                        4: 'Paused',
                        5: 'Error'
                        }
        msg ='{} state in Phonon!'.format( errorStates[state]) 
        self.info(self.mediaObeject.errorType())
        if state == 5 :
            self.warn(msg)
        else :
            self.info(msg)

    def itemActivated(self, item) :
        row = self.textList.row(item)
        text = item.text()
        if not text.startswith('#') :
            self.pronounce(item.text())
            self.findWord(text)
        if row+1 != self.textList.count() :
            self.debug('NOT last row!')
            self.textList.setCurrentRow(row+1)
        else :
            self.debug('Last row!')

    def toggleViewer(self) :
        if self.textViewer.isHidden() :
            self.resize(700, 500)
            self.textViewer.show()
        else :
            self.textViewer.hide()
            self.resize(350, 500)

    def backAndForward(self, forward=True) :
        inputLine = self.inputLine
        word = inputLine.text()
        setText = inputLine.setText
        candidates = self.candidates
        count = len(candidates)
        try :
            position = candidates.index(word)
            self.debug('Position found.')
        except :
            position = None
            self.debug('Position not found.')
        if forward :
            if position is None or position == count - 1 : # At end
                position = -1
            setText( candidates[position+1] )
        else :
            if position is None or position == 0 :
                position = count
            setText( candidates[position-1] )

    def completeHandler(self, goNext=True) :
        inputLine = self.inputLine
        candidates = self.candidates
        word = inputLine.text()
        if candidates :
            self.backAndForward(goNext)

    def play(self, path) :
        self.mediaObeject.setCurrentSource(Phonon.MediaSource(path))
        self.mediaObeject.play()
        
    def pronounce(self, word) :
        self.info('Preparing the url to pronounce.')
        url = self.baseURL.replace(self.MAGICWORD, word)
        if not self.autoProxy :
            self.debug('Without the autoProxy, play it using the url as the source.')
            self.play(url)
        else :
            self.info('With the autoProxy, play it after downloading the file.')
            try : # May happen HTTPError.
                resource = urlopen(url).read()
                tempFile = NamedTemporaryFile()
                tempFile.write(resource)
                self.play(tempFile.name)
            except HTTPError as error :
                self.warn(repr(error))
                self.warn('Pronounciation FAILED.')
        self.info('Pronounciation ended.')

    def findWord(self, word) :
        self.info('Finding word in the text file.')
        textViewer = self.textViewer
        if textViewer.isHidden() :
            return
        else :
            pass
        limit = 5
        contexts = list()
        textLines = list()
        corpuses = glob(''.join([self.corpusDir, '/*']))
        self.debug('Found corpuses : {}.'.format(corpuses))
        textViewer.clear()
        for corpus in corpuses :
            textLines.append(locateWord(corpus, word))
        for textLine in textLines :
            text, lines = textLine[0], textLine[1]
            title = ''.join( ['Title : ', basename(text[-1])] )
            if lines :
                for line in lines :
                    wantedLines = text[line-limit: line+limit]
                    #cleanLines = map(self.replace, wantedLines)
                    context = ''.join(wantedLines)
                    context = context.replace(word, ' '.join(['*', word, '*']))
                    context = context.replace('\n\n', self.MAGICWORD)
                    context = context.replace('\n', ' ')
                    context = context.replace(self.MAGICWORD, '\n\n')
                    contexts.append(''.join([title, '\n', context, '\n\n']))
        if contexts :
            for context in contexts :
                textViewer.append(context)
        else :
            textViewer.append('Sorry, {} not found.'.format(word))
        self.info('Word found and showed in the textViewer.')

    def wordCount(self, word=None) :
        """
        This function uses self.countWord to decide whether record and pronounce the input or not.
        RECORD : Add the input into the textList and write it into the file.
        If the word itself is correct, return True.
        Or if a wrong input were entered twice, return True.
        Otherwise with a one-time-entered wrong input, return False.
        """
        if self.countWord[0] == 0 : # The word is correct.
            self.countWord[1] = ''
            return True
        elif self.countWord[0] == 1 :
            msg = 'Maybe the word is WRONG? Playing beep and saving the word.'
            self.debug(msg)
            self.countWord[1] = word
            self.play('beep.mp3')
            return False
        elif self.countWord[0] == 2 :
            if word != self.countWord[1] : # Different word.
                self.debug('DIFEFRENT WORD.')
                self.countWord[0] = 1 # Check again.
                self.countWord[1] = word # Update it.
                self.play('beep.mp3')
                return False
            else :
                self.countWord[0] = 0
                self.countWord[1] = ''
            return True
        else :
            self.countWord[0] = 0

    def checkWord(self, word) :
        statusBar = self.statusBar
        showMessage = statusBar.showMessage

        candidates = self.correct(word)
        if candidates is None : # Not collected.
            self.countWord[0] += 1
            showMessage('Are you sure?', 3000)
        elif candidates[0] != word : # Can be corrected.
            self.countWord[0] += 1
            self.candidates = candidates
            msg = 'Do you mean {} ?'.format(' ,'.join(candidates))
            showMessage(msg, 5000)
        else : # Collected in the wordModel.
            self.findWord(word)
            self.countWord[0] = 0
            self.debug('Word collected in the wordModel.')
            return True

        msg = 'wrongTime = {} with the word {}.'.format(self.countWord[0], word)
        self.logger.debug(msg)

        return self.wordCount(word)

    def addText(self, text) :
        self.info('Starting to add text.')
        textList = self.textList

        if text.startswith('#') : # It is a comment.
            pass
        else : # It is a word.
            if self.checkWord(text) :
                self.pronounce(text)
            else : # self.checkWord(text) return False
                return

        self.inputLine.clear()
        textList.addItem(text)
        self.statusBar.clearMessage()
        textList.setCurrentRow( textList.count() - 1 )

        try : # With the try statement, it can be used as a pronunciation helper.
            flush(self.filePath, text)
        except Exception :
            self.debug('Using this freely without writing to a file as a pronunciation helper.')
        self.info('Text added.')

    def enteredText(self) :
        "Get the text from the input line and add it to the file and the list."
        self.info('Adding text to textList and the file.')
        textList = self.textList
        text = self.inputLine.text().strip().lower()
        self.debug( 'Input is {}.'.format(text) )

        self.addText(text)

        self.info('Text added.')

    def loadFile(self) :
        "Open the file dialog to select the file and try to start."
        # Open the file dialog.
        logger = getLogger('VocVoc.VocDialog.loadFile')
        info = logger.info
        debug = logger.debug
        debug('Preparing to load file.')
        textList = self.textList
        if ( self.fileDialog.exec() ) :
            debug('Dialog executed sucessfully.')
            filePath = self.fileDialog.selectedFiles()[0]
            fileName = basename(filePath)
            # Create or read file.
            try :
                with open(filePath, 'r+') as textFile :
                    debug('File exists, openning up.')
                    writenText = textFile.read()
                writenText = writenText.splitlines()
                textList.clear()
                textList.addItems( writenText )
                if not 'end' in writenText[-1].strip().lower() :
                    textList.setCurrentRow( len(writenText)-1 )
                else :
                    textList.setCurrentRow( 0 )
                debug('Added items to list and set current row to the last row.')
            except IOError as error : # File does not exist. We create one.
                debug('File does not exist. Trying to find the dight in the name.')
                listNumber = self.findDight.search(fileName)
                if listNumber is None : # No number found in the text.
                    logger.warn('Dight not found in the filename. Try again.')
                    msg = 'No number found in the file name.\nPlease try again.'
                    QMessageBox.warning(self, 'List number NOT found.',
                            msg,
                            QMessageBox.Ok)
                    return msg
                else : # No existing file but found the number in the file name.
                    debug('Dight Found. Creating file and adding first line.')
                    with open(filePath, 'x') as textFile :
                        firstLine = ''.join( ['# list ' ,str( listNumber.group() )] ) # Cannot put '\n' here.
                        textFile.write( ''.join([firstLine ,'\n']) )
                    textList.clear()
                    textList.addItem(firstLine) # Otherwise there would be a new line in the list.

            debug('Set inputLine to write-enabled.')
            self.inputLine.setReadOnly(False)
            debug('Pass textFile to the dialog')
            self.filePath = filePath
            info('File loaded.')
Example #20
0
class Visor(QtGui.QDockWidget, FORM_CLASS):
    """
    UI manager for the visor.

    This is the View component of the MVC pattern. Will manage
    inputs from the user using a combobox to select which
    dataset to use, provide a tree view of the elements
    included in those datasets, and provide the basic configuration
    options to download maps through WMS and WCS services using a
    controller and show them in QGIS.
    """
    groupAssignmentLock = RLock()

    def __init__(self, showEmptyDatasetNodes = False, parent=None):
        super(Visor, self).__init__(parent)
        self.setupUi(self)

        self.controller = VisorController.VisorController()
        self.controller.threddsServerMapObjectRetrieved.connect(self.onNewDatasetsAvailable)
        self.controller.threddsDataSetUpdated.connect(self.onDataSetUpdated)
        self.controller.mapImageRetrieved.connect(self.showNewImage)
        self.controller.standardMessage.connect(self.postInformationMessageToUser)
        self.controller.errorMessage.connect(self.postCriticalErrorToUser)
        self.controller.mapInfoRetrieved.connect(self._onMapInfoReceivedFromController)
        self.controller.batchDownloadFinished.connect(self.createLayerGroup)

        self.showEmptyDatasetNodes = showEmptyDatasetNodes #TODO: Self-explanatory...
        self.combo_dataset_list.currentIndexChanged.connect(self._onDataSetItemChanged)
        self.tree_widget.itemClicked.connect(self._onMapTreeWidgetItemClicked)
        self.tree_widget.itemExpanded.connect(self._onMapTreeWidgetItemExpanded)
        self.connect(self.combo_wcs_coverage, SIGNAL("currentIndexChanged(const QString&)"), self._onCoverageSelectorItemChanged)
        self.connect(self.combo_wms_layer, SIGNAL("currentIndexChanged(const QString&)"), self._onWMSLayerSelectorItemChanged)
        self.connect(self.combo_wms_style_type, SIGNAL("currentIndexChanged(const QString&)"), self._onWMSStyleTypeSelectorItemChanged)
        self.connect(self.combo_wms_time, SIGNAL("currentIndexChanged(int)"), self._onWMSFirstTimeChanged)
        self.connect(self.combo_wcs_time, SIGNAL("currentIndexChanged(int)"), self._onWCSFirstTimeChanged)

        self.button_req_map.clicked.connect(self._onbuttonReqMapClicked)
        #self.actionToggleAlwaysOnTop.toggled.connect(self._onAlwaysOnTopPrefsChanged)
        self.buttonManageServers.clicked.connect(self._onManageServersRequested)
        self.button_req_animation.clicked.connect(self.toggleAnimationMenu)

        #We add a status bar to this QDockWidget
        self.statusbar = QStatusBar()
        self.gridLayout.addWidget(self.statusbar)

        self.datasetInUse = None
        self.uiAnimation = None
        self.currentMap = None
        self.wcsAvailableTimes = []
        self.wmsAvailableTimes = []

        self.firstRunThisSession = True

    def show(self):
        if iface and not iface.mainWindow().restoreDockWidget(self):
            iface.mainWindow().addDockWidget(Qt.LeftDockWidgetArea, self)
        super(Visor, self).show()

        if self.firstRunThisSession is True:
            self.firstRunChecks()
            self.firstRunThisSession = False

        #If no dataset is selected yet, we will assume the first
        #thing the user will want to do is actually doing something
        #related to the servers (pick one from a list, or add a new
        #one) as there is little else that can be done at this point.
        #So we present them the screen to do so..
        if self.datasetInUse is None:
            self._onManageServersRequested()

    def firstRunChecks(self):
        """
        Convenience method to add any checks which should be performed
        when the user opens the plug-in for first time (be advised this
        is not the same as the first time the plug-in object is created,
        which is on QGIS load).
        """
        #Check GDAL version. Versions < 2.0 had a bug regarding
        #driver selection for network resource retrieval.
        #https://trac.osgeo.org/gdal/ticket/2696
        persistenceManager = ServerDataPersistenceManager.ServerStorageManager()
        if persistenceManager.getDontShowGDALErrorAnymore() is False:
            try:

                from osgeo import gdal
                if int(gdal.VersionInfo()) < 5000000:

                    message = ("Your GDAL libraries version is outdated. Versions\n"
                                                  +"under 2.0 are not guaranteed to work when\n"
                                                  +"attempting to load WCS Layers.\n"
                                                  +"Please update GDAL.")
            except ImportError:
                message = ("Your GDAL libraries version could not be read"
                                                  +"Versions under 2.0 are not guaranteed to work when\n"
                                                  +"attempting to load WCS Layers. If you have any issues,\n"
                                                  +"please update GDAL.")

            reply = QtGui.QMessageBox.question(self, 'GDAL: Unsupported version found',
                     message, "Close", "Don't show again")

            if reply == 1:
                persistenceManager.setDontShowGDALErrorAnymore()


    def toggleAnimationMenu(self):
        """
        Shows (or hides) the animation menu elements,
        and instantiate a controller.

        It seems I can not directly hide elements,
        but I can make another Widget in QDesigner and
        create/add it to a layout here so... oh well..
        """
        if self.uiAnimation is None:
            self.uiAnimation = AnimationFrame(parent = self)
            self.uiAnimation.errorSignal.connect(self.postCriticalErrorToUser)

            self.controller.mapInfoRetrieved.connect(self.uiAnimation.setAnimationInformation)
            if None is not self.currentMap :
                self.uiAnimation.setAnimationInformation(self.currentMap)

            self.uiAnimation.show()
            self.button_req_animation.setText("Hide animation menu <<")
        else:
            self.uiAnimation.hide()
            self.uiAnimation = None
            self.button_req_animation.setText("Show animation menu >>")


    def clearData(self):
        self.WMSBoundingBoxInfo.setText("No Bounding Box or CRS information available.")
        self.WMS_eastLabel.setText("East: No information.\n")
        self.WMS_westLabel.setText("West: No information.")
        self.WMS_northLabel.setText("North: No information.")
        self.WMS_southLabel.setText("South: No information.")
        self.combo_wms_layer.clear()
        self.combo_wms_style_type.clear()
        self.combo_wms_style_palette.clear()
        self.combo_wms_time.clear()
        self.combo_wms_time_last.clear()
        self.combo_wcs_coverage.clear()
        self.combo_wcs_time.clear()
        self.combo_wcs_time_last.clear()
        self.WCSBoundingBoxInfo.setText("No Bounding Box or CRS information available." )
        self.WCS_eastLabel.setText("East: No information.\n")
        self.WCS_westLabel.setText("West: No information.\n")
        self.WCS_northLabel.setText("North: No information.\n")
        self.WCS_southLabel.setText("South: No information.\n")


    #TODO: Unused (for now)
    @pyqtSlot(bool)
    def _onAlwaysOnTopPrefsChanged(self, newSettingBool):
        """
        Will change the alwaysontop window modifier to suit
        the user selection.
        """
        self.setWindowFlags(self.windowFlags() ^ Qt.WindowStaysOnTopHint)
        QtGui.QMainWindow.show(self)


    @pyqtSlot(list, str)
    def onNewDatasetsAvailable(self, inDataSets, serverName):
        """
        A callback for when the dataSet displayed
        needs to be updated.

        :param inDataSets:  list of DataSet objects which will be
                            available to the user.
        :type inDataSets: list of threddsFetcherRecursos.DataSet objects.

        :param serverName:  An user-friendly representation of this server name.
        :type serverName: str
        """
        StringList = []
        for dataSet in inDataSets:
            StringList.append(dataSet.getName())

        self.setWindowTitle("THREDDS Explorer - Connected: "+serverName)
        self.combo_dataset_list.clear()
        self.combo_dataset_list.addItems(StringList)
        self.combo_dataset_list.setCurrentIndex(0)
        self.postInformationMessageToUser("Dataset list updated: "+str(len(StringList))+ " elements.")
        self.clearData()


    @pyqtSlot(str)
    def postInformationMessageToUser(self, message):
        """
        Will post information messages to the user through
        the status bar.
        :param message: String to use as message to
                            the user.
        :type message: str
        """
        self.statusbar.showMessage(message)
        pass

    @pyqtSlot(str)
    def postCriticalErrorToUser(self, errorString):
        """
        To be used with non-recoverable error situations. Shows
        a message box with the error message.

        :param errorString: String to use as message to
                            the user.
        :type  errorString: str
        """
        box = QMessageBox()
        box.setText(errorString)
        box.setIcon(QMessageBox.Critical)
        box.exec_()


    @pyqtSlot(str)
    def _onDataSetItemChanged(self, stringItem):
        """
        Will receive notifications about this window dataSet
        chosen combobox when the item selected changes.
        """
        self.tree_widget.clear()
        self.datasetInUse = self.controller.getSingleDataset(self.combo_dataset_list.currentText())
        if self.datasetInUse is None:
            return #If no dataset is available to be shown, we will create no tree.

        rootItem = self.tree_widget.invisibleRootItem();
        newItem = QtGui.QTreeWidgetItem(rootItem, [self.datasetInUse.getName()])
        rootItem.addChild(self._createHierarchy(self.datasetInUse, newItem))


    def _createHierarchy(self, dataSet, treeItemParent):
        """
        Recursively creates a hierarchy of elements to populate
        a treeWidgetItem from a given dataSet.

        :param dataSet: DataSet object to create an hierarchy from.
        :type dataset: threddsFetcherRecursos.DataSet

        :param treeItemParent: Item which will be this
                                branch parent.
        :type treeItemParent: QTreeWidgetItem
        """
        i = 0
        itemsAlreadyAddedToElement = []
        while i < treeItemParent.childCount():
            child = treeItemParent.child(i)
            if child.text(0) == "Loading..." or child.text(0) == "No subsets found":
                treeItemParent.removeChild(child)
            else:
                itemsAlreadyAddedToElement.append(child)
            i = i+1
        elementsAlreadyInTreeItemParent = [x.text(0) for x in itemsAlreadyAddedToElement]
        if dataSet != None:
            for mapElement in dataSet.getAvailableMapList():
                if mapElement.getName() in elementsAlreadyInTreeItemParent:
                    continue
                else:
                    newItem = QtGui.QTreeWidgetItem(treeItemParent, [mapElement.getName()])
                    treeItemParent.addChild(newItem)

            subSets = dataSet.getSubSets()
            if len(subSets) == 0:
                #We add a dummy element so the element open icon is created..
                newItem = QtGui.QTreeWidgetItem(treeItemParent)
                newItem.setText(0,"No subsets found")
                treeItemParent.addChild(newItem)
            else:
                for dataset in subSets:
                    #If an item with the same name as this dataset is found as a subchild
                    #of the parent item, we will use it to build our tree. Otherwise, we
                    #create a new one and append it.
                    itemList = ([x for x in itemsAlreadyAddedToElement if x.text(0) == dataset.getName()])
                    if itemList is None or len(itemList) == 0:
                        item = QtGui.QTreeWidgetItem(treeItemParent, [dataset.getName()])
                        treeItemParent.addChild(self._createHierarchy(dataset, item))
                    else:
                        item = itemList[0]
                        self._createHierarchy(dataset, item)
        else:
            self.postCriticalErrorToUser("WARNING: Attempted to add a null dataset to view.")






    def _onMapTreeWidgetItemClicked(self, mQTreeWidgetItem, column):
        """
        Will receive notifications about the MapTreeWidget
        elements being clicked, so we can update the first
        combobox of WMS/WCS tabs with the layer list.
        """
        self.clearData()
        self.postInformationMessageToUser("")
        if None is mQTreeWidgetItem or None is mQTreeWidgetItem.parent():
            return

        self.controller.getMapObject(str(mQTreeWidgetItem.text(0)), str(mQTreeWidgetItem.parent().text(0)), self.datasetInUse)

    @pyqtSlot(object)
    def _onMapInfoReceivedFromController(self, mapInfoObject):
        #print("_onMapInfoReceivedFromController 1"+str(mapInfoObject))
        self.currentMap = mapInfoObject
        #print("_onMapInfoReceivedFromController 2"+str(self.currentMap))
        if self.currentMap is not None:
            #WCS Data update
            self.currentCoverages = self.controller.getWCSCoverages(self.currentMap)
            if self.currentCoverages is not None:
                for c in self.currentCoverages:
                    self.combo_wcs_coverage.addItem(c.getName())
            else:
                self.combo_wcs_coverage.addItem("No data available.")
            #WMS Data update
            self.currentWMSMapInfo = self.controller.getWMSMapInfo(self.currentMap)
            if self.currentWMSMapInfo is not None:
                for l in self.currentWMSMapInfo.getLayers():
                    self.combo_wms_layer.addItem(l.getName())
            else:
                self.combo_wms_layer.addItem("No data available.")



    def _onMapTreeWidgetItemExpanded(self, mQTreeWidgetItem):
        """
        Once a set is expanded in the tree view we will attempt to
        recover it's data and present it to the user.
        """
        setToUpdate = self.datasetInUse.searchSubsetsByName(
                          str(mQTreeWidgetItem.text(0)), exactMatch=True)
        if setToUpdate is not None and len(setToUpdate) > 0:
            self.controller.mapDataSet(setToUpdate[0], depth=1)



    def onDataSetUpdated(self, dataSetObject):
        """
        Will update the QTreeWidget to include the updated
        dataset object and it's new data.
        """
        if dataSetObject.getParent() is not None:
            parent = self.tree_widget.findItems(dataSetObject.getName(), Qt.MatchRecursive)
        self._createHierarchy(dataSetObject, parent[0])




    @pyqtSlot(str)
    def _onCoverageSelectorItemChanged(self, QStringItem):
        """
        Will triger when the user selects a coverage name in
        the combobox (or that list is updated) so the available
        times to request to server are updated in the other
        combobox for the WCS service.
        """
        self.combo_wcs_time.clear()
        if self.currentCoverages is not None:
            coverageElement = [ x for x in self.currentCoverages if x.getName() == str(QStringItem) ]
            if None is not coverageElement or len(coverageElement) > 0:
                try:
                    self.wcsAvailableTimes = coverageElement[0].getTiempos()
                    self.combo_wcs_time.addItems(self.wcsAvailableTimes)
                    BBinfo = coverageElement[0].getBoundingBoxInfo()
                    self.WCSBoundingBoxInfo.setText("CRS = "+BBinfo.getCRS()
                                                +"\n\n Bounding Box information (decimal degrees):" )
                    self.WCS_eastLabel.setText("East: \n"+BBinfo.getEast())
                    self.WCS_westLabel.setText("West: \n"+BBinfo.getWest())
                    self.WCS_northLabel.setText("North: \n"+BBinfo.getNorth())
                    self.WCS_southLabel.setText("South: \n"+BBinfo.getSouth())
                except IndexError:
                    pass

    @pyqtSlot(str)
    def _onWMSLayerSelectorItemChanged(self, QStringItem):
        self.combo_wms_style_type.clear()
        self.combo_wms_style_palette.clear()
        self.combo_wms_time.clear()

        #Only one should be returned here.
        if self.currentWMSMapInfo is not None:
            layerSelectedObject =  [ x for x in self.currentWMSMapInfo.getLayers()
                                    if x.getName() == str(QStringItem) ]

            if layerSelectedObject is not None and len(layerSelectedObject) == 1:
                self.wmsAvailableTimes = layerSelectedObject[0].getTimes()
                self.combo_wms_time.addItems(self.wmsAvailableTimes)
                self.wmsAvailableStyles = layerSelectedObject[0].getStyles()
                self.combo_wms_style_type.addItems(list({(x.getName().split(r"/"))[0]
                                                    for x in self.wmsAvailableStyles}))

                BBinfo = layerSelectedObject[0].getBoundingBoxInfo()
                self.WMSBoundingBoxInfo.setText("CRS = "+BBinfo.getCRS()
                                                +"\n\n Bounding Box information (decimal degrees):" )
                self.WMS_eastLabel.setText("East: \n"+BBinfo.getEast())
                self.WMS_westLabel.setText("West: \n"+BBinfo.getWest())
                self.WMS_northLabel.setText("North: \n"+BBinfo.getNorth())
                self.WMS_southLabel.setText("South: \n"+BBinfo.getSouth())

    @pyqtSlot(str)
    def _onWMSStyleTypeSelectorItemChanged(self, qstringitem):
        self.combo_wms_style_palette.clear()
        self.combo_wms_style_palette.addItems(list({(x.getName().split(r"/"))[1]
                                                    for x in self.wmsAvailableStyles
                                                    if str(qstringitem) in x.getName()}))

    @pyqtSlot(int)
    def _onWCSFirstTimeChanged(self, position):
        #print("self.wcsAvailableTimes"+str((sorted(self.wcsAvailableTimes))))
        #print("WCS INDEX: "+str(position))
        self.combo_wcs_time_last.clear()
        #print self.wcsAvailableTimes[position:]
        self.combo_wcs_time_last.addItems(
          (sorted(self.wcsAvailableTimes))[position:])

    @pyqtSlot(int)
    def _onWMSFirstTimeChanged(self, position):
        #print("self.wmsAvailableTimes"+str((sorted(self.wmsAvailableTimes))))
        #print("WMS INDEX: "+str(position))
        self.combo_wms_time_last.clear()
        #print self.wmsAvailableTimes[position:]
        self.combo_wms_time_last.addItems(
          self.wmsAvailableTimes[position:])


    def _onbuttonReqMapClicked(self):
        """
        Action to be performed when the user clicks the
        button to request a new map to be displayed,
        after selecting proper values in the rest of fields.

        This will also begin a qTimer which will check for
        async messages which would report to us the availability
        of a new image to be displayed.
        """
        #print(self.currentMap)
        self.postInformationMessageToUser("") #Reset error display.
        if self.tabWidget.currentIndex() == self.tabWidget.indexOf(self.tab_WCS):
            try:
                selectedBeginTimeIndex = self.wcsAvailableTimes.index(self.combo_wcs_time.currentText())
                selectedFinishTimeIndex = self.wcsAvailableTimes.index(self.combo_wcs_time_last.currentText())+1
                #print(str(self.currentMap))
                self.controller.asyncFetchWCSImageFile(self.currentMap,
                                                        self.combo_wcs_coverage.currentText(),
                                                        self.wcsAvailableTimes[selectedBeginTimeIndex
                                                                               :selectedFinishTimeIndex])
            except Exception:
                self.postInformationMessageToUser("There was an error retrieving the WCS data.")
        elif self.tabWidget.currentIndex() == self.tabWidget.indexOf(self.tab_WMS):
            try:
                selectedBeginTimeIndex = self.wmsAvailableTimes.index(self.combo_wms_time.currentText())
                selectedFinishTimeIndex = self.wmsAvailableTimes.index(self.combo_wms_time_last.currentText())+1
                style = self.combo_wms_style_type.currentText()+r"/"+self.combo_wms_style_palette.currentText()
                self.controller.asyncFetchWMSImageFile(self.currentMap,
                                                        self.combo_wms_layer.currentText(),
                                                        style,
                                                        self.wmsAvailableTimes[selectedBeginTimeIndex
                                                                               :selectedFinishTimeIndex])
            except Exception:
                self.postInformationMessageToUser("There was an error retrieving the WMS data.")



    @pyqtSlot(list, str)
    def createLayerGroup(self, layerList, groupName):
        groupifier = LayerGroupifier(layerList, groupName)
        groupifier.setSingleLayerSelectionModeInGroup(False)
        groupifier.statusSignal.connect(self.postInformationMessageToUser, Qt.DirectConnection)
        groupifier.groupifyComplete.connect(self._onNewLayerGroupGenerated)
        groupifier.groupify()

    @pyqtSlot(QgsLayerTreeGroup, list)
    def _onNewLayerGroupGenerated(self, groupObject, layerList):
        """
        Currently only used to show the first image of a newly created group
        so the user knows when the operation finishes.

        :param groupObject: The legend group object which was created.
        :type  groupObject: QgsLayerTreeGrupo

        :param layerList: The layers which are held in the group object.
        :type  layerList: [QgsLayer]
        """
        if (layerList[0]).isValid() is True:
            iface.legendInterface().setLayerVisible(layerList[0], True)
        else:
            self.postInformationMessageToUser("There was a problem showing a layer.")

    @pyqtSlot(tuple)
    def showNewImage(self, image):
        """
        Will order this UI to post a new image to the user
        through the qgis window.

        :param image: a tuple consisting of (imageOrLayerObject, Name, Service)
        :type image: (QgsRasterLayer, String, String)

        """
        self.postInformationMessageToUser("Layer '"+image[1]+"' ["+image[2]+"]retrieved")
        layer = image[0]

        if layer.isValid() is True:
            QgsMapLayerRegistry.instance().addMapLayer(layer)
            iface.zoomToActiveLayer()
            iface.legendInterface().refreshLayerSymbology(layer)
        else:
            self.postInformationMessageToUser("There was a problem loading the layer.")

    @pyqtSlot()
    def _onManageServersRequested(self):
        """
        Delegates the action of showing the server manager
        window to the controller.
        """
        self.controller.showServerManager()
class MainWindow(QMainWindow, Ui_MainWindow):

    '''
    Main app window
    '''

    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.setupUi(self)

        # promenne
        self.work_dir = ''
        self.master_work_dir = ''
        self.logovat = False
        self.flowini_file_master = ''
        self.flowini_data = ''
        self.file_dict = None
        self.displayed_mesh_list = {}
        self.displayed_mtr_list = {}
        self.result_elements = None
        self.result_times = None
        self.surface_elements = None
        self.comparation_table = None
        self.material = None
        self.mtr_index_disp_lst = {}
        self.logger = None
        self.msh = None
        self.tasks = None
        self.solutions = None
        self.problem_type = None
        self.canvas = None
        self.bcd_file = ''
        self.substances = False
        # setup app
        self.setup = None
        self._load_setup()

        # toolbar
        self.__setup_toolbar()
        # status bar
        self.status_bar = None
        self.__setup_statusbar()
        # mesh control
        self.__setup_mesh_control()
        # misc
        self.button_compare.clicked.connect(self.compare_concentrations)
        self.check_compare_sum.setChecked(True)
        self.button_export_csv.clicked.connect(self.export_compare_conc)
        self.button_export_csv.setDisabled(True)
        self.spin_grade_filter.setRange(0, 5)
        self.spin_grade_filter.setDisabled(True)
        self.button_grade_filter.setDisabled(True)
        self.button_grade_filter.clicked.connect(self.__filter_table_rslt)
        self.maps_section_height.textChanged.connect(self.__focus_on_cut)

        # progress bar
        self.tabWidget.currentChanged.connect(self._tab_controller)
        self.button_process_all.clicked.connect(self._analyze_data)
        self.button_process_newonly.clicked.connect(
            self._analyze_data_selected)

        # na zacatku neni nic
        self.tabWidget.hide()
        self.__remove_all_tabs()
        self.button_draw_maps.clicked.connect(self.map_concentrations)

    def map_concentrations(self):
        '''
        pracovni metoda pro obsluhu mapy koncentraci
        '''
        if not self.result_elements:
            self.read_concentrations()

        map_options = {
            "map_format": "png",
            "map_file": "{}{}mapa.png".format(self.work_dir, SEPAR),
            'xlabel': u'{}'.format(self.edit_chart_x_text.text()),
            'ylabel': u'{}'.format(self.edit_chart_y_text.text()),
            'title': u'{}'.format(self.edit_chart_title_text.text())
        }

        sim_time = str(self.maps_sim_time_select.currentText())

        if self.maps_radio_surface.isChecked():
            vals = self._read_surface_elements()
            if self.maps_check_nonzero.isChecked():
                vals = self.__remove_zeros_from_mesh_list(vals)
            self.messenger("Drawing map of concetration to file...")
            triangles = mapcon.get_triangles_surface(
                vals, self.msh.nodes, self.result_elements, sim_time, self.bcd_file)
        elif self.maps_radio_section.isChecked():
            try:
                height = float(self.maps_section_height.text())
            except ValueError:
                self.messenger("ERROR: Need altitude for the plane to cut")
                return False
            else:
                vals = self._mesh_find_through('z', height)
                if self.maps_check_nonzero.isChecked():
                    vals = self.__remove_zeros_from_mesh_list(vals)
                self.messenger("Drawing map of concetration to file...")
                triangles = mapcon.get_triangles_section(
                    vals, self.msh.nodes, self.result_elements, height, sim_time)

        else:
            self.messenger("NEXT TIME")
            return False

        triangulation = mapcon.prepare_triangulation(triangles)
        mapcon.draw_map(triangulation, map_options)
        self.map_conc_poup(triangulation)

        self.messenger("OK - map of concentrations is ready in the file")

    def map_conc_poup(self, triangulation):
        '''
        popup window for map concentration
        '''
        map_options = {
            'xlabel': u'{}'.format(self.edit_chart_x_text.text()),
            'ylabel': u'{}'.format(self.edit_chart_y_text.text()),
            'title': u'{}'.format(self.edit_chart_title_text.text())
        }

        self.canvas = mapcanvas.MapCanvas(triangulation, map_options)
        self.canvas.setGeometry(QRect(100, 100, 700, 700))
        self.canvas.show()

    def __focus_on_cut(self):
        '''
        if user adds height, switch the radio maps to section cut
        '''
        self.maps_radio_section.setChecked(True)

    def __remove_all_tabs(self):
        '''
        removes all tabs from the tab widget
        '''
        count = self.tabWidget.count() - 1
        while count >= 1:
            self.tabWidget.removeTab(count)
            count -= 1

    def __setup_statusbar(self):
        '''
        status bar setup - private method
        '''
        # status bar
        status_font = QFont("Helvetica [Cronyx]", 12)
        self.status_bar = QStatusBar(self)
        self.setStatusBar(self.status_bar)
        self.status_bar.setFont(status_font)

    def __setup_toolbar(self):
        '''
        toolbar setup
        '''
        # toolbar
        self.button_exit.clicked.connect(self.close)
        self.button_draw_charts.clicked.connect(self.draw_charts)
        self.button_save_tables.clicked.connect(self.create_tables)

        self.button_merge.clicked.connect(self.merge_result_files)
        self.box_merger.setHidden(True)

        float_validator = QDoubleValidator()
        self.edit_merge_minval.setValidator(float_validator)
        self.maps_section_height.setValidator(float_validator)
        self.edit_chart_min_conc.setValidator(float_validator)

        self.action_Open.triggered.connect(self.open_task_dir)
        self.action_Basic_problem.triggered.connect(self.analyse_basic_problem)
        self.actionSensitivity_task.triggered.connect(
            self.analyse_sensitivity_task)
        self.actionMonte_Carlo.triggered.connect(self.analyse_monte_carlo)

    def __setup_mesh_control(self):
        '''
        setup for mesh control block
        '''
        # mesh control
        self.button_mesh_imp_surface.clicked.connect(self._mesh_import_surface)
        self.button_mesh_imp_nonzero.clicked.connect(self._mesh_import_nonzero)
        self.button_mesh_import_all.clicked.connect(self._mesh_import_all)
        self.button_mesh_remove_all.clicked.connect(self._mesh_remove_all)
        self.button_mesh_import_mtr.clicked.connect(self._mesh_import_mtr)
        self.button_mesh_remove_mtr.clicked.connect(self._mesh_remove_mtr)
        self.button_mesh_imp_over.clicked.connect(self._mesh_import_over)
        self.button_mesh_imp_bellow.clicked.connect(self._mesh_import_bellow)
        self.button_mesh_rem_over.clicked.connect(self._mesh_remove_over)
        self.button_mesh_rem_bellow.clicked.connect(self._mesh_remove_bellow)
        self.button_mesh_imp_through.clicked.connect(self._mesh_import_through)
        self.button_mesh_rem_through.clicked.connect(self._mesh_remove_through)
        self.button_mesh_imp_elmid.clicked.connect(self._mesh_import_id)
        self.button_mesh_rem_elmid.clicked.connect(self._mesh_remove_id)
        self.mesh_list.itemClicked.connect(self._mesh_element_explorer_control)
        self.mesh_radio_z.setChecked(True)
        self.maps_radio_surface.setChecked(True)
        self.button_mesh_remove_zero.clicked.connect(self._mesh_remove_zero)

        integer_validator = QIntValidator()
        self.mesh_element_id_edit.setValidator(integer_validator)
        self.edit_mesh_crd.setValidator(integer_validator)

    
    def export_compare_conc(self):
        '''
        method for export comparsion table to csv file
        comparation has to be done first
        '''
        if not self.comparation_table:
            self.messenger('export error - data not exists')
            return False

        sname = None
        #selected substance 
        if self.substances:
            sname = str(self.select_substance_compar.currentText())
            
        csvexport.write_comparsion_tab(self.comparation_table, self.work_dir, sname)
        self.messenger('Table has been successfully exported to CSV file')
        self.button_export_csv.setDisabled(True)

    def compare_concentrations(self):
        '''
        method for concentration comparsion
        check what list and method has to be used
        '''
        if self.radio_compare_all.isChecked():
            self.__compare_selected_conc()
        elif self.radio_compare_selected.isChecked():
            self.__compare_selected_conc(self.displayed_mesh_list.keys())
        elif self.radio_compare_surface.isChecked():
            if not self.surface_elements:
                fname = self.work_dir + 'SEPAR' + 'master' + SEPAR + FNAME_SURF
                self.surface_elements = surface.read_result(fname)
            self.__compare_selected_conc(self.surface_elements)

    def merge_result_files(self):
        '''
        Method for merging result files
        parses all task dirs, opens result json files and merge values to one big table
        discards results in master dir
        '''
        sname, filename = self.__get_proper_filename_suma()  
        conc = transport.parse_task_dirs(self.work_dir, FNAME_ELEMS, sname)

        
        merged_table = {}
        
        minval = self.edit_merge_minval.text()

        try:
            minval = float(minval)
        except ValueError:
            minval = 0.0

        for fname in conc:
            elem_conc = transport.load_vysledek(fname)
            if not 'master' in fname:
                for elid, time_dict in elem_conc.items():
                    if merged_table.has_key(elid):
                        merged_table[elid].append(time_dict)
                    else:
                        merged_table[elid] = [time_dict, ]
            else:
                master_conc = elem_conc

        output_file = self.work_dir + SEPAR + FNAME_MERGE
        self.messenger(
            'Merging the results to one file / it may take a while...')
        merger.table_to_file(
            merged_table, master_conc, output_file, len(conc) - 1, minval)
        self.messenger(
            'Data has been successfully merged and exported to CSV file')
        
        
    def __get_proper_filename_suma(self):
        '''
        get proper filename for SUM concetration 
        depends if substances are present or not
        '''
        if self.substances:
            sname = str(self.select_substance_compar.currentText())
            filename = path.join(sname, FNAME_SUMA)
        else:
            sname = False
            filename = FNAME_SUMA 
            
        return sname, filename        

    def __compare_selected_conc(self, elm_list=None):
        '''
        wrapper for selected method
        parses all dirs and sums concentrations for all/selected elements
        output a table with sum
        '''
        
        sname, filename = self.__get_proper_filename_suma()  

        master = path.join(self.work_dir, 'master', filename)
        mas_conc_suma = transport.load_vysledek(master)
        mas_total = concentrations.sum_conc(mas_conc_suma, elm_list)

        
        list_of_conc = []
        task_numbers = []
        conc = transport.parse_task_dirs(self.work_dir, FNAME_SUMA, sname)
        for fname in conc:
            conc_suma = transport.load_vysledek(fname)
            task_nr = path.split(fname)[0]
            task_name = task_nr.split('\\')[-1]
            if task_name == 'master':
                task_name = '9999999'
            task_numbers.append(task_name)
            list_of_conc.append(concentrations.sum_conc(conc_suma, elm_list))

        grade_vector = concentrations.grade_result(mas_total, list_of_conc)
        table_rows = zip(task_numbers, list_of_conc, grade_vector)
        table_rows = sorted(
            table_rows, key=lambda grade: grade[2], reverse=True)

        self.__display_table_rslt(table_rows)
        # enable export
        self.comparation_table = table_rows
        self.button_export_csv.setEnabled(True)
        self.spin_grade_filter.setEnabled(True)
        self.button_grade_filter.setEnabled(True)

    def __display_table_rslt(self, table_rows):
        '''
        controls table_comp_rslt widget
        fill the widget with table_rows list
        @param table_rows: []
        '''

        rows_count = len(table_rows)
        model = QStandardItemModel(rows_count, 3)

        model.setHorizontalHeaderLabels(TAB_LABELS)

        # fill model with data
        for row in range(rows_count):
            for col in range(3):
                item = QStandardItem()
                item.setData(str(table_rows[row][col]), Qt.DisplayRole)
                model.setItem(row, col, item)

        # self.table_comp_rslt.clearContents()
        if self.table_comp_rslt.isSortingEnabled():
            self.table_comp_rslt.setSortingEnabled(False)

        proxy = numsort.NumberSortModel()
        proxy.setSourceModel(model)
        self.table_comp_rslt.setModel(proxy)
        self.table_comp_rslt.resizeColumnsToContents()
        self.table_comp_rslt.setSortingEnabled(True)

    def __filter_table_rslt(self):
        '''
        filter for grades
        '''
        grval = int(self.spin_grade_filter.value())
        newtab = [(elm, conc, grade)
                  for (elm, conc, grade) in self.comparation_table if (grade >= grval) or (elm == '9999999')]

        self.__display_table_rslt(newtab)

    def _tab_controller(self):
        '''
        controlling index of tab widget and throw action
        signal: tabWidget index change
        '''
        if not self.tabWidget.isHidden():
            actions = {
                'data_processing': '_data_dialog',
                'basic_analyser': '_analyser_dialog',
            }

            idx = str(self.tabWidget.currentWidget().objectName())
            if actions.has_key(idx) and self.problem_type:
                getattr(self, actions[idx])()

            if not self.result_elements:
                self.basic_analyser.setDisabled(True)
            else:
                self.basic_analyser.setEnabled(True)

    def _analyser_dialog(self):
        '''
        a dialog for analyser screen
        '''

        if not self.result_elements:
            self.read_concentrations()

        try:
            msg = 'found {} elements with non-zero concentration'.format(
                len(self.result_elements))
        except TypeError:
            self.messenger('Process data first.')
        else:
            self.label_basic_1.setText(msg)

        try:
            msg = '{} selected by element selector for drawing'.format(
                len(self.displayed_mesh_list))
        except TypeError:
            self.messenger('Process data first.')
        else:
            self.label_basic_2.setText(msg)

    def _data_dialog(self):
        '''
        a dialog for data processing
        '''
        
        self.progress_processing.setHidden(True)
        self.progress_processing.setMinimum(0)
        self.progress_processing.setValue(0)

        self.tasks = transport.get_result_files(self.work_dir, self.substances)
        n_tas = len(self.tasks)

        self.solutions = transport.parse_task_dirs(self.work_dir, FNAME_ELEMS)
        n_sols = len(self.solutions)

        if n_tas - n_sols == 0:
            self.button_process_newonly.setHidden(True)
            self.button_process_all.setText('Start')
        else:
            self.button_process_newonly.setVisible(True)
            self.button_process_all.setText('All')

        msg = "Found {} tasks to analyze. It may take a while\n".format(n_tas)
        msg += "{} tasks was already processed (compressed file found), {} still need processing\n".format(
            n_sols, n_tas - n_sols)
        if self.substances and n_sols > 0:
            msg += "\n\nYour task works with multiple substances. Now you close this primary task and open result for single substance in subfolder."
        self.label_processing_text.setText(msg)

        self.progress_processing.setMaximum(n_tas * 4)
        
    

    def _analyze_data(self):
        '''
        action for button_process_all
        takes all tasks in work_dir to process
        '''

        if not self.tasks:
            self.tasks = transport.get_result_files(
                self.work_dir, self.substances)

        self.messenger('Processing data, it may take a while')
        self._analyze_data_routine(self.tasks)
        self.messenger('Processing successfully finished', 300)
        self._data_dialog()

    def _analyze_data_selected(self):
        '''
        action for button_process_newonly
        takes only unprocessed tasks in work_dir to process
        '''
        if not self.tasks:
            self.tasks = transport.get_result_files(
                self.work_dir, self.substances)
        if not self.solutions:
            self.solutions = transport.parse_task_dirs(
                self.work_dir, FNAME_ELEMS)

        dir1 = [path.split(sol)[0] for sol in self.solutions]
        dir2 = [path.split(sol)[0] for sol in self.tasks]
        unproc_list = [transport.get_result_files(i, self.substances)[0]
                       for i in dir2 if i not in dir1]

        self.messenger('Processing data, it may take a while')
        self._analyze_data_routine(unproc_list)
        self.messenger('Processing successfully finished', 300)
        self._data_dialog()

    def _analyze_data_routine(self, task_list):
        '''
        analyze transport out data
        uses transport_multi module for multiprocess computation
        '''
        nr_of_proc = cpu_count() - 1
        self.progress_processing.setMaximum(len(task_list) * 4)
        self.progress_processing.setVisible(True)

        # Create queues
        task_queue = Queue()
        done_queue = Queue()
        # populate queue with data
        for result in task_list:
            task_queue.put(result)
        # Start worker processes
        for _ in range(nr_of_proc):
            Process(target=transport.worker, args=(
                task_queue, done_queue, self.substances)).start()
        # Get and print results
        nval = 0
        for _ in range(len(task_list)):
            nval = nval + done_queue.get()
            self.progress_processing.setValue(nval)

        self.progress_processing.setMaximum(nval)
        # Tell child processes to stop
        for _ in range(nr_of_proc):
            task_queue.put('STOP')

    def draw_charts(self):
        '''
        Draw charts
        apply filter of displayed list if any
        if not draw for all non-zero elements

        '''
        if not self.result_elements:
            self.read_concentrations()

        self.messenger('Data loaded sucessfully, starting drawing the charts')

        gdir = self.work_dir + SEPAR + 'charts' + SEPAR
        if not exists(gdir):
            mkdir(gdir)

        if len(self.displayed_mesh_list) > 0:
            filtered_dict = grafLinear.list_filter(
                self.result_elements, self.displayed_mesh_list.keys())
        else:
            filtered_dict = self.result_elements

        for xkey, xval in filtered_dict.items():
            self.draw_routine(xkey, xval, gdir)

        self.messenger('all charts sucessfully created')

    def draw_routine(self, xkey, xval, gdir):
        '''
        Test minimal conc and draw chart if values are greater
        then given minimum.
        '''
        min_con = float(self.edit_chart_min_conc.text()
                        ) if self.edit_chart_min_conc.text() else 0

        if max(xval.values()) > min_con:

            disp = grafLinear.fill_up_zeros(self.result_times, xval)
            data = {'disp': disp, 'times': self.result_times}
            settings = {
                'xkey': xkey,
                'where': gdir,
                'xlabel': str(self.edit_chart_x_text.text()),
                'ylabel': str(self.edit_chart_y_text.text()),
                'title': str(self.edit_chart_title_text.text())
            }

            grafLinear.draw_chart(data, settings)
            self.messenger('Chart for element {} created'.format(xkey))

        else:
            self.messenger(
                'Maximal concentration for element {} is bellow given value.'.format(xkey))

    def create_tables(self):
        '''Create csv table with results'''
        if not self.result_elements:
            self.read_concentrations()

        csvexport.write_single_conc_tab(
            self.result_elements, self.result_times, self.work_dir)
        self.messenger('table of concentrations created')

    def read_concentrations(self, fname=None):
        '''read concentration from TransportOut result file'''
        if fname == None:
            fname = self.master_work_dir + \
                SEPAR + self.file_dict['Transport_out']

        msg = ''
        try:
            self.result_elements = transport.load_vysledek(
                str(self.master_work_dir) + SEPAR + FNAME_ELEMS)
        except IOError:
            msg += 'Failed to load result data for elements.'
            self.result_elements = None
        try:
            self.result_times = transport.load_vysledek(
                str(self.master_work_dir) + SEPAR + FNAME_TIME)
        except IOError:
            msg += 'Failed to load result data for times.'
            self.result_times = None

        if msg:
            if self.substances:
                self.messenger("Your task works with multiple substances. Please open result for single substance in subfolder.")
            else:    
                self.messenger('{}. Please process data first'.format(msg))
            self.tabWidget.setCurrentWidget(self.data_processing)
            self.tabWidget.setCurrentIndex(2)  # 2 should be data processing
            return False
        else:
            return True

    def start_logger(self):
        '''
        logging tool startup
        '''
        self.logger = logger.get_it(
            __appname__, '{}{}.log'.format(self.output_dir, __appname__.lower()))
        self.logovat = True

    def messenger(self, msg, dtime=0):
        '''sending messages to status bar, and to log, if it's enabled'''
        self.status_bar.showMessage(msg, dtime)
        if self.logovat:
            self.logger.info(msg)

    def init_mesh_tools(self):
        '''
        mesh tools starter
        '''
        self._load_msh()
        self._load_mtr()
        self._load_surface()
        self._fill_mesh_mtr_form()
        self.messenger('MESH tools successfully loaded all data', 8000)

    def analyse_basic_problem(self):
        '''
        action for basic analyser
        '''

        self.master_work_dir = self.work_dir + SEPAR
        self.problem_type = 'basic'
        if self.find_and_open_inifile():
            self.box_merger.setHidden(True)
            self.__remove_all_tabs()
            self.tabWidget.addTab(self.basic_analyser, 'Basic Analyser')
            self.tabWidget.addTab(self.data_processing, 'Data Processing')
            self.init_mesh_tools()
            self.tabWidget.setCurrentWidget(self.basic_analyser)
            self._save_setup()

        if not self.result_elements:
            self.read_concentrations()

        self.__fill_maps_times()

        self._analyser_dialog()  # check the data before display manualy
        self._data_dialog()
        self.tabWidget.show()

    def analyse_sensitivity_task(self):
        '''
        action for sensitivity analyser
        '''
        self.master_work_dir = self.work_dir + SEPAR + 'master' + SEPAR
        self.problem_type = 'compare'
        if self.find_and_open_inifile():
            self.box_merger.setHidden(True)
            self.tabWidget.addTab(self.comparative_analyser, 'Sensitivity Analyser')
            self.tabWidget.addTab(self.data_processing, 'Data Processing')
            self.label_analyser_title.setText('Sensitivity Analyser')
            self.init_mesh_tools()
            self.tabWidget.setCurrentIndex(2)
            self._save_setup()

        self._analyser_dialog()  # check the data before display manualy
        self._data_dialog()     

        self.tabWidget.show()

    def analyse_monte_carlo(self):
        '''
        action for monte carlo analyser
        '''
        self.master_work_dir = self.work_dir + SEPAR + 'master' + SEPAR
        self.problem_type = 'compare'

        if self.find_and_open_inifile():
            self.tabWidget.addTab(self.comparative_analyser, 'Monte Carlo Analyser')
            self.tabWidget.addTab(self.data_processing, 'Data Processing')
            self.label_analyser_title.setText('Monte Carlo Analyser')
            self.init_mesh_tools()

            self.box_merger.setHidden(False)
            self.tabWidget.setCurrentIndex(2)
            self._save_setup()
            
        self._analyser_dialog()  # check the data before display manualy
        self._data_dialog()    

        self.tabWidget.show()

    def open_task_dir(self):
        '''SetUP output using Qfiledialog'''
        adr = "."
        tmp = QFileDialog.getExistingDirectory(
            self, "Open output directory", adr, options=QFileDialog.ShowDirsOnly)
        self.work_dir = str(tmp)
        if ruzne.isit_task_folder(tmp):
            self.identify_problem_type()
        else:
            self.messenger(
                'Directory does not contain problem.type file! Assuming basic problem.')
            self.analyse_basic_problem()

    def identify_problem_type(self):
        '''
        search dir for file problem.type
        fail if file not exist
        '''
        try:
            rfile = open(self.work_dir + SEPAR + 'problem.type')
        except IOError:
            self.messenger('Failed to open problem.type file')
            self.analyse_basic_problem()
        else:
            istr = rfile.read()
            if istr.strip() in IDSDICT.keys():
                func = getattr(self, IDSDICT[istr.strip()])
                func()
            else:
                self.messenger('ERROR: failed to recognize given problem')

    def find_and_open_inifile(self):
        '''search work dir for a flow ini to open it'''

        list_of_files = [ff for ff in listdir(
            self.master_work_dir) if ff.lower().endswith('ini')]

        if not list_of_files:
            self.messenger('ERROR: no ini files found, failed to continue')
            return False
        elif len(list_of_files) > 2:
            self.messenger(
                'ERROR: 2 or more ini files found, failed to continue')
            return False
        else:
            self.flowini_file_master = list_of_files[0].strip()
            fname = self.master_work_dir + self.flowini_file_master
            self.file_dict = flow.get_dict_from_file(fname)
            subs = flow.get_substances_from_file(fname)
            if int(subs['N_substances']) > 1:
                self.substances = True
                self.setup_substances_form(subs)
            else:
                #hide the group box for substances in comparative analyser
                self.group_compar_subst.setHidden(True)    

            return True
        
    def setup_substances_form(self, subs):
        '''
        fill qcombobox in comparative form with names of substances
        '''    
        names = subs['Substances'].split()
        self.select_substance_compar.clear()
        self.select_substance_compar.insertItems(0, names)
        self.select_substance_compar.repaint()


    def _load_setup(self):
        '''load setup, create a new file if not exists'''
        try:
            fread = open(__inifile__)
            self.setup = INIConfig(fread)
            self.work_dir = self.setup['Work']['Dir']
            fread.close()

        except IOError:
            setup = "[Work]\nDir = ''\n"
            fnew = open(__inifile__, 'w')
            print >> fnew, setup
            fnew.close()

    def __fill_maps_times(self):
        '''fill form in the maps draw / with simulation times'''
        if self.result_times:
            self.maps_sim_time_select.clear()
            wherefrom = self.result_times
            data = ["%s" % str(k) for k in sorted(wherefrom, reverse=True)]
            self.maps_sim_time_select.insertItems(0, data)
            self.maps_sim_time_select.repaint()

    def _save_setup(self):
        '''saves ini file'''
        self.setup['Work']['Dir'] = self.work_dir
        fnew = open(__inifile__, 'w')
        print >> fnew, self.setup
        fnew.close()

    def _load_surface(self, file_name=None):
        '''
        load surface elems using surface module
        first try to open prepared file, in case of file create a new one using surface module
        '''
        wdir = {'basic': self.work_dir, 'compare': self.master_work_dir}

        fname_bcd = wdir[self.problem_type] + SEPAR + self.file_dict['Boundary']
        self.bcd_file = fname_bcd

        if file_name is None:
            fname = self.master_work_dir + FNAME_SURF

        self.messenger('Loading Surface elements')
        if path.isfile(fname):
            self.surface_elements = surface.read_result(fname)
        else:
            fname_msh = wdir[self.problem_type] + SEPAR + self.file_dict['Mesh']
            self.surface_elements = surface.read(fname_bcd, fname_msh)
            surface.write(fname, self.surface_elements)

        self.messenger('Surface elements successfully loaded', 8000)

    def _load_msh(self, file_name=None):
        '''
        load mesh using mesh module
        '''
        if file_name is None:
            file_name = self.master_work_dir + self.file_dict['Mesh']

        self.messenger('Loading MSH')
        self.msh = mesh.Mesh()
        self.msh.read(file_name)
        self.messenger('MSH successfully loaded', 8000)

    def _load_mtr(self, file_name=None):
        '''load mtr from file, if not given look to the flow.ini settings'''
        if file_name == None:
            file_name = self.master_work_dir + SEPAR + self.file_dict['Material']

        self.messenger('Loading materials from MTR file')
        self.material = material.Material()
        self.material.getDictFromFile(file_name)
        self.messenger('MTR successfully loaded', 8000)

    def _fill_mesh_mtr_form(self):
        '''fill form in the mesh editor'''
        self.select_mesh_mtr.clear()
        wherefrom = self.msh.mtr_index.keys()
        data = ["%s" % str(k) for k in sorted(wherefrom)]
        self.select_mesh_mtr.insertItems(0, data)
        self.select_mesh_mtr.repaint()

    def _mesh_element_explorer_control(self):
        '''
        action for controlin mesh_element_explorer display block
        '''
        idxtu = str(self.mesh_list.currentIndex().data().toString())
        idxtu = idxtu.split()
        idx = idxtu[0]
        doc = "element id: {0}\n".format(idx)
        doc += "node : [x, y, z]\n"
        for node in self.msh.elements[int(idx)][2]:
            doc += "{0} : {1}\n".format(node, self.msh.nodes[node])
        self.mesh_element_explorer.clear()
        self.mesh_element_explorer.insertPlainText(doc)
        self.mesh_element_explorer.setReadOnly(True)

    def _mesh_import_list_updater(self, vals=None, todisp=0):
        '''helper for various list update functions
        @PARAM vals {mesh elements}
        @PARAM todisp number of elements for message
        '''
        if vals is not None and len(vals) > 0:
            self.displayed_mesh_list.update(vals)
            todisp = len(vals)
            msg = 'Loading %s elements to the list. It may take a while...' % str(
                todisp)
            self.messenger(msg)
            self._mesh_list_refresh()
            self.messenger('Selection of elements loaded', 8000)

    def _mesh_import_list_deleter(self, vals):
        '''
        deletes given values from displayed mesh list
        @param vals: {} of values to be deleted
        '''
        for key in vals.keys():
            try:
                del self.displayed_mesh_list[key]
            except KeyError:
                pass

        msg = 'Refreshing the list'
        self.messenger(msg)
        self._mesh_list_refresh()
        self.messenger('Refreshing finished', 8000)

    def _get_mesh_axis(self):
        '''
        check what radio is checked, return axis
        @return: axis string identifier
        '''
        if self.mesh_radio_x.isChecked():
            return 'x'
        elif self.mesh_radio_y.isChecked():
            return 'y'
        elif self.mesh_radio_z.isChecked():
            return 'z'
        else:
            self.messenger('Choose axis first!', 8000)
            return False

    def _read_surface_elements(self):
        '''
        imports surface elements to mesh list
        need surface_result file to work
        '''

        if not self.surface_elements:
            fname = self.work_dir + SEPAR + 'master' + SEPAR + FNAME_SURF
            self.surface_elements = surface.read_result(fname)

        vals = {}
        for elid in self.surface_elements:
            elid = int(elid)
            if self.msh.elements.has_key(elid):
                vals[elid] = self.msh.elements[elid]

        return vals

    def _mesh_import_surface(self):
        '''
        imports surface elements to mesh list
        need surface_result file to work
        '''
        vals = self._read_surface_elements()
        self._mesh_import_list_updater(vals)

    def __remove_zeros_from_mesh_list(self, mesh_list):
        '''
        removes elemtns with no (zero values) concentration in time from given mesh list
        '''
        if not self.result_elements:
            self.read_concentrations()

        for key in mesh_list.keys():
            if str(key) not in self.result_elements:
                del mesh_list[key]

        return mesh_list

    def _mesh_remove_zero(self):
        '''
        removes elemtns with no (zero values) concentration in time from mesh list
        '''

        self.displayed_mesh_list = self.__remove_zeros_from_mesh_list(
            self.displayed_mesh_list)

        msg = 'Refreshing the list'
        self.messenger(msg)
        self._mesh_list_refresh()
        self.messenger('Refreshing finished', 8000)

    def _mesh_import_nonzero(self):
        '''
        imports elements with nonzero concentration in time to mesh list
        '''
        if not self.result_elements:
            self.read_concentrations()

        vals = {}
        for elid in self.result_elements:
            elid = int(elid)
            if self.msh.elements.has_key(elid):
                vals[elid] = self.msh.elements[elid]

        self._mesh_import_list_updater(vals)

    def _mesh_import_axis(self, compare, axis='z'):
        '''import elements with coordinate in given axis,
           compare given val with the value of mesh spinbox
        @param compare: -1 for elements bellow, 0 for through, 1 for over
        @param axix: what axis (x, y, z)
        '''
        val = int(self.edit_mesh_crd.text())
        vals = {}
        for elid, elem in self.msh.elements.items():
            pridat = True
            for node_id in elem[2]:
                node_coord = self.msh.nodes[node_id][AXIS_TRANS[axis]]
                if cmp(node_coord, val) == compare or cmp(node_coord, val) == 0:
                    pridat = False
            if pridat:
                vals[elid] = self.msh.elements[elid]

        return vals

    def _mesh_find_through(self, axis='z', val='default'):
        '''import elements with at last one node over coordinate in given axis,
           such elements has to be cuted through given plane
           compare given val with the value of mesh spinbox
        @param axix: what axis (x, y, z)
        '''
        if val == 'default':
            val = int(self.edit_mesh_crd.text())

        vals = {}
        for elid, elem in self.msh.elements.items():
            nad = False
            pod = False
            for node_id in elem[2]:
                node_coord = self.msh.nodes[node_id][AXIS_TRANS[axis]]
                if cmp(node_coord, val) == 1 or cmp(node_coord, val) == 0:
                    nad = True
                elif cmp(node_coord, val) == -1 or cmp(node_coord, val) == 0:
                    pod = True
            if nad and pod:
                vals[elid] = self.msh.elements[elid]
        return vals

    def _mesh_import_through(self):
        '''import elements where one node has Z over value of mesh spinbox
        and therefore this element is cut by given alt
        '''
        axis = self._get_mesh_axis()
        if axis:
            vals = self._mesh_find_through(axis)
            self._mesh_import_list_updater(vals)

    def _mesh_import_over(self):
        '''import elements where all nodes has Z over value of mesh spinbox
        '''
        axis = self._get_mesh_axis()
        if axis:
            vals = self._mesh_import_axis(-1, axis)
            self._mesh_import_list_updater(vals)

    def _mesh_import_bellow(self):
        '''import elements where all nodes has Z bellow value of mesh spinbox
        '''
        axis = self._get_mesh_axis()
        if axis:
            vals = self._mesh_import_axis(1, axis)
            self._mesh_import_list_updater(vals)

    def _mesh_import_id(self):
        '''
        Import elemet with id given in form
        and update displayed list
        '''
        try:
            elmid = int(self.mesh_element_id_edit.text())
            vals = {elmid: self.msh.elements[elmid]}
            self._mesh_import_list_updater(vals)
        except KeyError:
            self.messenger('ERROR: no such element', 8000)
        except ValueError:
            self.messenger('ERROR: not valid element id', 8000)

    def _mesh_remove_id(self):
        '''
        Remove elemet with id given in form
        and update displayed list
        '''
        elmid = int(self.mesh_element_id_edit.text())
        try:
            vals = {elmid: self.msh.elements[elmid]}
            self._mesh_import_list_deleter(vals)
        except KeyError:
            self.messenger('ERROR: no such element', 8000)

    def _mesh_remove_over(self):
        '''removes elements where all nodes has coordinate in axis
         over value of mesh spinbox
        '''
        axis = self._get_mesh_axis()
        if axis:
            vals = self._mesh_import_axis(-1, axis)
            self._mesh_import_list_deleter(vals)

    def _mesh_remove_bellow(self):
        '''
        removes elements with coordinate in given axis
        bellow value of mesh spinbox
        '''
        axis = self._get_mesh_axis()
        if axis:
            vals = self._mesh_import_axis(1, axis)
            self._mesh_import_list_deleter(vals)

    def _mesh_remove_through(self):
        '''
        removes elements cuted through by given plane
        '''

        axis = self._get_mesh_axis()
        if axis:
            vals = self._mesh_find_through(axis)
            self._mesh_import_list_deleter(vals)

    def _mesh_import_mtr(self):
        '''import all nodes with material selected in the selec_mtr_mesh'''
        idx = int(self.select_mesh_mtr.currentText())
        vals = {}
        for i in self.msh.mtr_index[idx]:
            vals[i] = self.msh.elements[i]

        self._mesh_import_list_updater(vals)

    def _mesh_remove_mtr(self):
        '''delete all nodes with material selected in the selec_mtr_mesh'''
        idx = int(self.select_mesh_mtr.currentText())
        for i in self.msh.mtr_index[idx]:
            del self.displayed_mesh_list[i]

        msg = 'Deleting %s elements from the list. It may take a while...' % str(
            len(self.msh.mtr_index[idx]))
        self.messenger(msg)
        self._mesh_list_refresh()
        self.messenger('Selection of elements loaded', 8000)

    def _mesh_import_all(self):
        '''imports all mesh to list - msh file need to be loaded first'''
        try:
            msg = 'Loading %s elements to the list. It may take a while...' % str(
                len(self.msh.elements))
            self.messenger(msg)
            self.displayed_mesh_list = self.msh.elements.copy()
            self._mesh_list_refresh()
            self.messenger('MSH data loaded', 8000)
        except AttributeError:
            self.messenger('Error: load MSH data first!', 8000)

    def _mesh_remove_all(self):
        '''
        Clear mesh selected list completely
        '''
        try:
            self.displayed_mesh_list = {}
            self.mesh_list.clear()
        finally:
            self.messenger('Data removed from list', 8000)
            self.groupBox_2.setTitle('0 elements in the list')

    def _mesh_list_refresh(self):
        '''
        takes actual dict self.displayedList and refresh the view
        displays - element id, element type, material

        creates and refreshes dict of mtr {mtrid : [el1, el2]}
        '''
        try:
            self.mesh_list.clear()
            self.mtr_index_disp_lst = {}

            for key in sorted(self.displayed_mesh_list.keys()):
                # update of list
                mtr = self.displayed_mesh_list[key][1][0]
                tst = "%s %s %s" % (key, self.displayed_mesh_list[key][0], mtr)
                QListWidgetItem(str(tst), self.mesh_list)
                # update of index
                if self.mtr_index_disp_lst.has_key(mtr):
                    self.mtr_index_disp_lst[mtr].append(key)
                else:
                    self.mtr_index_disp_lst[mtr] = [key]

            # update the default index
            self.msh.mtr_index.update(self.mtr_index_disp_lst)
            self.mesh_list.repaint()
            msg = "{0} elements in the list".format(
                len(self.displayed_mesh_list))
            self.groupBox_2.setTitle(msg)

        except:
            self.messenger('Unexpected Error', 8000)
Example #22
0
class QTSeedEditor(QDialog):
    """
    DICOM viewer.
    """
    @staticmethod
    def get_line(mode='h'):
        line = QFrame()
        if mode == 'h':
            line.setFrameStyle(QFrame.HLine)
        elif mode == 'v':
            line.setFrameStyle(QFrame.VLine)

        line.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding)

        return line

    def initUI(self, shape, vscale, height=600,
               mode='seed'):
        """
        Initialize UI.

        Parameters
        ----------
        shape : (int, int, int)
            Shape of data matrix.
        vscale : (float, float, float)
            Voxel scaling.
        height : int
            Maximal slice height in pixels.
        mode : str
            Editor mode.
        """

        # picture
        grid = height / float(shape[1] * vscale[1])
        mgrid = (grid * vscale[0], grid * vscale[1])
        self.slice_box = SliceBox(shape[:-1], mgrid,
                                  mode)
        self.slice_box.setScrollFun(self.scrollSlices)
        self.connect(self.slice_box, SIGNAL('focus_slider'), self.focusSliceSlider)

        # sliders
        self.allow_select_slice = True
        self.n_slices = shape[2]
        self.slider = QSlider(Qt.Vertical)
        self.slider.label = QLabel()
        self.slider.label.setText('Slice: %d / %d' % (self.actual_slice,
                                                      self.n_slices))
        self.slider.setRange(1, self.n_slices)
        self.slider.valueChanged.connect(self.sliderSelectSlice)
        self.slider.setValue(self.actual_slice)

        self.slider_cw = {}
        self.slider_cw['c'] = QSlider(Qt.Horizontal)
        self.slider_cw['c'].valueChanged.connect(self.changeC)
        self.slider_cw['c'].label = QLabel()

        self.slider_cw['w'] = QSlider(Qt.Horizontal)
        self.slider_cw['w'].valueChanged.connect(self.changeW)
        self.slider_cw['w'].label = QLabel()

        self.view_label = QLabel('View size: %d x %d' % self.img_aview.shape[:-1])
        self.voxel_label = QLabel('Voxel size [mm]:\n  %.2f x %.2f x %.2f'\
                                      % tuple(self.voxel_size[np.array(self.act_transposition)]))

        combo_view_options = VIEW_TABLE.keys()
        combo_view = QComboBox(self)
        combo_view.activated[str].connect(self.setView)
        combo_view.addItems(combo_view_options)

        # buttons
        self.btn_quit = QPushButton("Return", self)
        self.btn_quit.clicked.connect(self.quit)

        combo_dmask = QComboBox(self)
        combo_dmask.activated.connect(self.changeMask)
        self.mask_points_tab, aux = self.init_draw_mask(DRAW_MASK, mgrid)
        for icon, label in aux:
            combo_dmask.addItem(icon, label)


        self.slice_box.setMaskPoints(self.mask_points_tab[combo_dmask.currentIndex()])

        self.status_bar = QStatusBar()

        vopts = []
        vmenu = []
        appmenu = []
        if mode == 'seed' and self.mode_fun is not None:
            btn_recalc = QPushButton("Recalculate", self)
            btn_recalc.clicked.connect(self.recalculate)
            appmenu.append(QLabel('<b>Segmentation mode</b><br><br><br>' +
                                  'Select the region of interest<br>' +
                                  'using the mouse buttons:<br><br>' +
                                  '&nbsp;&nbsp;<i>left</i> - inner region<br>' +
                                  '&nbsp;&nbsp;<i>right</i> - outer region<br><br>'))
            appmenu.append(btn_recalc)
            appmenu.append(QLabel())
            self.volume_label = QLabel('Volume:\n  unknown')
            appmenu.append(self.volume_label)

            # Set middle pencil as default (M. Jirik)
            combo_dmask.setCurrentIndex(1)
            self.slice_box.setMaskPoints(
                self.mask_points_tab[combo_dmask.currentIndex()])
            #  -----mjirik---end------

        if mode == 'seed' or mode == 'crop'\
                or mode == 'mask' or mode == 'draw':
            btn_del = QPushButton("Delete Seeds", self)
            btn_del.clicked.connect(self.deleteSliceSeeds)
            vmenu.append(None)
            vmenu.append(btn_del)

            combo_contour_options = ['fill', 'contours', 'off']
            combo_contour = QComboBox(self)
            combo_contour.activated[str].connect(self.changeContourMode)
            combo_contour.addItems(combo_contour_options)
            self.changeContourMode(combo_contour_options[combo_contour.currentIndex()])
            vopts.append(QLabel('Selection mode:'))
            vopts.append(combo_contour)


        if mode == 'mask':
            btn_recalc_mask = QPushButton("Recalculate mask", self)
            btn_recalc_mask.clicked.connect(self.updateMaskRegion_btn)
            btn_all = QPushButton("Select all", self)
            btn_all.clicked.connect(self.maskSelectAll)
            btn_reset = QPushButton("Reset selection", self)
            btn_reset.clicked.connect(self.resetSelection)
            btn_reset_seads = QPushButton("Reset seads", self)
            btn_reset_seads.clicked.connect(self.resetSeads)
            btn_add = QPushButton("Add selection", self)
            btn_add.clicked.connect(self.maskAddSelection)
            btn_rem = QPushButton("Remove selection", self)
            btn_rem.clicked.connect(self.maskRemoveSelection)
            btn_mask = QPushButton("Mask region", self)
            btn_mask.clicked.connect(self.maskRegion)
            appmenu.append(QLabel('<b>Mask mode</b><br><br><br>' +
                                  'Select the region to mask<br>' +
                                  'using the left mouse button<br><br>'))
            appmenu.append(self.get_line('h'))
            appmenu.append(btn_recalc_mask)
            appmenu.append(btn_all)
            appmenu.append(btn_reset)
            appmenu.append(btn_reset_seads)
            appmenu.append(self.get_line('h'))
            appmenu.append(btn_add)
            appmenu.append(btn_rem)
            appmenu.append(self.get_line('h'))
            appmenu.append(btn_mask)
            appmenu.append(self.get_line('h'))
            self.mask_qhull = None

        if mode == 'crop':
            btn_crop = QPushButton("Crop", self)
            btn_crop.clicked.connect(self.crop)
            appmenu.append(QLabel('<b>Crop mode</b><br><br><br>' +
                                  'Select the crop region<br>' +
                                  'using the left mouse button<br><br>'))
            appmenu.append(btn_crop)

        if mode == 'draw':
            appmenu.append(QLabel('<b>Manual segmentation<br> mode</b><br><br><br>' +
                                  'Mark the region of interest<br>' +
                                  'using the mouse buttons:<br><br>' +
                                  '&nbsp;&nbsp;<i>left</i> - draw<br>' +
                                  '&nbsp;&nbsp;<i>right</i> - erase<br>' +
                                  '&nbsp;&nbsp;<i>middle</i> - vol. erase<br><br>'))

            btn_reset = QPushButton("Reset", self)
            btn_reset.clicked.connect(self.resetSliceDraw)
            vmenu.append(None)
            vmenu.append(btn_reset)

            combo_erase_options = ['inside', 'outside']
            combo_erase = QComboBox(self)
            combo_erase.activated[str].connect(self.changeEraseMode)
            combo_erase.addItems(combo_erase_options)
            self.changeEraseMode(combo_erase_options[combo_erase.currentIndex()])
            vopts.append(QLabel('Volume erase mode:'))
            vopts.append(combo_erase)

        hbox = QHBoxLayout()
        vbox = QVBoxLayout()
        vbox_left = QVBoxLayout()
        vbox_app = QVBoxLayout()

        hbox.addWidget(self.slice_box)
        hbox.addWidget(self.slider)
        vbox_left.addWidget(self.slider.label)
        vbox_left.addWidget(self.view_label)
        vbox_left.addWidget(self.voxel_label)
        vbox_left.addWidget(QLabel())
        vbox_left.addWidget(QLabel('View plane:'))
        vbox_left.addWidget(combo_view)
        vbox_left.addWidget(self.get_line())
        vbox_left.addWidget(self.slider_cw['c'].label)
        vbox_left.addWidget(self.slider_cw['c'])
        vbox_left.addWidget(self.slider_cw['w'].label)
        vbox_left.addWidget(self.slider_cw['w'])
        vbox_left.addWidget(self.get_line())
        vbox_left.addWidget(QLabel('Drawing mask:'))
        vbox_left.addWidget(combo_dmask)

        for ii in vopts:
            vbox_left.addWidget(ii)

        for ii in vmenu:
            if ii is None:
                vbox_left.addStretch(1)

            else:
                vbox_left.addWidget(ii)

        for ii in appmenu:
            if ii is None:
                vbox_app.addStretch(1)

            else:
                vbox_app.addWidget(ii)

        vbox_app.addStretch(1)
        vbox_app.addWidget(self.btn_quit)

        hbox.addLayout(vbox_left)
        hbox.addWidget(self.get_line('v'))
        hbox.addLayout(vbox_app)
        vbox.addLayout(hbox)
        vbox.addWidget(self.status_bar)
        self.my_layout = vbox
        self.setLayout(vbox)

        self.setWindowTitle('Segmentation Editor')
        self.show()

    def __init__(self, img, viewPositions=None,
                 seeds=None, contours=None,
                 mode='seed', modeFun=None,
                 voxelSize=[1,1,1], volume_unit='mm3'):
        """
        Initiate Editor

        Parameters
        ----------
        img : array
            DICOM data matrix.
        actualSlice : int
            Index of actual slice.
        seeds : array
            Seeds, user defined regions of interest.
        contours : array
            Computed segmentation.
        mode : str
            Editor modes:
               'seed' - seed editor
               'crop' - manual crop
               'draw' - drawing
               'mask' - mask region
        modeFun : fun
            Mode function invoked by user button.
        voxelSize : tuple of float
            voxel size [mm]
        volume_unit : allow select output volume in mililiters or mm3
            [mm, ml]
        """

        QDialog.__init__(self)

        self.mode = mode
        self.mode_fun = modeFun

        self.actual_view = 'axial'
        self.act_transposition = VIEW_TABLE[self.actual_view]
        self.img = img
        self.img_aview = self.img.transpose(self.act_transposition)

        self.volume_unit = volume_unit

        self.last_view_position = {}
        for jj, ii in enumerate(VIEW_TABLE.iterkeys()):
            if viewPositions is None:
                viewpos = img.shape[VIEW_TABLE[ii][-1]] / 2

            else:
                viewpos = viewPositions[jj]

            self.last_view_position[ii] =\
                img.shape[VIEW_TABLE[ii][-1]] - viewpos - 1

        self.actual_slice = self.last_view_position[self.actual_view]

        # set contours
        self.contours = contours
        if self.contours is None:
            self.contours_aview = None
        else:
            self.contours_aview = self.contours.transpose(self.act_transposition)

        # masked data - has information about which data were removed
        # 1 == enabled, 0 == deleted
        # How to return:
        #       editorDialog.exec_()
        #       masked_data = editorDialog.masked
        self.masked = np.ones(self.img.shape, np.int8)

        self.voxel_size = np.squeeze(np.asarray(voxelSize))
        self.voxel_scale = self.voxel_size / float(np.min(self.voxel_size))
        self.voxel_volume = np.prod(voxelSize)

        # set seeds
        if seeds is None:
            self.seeds = np.zeros(self.img.shape, np.int8)

        else:
            self.seeds = seeds

        self.seeds_aview = self.seeds.transpose(self.act_transposition)
        self.seeds_modified = False

        self.initUI(self.img_aview.shape,
                    self.voxel_scale[np.array(self.act_transposition)],
                    600, mode)

        if mode == 'draw':
            self.seeds_orig = self.seeds.copy()
            self.slice_box.setEraseFun(self.eraseVolume)

        # set view window values C/W
        lb = np.min(img)
        self.img_min_val = lb
        ub = np.max(img)
        dul = np.double(ub) - np.double(lb)
        self.cw_range = {'c': [lb, ub], 'w': [1, dul]}
        self.slider_cw['c'].setRange(lb, ub)
        self.slider_cw['w'].setRange(1, dul)
        self.changeC(lb + dul / 2)
        self.changeW(dul)

        self.offset = np.zeros((3,), dtype=np.int16)

    def showStatus(self, msg):
        self.status_bar.showMessage(QString(msg))
        QApplication.processEvents()

    def init_draw_mask(self, draw_mask, grid):
        mask_points = []
        mask_iconlabel = []
        for mask, label in draw_mask:
            w, h = mask.shape
            xx, yy = mask.nonzero()
            mask_points.append((xx - w/2, yy - h/2))

            img = QImage(w, h, QImage.Format_ARGB32)
            img.fill(qRgba(255, 255, 255, 0))
            for ii in range(xx.shape[0]):
                img.setPixel(xx[ii], yy[ii], qRgba(0, 0, 0, 255))

            img = img.scaled(QSize(w * grid[0], h * grid[1]))
            icon = QIcon(QPixmap.fromImage(img))
            mask_iconlabel.append((icon, label))

        return mask_points, mask_iconlabel

    def saveSliceSeeds(self):
        aux = self.slice_box.getSliceSeeds()
        if aux is not None:
            self.seeds_aview[...,self.actual_slice] = aux
            self.seeds_modified = True

        else:
            self.seeds_modified = False

    def updateMaskRegion_btn(self):
        self.saveSliceSeeds()
        self.updateMaskRegion()

    def updateMaskRegion(self):
        crp = self.getCropBounds(return_nzs=True)
        if crp is not None:
            off, cri, nzs = crp
            if nzs[0].shape[0] <=5:
                self.showStatus("Not enough points (need >= 5)!")

            else:
                points = np.transpose(nzs)
                hull = Delaunay(points)
                X, Y, Z = np.mgrid[cri[0], cri[1], cri[2]]
                grid = np.vstack([X.ravel(), Y.ravel(), Z.ravel()]).T
                simplex = hull.find_simplex(grid)
                fill = grid[simplex >=0,:]
                fill = (fill[:,0], fill[:,1], fill[:,2])
                if self.contours is None or self.contours_old is None:
                    self.contours = np.zeros(self.img.shape, np.int8)
                    self.contours_old = self.contours.copy()
                else:
                    self.contours[self.contours != 2] = 0
                self.contours[fill] = 1
                self.contours_aview = self.contours.transpose(self.act_transposition)

                self.selectSlice(self.actual_slice)

    def maskRegion(self):
        self.masked[self.contours == 0] = 0

        self.img[self.contours != 2] = self.img_min_val
        self.contours.fill(0)
        self.contours_old = self.contours.copy()
        self.seeds.fill(0)
        self.selectSlice(self.actual_slice)

    def maskAddSelection(self):
        self.updateMaskRegion()
        if self.contours is None:
            return

        self.contours[self.contours == 1] = 2
        self.contours_old = self.contours.copy()
        self.seeds.fill(0)
        self.selectSlice(self.actual_slice)

    def maskRemoveSelection(self):
        self.updateMaskRegion()
        if self.contours is None:
            return

        self.contours[self.contours == 1] = 0
        self.contours_old = self.contours.copy()
        self.seeds.fill(0)
        self.selectSlice(self.actual_slice)

    def maskSelectAll(self):
        self.updateMaskRegion()

        self.seeds[0][0][0] = 1
        self.seeds[0][0][-1] = 1
        self.seeds[0][-1][0] = 1
        self.seeds[0][-1][-1] = 1
        self.seeds[-1][0][0] = 1
        self.seeds[-1][0][-1] = 1
        self.seeds[-1][-1][0] = 1
        self.seeds[-1][-1][-1] = 1

        self.updateMaskRegion()
        self.selectSlice(self.actual_slice)

    def resetSelection(self):
        self.updateMaskRegion()
        if self.contours is None:
            return

        self.contours.fill(0)
        self.contours_old = self.contours.copy()
        self.seeds.fill(0)
        self.selectSlice(self.actual_slice)

    def resetSeads(self):
        self.seeds.fill(0)
        if self.contours is not None:
            self.contours = self.contours_old.copy()
            self.contours_aview = self.contours.transpose(self.act_transposition)
        self.updateMaskRegion()
        self.selectSlice(self.actual_slice)

    def updateCropBounds(self):
        crp = self.getCropBounds()
        if crp is not None:
            _, cri = crp
            self.contours = np.zeros(self.img.shape, np.int8)
            self.contours[cri].fill(1)
            self.contours_aview = self.contours.transpose(self.act_transposition)

    def focusSliceSlider(self):
        self.slider.setFocus(True)

    def sliderSelectSlice(self, value):
        self.selectSlice(self.n_slices - value)

    def scrollSlices(self, inc):
        if abs(inc) > 0:
            new = self.actual_slice + inc
            self.selectSlice(new)

    def selectSlice(self, value, force=False):
        if not(self.allow_select_slice):
            return

        if (value < 0) or (value >= self.n_slices):
            return

        if (value != self.actual_slice) or force:
            self.saveSliceSeeds()
            if self.seeds_modified:
                if self.mode == 'crop':
                    self.updateCropBounds()

                elif self.mode == 'mask':
                    self.updateMaskRegion()

        if self.contours is None:
            contours = None

        else:
            contours = self.contours_aview[...,value]

        slider_val = self.n_slices - value
        self.slider.setValue(slider_val)
        self.slider.label.setText('Slice: %d / %d' % (slider_val, self.n_slices))

        self.slice_box.setSlice(self.img_aview[...,value],
                                self.seeds_aview[...,value],
                                contours)
        self.actual_slice = value

    def getSeeds(self):
        return self.seeds

    def getImg(self):
        return self.img

    def getOffset(self):
        return self.offset * self.voxel_size

    def getSeedsVal(self, label):
        return self.img[self.seeds==label]

    def getContours(self):
        return self.contours

    def setContours(self, contours):
        self.contours = contours
        self.contours_aview = self.contours.transpose(self.act_transposition)

        self.selectSlice(self.actual_slice)

    def changeCW(self, value, key):
        rg = self.cw_range[key]
        if (value < rg[0]) or (value > rg[1]):
            return

        if (value != self.slice_box.getCW()[key]):
            self.slider_cw[key].setValue(value)
            self.slider_cw[key].label.setText('%s: %d' % (key.upper(), value))
            self.slice_box.setCW(value, key)
            self.slice_box.updateSliceCW(self.img_aview[...,self.actual_slice])


    def changeC(self, value):
        self.changeCW(value, 'c')

    def changeW(self, value):
        self.changeCW(value, 'w')

    def setView(self, value):
        self.last_view_position[self.actual_view] = self.actual_slice
        # save seeds
        self.saveSliceSeeds()
        if self.seeds_modified:
            if self.mode == 'crop':
                self.updateCropBounds()

            elif self.mode == 'mask':
                self.updateMaskRegion()

        key = str(value)
        self.actual_view = key
        self.actual_slice = self.last_view_position[key]

        self.act_transposition = VIEW_TABLE[key]
        self.img_aview = self.img.transpose(self.act_transposition)
        self.seeds_aview = self.seeds.transpose(self.act_transposition)

        if self.contours is not None:
            self.contours_aview = self.contours.transpose(self.act_transposition)
            contours = self.contours_aview[...,self.actual_slice]

        else:
            contours = None

        vscale = self.voxel_scale[np.array(self.act_transposition)]
        height = self.slice_box.height()
        grid = height / float(self.img_aview.shape[1] * vscale[1])
        # width = (self.img_aview.shape[0] * vscale[0])[0]
        # if width > 800:
        #     height = 400
        #     grid = height / float(self.img_aview.shape[1] * vscale[1])

        mgrid = (grid * vscale[0], grid * vscale[1])

        self.slice_box.resizeSlice(new_slice_size=self.img_aview.shape[:-1],
                                   new_grid=mgrid)

        self.slice_box.setSlice(self.img_aview[...,self.actual_slice],
                                self.seeds_aview[...,self.actual_slice],
                                contours)

        self.allow_select_slice = False
        self.n_slices = self.img_aview.shape[2]
        slider_val = self.n_slices - self.actual_slice
        self.slider.setRange(1, self.n_slices)
        self.slider.setValue(slider_val)
        self.allow_select_slice = True

        self.slider.label.setText('Slice: %d / %d' % (slider_val,
                                                      self.n_slices))
        self.view_label.setText('View size: %d x %d' % self.img_aview.shape[:-1])

        self.adjustSize()
        self.adjustSize()

    def changeMask(self, val):
        self.slice_box.setMaskPoints(self.mask_points_tab[val])

    def changeContourMode(self, val):
        self.slice_box.contour_mode = str(val)
        self.slice_box.updateSlice()

    def changeEraseMode(self, val):
        self.slice_box.erase_mode = str(val)

    def eraseVolume(self, pos, mode):
        self.showStatus("Processing...")
        xyz = np.array(pos + (self.actual_slice,))
        p = np.zeros_like(xyz)
        p[np.array(self.act_transposition)] = xyz
        p = tuple(p)

        if self.seeds[p] > 0:
            if mode == 'inside':
                erase_reg(self.seeds, p, val=0)

            elif mode == 'outside':
                erase_reg(self.seeds, p, val=-1)
                idxs = np.where(self.seeds < 0)
                self.seeds.fill(0)
                self.seeds[idxs] = 1

        if self.contours is None:
            contours = None

        else:
            contours = self.contours_aview[...,self.actual_slice]

        self.slice_box.setSlice(self.img_aview[...,self.actual_slice],
                                self.seeds_aview[...,self.actual_slice],
                                contours)

        self.showStatus("Done")

    def cropUpdate(self, img):

        for ii in VIEW_TABLE.iterkeys():
            self.last_view_position[ii] = 0
        self.actual_slice = 0

        self.img = img
        self.img_aview = self.img.transpose(self.act_transposition)

        self.contours = None
        self.contours_aview = None

        self.seeds = np.zeros(self.img.shape, np.int8)
        self.seeds_aview = self.seeds.transpose(self.act_transposition)
        self.seeds_modified = False

        vscale = self.voxel_scale[np.array(self.act_transposition)]
        height = self.slice_box.height()
        grid = height / float(self.img_aview.shape[1] * vscale[1])
        mgrid = (grid * vscale[0], grid * vscale[1])

        self.slice_box.resizeSlice(new_slice_size=self.img_aview.shape[:-1],
                                   new_grid=mgrid)

        self.slice_box.setSlice(self.img_aview[...,self.actual_slice],
                                self.seeds_aview[...,self.actual_slice],
                                None)

        self.allow_select_slice = False
        self.n_slices = self.img_aview.shape[2]
        self.slider.setValue(self.actual_slice + 1)
        self.slider.setRange(1, self.n_slices)
        self.allow_select_slice = True

        self.slider.label.setText('Slice: %d / %d' % (self.actual_slice + 1,
                                                      self.n_slices))
        self.view_label.setText('View size: %d x %d' % self.img_aview.shape[:-1])

    def getCropBounds(self, return_nzs=False, flat=False):

        nzs = self.seeds.nonzero()
        cri = []
        flag = True
        for ii in range(3):
            if nzs[ii].shape[0] == 0:
                flag = False
                break

            smin, smax = np.min(nzs[ii]), np.max(nzs[ii])
            if not(flat):
                if smin == smax:
                    flag = False
                    break

            cri.append((smin, smax))

        if flag:
            cri = np.array(cri)

            out = []
            offset = []
            for jj, ii in enumerate(cri):
                out.append(slice(ii[0], ii[1] + 1))
                offset.append(ii[0])

            if return_nzs:
                return np.array(offset), tuple(out), nzs

            else:
                return np.array(offset), tuple(out)

        else:
            return None

    def crop(self):
        self.showStatus("Processing...")

        crp = self.getCropBounds()

        if crp is not None:
            offset, cri = crp
            crop = self.img[cri]
            self.img = np.ascontiguousarray(crop)
            self.offset += offset


            self.showStatus('Done')

        else:
            self.showStatus('Region not selected!')

        self.cropUpdate(self.img)

    def recalculate(self, event):
        self.saveSliceSeeds()
        if np.abs(np.min(self.seeds) - np.max(self.seeds)) < 2:
            self.showStatus('Inner and outer regions not defined!')
            return

        self.showStatus("Processing...")
        self.mode_fun(self)
        self.selectSlice(self.actual_slice)
        self.updateVolume()
        self.showStatus("Done")

    def deleteSliceSeeds(self, event):
        self.seeds_aview[...,self.actual_slice] = 0
        self.slice_box.setSlice(seeds=self.seeds_aview[...,self.actual_slice])
        self.slice_box.updateSlice()

    def resetSliceDraw(self, event):
        seeds_orig_aview = self.seeds_orig.transpose(self.act_transposition)
        self.seeds_aview[...,self.actual_slice] = seeds_orig_aview[...,self.actual_slice]
        self.slice_box.setSlice(seeds=self.seeds_aview[...,self.actual_slice])
        self.slice_box.updateSlice()

    def quit(self, event):
        self.close()

    def updateVolume(self):
        text = 'Volume:\n  unknown'
        if self.voxel_volume is not None:
            if self.mode == 'draw':
                vd = self.seeds

            else:
                vd = self.contours

            if vd is not None:
                nzs = vd.nonzero()
                nn = nzs[0].shape[0]
                if self.volume_unit == 'ml':
                    text = 'Volume [ml]:\n  %.2f' %\
                        (nn * self.voxel_volume / 1000)
                else:
                    text = 'Volume [mm3]:\n  %.2e' % (nn * self.voxel_volume)

        self.volume_label.setText(text)

    def getROI(self):
        crp = self.getCropBounds()
        if crp is not None:
            _, cri = crp

        else:
            cri = []
            for jj, ii in enumerate(self.img.shape):
                off = self.offset[jj]
                cri.append(slice(off, off + ii))

        return cri
class QTSeedEditor(QDialog):
    """
    DICOM viewer.
    """
    @staticmethod
    def get_line(mode='h'):
        line = QFrame()
        if mode == 'h':
            line.setFrameStyle(QFrame.HLine)
        elif mode == 'v':
            line.setFrameStyle(QFrame.VLine)

        line.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding)

        return line

    def initUI(self, shape, vscale, height=600,
               mode='seed'):
        """
        Initialize UI.

        Parameters
        ----------
        shape : (int, int, int)
            Shape of data matrix.
        vscale : (float, float, float)
            Voxel scaling.
        height : int
            Maximal slice height in pixels.
        mode : str
            Editor mode.
        """

        self.slab = {}

        # picture
        grid = height / float(shape[1] * vscale[1])
        mgrid = (grid * vscale[0], grid * vscale[1])
        self.slice_box = SliceBox(shape[:-1], mgrid, mode)
        self.slice_box.setScrollFun(self.scrollSlices)
        self.connect(self.slice_box, SIGNAL('focus_slider'), self.focusSliceSlider)

        # sliders
        self.allow_select_slice = True
        self.n_slices = shape[2]
        self.slider = QSlider(Qt.Vertical)
        self.slider.valueChanged.connect(self.sliderSelectSlice)
        self.slider.label = QLabel()
        self.slider.setRange(1, self.n_slices)

        self.slider_cw = {}
        self.slider_cw['c'] = QSlider(Qt.Horizontal)
        self.slider_cw['c'].valueChanged.connect(self.changeC)
        self.slider_cw['c'].label = QLabel()

        self.slider_cw['w'] = QSlider(Qt.Horizontal)
        self.slider_cw['w'].valueChanged.connect(self.changeW)
        self.slider_cw['w'].label = QLabel()

        self.view_label = QLabel('View size: %d x %d' % self.img_aview.shape[:-1])
        self.voxel_label = QLabel('Voxel size [mm]:\n  %.2f x %.2f x %.2f'\
                                      % tuple(self.voxel_size[np.array(self.act_transposition)]))

        combo_view_options = VIEW_TABLE.keys()
        combo_view = QComboBox(self)
        combo_view.activated[str].connect(self.setView)
        combo_view.addItems(combo_view_options)

        #radio button group for choosing seed class ------------------------
        self.current_class = 1
        self.slice_box.seed_mark = self.current_class
        number_group = QGroupBox(QString('Class markers'))
        vbox_NG = QVBoxLayout()
        r1 = QRadioButton('class 1')
        r1.setStyleSheet('QRadioButton {color: red}')
        r1.setChecked(True)
        r2 = QRadioButton('class 2')
        r2.setStyleSheet('QRadioButton {color: green}')
        r3 = QRadioButton('class 3')
        r3.setStyleSheet('QRadioButton {color: blue}')
        r4 = QRadioButton('class 4')
        r4.setStyleSheet('QRadioButton {color: cyan}')
        r5 = QRadioButton('class 5')
        r5.setStyleSheet('QRadioButton {color: magenta}')

        vbox_NG.addWidget(r1)
        vbox_NG.addWidget(r2)
        vbox_NG.addWidget(r3)
        vbox_NG.addWidget(r4)
        vbox_NG.addWidget(r5)

        number_group.setLayout(vbox_NG)

        self.button_group = QButtonGroup()
        self.button_group.addButton(r1, 1)
        self.button_group.addButton(r2, 2)
        self.button_group.addButton(r3, 3)
        self.button_group.addButton(r4, 4)
        self.button_group.addButton(r5, 5)
        self.connect(self.button_group, SIGNAL("buttonClicked(int)"), self.change_seed_class)
        #-------------------------------------------------------------------

        # buttons
        # btn_save = QPushButton('Save', self)
        # btn_save.clicked.connect(self.save)

        btn_quit = QPushButton("Quit", self)
        btn_quit.clicked.connect(self.quit)

        # btn_crop = QPushButton('Crop', self)
        # btn_crop.clicked.connect(self.crop)

        combo_dmask = QComboBox(self)
        combo_dmask.activated.connect(self.changeMask)
        self.mask_points_tab, aux = self.init_draw_mask(DRAW_MASK, mgrid)
        for icon, label in aux:
            combo_dmask.addItem(icon, label)

        self.slice_box.setMaskPoints(self.mask_points_tab[combo_dmask.currentIndex()])

        self.status_bar = QStatusBar()

        vopts = []
        vmenu = []
        appmenu = []
        if mode == 'seed':
            btn_recalc = QPushButton("Recalculate", self)
            btn_recalc.clicked.connect(self.recalculate)
            appmenu.append(QLabel('<b>Segmentation mode</b><br><br><br>' +
                                  'Select the region of interest<br>' +
                                  'using the mouse buttons.<br><br>'))
            appmenu.append(btn_recalc)
            appmenu.append(QLabel())
            self.volume_label = QLabel('Volume [mm3]:\n  unknown')
            appmenu.append(self.volume_label)

        if mode == 'seed' or mode == 'crop':
            btn_del = QPushButton("Delete Seeds", self)
            btn_del.clicked.connect(self.deleteSliceSeeds)
            vmenu.append(None)
            vmenu.append(btn_del)

            combo_contour_options = ['fill', 'contours', 'off']
            combo_contour = QComboBox(self)
            combo_contour.activated[str].connect(self.changeContourMode)
            combo_contour.addItems(combo_contour_options)
            self.changeContourMode(combo_contour_options[combo_contour.currentIndex()])
            vopts.append(QLabel('Selection mode:'))
            vopts.append(combo_contour)

        if mode == 'crop':
            btn_crop = QPushButton("Crop", self)
            btn_crop.clicked.connect(self.crop)
            appmenu.append(QLabel('<b>Crop mode</b><br><br><br>' +
                                  'Select the crop region<br>' +
                                  'using the left mouse button<br><br>'))
            appmenu.append(btn_crop)

        # if mode == 'draw':
        #     appmenu.append(QLabel('<b>Manual segmentation<br> mode</b><br><br><br>' +
        #                           'Mark the region of interest<br>' +
        #                           'using the mouse buttons:<br><br>' +
        #                           '&nbsp;&nbsp;<i>left</i> - draw<br>' +
        #                           '&nbsp;&nbsp;<i>right</i> - erase<br>' +
        #                           '&nbsp;&nbsp;<i>middle</i> - vol. erase<br><br>'))
        #
        #     btn_reset = QPushButton("Reset", self)
        #     btn_reset.clicked.connect(self.resetSliceDraw)
        #     vmenu.append(None)
        #     vmenu.append(btn_reset)
        #
        #     combo_erase_options = ['inside', 'outside']
        #     combo_erase = QComboBox(self)
        #     combo_erase.activated[str].connect(self.changeEraseMode)
        #     combo_erase.addItems(combo_erase_options)
        #     self.changeEraseMode(combo_erase_options[combo_erase.currentIndex()])
        #     vopts.append(QLabel('Volume erase mode:'))
        #     vopts.append(combo_erase)

        hbox = QHBoxLayout()
        vbox = QVBoxLayout()
        vbox_left = QVBoxLayout()
        vbox_app = QVBoxLayout()

        hbox.addWidget(self.slice_box)
        hbox.addWidget(self.slider)
        vbox_left.addWidget(self.slider.label)
        vbox_left.addWidget(self.view_label)
        vbox_left.addWidget(self.voxel_label)
        vbox_left.addWidget(QLabel())
        vbox_left.addWidget(QLabel('View plane:'))
        vbox_left.addWidget(combo_view)
        vbox_left.addWidget(self.get_line())
        vbox_left.addWidget(self.slider_cw['c'].label)
        vbox_left.addWidget(self.slider_cw['c'])
        vbox_left.addWidget(self.slider_cw['w'].label)
        vbox_left.addWidget(self.slider_cw['w'])
        vbox_left.addWidget(self.get_line())
        vbox_left.addWidget(QLabel('Drawing mask:'))
        vbox_left.addWidget(combo_dmask)

        for ii in vopts:
            vbox_left.addWidget(ii)

        for ii in vmenu:
            if ii is None:
                vbox_left.addStretch(1)

            else:
                vbox_left.addWidget(ii)

        for ii in appmenu:
            if ii is None:
                vbox_app.addStretch(1)

            else:
                vbox_app.addWidget(ii)

        vbox_left.addWidget(self.get_line())
        vbox_left.addWidget(number_group)

        # vbox_app.addWidget(btn_crop)

        vbox_app.addStretch(1)
        # vbox_app.addWidget(btn_save)
        vbox_app.addWidget(btn_quit)

        hbox.addLayout(vbox_left)
        hbox.addWidget(self.get_line('v'))
        hbox.addLayout(vbox_app)
        vbox.addLayout(hbox)
        vbox.addWidget(self.status_bar)
        self.setLayout(vbox)

        self.setWindowTitle('Segmentation Editor')
        self.show()

    def __init__(self, img, actualSlice=0,
                 seeds=None, contours=None,
                 mode='seed', modeFun=None,
                 voxelSize=[1,1,1]):
        """
        Initiate Editor

        Parameters
        ----------
        img : array
            DICOM data matrix.
        actualSlice : int
            Index of actual slice.
        seeds : array
            Seeds, user defined regions of interest.
        contours : array
            Computed segmentation.
        mode : str
            Editor modes:
               'seed' - seed editor
               'crop' - manual crop
               'draw' - drawing
        modeFun : fun
            Mode function invoked by user button.
        voxelSize : tuple of float
            voxel size [mm]
        """

        QDialog.__init__(self)

        self.mode = mode
        self.mode_fun = modeFun
        # self.datapath = datapath

        self.actual_view = 'axial'
        self.act_transposition = VIEW_TABLE[self.actual_view]
        self.last_view_position = {}
        for ii in VIEW_TABLE.iterkeys():
            self.last_view_position[ii] = img.shape[VIEW_TABLE[ii][-1]] - 1

        self.img = img
        self.img_aview = self.img.transpose(self.act_transposition)
        self.actual_slice = self.img_aview.shape[-1] - actualSlice - 1
        self.last_view_position[self.actual_view] = self.actual_slice

        # set contours
        self.contours = contours
        if self.contours is None:
            self.contours_aview = None

        else:
            self.contours_aview = self.contours.transpose(self.act_transposition)

        self.voxel_size = np.array(voxelSize)
        self.voxel_scale = self.voxel_size / float(np.min(self.voxel_size))
        self.voxel_volume = np.prod(voxelSize)

        # set seeds
        if seeds is None:
            self.seeds = np.zeros(self.img.shape, np.int8)
        else:
            self.seeds = seeds

        self.seeds_aview = self.seeds.transpose(self.act_transposition)
        self.seeds_modified = False

        self.initUI(self.img_aview.shape,
                    self.voxel_scale[np.array(self.act_transposition)],
                    600, mode)

        if mode == 'draw':
            self.seeds_orig = self.seeds.copy()
            self.slice_box.setEraseFun(self.eraseVolume)

        # set view window values C/W
        lb = np.min(img)
        ub = np.max(img)
        dul = ub - lb
        self.cw_range = {'c': [lb, ub], 'w': [1, dul]}
        self.slider_cw['c'].setRange(lb, ub)
        self.slider_cw['w'].setRange(1, dul)
        self.changeC(lb + dul / 2)
        self.changeW(dul)

        self.offset = np.zeros((3,), dtype=np.int16)

    def change_seed_class(self, id):
        self.current_class = id
        self.slice_box.seed_mark = self.current_class
        # print 'Current seed class changed to ', id, '.'

    def showStatus(self, msg):
        self.status_bar.showMessage(QString(msg))
        QApplication.processEvents()

    def init_draw_mask(self, draw_mask, grid):
        mask_points = []
        mask_iconlabel = []
        for mask, label in draw_mask:
            w, h = mask.shape
            xx, yy = mask.nonzero()
            mask_points.append((xx - w/2, yy - h/2))

            img = QImage(w, h, QImage.Format_ARGB32)
            img.fill(qRgba(255, 255, 255, 0))
            for ii in range(xx.shape[0]):
                img.setPixel(xx[ii], yy[ii], qRgba(0, 0, 0, 255))

            img = img.scaled(QSize(w * grid[0], h * grid[1]))
            icon = QIcon(QPixmap.fromImage(img))
            mask_iconlabel.append((icon, label))

        return mask_points, mask_iconlabel

    def saveSliceSeeds(self):
        aux = self.slice_box.getSliceSeeds()
        if aux is not None:
            self.seeds_aview[...,self.actual_slice] = aux
            self.seeds_modified = True

        else:
            self.seeds_modified = False

    def updateCropBounds(self):
        crp = self.getCropBounds()
        if crp is not None:
            _, cri = crp
            self.contours = np.zeros(self.img.shape, np.int8)
            self.contours[cri].fill(1)
            self.contours_aview = self.contours.transpose(self.act_transposition)

    def focusSliceSlider(self):
        self.slider.setFocus(True)

    def sliderSelectSlice(self, value):
        self.selectSlice(self.n_slices - value)

    def scrollSlices(self, inc):
        if abs(inc) > 0:
            new = self.actual_slice + inc
            self.selectSlice(new)

    def selectSlice(self, value, force=False):
        if not(self.allow_select_slice):
            return

        if (value < 0) or (value >= self.n_slices):
            return

        if (value != self.actual_slice) or force:
            self.saveSliceSeeds()
            if self.seeds_modified and (self.mode == 'crop'):
                self.updateCropBounds()

        if self.contours is None:
            contours = None

        else:
            contours = self.contours_aview[...,value]

        slider_val = self.n_slices - value
        self.slider.setValue(slider_val)
        self.slider.label.setText('Slice: %d / %d' % (slider_val, self.n_slices))

        self.slice_box.setSlice(self.img_aview[...,value],
                                self.seeds_aview[...,value],
                                contours)
        self.actual_slice = value

    def getSeeds(self):
        return self.seeds

    def getImg(self):
        return self.img

    def getOffset(self):
        return self.offset * self.voxel_size

    def getSeedsVal(self, label):
        return self.img[self.seeds==label]

    def getContours(self):
        return self.contours

    def setContours(self, contours):
        self.contours = contours
        self.contours_aview = self.contours.transpose(self.act_transposition)

        self.selectSlice(self.actual_slice)

    def changeCW(self, value, key):
        rg = self.cw_range[key]
        if (value < rg[0]) or (value > rg[1]):
            return

        if (value != self.slice_box.getCW()[key]):
            self.slider_cw[key].setValue(value)
            self.slider_cw[key].label.setText('%s: %d' % (key.upper(), value))
            self.slice_box.setCW(value, key)
            self.slice_box.updateSliceCW(self.img_aview[...,self.actual_slice])


    def changeC(self, value):
        self.changeCW(value, 'c')

    def changeW(self, value):
        self.changeCW(value, 'w')

    def setView(self, value):
        self.last_view_position[self.actual_view] = self.actual_slice
        # save seeds
        self.saveSliceSeeds()
        if self.seeds_modified and (self.mode == 'crop'):
            self.updateCropBounds(self.seeds_aview[...,self.actual_slice])

        key = str(value)
        self.actual_view = key
        self.actual_slice = self.last_view_position[key]

        self.act_transposition = VIEW_TABLE[key]
        self.img_aview = self.img.transpose(self.act_transposition)
        self.seeds_aview = self.seeds.transpose(self.act_transposition)

        if self.contours is not None:
            self.contours_aview = self.contours.transpose(self.act_transposition)
            contours = self.contours_aview[...,self.actual_slice]

        else:
            contours = None

        vscale = self.voxel_scale[np.array(self.act_transposition)]
        height = self.slice_box.height()
        grid = height / float(self.img_aview.shape[1] * vscale[1])
        mgrid = (grid * vscale[0], grid * vscale[1])

        self.slice_box.resizeSlice(new_slice_size=self.img_aview.shape[:-1],
                                   new_grid=mgrid)

        self.slice_box.setSlice(self.img_aview[...,self.actual_slice],
                                self.seeds_aview[...,self.actual_slice],
                                contours)

        self.allow_select_slice = False
        self.n_slices = self.img_aview.shape[2]
        slider_val = self.n_slices - self.actual_slice
        self.slider.setValue(slider_val)
        self.slider.setRange(1, self.n_slices)
        self.allow_select_slice = True

        self.slider.label.setText('Slice: %d / %d' % (slider_val,
                                                      self.n_slices))
        self.view_label.setText('View size: %d x %d' % self.img_aview.shape[:-1])

    def changeMask(self, val):
        self.slice_box.setMaskPoints(self.mask_points_tab[val])

    def changeContourMode(self, val):
        self.slice_box.contour_mode = str(val)
        self.slice_box.updateSlice()

    def changeEraseMode(self, val):
        self.slice_box.erase_mode = str(val)

    def eraseVolume(self, pos, mode):
        self.showStatus("Processing...")
        xyz = pos + (self.actual_slice,)
        p = tuple(np.array(xyz)[np.array(self.act_transposition)])
        if self.seeds[p] > 0:
            if mode == 'inside':
                erase_reg(self.seeds, p, val=0)

            elif mode == 'outside':
                erase_reg(self.seeds, p, val=-1)
                idxs = np.where(self.seeds < 0)
                self.seeds.fill(0)
                self.seeds[idxs] = 1

        if self.contours is None:
            contours = None

        else:
            contours = self.contours_aview[...,self.actual_slice]

        self.slice_box.setSlice(self.img_aview[...,self.actual_slice],
                                self.seeds_aview[...,self.actual_slice],
                                contours)

        self.showStatus("Done")

    def cropUpdate(self, img):

        for ii in VIEW_TABLE.iterkeys():
            self.last_view_position[ii] = 0
        self.actual_slice = 0

        self.img = img
        self.img_aview = self.img.transpose(self.act_transposition)

        self.contours = None
        self.contours_aview = None

        self.seeds = np.zeros(self.img.shape, np.int8)
        self.seeds_aview = self.seeds.transpose(self.act_transposition)
        self.seeds_modified = False

        vscale = self.voxel_scale[np.array(self.act_transposition)]
        height = self.slice_box.height()
        grid = height / float(self.img_aview.shape[1] * vscale[1])
        mgrid = (grid * vscale[0], grid * vscale[1])

        self.slice_box.resizeSlice(new_slice_size=self.img_aview.shape[:-1],
                                   new_grid=mgrid)

        self.slice_box.setSlice(self.img_aview[...,self.actual_slice],
                                self.seeds_aview[...,self.actual_slice],
                                None)

        self.allow_select_slice = False
        self.n_slices = self.img_aview.shape[2]
        self.slider.setValue(self.actual_slice + 1)
        self.slider.setRange(1, self.n_slices)
        self.allow_select_slice = True

        self.slider.label.setText('Slice: %d / %d' % (self.actual_slice + 1,
                                                      self.n_slices))
        self.view_label.setText('View size: %d x %d' % self.img_aview.shape[:-1])

    def getCropBounds(self):

        nzs = self.seeds.nonzero()
        cri = []
        flag = True
        for ii in range(3):
            if nzs[ii].shape[0] == 0:
                flag = False
                break

            smin, smax = np.min(nzs[ii]), np.max(nzs[ii])
            if smin == smax:
                flag = False
                break

            cri.append((smin, smax))

        if flag:
            cri = np.array(cri)

            out = []
            offset = []
            for jj, ii in enumerate(cri):
                out.append(slice(ii[0], ii[1] + 1))
                offset.append(ii[0])

            return np.array(offset), tuple(out)

        else:
            return None

    def crop(self):
        self.showStatus("Processing...")

        crp = self.getCropBounds()

        if crp is not None:
            offset, cri = crp
            crop = self.img[cri]
            self.img = np.ascontiguousarray(crop)
            self.offset += offset


            self.showStatus('Done')

        else:
            self.showStatus('Region not selected!')

        self.cropUpdate(self.img)

    def recalculate(self, event):
        self.saveSliceSeeds()
        if np.abs(np.min(self.seeds) - np.max(self.seeds)) < 2:
            self.showStatus('At least two regions must be marked!')
            return

        self.showStatus("Processing...")
        # idx = 3
        # s = random_walker(self.img[idx,:,:], self.seeds[idx,:,:])#, mode='cg_mg')
        # plt.figure()
        # plt.imshow(mark_boundaries(self.img[idx,:,:], s))
        # plt.show()
        # self.segmentation = np.zeros(self.img.shape)
        # self.segmentation[idx,:,:] = s
        self.segmentation = random_walker(self.img, self.seeds, mode='cg_mg')
        self.setContours(self.segmentation - 1)
        self.selectSlice(self.actual_slice)
        # self.updateVolume()
        self.showStatus("Done")

    def deleteSliceSeeds(self, event):
        self.seeds_aview[...,self.actual_slice] = 0
        self.slice_box.setSlice(seeds=self.seeds_aview[...,self.actual_slice])
        self.slice_box.updateSlice()

    def resetSliceDraw(self, event):
        seeds_orig_aview = self.seeds_orig.transpose(self.act_transposition)
        self.seeds_aview[...,self.actual_slice] = seeds_orig_aview[...,self.actual_slice]
        self.slice_box.setSlice(seeds=self.seeds_aview[...,self.actual_slice])
        self.slice_box.updateSlice()

    def quit(self, event):
        self.close()

    # def save(self, event):
    #     odp = os.path.expanduser("~/lisa_data")
    #     if not op.exists(odp):
    #         os.makedirs(odp)
    #
    #     data = self.export()
    #     # data['version'] = self.version
    #     # data['experiment_caption'] = self.experiment_caption
    #     # data['lisa_operator_identifier'] = self.lisa_operator_identifier
    #     pth, filename = op.split(op.normpath(self.datapath))
    #     # filename += "-" + self.experiment_caption
    #     filepath = 'org-' + filename + '.pklz'
    #     filepath = op.join(odp, filepath)
    #     filepath = misc.suggest_filename(filepath)
    #     misc.obj_to_file(data, filepath, filetype='pklz')
    #
    #     filepath = 'organ_last.pklz'
    #     filepath = op.join(odp, filepath)
    #     misc.obj_to_file(data, filepath, filetype='pklz')

    # def export(self):
    #     slab = {}
    #     slab['none'] = 0
    #     slab['liver'] = 1
    #     slab['lesions'] = 6
    #     slab.update(self.slab)
    #
    #     data = {}
    #     data['version'] = (1, 0, 1)
    #     data['data3d'] = self.img
    #     # data['crinfo'] = self.crinfo
    #     data['segmentation'] = self.segmentation
    #     data['slab'] = slab
    #     # data['voxelsize_mm'] = self.voxelsize_mm
    #     # data['orig_shape'] = self.orig_shape
    #     # data['processing_time'] = self.processing_time
    #     return data

    def updateVolume(self):
        text = 'Volume [mm3]:\n  unknown'
        if self.voxel_volume is not None:
            if self.mode == 'draw':
                vd = self.seeds

            else:
                vd = self.contours

            if vd is not None:
                nzs = vd.nonzero()
                nn = nzs[0].shape[0]
                text = 'Volume [mm3]:\n  %.2e' % (nn * self.voxel_volume)

        self.volume_label.setText(text)

    def getROI(self):
        crp = self.getCropBounds()
        if crp is not None:
            _, cri = crp

        else:
            cri = []
            for jj, ii in enumerate(self.img.shape):
                off = self.offset[jj]
                cri.append(slice(off, off + ii))

        return cri
Example #24
0
 def showMessage(self, message, timeout):
     self._widgetStatus.hide()
     self._replaceWidget.setVisible(False)
     self.show()
     QStatusBar.showMessage(self, message, timeout)
Example #25
0
class OWWidget(QDialog, metaclass=WidgetMetaClass):
    # Global widget count
    widget_id = 0

    # Widget description
    name = None
    id = None
    category = None
    version = None
    description = None
    long_description = None
    icon = "icons/Unknown.png"
    priority = sys.maxsize
    author = None
    author_email = None
    maintainer = None
    maintainer_email = None
    help = None
    help_ref = None
    url = None
    keywords = []
    background = None
    replaces = None
    inputs = []
    outputs = []

    # Default widget layout settings
    want_basic_layout = True
    want_main_area = True
    want_control_area = True
    want_graph = False
    show_save_graph = True
    want_status_bar = False
    no_report = False

    save_position = True
    resizing_enabled = True

    widgetStateChanged = Signal(str, int, str)
    blockingStateChanged = Signal(bool)
    progressBarValueChanged = Signal(float)
    processingStateChanged = Signal(int)

    settingsHandler = None
    """:type: SettingsHandler"""

    savedWidgetGeometry = settings.Setting(None)

    def __new__(cls, parent=None, *args, **kwargs):
        self = super().__new__(cls, None, cls.get_flags())
        QDialog.__init__(self, None, self.get_flags())

        stored_settings = kwargs.get('stored_settings', None)
        if self.settingsHandler:
            self.settingsHandler.initialize(self, stored_settings)

        self.signalManager = kwargs.get('signal_manager', None)
        self.__env = _asmappingproxy(kwargs.get("env", {}))

        setattr(self, gui.CONTROLLED_ATTRIBUTES, ControlledAttributesDict(self))
        self.__reportData = None

        OWWidget.widget_id += 1
        self.widget_id = OWWidget.widget_id

        if self.name:
            self.setCaption(self.name)

        self.setFocusPolicy(Qt.StrongFocus)

        self.startTime = time.time()    # used in progressbar

        self.widgetState = {"Info": {}, "Warning": {}, "Error": {}}

        self.__blocking = False
        # flag indicating if the widget's position was already restored
        self.__was_restored = False

        self.__progressBarValue = -1
        self.__progressState = 0
        self.__statusMessage = ""

        if self.want_basic_layout:
            self.insertLayout()

        return self

    def __init__(self, *args, **kwargs):
        """QDialog __init__ was already called in __new__,
        please do not call it here."""

    @classmethod
    def get_flags(cls):
        return (Qt.Window if cls.resizing_enabled
                else Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint)

    # noinspection PyAttributeOutsideInit
    def insertLayout(self):
        def createPixmapWidget(self, parent, iconName):
            w = QLabel(parent)
            parent.layout().addWidget(w)
            w.setFixedSize(16, 16)
            w.hide()
            if os.path.exists(iconName):
                w.setPixmap(QPixmap(iconName))
            return w

        self.setLayout(QVBoxLayout())
        self.layout().setMargin(2)

        self.warning_bar = gui.widgetBox(self, orientation="horizontal",
                                         margin=0, spacing=0)
        self.warning_icon = gui.widgetLabel(self.warning_bar, "")
        self.warning_label = gui.widgetLabel(self.warning_bar, "")
        self.warning_label.setStyleSheet("padding-top: 5px")
        self.warning_bar.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Maximum)
        gui.rubber(self.warning_bar)
        self.warning_bar.setVisible(False)

        self.topWidgetPart = gui.widgetBox(self,
                                           orientation="horizontal", margin=0)
        self.leftWidgetPart = gui.widgetBox(self.topWidgetPart,
                                            orientation="vertical", margin=0)
        if self.want_main_area:
            self.leftWidgetPart.setSizePolicy(
                QSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding))
            self.leftWidgetPart.updateGeometry()
            self.mainArea = gui.widgetBox(self.topWidgetPart,
                                          orientation="vertical",
                                          sizePolicy=QSizePolicy(QSizePolicy.Expanding,
                                                                 QSizePolicy.Expanding),
                                          margin=0)
            self.mainArea.layout().setMargin(4)
            self.mainArea.updateGeometry()

        if self.want_control_area:
            self.controlArea = gui.widgetBox(self.leftWidgetPart,
                                             orientation="vertical", margin=4)

        if self.want_graph and self.show_save_graph:
            graphButtonBackground = gui.widgetBox(self.leftWidgetPart,
                                                  orientation="horizontal", margin=4)
            self.graphButton = gui.button(graphButtonBackground,
                                          self, "&Save Graph")
            self.graphButton.setAutoDefault(0)

        if self.want_status_bar:
            self.widgetStatusArea = QFrame(self)
            self.statusBarIconArea = QFrame(self)
            self.widgetStatusBar = QStatusBar(self)

            self.layout().addWidget(self.widgetStatusArea)

            self.widgetStatusArea.setLayout(QHBoxLayout(self.widgetStatusArea))
            self.widgetStatusArea.layout().addWidget(self.statusBarIconArea)
            self.widgetStatusArea.layout().addWidget(self.widgetStatusBar)
            self.widgetStatusArea.layout().setMargin(0)
            self.widgetStatusArea.setFrameShape(QFrame.StyledPanel)

            self.statusBarIconArea.setLayout(QHBoxLayout())
            self.widgetStatusBar.setSizeGripEnabled(0)

            self.statusBarIconArea.hide()

            self._warningWidget = createPixmapWidget(
                self.statusBarIconArea,
                gui.resource_filename("icons/triangle-orange.png"))
            self._errorWidget = createPixmapWidget(
                self.statusBarIconArea,
                gui.resource_filename("icons/triangle-red.png"))

        if not self.resizing_enabled:
            self.layout().setSizeConstraint(QVBoxLayout.SetFixedSize)

    def updateStatusBarState(self):
        if not hasattr(self, "widgetStatusArea"):
            return
        if self.widgetState["Warning"] or self.widgetState["Error"]:
            self.widgetStatusArea.show()
        else:
            self.widgetStatusArea.hide()

    def setStatusBarText(self, text, timeout=5000):
        if hasattr(self, "widgetStatusBar"):
            self.widgetStatusBar.showMessage(" " + text, timeout)

    # TODO add!
    def prepareDataReport(self, data):
        pass

    def __restoreWidgetGeometry(self):
        restored = False
        if self.save_position:
            geometry = self.savedWidgetGeometry
            if geometry is not None:
                restored = self.restoreGeometry(QByteArray(geometry))

            if restored and not self.windowState() & \
                    (Qt.WindowMaximized | Qt.WindowFullScreen):
                space = qApp.desktop().availableGeometry(self)
                frame, geometry = self.frameGeometry(), self.geometry()

                #Fix the widget size to fit inside the available space
                width = space.width() - (frame.width() - geometry.width())
                width = min(width, geometry.width())
                height = space.height() - (frame.height() - geometry.height())
                height = min(height, geometry.height())
                self.resize(width, height)

                # Move the widget to the center of available space if it is
                # currently outside it
                if not space.contains(self.frameGeometry()):
                    x = max(0, space.width() / 2 - width / 2)
                    y = max(0, space.height() / 2 - height / 2)

                    self.move(x, y)
        return restored

    def __updateSavedGeometry(self):
        if self.__was_restored and self.isVisible():
            # Update the saved geometry only between explicit show/hide
            # events (i.e. changes initiated by the user not by Qt's default
            # window management).
            self.savedWidgetGeometry = self.saveGeometry()

    # when widget is resized, save the new width and height
    def resizeEvent(self, ev):
        QDialog.resizeEvent(self, ev)
        # Don't store geometry if the widget is not visible
        # (the widget receives a resizeEvent (with the default sizeHint)
        # before first showEvent and we must not overwrite the the
        # savedGeometry with it)
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()

    def moveEvent(self, ev):
        QDialog.moveEvent(self, ev)
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()

    # set widget state to hidden
    def hideEvent(self, ev):
        if self.save_position:
            self.__updateSavedGeometry()
        QDialog.hideEvent(self, ev)

    def closeEvent(self, ev):
        if self.save_position and self.isVisible():
            self.__updateSavedGeometry()
        QDialog.closeEvent(self, ev)

    def showEvent(self, ev):
        QDialog.showEvent(self, ev)
        if self.save_position and not self.__was_restored:
            # Restore saved geometry on show
            self.__restoreWidgetGeometry()
            self.__was_restored = True

    def wheelEvent(self, event):
        """ Silently accept the wheel event. This is to ensure combo boxes
        and other controls that have focus don't receive this event unless
        the cursor is over them.
        """
        event.accept()

    def setCaption(self, caption):
        # we have to save caption title in case progressbar will change it
        self.captionTitle = str(caption)
        self.setWindowTitle(caption)

    # put this widget on top of all windows
    def reshow(self):
        self.show()
        self.raise_()
        self.activateWindow()

    def send(self, signalName, value, id=None):
        if not any(s.name == signalName for s in self.outputs):
            raise ValueError('{} is not a valid output signal for widget {}'.format(
                signalName, self.name))
        if self.signalManager is not None:
            self.signalManager.send(self, signalName, value, id)

    def __setattr__(self, name, value):
        """Set value to members of this instance or any of its members.

        If member is used in a gui control, notify the control about the change.

        name: name of the member, dot is used for nesting ("graph.point.size").
        value: value to set to the member.

        """

        names = name.rsplit(".")
        field_name = names.pop()
        obj = reduce(lambda o, n: getattr(o, n, None), names, self)
        if obj is None:
            raise AttributeError("Cannot set '{}' to {} ".format(name, value))

        if obj is self:
            super().__setattr__(field_name, value)
        else:
            setattr(obj, field_name, value)

        notify_changed(obj, field_name, value)

        if self.settingsHandler:
            self.settingsHandler.fast_save(self, name, value)

    def openContext(self, *a):
        self.settingsHandler.open_context(self, *a)

    def closeContext(self):
        self.settingsHandler.close_context(self)

    def retrieveSpecificSettings(self):
        pass

    def storeSpecificSettings(self):
        pass

    def saveSettings(self):
        self.settingsHandler.update_defaults(self)

    def onDeleteWidget(self):
        """
        Invoked by the canvas to notify the widget it has been deleted
        from the workflow.

        If possible, subclasses should gracefully cancel any currently
        executing tasks.
        """
        pass

    def handleNewSignals(self):
        """
        Invoked by the workflow signal propagation manager after all
        signals handlers have been called.

        Reimplement this method in order to coalesce updates from
        multiple updated inputs.
        """
        pass

    # ############################################
    # PROGRESS BAR FUNCTIONS

    def progressBarInit(self, processEvents=QEventLoop.AllEvents):
        """
        Initialize the widget's progress (i.e show and set progress to 0%).

        .. note::
            This method will by default call `QApplication.processEvents`
            with `processEvents`. To suppress this behavior pass
            ``processEvents=None``.

        :param processEvents: Process events flag
        :type processEvents: `QEventLoop.ProcessEventsFlags` or `None`
        """
        self.startTime = time.time()
        self.setWindowTitle(self.captionTitle + " (0% complete)")

        if self.__progressState != 1:
            self.__progressState = 1
            self.processingStateChanged.emit(1)

        self.progressBarSet(0, processEvents)

    def progressBarSet(self, value, processEvents=QEventLoop.AllEvents):
        """
        Set the current progress bar to `value`.

        .. note::
            This method will by default call `QApplication.processEvents`
            with `processEvents`. To suppress this behavior pass
            ``processEvents=None``.

        :param float value: Progress value
        :param processEvents: Process events flag
        :type processEvents: `QEventLoop.ProcessEventsFlags` or `None`
        """
        old = self.__progressBarValue
        self.__progressBarValue = value

        if value > 0:
            if self.__progressState != 1:
                warnings.warn("progressBarSet() called without a "
                              "preceding progressBarInit()",
                              stacklevel=2)
                self.__progressState = 1
                self.processingStateChanged.emit(1)

            usedTime = max(1, time.time() - self.startTime)
            totalTime = (100.0 * usedTime) / float(value)
            remainingTime = max(0, totalTime - usedTime)
            h = int(remainingTime / 3600)
            min = int((remainingTime - h * 3600) / 60)
            sec = int(remainingTime - h * 3600 - min * 60)
            if h > 0:
                text = "%(h)d:%(min)02d:%(sec)02d" % vars()
            else:
                text = "%(min)d:%(sec)02d" % vars()
            self.setWindowTitle(self.captionTitle +
                                " (%(value).2f%% complete, remaining time: %(text)s)" % vars())
        else:
            self.setWindowTitle(self.captionTitle + " (0% complete)")

        if old != value:
            self.progressBarValueChanged.emit(value)

        if processEvents is not None and processEvents is not False:
            qApp.processEvents(processEvents)

    def progressBarValue(self):
        return self.__progressBarValue

    progressBarValue = pyqtProperty(float, fset=progressBarSet,
                                    fget=progressBarValue)

    processingState = pyqtProperty(int, fget=lambda self: self.__progressState)

    def progressBarAdvance(self, value, processEvents=QEventLoop.AllEvents):
        self.progressBarSet(self.progressBarValue + value, processEvents)

    def progressBarFinished(self, processEvents=QEventLoop.AllEvents):
        """
        Stop the widget's progress (i.e hide the progress bar).

        .. note::
            This method will by default call `QApplication.processEvents`
            with `processEvents`. To suppress this behavior pass
            ``processEvents=None``.

        :param processEvents: Process events flag
        :type processEvents: `QEventLoop.ProcessEventsFlags` or `None`
        """
        self.setWindowTitle(self.captionTitle)
        if self.__progressState != 0:
            self.__progressState = 0
            self.processingStateChanged.emit(0)

        if processEvents is not None and processEvents is not False:
            qApp.processEvents(processEvents)

    #: Widget's status message has changed.
    statusMessageChanged = Signal(str)

    def setStatusMessage(self, text):
        if self.__statusMessage != text:
            self.__statusMessage = text
            self.statusMessageChanged.emit(text)

    def statusMessage(self):
        return self.__statusMessage

    def keyPressEvent(self, e):
        if (int(e.modifiers()), e.key()) in OWWidget.defaultKeyActions:
            OWWidget.defaultKeyActions[int(e.modifiers()), e.key()](self)
        else:
            QDialog.keyPressEvent(self, e)

    def information(self, id=0, text=None):
        self.setState("Info", id, text)

    def warning(self, id=0, text=""):
        self.setState("Warning", id, text)

    def error(self, id=0, text=""):
        self.setState("Error", id, text)

    def setState(self, state_type, id, text):
        changed = 0
        if type(id) == list:
            for val in id:
                if val in self.widgetState[state_type]:
                    self.widgetState[state_type].pop(val)
                    changed = 1
        else:
            if type(id) == str:
                text = id
                id = 0
            if not text:
                if id in self.widgetState[state_type]:
                    self.widgetState[state_type].pop(id)
                    changed = 1
            else:
                self.widgetState[state_type][id] = text
                changed = 1

        if changed:
            if type(id) == list:
                for i in id:
                    self.widgetStateChanged.emit(state_type, i, "")
            else:
                self.widgetStateChanged.emit(state_type, id, text or "")

        tooltip_lines = []
        highest_type = None
        for a_type in ("Error", "Warning", "Info"):
            msgs_for_ids = self.widgetState.get(a_type)
            if not msgs_for_ids:
                continue
            msgs_for_ids = list(msgs_for_ids.values())
            if not msgs_for_ids:
                continue
            tooltip_lines += msgs_for_ids
            if highest_type is None:
                highest_type = a_type

        if highest_type is None:
            self.set_warning_bar(None)
        elif len(tooltip_lines) == 1:
            msg = tooltip_lines[0]
            if "\n" in msg:
                self.set_warning_bar(
                    highest_type, msg[:msg.index("\n")] + " (...)", msg)
            else:
                self.set_warning_bar(
                    highest_type, tooltip_lines[0], tooltip_lines[0])
        else:
            self.set_warning_bar(
                highest_type,
                "{} problems during execution".format(len(tooltip_lines)),
                "\n".join(tooltip_lines))

        return changed

    def set_warning_bar(self, state_type, text=None, tooltip=None):
        colors = {"Error": ("#ffc6c6", "black", QStyle.SP_MessageBoxCritical),
                  "Warning": ("#ffffc9", "black", QStyle.SP_MessageBoxWarning),
                  "Info": ("#ceceff", "black", QStyle.SP_MessageBoxInformation)}
        current_height = self.height()
        if state_type is None:
            if not self.warning_bar.isHidden():
                new_height = current_height - self.warning_bar.height()
                self.warning_bar.setVisible(False)
                self.resize(self.width(), new_height)
            return
        background, foreground, icon = colors[state_type]
        style = QApplication.instance().style()
        self.warning_icon.setPixmap(style.standardIcon(icon).pixmap(14, 14))

        self.warning_bar.setStyleSheet(
            "background-color: {}; color: {};"
            "padding: 3px; padding-left: 6px; vertical-align: center".
            format(background, foreground))
        self.warning_label.setText(text)
        self.warning_bar.setToolTip(tooltip)
        if self.warning_bar.isHidden():
            self.warning_bar.setVisible(True)
            new_height = current_height + self.warning_bar.height()
            self.resize(self.width(), new_height)

    def widgetStateToHtml(self, info=True, warning=True, error=True):
        iconpaths = {
            "Info": gui.resource_filename("icons/information.png"),
            "Warning": gui.resource_filename("icons/warning.png"),
            "Error": gui.resource_filename("icons/error.png")
        }
        items = []

        for show, what in [(info, "Info"), (warning, "Warning"),
                           (error, "Error")]:
            if show and self.widgetState[what]:
                items.append('<img src="%s" style="float: left;"> %s' %
                             (iconpaths[what],
                              "\n".join(self.widgetState[what].values())))
        return "<br>".join(items)

    @classmethod
    def getWidgetStateIcons(cls):
        if not hasattr(cls, "_cached__widget_state_icons"):
            info = QPixmap(gui.resource_filename("icons/information.png"))
            warning = QPixmap(gui.resource_filename("icons/warning.png"))
            error = QPixmap(gui.resource_filename("icons/error.png"))
            cls._cached__widget_state_icons = \
                {"Info": info, "Warning": warning, "Error": error}
        return cls._cached__widget_state_icons

    defaultKeyActions = {}

    if sys.platform == "darwin":
        defaultKeyActions = {
            (Qt.ControlModifier, Qt.Key_M):
                lambda self: self.showMaximized
                if self.isMinimized() else self.showMinimized(),
            (Qt.ControlModifier, Qt.Key_W):
                lambda self: self.setVisible(not self.isVisible())}

    def setBlocking(self, state=True):
        """
        Set blocking flag for this widget.

        While this flag is set this widget and all its descendants
        will not receive any new signals from the workflow signal manager.

        This is useful for instance if the widget does it's work in a
        separate thread or schedules processing from the event queue.
        In this case it can set the blocking flag in it's processNewSignals
        method schedule the task and return immediately. After the task
        has completed the widget can clear the flag and send the updated
        outputs.

        .. note::
            Failure to clear this flag will block dependent nodes forever.
        """
        if self.__blocking != state:
            self.__blocking = state
            self.blockingStateChanged.emit(state)

    def isBlocking(self):
        """
        Is this widget blocking signal processing.
        """
        return self.__blocking

    def resetSettings(self):
        self.settingsHandler.reset_settings(self)

    def workflowEnv(self):
        """
        Return (a view to) the workflow runtime environment.

        Returns
        -------
        env : types.MappingProxyType
        """
        return self.__env

    def workflowEnvChanged(self, key, value, oldvalue):
        """
        A workflow environment variable `key` has changed to value.

        Called by the canvas framework to notify widget of a change
        in the workflow runtime environment.

        The default implementation does nothing.
        """
        pass
Example #26
0
class OWWidget(QDialog, metaclass=WidgetMetaClass):
    # Global widget count
    widget_id = 0

    # Widget description
    name = None
    id = None
    category = None
    version = None
    description = None
    long_description = None
    icon = "icons/Unknown.png"
    priority = sys.maxsize
    author = None
    author_email = None
    maintainer = None
    maintainer_email = None
    help = None
    help_ref = None
    url = None
    keywords = []
    background = None
    replaces = None
    inputs = []
    outputs = []

    # Default widget layout settings
    want_basic_layout = True
    want_main_area = True
    want_control_area = True
    want_graph = False
    show_save_graph = True
    want_status_bar = False
    no_report = False

    save_position = False
    resizing_enabled = True

    widgetStateChanged = Signal(str, int, str)
    blockingStateChanged = Signal(bool)
    asyncCallsStateChange = Signal()
    progressBarValueChanged = Signal(float)
    processingStateChanged = Signal(int)

    settingsHandler = None
    """:type: SettingsHandler"""

    def __new__(cls, parent=None, *args, **kwargs):
        self = super().__new__(cls, None, cls.get_flags())
        QDialog.__init__(self, None, self.get_flags())

        # 'current_context' MUST be the first thing assigned to a widget
        self.current_context = settings.Context()
        if self.settingsHandler:
            stored_settings = kwargs.get("stored_settings", None)
            self.settingsHandler.initialize(self, stored_settings)

        # number of control signals that are currently being processed
        # needed by signalWrapper to know when everything was sent
        self.needProcessing = 0  # used by signalManager
        self.signalManager = kwargs.get("signal_manager", None)

        setattr(self, gui.CONTROLLED_ATTRIBUTES, ControlledAttributesDict(self))
        self._guiElements = []  # used for automatic widget debugging
        self.__reportData = None

        # TODO: position used to be saved like this. Reimplement.
        # if save_position:
        #    self.settingsList = getattr(self, "settingsList", []) + \
        #                        ["widgetShown", "savedWidgetGeometry"]

        OWWidget.widget_id += 1
        self.widget_id = OWWidget.widget_id

        # TODO: kill me
        self.__dict__.update(environ.directories)

        if self.name:
            self.setCaption(self.name.replace("&", ""))
        self.setFocusPolicy(Qt.StrongFocus)

        self.wrappers = []  # stored wrappers for widget events
        self.linksIn = {}  # signalName : (dirty, widFrom, handler, signalData)
        self.linksOut = {}  # signalName: (signalData, id)
        self.connections = {}  # keys are (control, signal) and values are
        # wrapper instances. Used in connect/disconnect
        self.callbackDeposit = []
        self.startTime = time.time()  # used in progressbar

        self.widgetState = {"Info": {}, "Warning": {}, "Error": {}}

        self.__blocking = False

        if self.want_basic_layout:
            self.insertLayout()

        return self

    def __init__(self, *args, **kwargs):
        """QDialog __init__ was already called in __new__,
        please do not call it here."""

    @classmethod
    def get_flags(cls):
        return Qt.Window if cls.resizing_enabled else Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint

    def insertLayout(self):
        def createPixmapWidget(self, parent, iconName):
            w = QLabel(parent)
            parent.layout().addWidget(w)
            w.setFixedSize(16, 16)
            w.hide()
            if os.path.exists(iconName):
                w.setPixmap(QPixmap(iconName))
            return w

        self.setLayout(QVBoxLayout())
        self.layout().setMargin(2)

        self.topWidgetPart = gui.widgetBox(self, orientation="horizontal", margin=0)
        self.leftWidgetPart = gui.widgetBox(self.topWidgetPart, orientation="vertical", margin=0)
        if self.want_main_area:
            self.leftWidgetPart.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding))
            self.leftWidgetPart.updateGeometry()
            self.mainArea = gui.widgetBox(
                self.topWidgetPart,
                orientation="vertical",
                sizePolicy=QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding),
                margin=0,
            )
            self.mainArea.layout().setMargin(4)
            self.mainArea.updateGeometry()

        if self.want_control_area:
            self.controlArea = gui.widgetBox(self.leftWidgetPart, orientation="vertical", margin=4)

        if self.want_graph and self.show_save_graph:
            graphButtonBackground = gui.widgetBox(self.leftWidgetPart, orientation="horizontal", margin=4)
            self.graphButton = gui.button(graphButtonBackground, self, "&Save Graph")
            self.graphButton.setAutoDefault(0)

        if self.want_status_bar:
            self.widgetStatusArea = QFrame(self)
            self.statusBarIconArea = QFrame(self)
            self.widgetStatusBar = QStatusBar(self)

            self.layout().addWidget(self.widgetStatusArea)

            self.widgetStatusArea.setLayout(QHBoxLayout(self.widgetStatusArea))
            self.widgetStatusArea.layout().addWidget(self.statusBarIconArea)
            self.widgetStatusArea.layout().addWidget(self.widgetStatusBar)
            self.widgetStatusArea.layout().setMargin(0)
            self.widgetStatusArea.setFrameShape(QFrame.StyledPanel)

            self.statusBarIconArea.setLayout(QHBoxLayout())
            self.widgetStatusBar.setSizeGripEnabled(0)

            self.statusBarIconArea.hide()

            self._warningWidget = createPixmapWidget(
                self.statusBarIconArea, os.path.join(self.widgetDir, "icons/triangle-orange.png")
            )
            self._errorWidget = createPixmapWidget(
                self.statusBarIconArea, os.path.join(self.widgetDir + "icons/triangle-red.png")
            )

    # status bar handler functions
    def setState(self, stateType, id, text):
        stateChanged = super().setState(stateType, id, text)
        if not stateChanged or not hasattr(self, "widgetStatusArea"):
            return

        iconsShown = 0
        warnings = [("Warning", self._warningWidget, self._owWarning), ("Error", self._errorWidget, self._owError)]
        for state, widget, use in warnings:
            if not widget:
                continue
            if use and self.widgetState[state]:
                widget.setToolTip("\n".join(self.widgetState[state].values()))
                widget.show()
                iconsShown = 1
            else:
                widget.setToolTip("")
                widget.hide()

        if iconsShown:
            self.statusBarIconArea.show()
        else:
            self.statusBarIconArea.hide()

        if (stateType == "Warning" and self._owWarning) or (stateType == "Error" and self._owError):
            if text:
                self.setStatusBarText(stateType + ": " + text)
            else:
                self.setStatusBarText("")
        self.updateStatusBarState()

    def updateWidgetStateInfo(self, stateType, id, text):
        html = self.widgetStateToHtml(self._owInfo, self._owWarning, self._owError)
        if html:
            self.widgetStateInfoBox.show()
            self.widgetStateInfo.setText(html)
            self.widgetStateInfo.setToolTip(html)
        else:
            if not self.widgetStateInfoBox.isVisible():
                dHeight = -self.widgetStateInfoBox.height()
            else:
                dHeight = 0
            self.widgetStateInfoBox.hide()
            self.widgetStateInfo.setText("")
            self.widgetStateInfo.setToolTip("")
            width, height = self.width(), self.height() + dHeight
            self.resize(width, height)

    def updateStatusBarState(self):
        if not hasattr(self, "widgetStatusArea"):
            return
        if self.widgetState["Warning"] or self.widgetState["Error"]:
            self.widgetStatusArea.show()
        else:
            self.widgetStatusArea.hide()

    def setStatusBarText(self, text, timeout=5000):
        if hasattr(self, "widgetStatusBar"):
            self.widgetStatusBar.showMessage(" " + text, timeout)

    # TODO add!
    def prepareDataReport(self, data):
        pass

    def getIconNames(self, iconName):
        # if canvas sent us a prepared list of valid names, just return those
        if type(iconName) == list:
            return iconName

        names = []
        name, ext = os.path.splitext(iconName)
        for num in [16, 32, 42, 60]:
            names.append("%s_%d%s" % (name, num, ext))
        fullPaths = []
        module_dir = os.path.dirname(sys.modules[self.__module__].__file__)
        for paths in [(self.widgetDir, name), (self.widgetDir, "icons", name), (module_dir, "icons", name)]:
            for name in names + [iconName]:
                fname = os.path.join(*paths)
                if os.path.exists(fname):
                    fullPaths.append(fname)
            if fullPaths != []:
                break

        if len(fullPaths) > 1 and fullPaths[-1].endswith(iconName):
            # if we have the new icons we can remove the default icon
            fullPaths.pop()
        return fullPaths

    def setWidgetIcon(self, iconName):
        iconNames = self.getIconNames(iconName)
        icon = QIcon()
        for name in iconNames:
            pix = QPixmap(name)
            icon.addPixmap(pix)

        self.setWindowIcon(icon)

    # ##############################################
    def isDataWithClass(self, data, wantedVarType=None, checkMissing=False):
        self.error([1234, 1235, 1236])
        if not data:
            return 0
        if not data.domain.classVar:
            self.error(1234, "A data set with a class attribute is required.")
            return 0
        if wantedVarType and data.domain.classVar.varType != wantedVarType:
            self.error(1235, "Unable to handle %s class." % str(data.domain.class_var.var_type).lower())
            return 0
        if checkMissing and not orange.Preprocessor_dropMissingClasses(data):
            self.error(1236, "Unable to handle data set with no known classes")
            return 0
        return 1

    # call processEvents(), but first remember position and size of widget in
    # case one of the events would be move or resize
    # call this function if needed in __init__ of the widget
    def safeProcessEvents(self):
        keys = ["widgetShown"]
        vals = [(key, getattr(self, key, None)) for key in keys]
        qApp.processEvents()
        for (key, val) in vals:
            if val != None:
                setattr(self, key, val)

    # this function is called at the end of the widget's __init__ when the
    # widgets is saving its position and size parameters
    def restoreWidgetPosition(self):
        if self.save_position:
            geometry = getattr(self, "savedWidgetGeometry", None)
            restored = False
            if geometry is not None:
                restored = self.restoreGeometry(QByteArray(geometry))

            if restored:
                space = qApp.desktop().availableGeometry(self)
                frame, geometry = self.frameGeometry(), self.geometry()

                # Fix the widget size to fit inside the available space
                width = space.width() - (frame.width() - geometry.width())
                width = min(width, geometry.width())
                height = space.height() - (frame.height() - geometry.height())
                height = min(height, geometry.height())
                self.resize(width, height)

                # Move the widget to the center of available space if it is
                # currently outside it
                if not space.contains(self.frameGeometry()):
                    x = max(0, space.width() / 2 - width / 2)
                    y = max(0, space.height() / 2 - height / 2)

                    self.move(x, y)

    # this is called in canvas when loading a schema. it opens the widgets
    # that were shown when saving the schema
    def restoreWidgetStatus(self):
        if self.save_position and getattr(self, "widgetShown", None):
            self.show()

    # when widget is resized, save new width and height into widgetWidth and
    # widgetHeight. some widgets can put this two variables into settings and
    # last widget shape is restored after restart
    def resizeEvent(self, ev):
        QDialog.resizeEvent(self, ev)
        # Don't store geometry if the widget is not visible
        # (the widget receives the resizeEvent before showEvent and we must not
        # overwrite the the savedGeometry before then)
        if self.save_position and self.isVisible():
            self.savedWidgetGeometry = str(self.saveGeometry())

    # set widget state to hidden
    def hideEvent(self, ev):
        if self.save_position:
            self.widgetShown = 0
            self.savedWidgetGeometry = str(self.saveGeometry())
        QDialog.hideEvent(self, ev)

    # set widget state to shown
    def showEvent(self, ev):
        QDialog.showEvent(self, ev)
        if self.save_position:
            self.widgetShown = 1
        self.restoreWidgetPosition()

    def closeEvent(self, ev):
        if self.save_position:
            self.savedWidgetGeometry = str(self.saveGeometry())
        QDialog.closeEvent(self, ev)

    def wheelEvent(self, event):
        """ Silently accept the wheel event. This is to ensure combo boxes
        and other controls that have focus don't receive this event unless
        the cursor is over them.
        """
        event.accept()

    def setCaption(self, caption):
        if self.parent != None and isinstance(self.parent, QTabWidget):
            self.parent.setTabText(self.parent.indexOf(self), caption)
        else:
            # we have to save caption title in case progressbar will change it
            self.captionTitle = str(caption)
            self.setWindowTitle(caption)

    # put this widget on top of all windows
    def reshow(self):
        self.show()
        self.raise_()
        self.activateWindow()

    def send(self, signalName, value, id=None):
        if self.signalManager is not None:
            self.signalManager.send(self, signalName, value, id)

    def __setattr__(self, name, value):
        """Set value to members of this instance or any of its members.

        If member is used in a gui control, notify the control about the change.

        name: name of the member, dot is used for nesting ("graph.point.size").
        value: value to set to the member.

        """

        names = name.rsplit(".")
        field_name = names.pop()
        obj = reduce(lambda o, n: getattr(o, n, None), names, self)
        if obj is None:
            raise AttributeError("Cannot set '{}' to {} ".format(name, value))

        if obj is self:
            super().__setattr__(field_name, value)
        else:
            setattr(obj, field_name, value)

        notify_changed(obj, field_name, value)

        if self.settingsHandler:
            self.settingsHandler.fast_save(self, name, value)

    def openContext(self, *a):
        self.settingsHandler.open_context(self, *a)

    def closeContext(self):
        if self.current_context is not None:
            self.settingsHandler.close_context(self)
        self.current_context = None

    def retrieveSpecificSettings(self):
        pass

    def storeSpecificSettings(self):
        pass

    def saveSettings(self):
        self.settingsHandler.update_defaults(self)

    # this function is only intended for derived classes to send appropriate
    # signals when all settings are loaded
    def activateLoadedSettings(self):
        pass

    # reimplemented in other widgets
    def onDeleteWidget(self):
        pass

    def setOptions(self):
        pass

    def handleNewSignals(self):
        # this is called after all new signals have been handled
        # implement this in your widget if you want to process something only
        # after you received multiple signals
        pass

    # ############################################
    # PROGRESS BAR FUNCTIONS

    def progressBarInit(self):
        self.progressBarValue = 0
        self.startTime = time.time()
        self.setWindowTitle(self.captionTitle + " (0% complete)")
        self.processingStateChanged.emit(1)

    def progressBarSet(self, value):
        if value > 0:
            self.__progressBarValue = value
            usedTime = max(1, time.time() - self.startTime)
            totalTime = (100.0 * usedTime) / float(value)
            remainingTime = max(0, totalTime - usedTime)
            h = int(remainingTime / 3600)
            min = int((remainingTime - h * 3600) / 60)
            sec = int(remainingTime - h * 3600 - min * 60)
            if h > 0:
                text = "%(h)d:%(min)02d:%(sec)02d" % vars()
            else:
                text = "%(min)d:%(sec)02d" % vars()
            self.setWindowTitle(self.captionTitle + " (%(value).2f%% complete, remaining time: %(text)s)" % vars())
        else:
            self.setWindowTitle(self.captionTitle + " (0% complete)")

        self.progressBarValueChanged.emit(value)

        qApp.processEvents()

    def progressBarValue(self):
        return self.__progressBarValue

    progressBarValue = pyqtProperty(float, fset=progressBarSet, fget=progressBarValue)

    def progressBarAdvance(self, value):
        self.progressBarSet(self.progressBarValue + value)

    def progressBarFinished(self):
        self.setWindowTitle(self.captionTitle)
        self.processingStateChanged.emit(0)

    def openWidgetHelp(self):
        if "widgetInfo" in self.__dict__:  # This widget is on a canvas.
            qApp.canvasDlg.helpWindow.showHelpFor(self.widgetInfo, True)

    def keyPressEvent(self, e):
        if e.key() in (Qt.Key_Help, Qt.Key_F1):
            self.openWidgetHelp()
        #    e.ignore()
        elif (int(e.modifiers()), e.key()) in OWWidget.defaultKeyActions:
            OWWidget.defaultKeyActions[int(e.modifiers()), e.key()](self)
        else:
            QDialog.keyPressEvent(self, e)

    def information(self, id=0, text=None):
        self.setState("Info", id, text)
        # self.setState("Warning", id, text)

    def warning(self, id=0, text=""):
        self.setState("Warning", id, text)

    def error(self, id=0, text=""):
        self.setState("Error", id, text)

    def setState(self, stateType, id, text):
        changed = 0
        if type(id) == list:
            for val in id:
                if val in self.widgetState[stateType]:
                    self.widgetState[stateType].pop(val)
                    changed = 1
        else:
            if type(id) == str:
                text = id
                id = 0
            if not text:
                if id in self.widgetState[stateType]:
                    self.widgetState[stateType].pop(id)
                    changed = 1
            else:
                self.widgetState[stateType][id] = text
                changed = 1

        if changed:
            if type(id) == list:
                for i in id:
                    self.widgetStateChanged.emit(stateType, i, "")
            else:
                self.widgetStateChanged.emit(stateType, id, text or "")
        return changed

    def widgetStateToHtml(self, info=True, warning=True, error=True):
        pixmaps = self.getWidgetStateIcons()
        items = []
        iconPath = {
            "Info": "canvasIcons:information.png",
            "Warning": "canvasIcons:warning.png",
            "Error": "canvasIcons:error.png",
        }
        for show, what in [(info, "Info"), (warning, "Warning"), (error, "Error")]:
            if show and self.widgetState[what]:
                items.append(
                    '<img src="%s" style="float: left;"> %s'
                    % (iconPath[what], "\n".join(self.widgetState[what].values()))
                )
        return "<br>".join(items)

    @classmethod
    def getWidgetStateIcons(cls):
        if not hasattr(cls, "_cached__widget_state_icons"):
            iconsDir = os.path.join(environ.canvas_install_dir, "icons")
            QDir.addSearchPath("canvasIcons", os.path.join(environ.canvas_install_dir, "icons/"))
            info = QPixmap("canvasIcons:information.png")
            warning = QPixmap("canvasIcons:warning.png")
            error = QPixmap("canvasIcons:error.png")
            cls._cached__widget_state_icons = {"Info": info, "Warning": warning, "Error": error}
        return cls._cached__widget_state_icons

    defaultKeyActions = {}

    if sys.platform == "darwin":
        defaultKeyActions = {
            (Qt.ControlModifier, Qt.Key_M): lambda self: self.showMaximized
            if self.isMinimized()
            else self.showMinimized(),
            (Qt.ControlModifier, Qt.Key_W): lambda self: self.setVisible(not self.isVisible()),
        }

    def setBlocking(self, state=True):
        """ Set blocking flag for this widget. While this flag is set this
        widget and all its descendants will not receive any new signals from
        the signal manager
        """
        if self.__blocking != state:
            self.__blocking = state
            self.blockingStateChanged.emit(state)

    def isBlocking(self):
        """ Is this widget blocking signal processing.
        """
        return self.__blocking

    def resetSettings(self):
        self.settingsHandler.reset_settings(self)
Example #27
0
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.__central_widget = CentralWidget(None)

        self.__board = self.__central_widget.board
        self.__nodes_view = self.__central_widget.nodes_widget

        self.__status_bar = QStatusBar()

        self.setCentralWidget(self.__central_widget)
        self.setStatusBar(self.__status_bar)
        self.setMinimumSize(self.__central_widget.size())

        self.__board.on_click.connect(self.on_piece_click)
        self.__nodes_view.node_click.connect(self.on_point_click)

        self.__central_widget.button_bar.open_click.connect(self.on_open_click)
        self.__central_widget.button_bar.save_click.connect(self.on_save_click)
        self.__central_widget.button_bar.pass_click.connect(self.on_pass_click)
        self.__central_widget.button_bar.resign_click.connect(
            self.on_resign_click)
        self.__central_widget.button_bar.count_click.connect(
            self.on_count_click)

        self.__status_bar.showMessage("Juego cargado.")

    def add_piece(self, letter, y, text, color):
        self.__board.add_piece(letter, y, text, color)

    def add_square(self, letter, y, color):
        self.__board.add_square(letter, y, color)

    def remove_piece(self, letter, y):
        self.__board.remove_piece(letter, y)

    def add_point(self, x, y, text, color):
        self.__nodes_view.add_point(x, y, text, color)

    def remove_point(self, x, y):
        self.__nodes_view.remove_point(x, y)

    def add_line(self, cord_1, cord_2, color):
        self.__nodes_view.add_line(cord_1, cord_2, color)

    def remove_line(self, cord_1, cord_2):
        self.__nodes_view.remove_line(cord_1, cord_2)

    def set_result(self, points):
        self.__central_widget.button_bar.set_result(points)

    def on_piece_click(self, letra, y):
        pass

    def on_point_click(self, x, y):
        pass

    def on_open_click(self, path):
        pass

    def on_save_click(self, path):
        pass

    def on_pass_click(self):
        pass

    def on_count_click(self):
        pass

    def on_resign_click(self):
        pass

    def show_message(self, message):
        self.__status_bar.showMessage(message)
Example #28
0
class Visor(QtGui.QDockWidget, FORM_CLASS):
    """
    UI manager for the visor.

    This is the View component of the MVC pattern. Will manage
    inputs from the user using a combobox to select which
    dataset to use, provide a tree view of the elements
    included in those datasets, and provide the basic configuration
    options to download maps through WMS and WCS services using a
    controller and show them in QGIS.
    """

    groupAssignmentLock = RLock()

    def __init__(self, showEmptyDatasetNodes=False, parent=None):
        """Constructor."""

        super(Visor, self).__init__(parent)
        self.setupUi(self)

        self.controller = VisorController.VisorController()
        self.controller.threddsServerMapObjectRetrieved.connect(
            self.onNewDatasetsAvailable)
        self.controller.threddsDataSetUpdated.connect(self.onDataSetUpdated)
        self.controller.mapImageRetrieved.connect(self.showNewImage)
        self.controller.standardMessage.connect(
            self.postInformationMessageToUser)
        self.controller.errorMessage.connect(self.postCriticalErrorToUser)
        self.controller.mapInfoRetrieved.connect(
            self._onMapInfoReceivedFromController)
        self.controller.batchDownloadFinished.connect(self.createLayerGroup)

        self.showEmptyDatasetNodes = showEmptyDatasetNodes  # TODO: self-explanatory...
        self.combo_dataset_list.currentIndexChanged.connect(
            self._onDataSetItemChanged)
        self.tree_widget.itemClicked.connect(self._onMapTreeWidgetItemClicked)
        self.tree_widget.itemExpanded.connect(
            self._onMapTreeWidgetItemExpanded)

        self.tabWidget.currentChanged.connect(self.runWhenTabChange)

        self.connect(self.combo_wcs_coverage,
                     SIGNAL("currentIndexChanged(const QString&)"),
                     self._onCoverageSelectorItemChanged)
        self.connect(self.combo_wms_layer,
                     SIGNAL("currentIndexChanged(const QString&)"),
                     self._onWMSLayerSelectorItemChanged)
        self.connect(self.combo_wms_style_type,
                     SIGNAL("currentIndexChanged(const QString&)"),
                     self._onWMSStyleTypeSelectorItemChanged)
        self.connect(self.combo_wms_time, SIGNAL("currentIndexChanged(int)"),
                     self._onWMSFirstTimeChanged)
        self.connect(self.combo_wcs_time, SIGNAL("currentIndexChanged(int)"),
                     self._onWCSFirstTimeChanged)

        self.button_req_map.clicked.connect(self._onbuttonReqMapClicked)
        #self.actionToggleAlwaysOnTop.toggled.connect(self._onAlwaysOnTopPrefsChanged)
        self.buttonManageServers.clicked.connect(
            self._onManageServersRequested)
        self.button_req_animation.clicked.connect(self.toggleAnimationMenu)

        # We add a status bar to this QDockWidget:
        self.statusbar = QStatusBar()
        self.gridLayout.addWidget(self.statusbar)

        self.datasetInUse = None
        self.uiAnimation = None
        self.currentMap = None
        self.wcsAvailableTimes = []
        self.wmsAvailableTimes = []

        self.firstRunThisSession = True

    def show(self):
        if iface and not iface.mainWindow().restoreDockWidget(self):
            iface.mainWindow().addDockWidget(Qt.LeftDockWidgetArea, self)

        super(Visor, self).show()

        if self.firstRunThisSession:
            self.firstRunChecks()
            self.firstRunThisSession = False

        # If no dataset is selected yet, we will assume the first
        # thing the user will want to do is actually doing something
        # related to the servers (pick one from a list, or add a new
        # one) as there is little else that can be done at this point.
        # So we present them the screen to do so...
        if self.datasetInUse is None:
            self._onManageServersRequested()

    def firstRunChecks(self):
        """Convenience method to add any checks which should be performed
        when the user opens the plug-in for first time (be advised this
        is not the same as the first time the plug-in object is created,
        which is on QGIS load)."""
        pass

    def runWhenTabChange(self):
        """Convenience method to add any actions to be performed at tab change."""

        # Show warning for GDAL version, if needed.
        self.checkGdalWindowWarning()

    def checkGdalWindowWarning(self):
        """Method to show GDAL version warning if need be."""

        # If OS is linux:
        if sys.platform.startswith('linux'):
            # Show warning only if WCS tab selected:
            if self.tabWidget.currentIndex() == self.tabWidget.indexOf(
                    self.tab_WCS):
                self.createGDALWindowWarning()

    def createGDALWindowWarning(self):
        """Check GDAL version. Versions < 2.0 had a bug regarding driver selection
        for network resource retrieval (https://trac.osgeo.org/gdal/ticket/2696)."""

        persistenceManager = ServerDataPersistenceManager.ServerStorageManager(
        )

        if persistenceManager.show_GDAL_error:
            try:
                from osgeo import gdal
                if int(gdal.VersionInfo()) < 2000000:
                    # Show warning window, and allow for "don't show again":
                    message = "Your GDAL libraries version is outdated. Versions\n"
                    message += "under 2.0 are not guaranteed to work when\n"
                    message += "attempting to load WCS Layers.\nPlease update GDAL."

                    reply = QtGui.QMessageBox.question(
                        self, 'GDAL: Unsupported version found', (message),
                        "Close", "Don't show again")

                    # If requested to, record setting not to show warning again:
                    if reply == 1:
                        persistenceManager.show_GDAL_error = False

            except ImportError:
                # Show warning window, and allow for "don't show again":
                message = "Your GDAL libraries version could not be read"
                message += "Versions under 2.0 are not guaranteed to work when\n"
                message += "attempting to load WCS Layers. If you have any issues,\n"
                message += "please update GDAL."

                reply = QtGui.QMessageBox.question(
                    self, 'GDAL: Unsupported version found', (message),
                    "Close", "Don't show again")

                # If requested to, record setting not to show warning again:
                if reply == 1:
                    persistenceManager.show_GDAL_error = False

    def toggleAnimationMenu(self):
        """Shows (or hides) the animation menu elements,
        and instantiate a controller.

        It seems I can not directly hide elements,
        but I can make another Widget in QDesigner and
        create/add it to a layout here so... oh well..."""

        if self.uiAnimation is None:
            self.uiAnimation = AnimationFrame(parent=self)
            self.uiAnimation.errorSignal.connect(self.postCriticalErrorToUser)

            self.controller.mapInfoRetrieved.connect(
                self.uiAnimation.setAnimationInformation)
            if None is not self.currentMap:
                self.uiAnimation.setAnimationInformation(self.currentMap)

            self.uiAnimation.show()
            self.button_req_animation.setText("Hide animation menu <<")
        else:
            self.uiAnimation.hide()
            self.uiAnimation = None
            self.button_req_animation.setText("Show animation menu >>")

    def clearData(self):
        self.WMSBoundingBoxInfo.setText(
            "No Bounding Box or CRS information available.")
        self.WMS_northBound.setText("East: No info")
        self.WMS_southBound.setText("West: No info")
        self.WMS_eastBound.setText("North: No info")
        self.WMS_westBound.setText("South: No info")
        self.combo_wms_layer.clear()
        self.combo_wms_style_type.clear()
        self.combo_wms_style_palette.clear()
        self.combo_wms_time.clear()
        self.combo_wms_time_last.clear()
        self.combo_wcs_coverage.clear()
        self.combo_wcs_time.clear()
        self.combo_wcs_time_last.clear()
        self.WCSBoundingBoxInfo.setText(
            "No Bounding Box or CRS information available.")
        self.WCS_northBound.setText("East: No info")
        self.WCS_southBound.setText("West: No info")
        self.WCS_eastBound.setText("North: No info")
        self.WCS_westBound.setText("South: No info")

    # TODO: Unused (for now)
    @pyqtSlot(bool)
    def _onAlwaysOnTopPrefsChanged(self, newSettingBool):
        """Will change the alwaysontop window modifier to suit the user selection."""

        self.setWindowFlags(self.windowFlags() ^ Qt.WindowStaysOnTopHint)
        QtGui.QMainWindow.show(self)

    @pyqtSlot(list, str)
    def onNewDatasetsAvailable(self, inDataSets, serverName):
        """
        A callback for when the dataSet displayed
        needs to be updated.

        :param inDataSets:  list of DataSet objects which will be
                            available to the user.
        :type inDataSets: list of threddsFetcherRecursos.DataSet objects.

        :param serverName:  An user-friendly representation of this server name.
        :type serverName: str
        """
        StringList = []
        for dataSet in inDataSets:
            StringList.append(dataSet.getName())

        self.setWindowTitle("THREDDS Explorer - Connected: " + serverName)
        self.combo_dataset_list.clear()
        self.combo_dataset_list.addItems(StringList)
        self.combo_dataset_list.setCurrentIndex(0)
        self.postInformationMessageToUser("Dataset list updated: " +
                                          str(len(StringList)) + " elements.")
        self.clearData()

    @pyqtSlot(str)
    def postInformationMessageToUser(self, message):
        """
        Will post information messages to the user through
        the status bar.
        :param message: String to use as message to
                            the user.
        :type message: str
        """

        self.statusbar.showMessage(message)

    @pyqtSlot(str)
    def postCriticalErrorToUser(self, errorString):
        """
        To be used with non-recoverable error situations. Shows
        a message box with the error message.

        :param errorString: String to use as message to
                            the user.
        :type  errorString: str
        """

        box = QMessageBox()
        box.setText(errorString)
        box.setIcon(QMessageBox.Critical)
        box.exec_()

    @pyqtSlot(str)
    def _onDataSetItemChanged(self, stringItem):
        """Will receive notifications about this window dataSet
        chosen combobox when the item selected changes."""

        self.tree_widget.clear()
        self.datasetInUse = self.controller.getSingleDataset(
            self.combo_dataset_list.currentText())
        if self.datasetInUse is None:
            return  #If no dataset is available to be shown, we will create no tree.

        rootItem = self.tree_widget.invisibleRootItem()
        newItem = QtGui.QTreeWidgetItem(rootItem,
                                        [self.datasetInUse.getName()])
        rootItem.addChild(self._createHierarchy(self.datasetInUse, newItem))

    def _createHierarchy(self, dataSet, treeItemParent):
        """Recursively creates a hierarchy of elements to populate
        a treeWidgetItem from a given dataSet.

        :param dataSet: DataSet object to create an hierarchy from.
        :type dataset: threddsFetcherRecursos.DataSet

        :param treeItemParent: Item which will be this
                                branch parent.
        :type treeItemParent: QTreeWidgetItem"""

        i = 0
        itemsAlreadyAddedToElement = []
        while i < treeItemParent.childCount():
            child = treeItemParent.child(i)
            if child.text(0) == "Loading..." or child.text(
                    0) == "No subsets found":
                treeItemParent.removeChild(child)
            else:
                itemsAlreadyAddedToElement.append(child)
            i = i + 1
        elementsAlreadyInTreeItemParent = [
            x.text(0) for x in itemsAlreadyAddedToElement
        ]
        if dataSet != None:
            for mapElement in dataSet.getAvailableMapList():
                if mapElement.getName() in elementsAlreadyInTreeItemParent:
                    continue
                else:
                    newItem = QtGui.QTreeWidgetItem(treeItemParent,
                                                    [mapElement.getName()])
                    treeItemParent.addChild(newItem)

            subSets = dataSet.getSubSets()
            if len(subSets) == 0:
                #We add a dummy element so the element open icon is created..
                newItem = QtGui.QTreeWidgetItem(treeItemParent)
                newItem.setText(0, "No subsets found")
                treeItemParent.addChild(newItem)
            else:
                for dataset in subSets:
                    #If an item with the same name as this dataset is found as a subchild
                    #of the parent item, we will use it to build our tree. Otherwise, we
                    #create a new one and append it.
                    itemList = ([
                        x for x in itemsAlreadyAddedToElement
                        if x.text(0) == dataset.getName()
                    ])
                    if itemList is None or len(itemList) == 0:
                        item = QtGui.QTreeWidgetItem(treeItemParent,
                                                     [dataset.getName()])
                        treeItemParent.addChild(
                            self._createHierarchy(dataset, item))
                    else:
                        item = itemList[0]
                        self._createHierarchy(dataset, item)
        else:
            self.postCriticalErrorToUser(
                "WARNING: Attempted to add a null dataset to view.")

    def _onMapTreeWidgetItemClicked(self, mQTreeWidgetItem, column):
        """
        Will receive notifications about the MapTreeWidget
        elements being clicked, so we can update the first
        combobox of WMS/WCS tabs with the layer list.
        """

        self.clearData()
        self.postInformationMessageToUser("")
        if None is mQTreeWidgetItem or None is mQTreeWidgetItem.parent():
            return

        self.controller.getMapObject(str(mQTreeWidgetItem.text(0)),
                                     str(mQTreeWidgetItem.parent().text(0)),
                                     self.datasetInUse)

    @pyqtSlot(object)
    def _onMapInfoReceivedFromController(self, mapInfoObject):
        #print("_onMapInfoReceivedFromController 1"+str(mapInfoObject))
        self.currentMap = mapInfoObject
        #print("_onMapInfoReceivedFromController 2"+str(self.currentMap))
        if self.currentMap is not None:
            #WCS Data update
            self.currentCoverages = self.controller.getWCSCoverages(
                self.currentMap)
            if self.currentCoverages is not None:
                for c in self.currentCoverages:
                    self.combo_wcs_coverage.addItem(c.getName())
            else:
                self.combo_wcs_coverage.addItem("No data available.")
            #WMS Data update
            self.currentWMSMapInfo = self.controller.getWMSMapInfo(
                self.currentMap)
            if self.currentWMSMapInfo is not None:
                for l in self.currentWMSMapInfo.getLayers():
                    self.combo_wms_layer.addItem(l.getName())
            else:
                self.combo_wms_layer.addItem("No data available.")

    def _onMapTreeWidgetItemExpanded(self, mQTreeWidgetItem):
        """
        Once a set is expanded in the tree view we will attempt to
        recover it's data and present it to the user.
        """
        setToUpdate = self.datasetInUse.searchSubsetsByName(str(
            mQTreeWidgetItem.text(0)),
                                                            exactMatch=True)
        if setToUpdate is not None and len(setToUpdate) > 0:
            self.controller.mapDataSet(setToUpdate[0], depth=1)

    def onDataSetUpdated(self, dataSetObject):
        """
        Will update the QTreeWidget to include the updated
        dataset object and it's new data.
        """
        if dataSetObject.getParent() is not None:
            parent = self.tree_widget.findItems(dataSetObject.getName(),
                                                Qt.MatchRecursive)
        self._createHierarchy(dataSetObject, parent[0])

    @pyqtSlot(str)
    def _onCoverageSelectorItemChanged(self, QStringItem):
        """
        Will triger when the user selects a coverage name in
        the combobox (or that list is updated) so the available
        times to request to server are updated in the other
        combobox for the WCS service.
        """

        self.combo_wcs_time.clear()
        if self.currentCoverages is not None:
            coverageElement = [
                x for x in self.currentCoverages
                if x.getName() == str(QStringItem)
            ]
            if None is not coverageElement or len(coverageElement) > 0:
                try:
                    self.wcsAvailableTimes = coverageElement[0].getTiempos()
                    self.combo_wcs_time.addItems(self.wcsAvailableTimes)
                    BBinfo = coverageElement[0].getBoundingBoxInfo()
                    self.WCSBoundingBoxInfo.setText(
                        "CRS = " + BBinfo.getCRS() +
                        "\n\n Bounding Box information (decimal degrees):")
                    self.WCS_eastBound.setText(BBinfo.getEast())
                    self.WCS_westBound.setText(BBinfo.getWest())
                    self.WCS_northBound.setText(BBinfo.getNorth())
                    self.WCS_southBound.setText(BBinfo.getSouth())
                except IndexError:
                    pass

    @pyqtSlot(str)
    def _onWMSLayerSelectorItemChanged(self, QStringItem):
        self.combo_wms_style_type.clear()
        self.combo_wms_style_palette.clear()
        self.combo_wms_time.clear()

        # Only one should be returned here.
        if self.currentWMSMapInfo is not None:
            layerSelectedObject = [
                x for x in self.currentWMSMapInfo.getLayers()
                if x.getName() == str(QStringItem)
            ]

            if layerSelectedObject is not None and len(
                    layerSelectedObject) == 1:
                self.wmsAvailableTimes = layerSelectedObject[0].getTimes()
                self.combo_wms_time.addItems(self.wmsAvailableTimes)
                self.wmsAvailableStyles = layerSelectedObject[0].getStyles()
                self.combo_wms_style_type.addItems(
                    list({(x.getName().split(r"/"))[0]
                          for x in self.wmsAvailableStyles}))

                BBinfo = layerSelectedObject[0].getBoundingBoxInfo()
                self.WMSBoundingBoxInfo.setText(
                    "CRS = " + BBinfo.getCRS() +
                    "\n\n Bounding Box information (decimal degrees):")
                self.WMS_eastBound.setText(BBinfo.getEast())
                self.WMS_westBound.setText(BBinfo.getWest())
                self.WMS_northBound.setText(BBinfo.getNorth())
                self.WMS_southBound.setText(BBinfo.getSouth())

    @pyqtSlot(str)
    def _onWMSStyleTypeSelectorItemChanged(self, qstringitem):
        self.combo_wms_style_palette.clear()
        self.combo_wms_style_palette.addItems(
            list({(x.getName().split(r"/"))[1]
                  for x in self.wmsAvailableStyles
                  if str(qstringitem) in x.getName()}))

    @pyqtSlot(int)
    def _onWCSFirstTimeChanged(self, position):
        #print("self.wcsAvailableTimes"+str((sorted(self.wcsAvailableTimes))))
        #print("WCS INDEX: "+str(position))
        self.combo_wcs_time_last.clear()
        #print self.wcsAvailableTimes[position:]
        self.combo_wcs_time_last.addItems(
            (sorted(self.wcsAvailableTimes))[position:])

    @pyqtSlot(int)
    def _onWMSFirstTimeChanged(self, position):
        #print("self.wmsAvailableTimes"+str((sorted(self.wmsAvailableTimes))))
        #print("WMS INDEX: "+str(position))
        self.combo_wms_time_last.clear()
        #print self.wmsAvailableTimes[position:]
        self.combo_wms_time_last.addItems(self.wmsAvailableTimes[position:])

    def _onbuttonReqMapClicked(self):
        """
        Action to be performed when the user clicks the
        button to request a new map to be displayed,
        after selecting proper values in the rest of fields.
        """
        self.postInformationMessageToUser("")  # reset error display.
        if self.tabWidget.currentIndex() == self.tabWidget.indexOf(
                self.tab_WCS):
            try:
                selectedBeginTimeIndex = self.wcsAvailableTimes.index(
                    self.combo_wcs_time.currentText())
                selectedFinishTimeIndex = self.wcsAvailableTimes.index(
                    self.combo_wcs_time_last.currentText()) + 1

                # We retrieve some information about the current selected map, useful
                # to grab the actual CRS used by the map service. Should be changed if
                # CRS is to be user-selectable later via dropdown menu or anything like
                # that.
                if self.currentCoverages is not None:
                    coverageElement = [
                        x for x in self.currentCoverages if x.getName() == str(
                            self.combo_wcs_coverage.currentText())
                    ]
                if None is not coverageElement or len(coverageElement) > 0:
                    try:
                        try:
                            north = float(self.WCS_northBound.text())
                            south = float(self.WCS_southBound.text())
                            east = float(self.WCS_eastBound.text())
                            west = float(self.WCS_westBound.text())
                        except ValueError:
                            self.postCriticalErrorToUser(
                                "Bounding box values were not valid." +
                                "\nCheck only decimal numbers are used\n(example: 12.44)"
                            )
                            return
                        # We retrieve the bounding box CRS information from the
                        # requested coverage, and get the actual box values
                        # from the UI.
                        BBinfo = coverageElement[0].getBoundingBoxInfo()
                        boundingBoxToDownload = BoundingBox()
                        boundingBoxToDownload.setCRS(BBinfo.getCRS())
                        boundingBoxToDownload.setEast(east)
                        boundingBoxToDownload.setWest(west)
                        boundingBoxToDownload.setNorth(north)
                        boundingBoxToDownload.setSouth(south)
                        self.controller.asyncFetchWCSImageFile(
                            self.currentMap,
                            self.combo_wcs_coverage.currentText(),
                            self.wcsAvailableTimes[selectedBeginTimeIndex:
                                                   selectedFinishTimeIndex],
                            boundingBox=boundingBoxToDownload)
                    except IndexError:
                        pass
            except Exception as exc:
                self.postInformationMessageToUser(
                    "There was an error retrieving the WCS data.")
                QgsMessageLog.logMessage(traceback.format_exc(),
                                         "THREDDS Explorer",
                                         QgsMessageLog.CRITICAL)
        elif self.tabWidget.currentIndex() == self.tabWidget.indexOf(
                self.tab_WMS):
            try:
                selectedBeginTimeIndex = self.wmsAvailableTimes.index(
                    self.combo_wms_time.currentText())
                selectedFinishTimeIndex = self.wmsAvailableTimes.index(
                    self.combo_wms_time_last.currentText()) + 1
                style = self.combo_wms_style_type.currentText(
                ) + r"/" + self.combo_wms_style_palette.currentText()

                #We retrieve some information about the current selected map, useful
                #to grab the actual CRS used by the map service. Should be changed if
                #CRS is to be user-selectable later via dropdown menu or anything like
                #that.
                #Only one should be returned here.
                if self.currentWMSMapInfo is not None:
                    layerSelectedObject = [
                        x for x in self.currentWMSMapInfo.getLayers() if
                        x.getName() == str(self.combo_wms_layer.currentText())
                    ]

                #We retrieve the bounding box CRS information from the
                #requested coverage, and get the actual box values
                #from the UI.
                if None is not layerSelectedObject or len(
                        layerSelectedObject) > 0:
                    try:
                        north = float(self.WMS_northBound.text())
                        south = float(self.WMS_southBound.text())
                        east = float(self.WMS_eastBound.text())
                        west = float(self.WMS_westBound.text())
                    except ValueError:
                        self.postCriticalErrorToUser(
                            "Bounding box values were not valid." +
                            "\nCheck only decimal numbers are used\n(example: 12.44)"
                        )
                        return

                    BBinfo = layerSelectedObject[0].getBoundingBoxInfo()
                    boundingBoxToDownload = BoundingBox()
                    boundingBoxToDownload.setCRS(BBinfo.getCRS())
                    boundingBoxToDownload.setEast(east)
                    boundingBoxToDownload.setWest(west)
                    boundingBoxToDownload.setNorth(north)
                    boundingBoxToDownload.setSouth(south)
                    self.controller.asyncFetchWMSImageFile(
                        self.currentMap,
                        self.combo_wms_layer.currentText(),
                        style,
                        self.wmsAvailableTimes[
                            selectedBeginTimeIndex:selectedFinishTimeIndex],
                        boundingBox=boundingBoxToDownload)
            except Exception as exc:
                print(exc)
                self.postInformationMessageToUser(
                    "There was an error retrieving the WMS data.")
                QgsMessageLog.logMessage(traceback.format_exc(),
                                         "THREDDS Explorer",
                                         QgsMessageLog.CRITICAL)

    @pyqtSlot(list, str)
    def createLayerGroup(self, layerList, groupName):
        if layerList:
            groupifier = LayerGroupifier(layerList, groupName)
            groupifier.setSingleLayerSelectionModeInGroup(False)
            groupifier.statusSignal.connect(self.postInformationMessageToUser,
                                            Qt.DirectConnection)
            groupifier.groupifyComplete.connect(self._onNewLayerGroupGenerated)
            groupifier.groupify()
        else:
            self.postInformationMessageToUser(
                "There was a problem showing the time series.")

    @pyqtSlot(QgsLayerTreeGroup, list)
    def _onNewLayerGroupGenerated(self, groupObject, layerList):
        """
        Currently only used to show the first image of a newly created group
        so the user knows when the operation finishes.

        :param groupObject: The legend group object which was created.
        :type  groupObject: QgsLayerTreeGrupo

        :param layerList: The layers which are held in the group object.
        :type  layerList: [QgsLayer]
        """
        if (layerList[0]).isValid():
            iface.legendInterface().setLayerVisible(layerList[0], True)
        else:
            self.postInformationMessageToUser(
                "There was a problem showing a layer.")

    @pyqtSlot(tuple)
    def showNewImage(self, image):
        """
        Will order this UI to post a new image to the user
        through the qgis window.

        :param image: a tuple consisting of (imageOrLayerObject, Name, Service)
        :type image: (QgsRasterLayer, String, String)

        """

        self.postInformationMessageToUser("Layer '" + image[1] + "' [" +
                                          image[2] + "]retrieved")
        layer = image[0]
        if layer and layer.isValid():
            QgsMapLayerRegistry.instance().addMapLayer(layer)
            iface.zoomToActiveLayer()
            iface.legendInterface().refreshLayerSymbology(layer)
        else:
            self.postInformationMessageToUser(
                "There was a problem loading the layer.")

    @pyqtSlot()
    def _onManageServersRequested(self):
        """Delegates the action of showing the server manager window to the controller."""

        self.controller.showServerManager()
Example #29
0
class StatusBarManager ( HasFacets ):
    """ A status bar manager realizes itself in a status bar control. """

    #-- Facet Definitions ------------------------------------------------------

    # FIXME v3: Is this used anywhere?
    # The manager's unique identifier (if it has one).
    id = Str

    # The message displayed in the first field of the status bar.
    message = Property

    # The messages to be displayed in the status bar fields.
    messages = List( Unicode )

    # The toolkit-specific control that represents the status bar.
    status_bar = Any

    #-- StatusBarManager Interface ---------------------------------------------

    def create_status_bar ( self, parent ):
        """ Creates a status bar. """

        if self.status_bar is None:
            self.status_bar = QStatusBar( parent )
            self.status_bar.setSizeGripEnabled( False )

            if len( self.messages ) > 1:
                self._show_messages()
            else:
                self.status_bar.showMessage( self.message )

        return self.status_bar

    #-- Property Implementations -----------------------------------------------

    def _get_message ( self ):

        if len( self.messages ) > 0:
            message = self.messages[ 0 ]
        else:
            message = ''

        return message

    def _set_message ( self, value ):

        if len( self.messages ) > 0:
            old = self.messages[ 0 ]
            self.messages[ 0 ] = value
        else:
            old = ''
            self.messages.append( old )

        self.facet_property_set( 'message', old, value )

    #-- Facet Event Handlers ---------------------------------------------------

    def _messages_set ( self ):
        """ Sets the text displayed on the status bar. """

        if self.status_bar is not None:
            self._show_messages()


    def _messages_items_set ( self ):
        """ Sets the text displayed on the status bar. """

        if self.status_bar is not None:
            self._show_messages()

    #-- Private Methods --------------------------------------------------------

    def _show_messages ( self ):
        """ Display the list of messages. """

        # FIXME v3: At the moment we just string them together but we may
        # decide to put all but the first message into separate widgets.  We
        # probably also need to extend the API to allow a "message" to be a
        # widget - depends on what wx is capable of.
        self.status_bar.showMessage( "  ".join( self.messages ) )

#-- EOF ------------------------------------------------------------------------
Example #30
0
 def showMessage(self, message, timeout):
     self._widgetStatus.hide()
     self._replaceWidget.setVisible(False)
     self.show()
     QStatusBar.showMessage(self, message, timeout)
Example #31
0
class ImportDataDlg(QDialog):
    __isJy = False  # 数据校验成功的标志
    __mlist = []  # 定义一个列表用于保存从excel表中取出的数据

    def __init__(self, iface, parent=None, impType=ImpDateType.SITEANDCELL):
        super(ImportDataDlg, self).__init__()
        self.iface = iface
        self.parent = parent
        self.impType = impType
        self.initView()

    def initView(self):
        if self.impType == ImpDateType.SERVINGCELL:
            self.setWindowTitle(u'相邻小区数据导入')
        else:
            self.setWindowTitle(u'基站和小区数据导入')
        self.setWindowIcon(QIcon('images/logo.png'))
        self.resize(620, 480)

        # 数据表格
        self.tableWidget = QTableWidget(self)
        self.tableWidget.setAlternatingRowColors(True)
        self.tableWidget.setRowCount(7)
        # 设置当前Table不能编辑
        self.tableWidget.setEditTriggers(QAbstractItemView.NoEditTriggers)
        # 初始化表格上下文菜单
        self.initTableContextMenu()
        # 初始化表头
        self.initTableHeader()
        # 导入出错列表
        self.listWidget = QListWidget(self)
        # 按钮组
        impBtn = QPushButton(u"导入EXCEL表", self)
        yzBtn = QPushButton(u"数据检验", self)
        impdateBtn = QPushButton(u"导入数据", self)
        btnVBox = QVBoxLayout()
        btnVBox.addWidget(impBtn)
        btnVBox.addWidget(yzBtn)
        btnVBox.addWidget(impdateBtn)
        # 错误列表与按钮组
        hBox = QHBoxLayout()
        hBox.setMargin(20)
        hBox.addWidget(self.listWidget)
        hBox.addLayout(btnVBox)

        self.mbar = QStatusBar(self)
        self.mbar.showMessage(u'准备就绪...')

        self.maction = QToolBar(self)
        self.editAction = QAction(u'编辑', self.maction)
        self.editAction.setCheckable(True)

        self.combox = QComboBox(self)
        self.combox.addItems(HeadsConfig.ImpExcelName)

        self.maction.addWidget(self.combox)
        self.maction.addAction(self.editAction)

        vBox = QVBoxLayout()
        vBox.addWidget(self.maction)
        vBox.addWidget(self.tableWidget)
        vBox.addLayout(hBox)
        vBox.addWidget(self.mbar)

        vBox.setStretchFactor(self.tableWidget, 9)
        vBox.setStretchFactor(hBox, 5)
        vBox.setStretchFactor(self.mbar, 1)

        self.setLayout(vBox)

        QObject.connect(impBtn, SIGNAL('clicked()'), self.impClick)
        QObject.connect(yzBtn, SIGNAL('clicked()'), self.yzClick)
        QObject.connect(impdateBtn, SIGNAL('clicked()'), self.impdateClick)
        QObject.connect(self.editAction, SIGNAL('triggered()'), self.editClick)
        QObject.connect(self.combox, SIGNAL('currentIndexChanged(int)'),
                        self.comboxChange)

        self.listWidget.doubleClicked.connect(self.mlistClicked)
        # self.connect(self.listWidget, SIGNAL("itemDoubleClicked (QListWidgetItem)"), self.mlistClicked)

    def initTableContextMenu(self):
        self.tableWidget.setContextMenuPolicy(Qt.CustomContextMenu)
        self.popMenu = QMenu(self.tableWidget)
        delAction = QAction(u'删除', self)  # 删除
        self.popMenu.addAction(delAction)

    # 设置表格可以双击修改数据
    def setEditTriggers(self, isTrigger):
        if isTrigger:
            self.tableWidget.setEditTriggers(QAbstractItemView.DoubleClicked)
        else:
            self.tableWidget.setEditTriggers(QAbstractItemView.NoEditTriggers)

    # 提供给外部修改状态栏消息
    def setStatusBarMsg(self, msg):
        self.mbar.showMessage(msg)

    # 选框改变时,清空当前表格的全部内容,包括表格头
    def updateType(self, mtype):
        self.impType = mtype
        self.tableWidget.clear()  # 清空表格所有内容
        self.initTableHeader()

    # 初始化表格的每个Item
    def initTable(self, mlist):
        self.tableWidget.setRowCount(len(mlist))
        for (i, v) in enumerate(mlist):
            for (j, item) in enumerate(v):
                if type(item) != str:
                    item = unicode(item)
                if item == None:
                    item = u""
                tabItem = QTableWidgetItem(item)
                tabItem.setTextAlignment(Qt.AlignCenter)
                self.tableWidget.setItem(i, j, tabItem)

    # 初始化错误信息列表
    def initListView(self, mlist):

        for iv in mlist:
            lisItm = QListWidgetItem(self.listWidget)
            lisItm.setTextColor(Qt.red)
            lisItm.setData(Qt.UserRole, iv)
            if isinstance(iv, basestring):
                # 如果错误信息是一行字符串
                lisItm.setText(iv)
            else:
                lisItm.setText(u'第' + unicode(str(iv['row'] + 1)) + u'行,第' +
                               unicode(str(iv['col'] + 1)) + u'列:' + iv['msg'])

    # 初始化Table的头
    def initTableHeader(self):
        self.heads = []
        if self.impType == ImpDateType.SITEANDCELL:
            # 获取当前项目图层的字段名
            cell_layer = getLayerByName(u"小区", self.iface)
            for head in HeadsConfig.SiteANDCellHead:
                self.heads.append(head)
            if len(cell_layer.pendingFields()) > 55:
                for (index, field) in enumerate(cell_layer.pendingFields()):
                    if index > 54:
                        field_name = field.name().strip()
                        self.heads.append(field_name)

        else:
            self.heads = HeadsConfig.ServingCellHead

        self.tableWidget.setColumnCount(len(self.heads))  # 设置表格的列数
        for (i, h) in enumerate(self.heads):
            tabItem = QTableWidgetItem(h)
            self.tableWidget.setHorizontalHeaderItem(i, tabItem)

    # 自定义为Table添加Item
    def addTableItem(self, row, col, content):
        tabItem = QTableWidgetItem(content)
        self.tableWidget.setItem(row, col, tabItem)

    # 修改Item的内容
    def editTableItem(self, row, col, content):
        tabItem = self.tableWidget.item(row, col)
        tabItem.setText(content)
        self.tableWidget.setItem(row, col, tabItem)

    # 从Excel表读取数据(导入Excel表)
    def impClick(self):
        fileName = QFileDialog.getOpenFileName(self, u'基站小区数据导入', '/',
                                               'Excel Files (*.xls *.xlsx)')
        if fileName.strip() != "":
            self.setStatusBarMsg(u'选择完毕:' + fileName)
            importData = GetDataFromExcel(fileName, self.impType, self.heads)
            self.__mlist = []
            self.__mlist.extend(importData.getData())
            self.tableWidget.clearContents()
            self.listWidget.clear()
            self.initTable(self.__mlist)
            self.setStatusBarMsg(u'数据导入完成...')
            self.__isJy = False  # 导入完数据后,说明需要重新验证数据的正确性

        else:
            QMessageBox.information(self.parent, u"错误", u"请选中文件")

    # 数据验证按钮点击事件处理
    def yzClick(self):
        if len(self.__mlist) > 0:
            self.erlist = []  # 定义一个列表用于保存数据验证错误的数据
            # 清楚全部的Item
            if self.listWidget.count() > 0:
                self.listWidget.clear()

            # 根据tableWidget更新self.__mlist
            for (r, items) in enumerate(self.__mlist):
                for (v, item) in enumerate(self.__mlist[r]):
                    if self.tableWidget.item(r, v).text() == u"":
                        continue
                    else:
                        # 跟据self.__mlist[r][v]数据类型进行比对
                        if type(self.__mlist[r][v]) == int:
                            if unicode(self.__mlist[r][v]) != (
                                    self.tableWidget.item(r, v).text()):
                                self.__mlist[r][v] = int(
                                    self.tableWidget.item(r, v).text())
                        elif type(self.__mlist[r][v]) == float:
                            if unicode(self.__mlist[r][v]) != (
                                    self.tableWidget.item(r, v).text()):
                                self.__mlist[r][v] = float(
                                    self.tableWidget.item(r, v).text())
                        elif type(self.__mlist[r][v]) == str:
                            if unicode(self.__mlist[r][v]
                                       ) != self.tableWidget.item(r, v).text():
                                self.__mlist[r][v] = str(
                                    self.tableWidget.item(r, v).text())
                        elif type(self.__mlist[r][v]) == unicode:
                            if (self.__mlist[r][v]) != self.tableWidget.item(
                                    r, v).text():
                                self.__mlist[r][v] = self.tableWidget.item(
                                    r, v).text()
                        else:
                            print type(self.__mlist[r][v])
            # 执行数据校验函数
            self.erlist = checkDataByDataType(self.__mlist, self.impType)
            if len(self.erlist) > 0:
                self.initListView(self.erlist)
                QMessageBox.information(self.parent, u'数据校验',
                                        u'数据校验失败,请检查数据正确性后,再导入到地图中')
                self.__isJy = False
            else:
                QMessageBox.information(self.parent, u'数据校验', u'数据校验成功,没有错误数据')
                self.__isJy = True
        else:
            QMessageBox.warning(self.parent, u'数据校验', u'请先导入Excel数据后再操作!')

    # 导入数据到地图中
    def impdateClick(self):
        if self.__isJy:  # 如果数据校验成功
            if self.impType == ImpDateType.SITEANDCELL:
                # 导入基站小区
                importDataToLayer = ImportDataToLayer(self.iface, self.__mlist,
                                                      self.parent)
                if importDataToLayer.importSiteAndCellData():
                    QMessageBox.information(self.parent, u"导入数据", u"导入数据成功!")
                else:
                    QMessageBox.critical(self.parent, u"导入数据", u"导入数据失败!")

            else:
                # 导入相邻小区
                importDataToLayer = ImportDataToLayer(self.iface, self.__mlist,
                                                      self.parent)
                if importDataToLayer.importSCellData():
                    QMessageBox.information(self.parent, u"导入数据", u"导入数据成功!")
                else:
                    QMessageBox.critical(self.parent, u"导入数据", u"导入数据失败!")
        else:
            QMessageBox.warning(self.parent, u'数据导入', u'请确保校验数据成功后,再导入到地图中')

    # 编辑Action点击事件
    def editClick(self):
        self.setEditTriggers(self.editAction.isChecked())

    # 错误列表双击事件处理
    def mlistClicked(self, listItem):
        itemData = listItem.data(Qt.UserRole)
        self.tableWidget.setFocus()
        self.tableWidget.setCurrentCell(itemData['row'], itemData['col'])

    # 选框改变事件
    def comboxChange(self, index):
        self.updateType(index)

    # 字段验证是否为空
    def __validNull(self, name, col, row, itm, rowitm):
        if itm is None or itm == '':
            tmpMap = {}
            tmpMap['col'] = col
            tmpMap['row'] = row
            tmpMap['msg'] = unicode(name) + u'不能为空'
            tmpMap['item'] = rowitm
            return tmpMap
        else:
            return None

    # 导入数据线程开始信号  绑定函数
    def impStart(self):
        self.setStatusBarMsg(u'准备导入...')

    # 导入数据线程发生异常信号 绑定函数
    def impError(self, e, exception_string):
        self.setStatusBarMsg(u'发生错误:' + unicode(e))
        QMessageBox.warning(self.parent, u'Excel数据导入', u'发生错误:' + unicode(e))

    # 导入数据线程完成信号 绑定函数
    def impFinish(self, mylist):
        self.__mlist = []
        self.__mlist.extend(mylist)
        self.mthread.quit()
        self.mthread.wait()
        self.mthread.deleteLater()
        self.impDateThread.deleteLater()

        self.tableWidget.clearContents()
        self.listWidget.clear()
        self.initTable(self.__mlist)
        self.setStatusBarMsg(u'数据导入完成...')
        self.__isJy = False  # 导入完数据后,说明需要重新验证数据的正确性

    # 导入数据到地图线程发生异常信号 绑定函数
    def impError1(self, e, exception_string):
        self.setStatusBarMsg(u"导入数据发生错误")
        QMessageBox.critical(self, u'数据导入', u"发生错误:" + unicode(e))

    # 导入数据到地图线程完成信号 绑定函数
    def impFinish1(self, mylist):
        self.threadImp.quit()
        self.threadImp.wait()
        self.threadImp.deleteLater()
        self.impFeatureThread.deleteLater()
        remsg = u'数据导入完成!'
        layer = None
        if self.impType == LayerType.SITE:
            # remsg = u'基站' + remsg
            layer = getLayerByName(u'基站', self.iface)
        elif self.impType == LayerType.CELL:
            # remsg = u'小区' + remsg
            layer = getLayerByName(u'小区', self.iface)
        else:
            remsg = u'相邻小区' + remsg
            layer = getLayerByName(u'相邻小区', self.iface)
        self.setStatusBarMsg(remsg)
        layer.updateExtents()  # 更新地图数据
        self.iface.actionDraw().trigger()
        QMessageBox.information(self, u'数据导入', remsg)

        merlist = []
        for eritm in self.erlist:
            merlist.append(eritm['item'])
        self.tableWidget.clearContents()  # 先清楚表格的内容,再将错误的行显示到表格中
        self.initTable(merlist)
Example #32
0
 def showMessage(self, message, timeout):
     if settings.SHOW_STATUS_NOTIFICATIONS:
         self._widgetStatus.hide()
         self._replaceWidget.setVisible(False)
         self.show()
         QStatusBar.showMessage(self, message, timeout)
Example #33
0
class WordTester(QMainWindow, ui.ui_WordTesterWindow.Ui_WordTesterWindow):
    """
    Main class of the program. Subclass of QMainWindow.
    """
    def __init__(self, clipboard, parent=None):
        super(WordTester, self).__init__(parent)
        self.setupUi(self)

        ########################################
        # WORDS TABLE STUFF (most of it)
        ########################################

        self.model = WordTableModel.WordTableModel()
        self.wordsTable.setModel(self.model)
        self.wordsTable.setItemDelegate(WordTableDelegate.WordTableDelegate(self))

        self.wordsTable.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.wordsTable.setContextMenuPolicy(Qt.CustomContextMenu)
        self.wordsTable.customContextMenuRequested.connect(self.contextMenu)
        self.wordsTable.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.wordsTable.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        showSearchBarAction = self.createAction("Find",self.showSearchBar, QKeySequence.Find)
        closeSearchBarAction = self.createAction("Close search bar", self.closeSearchBar, Qt.Key_Escape)
        searchForNextWordAction = self.createAction("Find next", self.searchForNextOccurance, QKeySequence.FindNext)
        wordsTableActions = (showSearchBarAction, closeSearchBarAction, searchForNextWordAction)
        self.wordsTable.addActions(wordsTableActions)

        header = self.wordsTable.horizontalHeader()
        self.connect(header, SIGNAL("sectionClicked(int)"),
                    self.sortTable)
        self.connect(self.model, SIGNAL("dataChanged(QModelIndex,QModelIndex)"),
                    self.fnameChanged)

        ##############################################
        # OTHER WIDGETS
        ##############################################

        self.clipboard = clipboard

        self.statusBar = QStatusBar(self)
        self.setStatusBar(self.statusBar)

        self.allCheckBox.setChecked(True)

        self.searchBarFrame.setVisible(False)
        self.searchCheckBox.setChecked(True)

        ###############################################
        # ACTIONS
        ###############################################

        # file menu actions
        newFileAction = self.createAction("&New", self.newFile, QKeySequence.New,
                                tip = "Create list of words")
        openFileAction = self.createAction("&Open", self.load, QKeySequence.Open,
                                tip = "Open Word Tester file")
        saveFileAction = self.createAction("&Save", self.save, QKeySequence.Save,
                                tip = "Save Word Tester file")
        saveAsFileAction = self.createAction("Save &As", self.saveAs,
                                tip = "Save Word Tester file using a new name")
        importFileAction = self.createAction("&Import", self.importWords,
                                tip = "Import words from .txt file")
        exportFileAction = self.createAction("&Export", self.exportWords,
                                tip = "Export words to .txt file")
        closeAction = self.createAction("&Quit", self.close)

        self.fileMenu = self.menuBar().addMenu("&File")
        self.fileMenuActions = (newFileAction, openFileAction, saveFileAction,
                                saveAsFileAction, importFileAction, exportFileAction,
                                closeAction)

        # edit menu actions
        addWordsAction = self.createAction("Add", self.addWords, "CTRL++", tip = "Add new words")
        deleteWordsAction = self.createAction("Delete", self.deleteWords,
                                    QKeySequence.Delete, tip = "Delete words")
        showInfoAction = self.createAction("Properties", self.showInfo)

        easyAction = self.createAction("To easy", lambda difficulty = "EASY": self.changeDifficulty(difficulty))
        mediumAction = self.createAction("To medium", lambda difficulty = "MEDIUM": self.changeDifficulty(difficulty))
        hardAction = self.createAction("To hard", lambda difficulty = "HARD": self.changeDifficulty(difficulty))

        addWordsFromFileAction = self.createAction("Add from file..", self.addWordsFromFile, \
                                                    tip = "Add words from existing file, duplicates won't be appended")

        showAllAction = self.createAction("Show all", self.showAll, "CTRL+A", tip = "Show all words")
        hideAction = self.createAction("Hide", self.hideWords, "CTRL+H", tip = "Hide selected words")

        self.editMenu = self.menuBar().addMenu("&Edit")
        self.editMenuActions = (addWordsAction, hideAction, deleteWordsAction, showSearchBarAction, showAllAction)
        self.editMenu.addActions(self.editMenuActions)
        # submenu change difficulty
        difficultyMenu = self.editMenu.addMenu("Change difficulty")
        self.difficultyActions = (easyAction, mediumAction, hardAction)
        difficultyMenu.addActions(self.difficultyActions)
        self.editMenu.addSeparator()
        self.editMenu.addAction(addWordsFromFileAction)

        # words table context menu actions
        self.contextMenuActions = (deleteWordsAction, hideAction, showAllAction, showInfoAction)

        ###############################################
        # CONNECTIONS
        ###############################################

        self.connect(self, SIGNAL("resizeColumns"), self.resizeColumns)
        self.connect(self, SIGNAL("fnameChanged"), self.fnameChanged)
        self.connect(self.searchLineEdit, SIGNAL("returnPressed()"),
                    self.searchForWord)
        self.connect(self.searchCheckBox, SIGNAL("stateChanged(int)"),
                    self.searchLineEdit.setFocus)
        self.connect(self.fileMenu, SIGNAL("aboutToShow()"),
                                self.updateFileMenu)

        for checkBox in (self.allCheckBox, self.easyCheckBox, \
                        self.mediumCheckBox, self.hardCheckBox):
            self.connect(checkBox, SIGNAL("clicked()"),
                        lambda text = checkBox.text().toLower(): self.showGroup(text))

        self.connect(self.beginTestButton, SIGNAL("clicked()"),
                        self.beginTest)
        self.connect(self.wordsOnlyCheckBox, SIGNAL("stateChanged(int)"),
                        self.showWordsOnly)
        self.connect(self.clipboard, SIGNAL("dataChanged()"),
                        self.scanKeyboard)

        ####################################################
        # SETTINGS
        ####################################################

        settings = QSettings(QSettings.IniFormat, QSettings.UserScope,
                            "Word Tester", "michauStuff")
        self.recentFiles = settings.value("RecentFiles").toStringList()
        self.restoreGeometry(settings.value("Geometry").toByteArray())

        #####################################################
        # VARIABLES AND STARTUP FUNCTIONS
        #####################################################

        self.setWindowTitle('Word Tester - Unknown File')
        self.recentlySearchedWord = ""

        self.updateFileMenu()
        # QTimer.singleShot just in case the file is large and will take a while to load
        if settings.value("LastFile") == QVariant() or settings.value("LastFile").toString().isEmpty():
            QTimer.singleShot(0, lambda num = 5: self.initialWords(num)) # if there is no Last File
        else:
            QTimer.singleShot(0, self.loadInitialFile)

    ######################################################
    # reimplemented QMainWindow methods
    ######################################################

    def resizeEvent(self, event):
        self.emit(SIGNAL("resizeColumns"))
        QMainWindow.resizeEvent(self, event)

    def closeEvent(self, event):
        if self.okToContinue():
            self.addRecentFiles(self.model.fname)
            settings = QSettings(QSettings.IniFormat, QSettings.UserScope, "Word Tester", "michauStuff")
            filename = QVariant(QString(self.model.fname)) \
                    if self.model.fname is not None else QVariant()
            settings.setValue("LastFile", filename)
            settings.setValue("Geometry", self.saveGeometry())
            recentFiles = QVariant(self.recentFiles) if self.recentFiles \
                else QVariant()
            settings.setValue("RecentFiles", recentFiles)
        else:
            event.ignore()

    # this is not really a QMainWindow method, but fits in here
    def contextMenu(self, pos):
        menu = QMenu()
        menu.addActions(self.contextMenuActions)
        difficultyMenu = menu.addMenu("Change difficulty")
        difficultyMenu.addActions(self.difficultyActions)
        # change position a little to have a corner of the menu where the mouse is
	if platform.system() != "Windows":
            menu.exec_(self.wordsTable.mapToGlobal(QPoint(pos.x()+18,pos.y()	+24)))
        else:
            menu.exec_(self.wordsTable.mapToGlobal(QPoint(pos.x()+16,pos.y()	+24)))

    ######################################################
    # SIGNALS
    ######################################################

    def resizeColumns(self):
        """
        Resizes WORD and DIFFICULTY columns to match their content. MEANIGS and
        CONTEXT columns share the left space - 50 pixels of margin.
        """
        size = self.wordsTable.size()
        tableWidth = size.width()
        # this used to be resizeColumns() method
        for column in (WORD,DIFFICULTY):
            self.wordsTable.resizeColumnToContents(column)
        wordColumnWidth = self.wordsTable.columnWidth(WORD)
        difficultyColumnWidth = self.wordsTable.columnWidth(DIFFICULTY)
        self.wordsTable.setColumnWidth(MEANINGS, \
            (tableWidth - wordColumnWidth - difficultyColumnWidth)/2 - 25)
        self.wordsTable.setColumnWidth(CONTEXT, \
            (tableWidth - wordColumnWidth - difficultyColumnWidth)/2 - 25)

    def fnameChanged(self):
        """
        Sets main window's title.
        """
        if self.model.fname.isEmpty():
            if self.model.dirty:
                title = 'Word Tester - Unknown File*'
            else:
                title = 'Word Tester - Unknown File'
        else:
            if self.model.dirty:
                title = 'Word Tester - %s*' % \
                    os.path.basename(unicode(self.model.fname))
            else:
                title = 'Word Tester - %s' % \
                    os.path.basename(unicode(self.model.fname))
        self.setWindowTitle(title)


    def scanKeyboard(self, mode = QClipboard.Clipboard):
        """
        If the scan option is on, retrieves text from system keyboard and places
        it in a new word.
        """
        if not self.scanCheckBox.isChecked():
            return None
        text = self.clipboard.text(mode)
        text = text.split("\n")
        # if there is more than one word
        for word in text:
            row = self.model.rowCount()
            self.model.insertRows(row+1,1)
            index = self.model.index(self.model.rowCount()-1,WORD)
            self.wordsTable.setCurrentIndex(index)
            self.model.setData(index,QVariant(word))


    ############################################################
    # HELPER METHODS
    ############################################################

    def createAction(self, text, slot=None, shortcut=None, icon=None,
                     tip=None, checkable=False, signal="triggered()"):
        action = QAction(text, self)
        if icon is not None:
            action.setIcon(QIcon(":/%s.png" % icon))
        if shortcut is not None:
            action.setShortcut(shortcut)
        if tip is not None:
            action.setToolTip(tip)
            action.setStatusTip(tip)
        if slot is not None:
            self.connect(action, SIGNAL(signal), slot)
        if checkable:
            action.setCheckable(True)
        return action

    def getVisibleWords(self):
        """
        Returns words that are visible at the moment.
        
        :rtype WordContainer
            WordContainer with a list of all visible words.
        """
        visibleWords = WordContainerClass.WordContainer()
        # iterate over main WordContainer. If the word is hidden, create
        # index in visibleWords (*) and then tie that index to the word in self.model.words.
        # visibleWords.append(self.model.words[row]) would create a shallow copy, so
        # changes made to it wouldn't be reflected in self.model.words
        for row in range(len(self.model.words)):
            if not self.wordsTable.isRowHidden(row):
                visibleWords.append('x') #(*)
                visibleWords[-1] = self.model.words[row]
        return visibleWords
    
    
    def okToContinue(self):
        if self.model.dirty:
            ans = QMessageBox.question(self, "Word Tester - Unsaved changes",
                    "Save unsaved changes?",
                    QMessageBox.Yes|QMessageBox.No|QMessageBox.Cancel)
            if ans == QMessageBox.Yes:
                self.save()
            elif ans == QMessageBox.Cancel:
                return False
        return True
    
    def addRecentFiles(self, fname):
        if fname is None:
            return
        if not self.recentFiles.contains(fname):
            self.recentFiles.prepend(QString(fname))
            while self.recentFiles.count() > 9:
                self.recentFiles.takeLast()

    def loadRecentFile(self, fname):
        if self.okToContinue():
            try:
                self.addRecentFiles(self.model.fname)
                self.model.fname = fname
                self.model.load()
            except (pickle.UnpicklingError, IOError, AttributeError, wtexception.FileHandlingExceptions) as e:
                self.statusBar.showMessage(str(e),5000)
            self.emit(SIGNAL("resizeColumns"))
            self.emit(SIGNAL("fnameChanged"))
    
    ######################################################
    # GUI FUNCTIONALITY
    ######################################################

    def showSearchBar(self):
        if self.searchBarFrame.isVisible():
            self.searchBarFrame.setVisible(False)
            self.wordsTable.setFocus()
        else:
            self.searchBarFrame.setVisible(True)
            self.searchLineEdit.selectAll()
            self.searchLineEdit.setFocus()


    def updateFileMenu(self):
        """
        Creates new file menu everytime the user invokes it. File menu can't be created
        only once, because of recent files section which has to be refreshed.
        """
        self.fileMenu.clear()
        # add all fixed actions, but close
        self.fileMenu.addActions(self.fileMenuActions[:-1])
        current = QString(self.model.fname) \
                if self.model.fname is not None else None
        recentFiles = []
        for fname in self.recentFiles:
            if fname != current and QFile.exists(fname):
                recentFiles.append(fname)
        if recentFiles:
            self.fileMenu.addSeparator()
            self.recentFilesMenu = self.fileMenu.addMenu("Recent Files")
            for i, fname in enumerate(recentFiles):
                action = QAction("&%d %s" % (
                        i + 1, QFileInfo(fname).fileName()), self)
                action.setData(QVariant(fname))
                self.connect(action, SIGNAL("triggered()"),
                             lambda file = fname: self.loadRecentFile(file))
                self.recentFilesMenu.addAction(action)
        self.fileMenu.addSeparator()
        # add the last action - close
        self.fileMenu.addAction(self.fileMenuActions[-1])


    def closeSearchBar(self):
        if self.searchBarFrame.isVisible():
            self.searchBarFrame.setVisible(False)
            self.wordsTable.setFocus()


    def searchForWord(self):
        self.recentlySearchedWord = unicode(self.searchLineEdit.text())
        backwards = self.searchCheckBox.isChecked()
        try:
            row = self.model.words.searchForWord(unicode(self.searchLineEdit.text()))
        except ValueError:
            return None # not found
        if backwards:
            # of backwards is on, we don't have to worry if searched word is before
            # or after the current one
            index = self.model.index(row, WORD)
        else:
            currentRow = self.wordsTable.currentIndex().row()
            # if the searched word is in the [currentRow:] part of self.model.words
            row = self.model.words.slice(currentRow).searchForWord(unicode(self.searchLineEdit.text()))
            if row != -1:
                index = self.model.index(row + currentRow, WORD)
            else:
                return None
        self.wordsTable.setCurrentIndex(index)
        self.searchLineEdit.selectAll()
        self.wordsTable.setFocus()


    def searchForNextOccurance(self):
        if self.recentlySearchedWord == "":
            return None
        else:
            backwards = self.searchCheckBox.isChecked()
            currentRow = self.wordsTable.currentIndex().row()+1
            row = self.model.words.slice(currentRow).searchForWord(self.recentlySearchedWord)
            if row != -1:
                # row is a position of searched row in [currentRow:] list, so row + currentRow
                # is a position in the whole list
                index = self.model.index(row + currentRow, WORD)
            else:
                if backwards:
                    row = self.model.words.slice(0,currentRow-1).searchForWord(self.recentlySearchedWord)
                    if row != -1:
                        index = self.model.index(row, WORD)
                    else:
                        return None
                else:
                    return None
            self.wordsTable.setCurrentIndex(index)
            self.searchLineEdit.selectAll()

    def showInfo(self):
        indexes = self.wordsTable.selectedIndexes()
        if (len(indexes)/self.model.columnCount() > 1):
            QMessageBox.warning(self,"Error!","Please select only one word.")
            return
        else:
            index = indexes[0]
        dialog = InfoDialog.InfoDialog(self.model.words[index.row()])
        if dialog.exec_():
            whatChanged = dialog.result()
            if whatChanged[0]:
                self.model.words[index.row()].setWord(whatChanged[0])
                self.model.dirty = True
            if whatChanged[1]:
                self.model.words[index.row()].setMeanings(whatChanged[1])
                self.model.dirty = True
            if whatChanged[2]:
                self.model.words[index.row()].setContext(whatChanged[2])
                self.model.dirty = True
            if whatChanged[3]:
                self.model.words[index.row()].setDifficulty(whatChanged[3])
                self.model.dirty = True
            if self.model.dirty:
                self.emit(SIGNAL("fnameChanged"))
                self.emit(SIGNAL("resizeColumns"))
                self.model.reset()


    ###########################################################
    # SAVE, LOAD, EXPORT, IMPORT
    ###########################################################

    def loadInitialFile(self):
        settings = QSettings(QSettings.IniFormat, QSettings.UserScope, "Word Tester", "michauStuff")
        fname = settings.value("LastFile").toString()
        if fname and QFile.exists(fname):
            self.model.fname = fname
            self.model.load()
            self.emit(SIGNAL("resizeColumns"))
        else:
            self.newFile()
        self.emit(SIGNAL("fnameChanged"))


    def addWordsFromFile(self):
        dir = os.path.dirname(unicode(self.model.fname)) \
            if not self.model.fname.isEmpty() else '.'
        fname = QFileDialog.getOpenFileName(self,
                        "Select a file to add words from", dir,
                        "Word Tester file *.pkl *.txt")
        if fname:
            if fname.endsWith(".pkl"):
                try:
                    self.model.load(fname,True)
                except (pickle.UnpicklingError, IOError, AttributeError, wtexception.FileHandlingExceptions) as e:
                    self.statusBar.showMessage(str(e),5000)
            elif fname.endsWith(".txt"):
                try:
                    self.model.importWords(fname,True)
                except (IOError, wtexception.FileHandlingExceptions) as e:
                    self.statusBar.showMessage(unicode(e),15000)

        self.emit(SIGNAL("resizeColumns"))
        self.emit(SIGNAL("fnameChanged"))


    def newFile(self):
        if self.okToContinue():
            self.model.removeRows(0,len(self.model.words))
            self.initialWords(5)
            self.addRecentFiles(self.model.fname)
            self.model.fname = QString()
            self.model.dirty = False
            self.emit(SIGNAL("fnameChanged"))


    def save(self):
        if self.model.fname.isEmpty():
            self.saveAs()
        else:
            self.model.removeDuplicates()
            try:
                self.model.save()
            except (pickle.PicklingError, IOError, wtexception.FileHandlingExceptions) as e:
                self.statusBar.showMessage(str(e),5000)
            else:
                self.emit(SIGNAL("fnameChanged"))

    def saveAs(self):
        dir = self.model.fname if not self.model.fname.isEmpty() else '.'
        fname = QFileDialog.getSaveFileName(self,
                        "Word Tester - Save Words", dir,
                        "Word Tester file *.pkl")
        if fname:
            if not fname.contains('.'):
                fname.append('.pkl')
            self.model.fname = fname
            self.save()

    def load(self):
        """
        Loads one or more files. If there are > 1 files to load, the new filename
        is set to Unknown.

        :throws:
            pickle.UnpicklingError, IOError, AttributeError, wtexception.FileHandlingExceptions (from model.import())
        """
        if not self.okToContinue():
            return
        dir = os.path.dirname(unicode(self.model.fname)) \
            if not self.model.fname.isEmpty() else '.'
        fnames = QFileDialog.getOpenFileNames(self,
                        "Select a file to open", dir,
                        "Word Tester files *.pkl")
        if not fnames.isEmpty():
            try:
                if len(fnames) != 1: # if the user chose more than one file to open
                    self.model.load(fnames[0])
                else:
                    self.addRecentFiles(self.model.fname)
                    self.model.fname = fnames[0]
                    self.model.load()
            except (pickle.UnpicklingError, IOError, AttributeError, wtexception.FileHandlingExceptions) as e:
                self.statusBar.showMessage(str(e),5000)
            # the first file was already loaded
            fnames.removeAt(0)
            for fname in fnames: # if there are any left
                try:
                    self.model.load(fname, True) # true, because we are appending to the fnames[0]
                except (pickle.UnpicklingError, IOError, AttributeError, wtexception.FileHandlingExceptions) as e:
                    self.statusBar.showMessage(str(e),5000)
            # if the user loaded more than one file, we set fname as Unknown
            if len(fnames) != 0:
                self.model.fname = QString()
            self.emit(SIGNAL("fnameChanged"))
        self.emit(SIGNAL("fillTheSize"))

    def importWords(self):
        """
        Imports one or more .txt files. The current filename is not changed.

        :throws:
            IOError, wtexception.FileHandlingExceptions (from model.import())
        """
        if not self.okToContinue():
            return
        dir = os.path.dirname(unicode(self.model.fname)) \
            if not self.model.fname.isEmpty() else '.'
        fnames = QFileDialog.getOpenFileNames(self,
                        "Select a file to import from", dir,
                        "(UTF-8) *.txt")
        # the idea is identical as in self.load()
        if not fnames.isEmpty():
            try:
                self.model.importWords(fnames[0])
            except (IOError, wtexception.FileHandlingExceptions) as e:
                self.statusBar.showMessage(unicode(e),15000)
            fnames.removeAt(0)
            for fname in fnames:
                try:
                    self.model.importWords(fname, True)
                except (IOError, wtexception.FileHandlingExceptions) as e:
                    self.statusBar.showMessage(unicode(e),15000)
            self.emit(SIGNAL("fnameChanged"))
            self.emit(SIGNAL("resizeColumns"))

    def exportWords(self):
        """
        Exports all visibleWords to the .txt file.

        :throws:
            IOError, wtexception.FileHandlingExceptions (from model.export())
        """
        dir = os.path.dirname(unicode(self.model.fname)) \
            if not self.model.fname.isEmpty() else '.'
        fname = QFileDialog.getSaveFileName(self,
                        "Word Tester - Export to txt", dir,
                        "(UTF-8) *.txt")
        if fname:
            if not fname.contains('.'):
                fname.append('.txt')
            try:
                # if all words are visible at the moment
                if len(self.getVisibleWords()) == len(self.model.words):
                    self.model.exportWords(fname)
                else:
                    self.model.exportWords(fname, self.getVisibleWords())
            except (IOError, wtexception.FileHandlingExceptions) as e:
                self.statusBar.showMessage(str(e),5000)

    #########################################################
    # PRESENTING DATA
    #########################################################

    def hideWords(self):
        index = self.wordsTable.selectedIndexes()
        for item in index:
            self.wordsTable.hideRow(item.row())
        self.emit(SIGNAL("resizeColumns"))


    def addWords(self):
        dialog = AddWordsDialog.AddWordsDialog(self)
        if dialog.exec_():
            toAdd = dialog.spinBox.value()
            row = self.model.rowCount()
            self.model.insertRows(row, toAdd)
            self.emit(SIGNAL("fnameChanged"))
            self.emit(SIGNAL("resizeColumns"))


    def deleteWords(self):
        index = self.wordsTable.selectedIndexes()
        while len(index) != 0:
            self.model.removeRows(index[0].row(),1,index[0])
            index = self.wordsTable.selectedIndexes()
        self.emit(SIGNAL("fnameChanged"))
        self.emit(SIGNAL("resizeColumns"))
        
    def changeDifficulty(self, difficulty):
        index = self.wordsTable.selectedIndexes()
        for item in index:
            # index can be in any column, by calling item.sibling we take an index
            # from the same row, but DIFFICULTY column
            self.model.setData(item.sibling(item.row(),DIFFICULTY),QVariant(difficulty))
        self.model.dirty = True
        self.emit(SIGNAL("fnameChanged"))
        self.model.reset()

    def showWordsOnly(self):
        if self.wordsOnlyCheckBox.isChecked():
            for column in (MEANINGS,CONTEXT,DIFFICULTY):
                self.wordsTable.hideColumn(column)
        else:
            for column in (MEANINGS,CONTEXT,DIFFICULTY):
                self.wordsTable.showColumn(column)


    # looks likes it unnecessary, but showAllAction is connected to it
    def showAll(self):
        self.showGroup()


    def showGroup(self,text = None):
        def showRows(self):
            for row in range(len(self.model.words)):
                index = self.model.index(row, DIFFICULTY)
                checked = (self.easyCheckBox.isChecked(),self.mediumCheckBox.isChecked(), \
                                    self.hardCheckBox.isChecked())
                difficulty = index.data().toString()
                if difficulty == 'EASY' and not checked[0]:
                    self.wordsTable.hideRow(row)
                elif difficulty == 'MEDIUM' and not checked[1]:
                    self.wordsTable.hideRow(row)
                elif difficulty == 'HARD' and not checked[2]:
                    self.wordsTable.hideRow(row)
                else:
                    self.wordsTable.showRow(row)

        # checkBoxes send a signal and their text
        if text in ('easy','medium','hard'):
            if self.allCheckBox.isChecked():
                self.allCheckBox.setChecked(False)
            showRows(self)
        else:
            # show all words, and uncheck any checked checkBoxes
            self.allCheckBox.setChecked(True)
            for row in range(len(self.model.words)):
                self.wordsTable.showRow(row)
            for checkBox in (self.easyCheckBox, self.mediumCheckBox, self.hardCheckBox):
                checkBox.setChecked(False)

        # one of the checkBoxes always has to be checked
        if not self.easyCheckBox.isChecked() and not self.mediumCheckBox.isChecked() and not \
                            self.hardCheckBox.isChecked() and not self.allCheckBox.isChecked():
            self.allCheckBox.setChecked(True)
            # hard coded checking as above apparently doesn't send a signal, so
            # we hard code a click
            self.allCheckBox.click()

    def sortTable(self, section):
        if section == WORD:
            self.model.sortByWord()
        elif section == DIFFICULTY:
            self.model.sortByDifficulty()
        # to update visible words
        for checkBox in (self.easyCheckBox, self.mediumCheckBox, self.hardCheckBox):
            if checkBox.isChecked():
                self.showGroup(checkBox.text().toLower())
                break
        self.emit(SIGNAL("resizeColumns"))
        

    def initialWords(self, number = 1):
        """
        Supplies number of empty words, when there is no file to load at the startup.
        """
        row = self.model.rowCount()
        self.model.insertRows(row, number)
        index = self.model.index(row, 0)
        self.model.dirty = False
        self.wordsTable.setFocus()
        self.wordsTable.setCurrentIndex(index)

    #########################################################
    # TEST
    #########################################################

    def beginTest(self):
        """
        Prepares WordContainer of words that the test will be performed on.
        Executes TestDialog.
        """
        visibleWords = self.getVisibleWords()
        # we don't want any empty words in the test
        if not visibleWords.validWords():
            return None
        difficulty = self.testDifficultyComboBox.currentText()
        # some idiot-proof checking
        if not self.repetitionsCheckBox.isChecked():
            if self.numberOfWordsSpinBox.value() > len(visibleWords) \
                and difficulty == 'All':
                    QMessageBox.warning(self,"Error!","Maxiumum number of words (without 'Allow repetition')" + \
                                        " is %d." % len(visibleWords))
                    return
            if difficulty != 'All' and len(visibleWords.getGroupOfWords(difficulty)) < self.numberOfWordsSpinBox.value():
                    QMessageBox.warning(self,"Error!","Maxiumum number of words (without 'Allow repetition')" + \
                                        " is %d." % len(visibleWords.getGroupOfWords(difficulty)))
                    return

        if difficulty == 'All':
            dialog = TestDialog.TestDialog(visibleWords, self)
        # in every difficulty there is some more idiot-proof checking
        # if all goes well create a TestDialog instance, which is later executed
        elif difficulty == 'Easy':
            if len(visibleWords.getGroupOfWords(difficulty)) == 0:
                self.statusBar.showMessage("No words with difficulty: " + difficulty,5000)
                return None
            dialog = TestDialog.TestDialog(visibleWords, self, difficulty)
        elif difficulty == 'Medium':
            if len(visibleWords.getGroupOfWords(difficulty)) == 0:
                self.statusBar.showMessage("No words with difficulty: " + difficulty,5000)
                return None
            dialog = TestDialog.TestDialog(visibleWords, self, difficulty)
        elif difficulty == 'Hard':
            if len(visibleWords.getGroupOfWords(difficulty)) == 0:
                self.statusBar.showMessage("No words with difficulty: " + difficulty,5000)
                return None
            dialog = TestDialog.TestDialog(visibleWords, self, difficulty)
        if dialog.exec_():
            # if the user is working on an Unknown File (not saved), the program
            # won't record results, without saving the file
            if not self.model.fname.isEmpty():
                self.model.dirty = True
                QTimer.singleShot(0, self.save)
            else:
                ans = QMessageBox.question(self, "Word Tester - Unsaved changes",
                                "Do you want to save the file? If not, results of " + \
                                "the test won't be saved.",
                                QMessageBox.Yes|QMessageBox.No)
                if ans == QMessageBox.Yes:
                    self.model.dirty = True
                    QTimer.singleShot(0, self.save)
            # to update visible words
            for checkBox in (self.easyCheckBox, self.mediumCheckBox, self.hardCheckBox):
                if checkBox.isChecked():
                    self.showGroup(checkBox.text().toLower())
                    return