class dockUrlMonitor(QTreeView):
    ''' dock widget for get all credentials logger netcreds'''
    def __init__(self, parent=None,info={}):
        super(dockUrlMonitor, self).__init__(parent)
        self.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.model = QStandardItemModel()
        self.model.setHorizontalHeaderLabels(['URL','HTTP-Headers'])
        self.setModel(self.model)
        self.setUniformRowHeights(True)
        self.setColumnWidth(0,130)

    def writeModeData(self,data):
        ''' get data output and add on QtableWidgets '''
        ParentMaster = QStandardItem('[ {0[src]} > {0[dst]} ] {1[Method]} {1[Host]}{1[Path]}'.format(
        data['urlsCap']['IP'], data['urlsCap']['Headers']))
        ParentMaster.setIcon(QIcon('icons/accept.png'))
        ParentMaster.setSizeHint(QSize(30,30))
        for item in data['urlsCap']['Headers']:
            ParentMaster.appendRow([QStandardItem('{}'.format(item)),
            QStandardItem(data['urlsCap']['Headers'][item])])
        self.model.appendRow(ParentMaster)
        self.setFirstColumnSpanned(ParentMaster.row(),
        self.rootIndex(), True)
        self.scrollToBottom()

    def clear(self):
        self.model.clear()

    def stopProcess(self):
        self.clearSelection()
Exemple #2
0
class dockUrlMonitor(QTreeView):
    ''' dock widget for get all credentials logger netcreds'''
    def __init__(self, parent=None, info={}):
        super(dockUrlMonitor, self).__init__(parent)
        self.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.model = QStandardItemModel()
        self.model.setHorizontalHeaderLabels(['URL', 'HTTP-Headers'])
        self.setModel(self.model)
        self.setUniformRowHeights(True)
        self.setColumnWidth(0, 130)

    def writeModeData(self, data):
        ''' get data output and add on QtableWidgets '''
        ParentMaster = QStandardItem(
            '[ {0[src]} > {0[dst]} ] {1[Method]} {1[Host]}{1[Path]}'.format(
                data['urlsCap']['IP'], data['urlsCap']['Headers']))
        ParentMaster.setIcon(QIcon('icons/accept.png'))
        ParentMaster.setSizeHint(QSize(30, 30))
        for item in data['urlsCap']['Headers']:
            ParentMaster.appendRow([
                QStandardItem('{}'.format(item)),
                QStandardItem(data['urlsCap']['Headers'][item])
            ])
        self.model.appendRow(ParentMaster)
        self.setFirstColumnSpanned(ParentMaster.row(), self.rootIndex(), True)
        self.scrollToBottom()

    def clear(self):
        self.model.clear()

    def stopProcess(self):
        self.clearSelection()
Exemple #3
0
class BreakpointsViewer(QTreeView):
    """ showing bps  """
    def __init__(self, parent):
        super(QTreeView, self).__init__(parent)
        self.setAutoScroll(True)
        self.source_files = {}
        self.focus_signal = None
        self.header().setResizeMode(QHeaderView.ResizeToContents)
        self._show_args = None
        self.bp_data = QStandardItemModel()
        self.setModel(self.bp_data)
        self.setAlternatingRowColors(True)

    def set_focus_signal(self, signal):
        """ set callback to focus source file line"""
        self.focus_signal = signal

    def clear(self):
        """ clear the widget"""
        self.bp_data.clear()
        self.bp_data.setHorizontalHeaderLabels(['ID', 'Hits', 'Ignore Count', 'Condition'])

    def update_bp_info(self, target):
        """ update breakpoint info """
        self.clear()
        root = self.bp_data.invisibleRootItem()
        for breakpoint in target.breakpoint_iter():
            if not breakpoint.IsValid() or breakpoint.IsInternal():
                continue
            bp_item = QStandardItem(str(breakpoint.id))
            bp_row =[bp_item, QStandardItem(str(breakpoint.GetHitCount())), 
                       QStandardItem(str(breakpoint.GetIgnoreCount())), QStandardItem(str(breakpoint.GetCondition()))]
            for loc in breakpoint:
                loc_row = [QStandardItem(str(loc.GetID())), QStandardItem(str(loc.GetAddress().GetLineEntry()))]
                bp_item.appendRow(loc_row)
            root.appendRow(bp_row)
        self.expandToDepth(1)

    def mousePressEvent(self, event):
        """ overrided """
        idx = self.indexAt(event.pos())
        if idx.isValid() and self.focus_signal:
            model = idx.model()
            idx = idx.sibling(idx.row(), 0)
            if idx.isValid():
                item = model.itemFromIndex(idx)
                if item and item.isSelectable():
                    if item in self.source_files:
                        file_info = self.source_files[item]
                        if self.focus_signal:
                            self.focus_signal.emit(file_info.GetFileSpec().fullpath,
                                                   file_info.GetLine())
                        self.frame_changed.emit(self.frames[item])

                    else:
                        logging.error('frame cannot find associated source file')

        QTreeView.mousePressEvent(self, event)
Exemple #4
0
class HistoryDialog(QDialog, Ui_HistoryDialogPythonConsole):
    def __init__(self, parent):
        QDialog.__init__(self, parent)
        self.setupUi(self)
        self.parent = parent
        self.setWindowTitle(
            QCoreApplication.translate("PythonConsole",
                                       "Python Console - Command History"))
        self.listView.setToolTip(
            QCoreApplication.translate("PythonConsole",
                                       "Double click on item to execute"))
        self.model = QStandardItemModel(self.listView)

        self._reloadHistory()

        self.deleteScut = QShortcut(QKeySequence(Qt.Key_Delete), self)
        self.deleteScut.activated.connect(self._deleteItem)
        self.listView.doubleClicked.connect(self._runHistory)
        self.reloadHistory.clicked.connect(self._reloadHistory)
        self.saveHistory.clicked.connect(self._saveHistory)

    def _runHistory(self, item):
        cmd = item.data(Qt.DisplayRole)
        self.parent.runCommand(unicode(cmd))

    def _saveHistory(self):
        self.parent.writeHistoryFile(True)

    def _reloadHistory(self):
        self.model.clear()
        for i in self.parent.history:
            item = QStandardItem(i)
            if sys.platform.startswith('win'):
                item.setSizeHint(QSize(18, 18))
            self.model.appendRow(item)

        self.listView.setModel(self.model)
        self.listView.scrollToBottom()

    def _deleteItem(self):
        itemsSelected = self.listView.selectionModel().selectedIndexes()
        if itemsSelected:
            item = itemsSelected[0].row()
            ## Remove item from the command history (just for the current session)
            self.parent.history.pop(item)
            self.parent.historyIndex -= 1
            ## Remove row from the command history dialog
            self.model.removeRow(item)
Exemple #5
0
class HistoryDialog(QDialog, Ui_HistoryDialogPythonConsole):

    def __init__(self, parent):
        QDialog.__init__(self, parent)
        self.setupUi(self)
        self.parent = parent
        self.setWindowTitle(QCoreApplication.translate("PythonConsole",
                                                       "Python Console - Command History"))
        self.listView.setToolTip(QCoreApplication.translate("PythonConsole",
                                                            "Double click on item to execute"))
        self.model = QStandardItemModel(self.listView)

        self._reloadHistory()

        self.deleteScut = QShortcut(QKeySequence(Qt.Key_Delete), self)
        self.deleteScut.activated.connect(self._deleteItem)
        self.listView.doubleClicked.connect(self._runHistory)
        self.reloadHistory.clicked.connect(self._reloadHistory)
        self.saveHistory.clicked.connect(self._saveHistory)

    def _runHistory(self, item):
        cmd = item.data(Qt.DisplayRole)
        self.parent.runCommand(unicode(cmd))

    def _saveHistory(self):
        self.parent.writeHistoryFile(True)

    def _reloadHistory(self):
        self.model.clear()
        for i in self.parent.history:
            item = QStandardItem(i)
            if sys.platform.startswith('win'):
                item.setSizeHint(QSize(18, 18))
            self.model.appendRow(item)

        self.listView.setModel(self.model)
        self.listView.scrollToBottom()

    def _deleteItem(self):
        itemsSelected = self.listView.selectionModel().selectedIndexes()
        if itemsSelected:
            item = itemsSelected[0].row()
            ## Remove item from the command history (just for the current session)
            self.parent.history.pop(item)
            self.parent.historyIndex -= 1
            ## Remove row from the command history dialog
            self.model.removeRow(item)
Exemple #6
0
class DataModel(object):
    def __init__(self):
        super(DataModel,self).__init__()
        self.servers     = QStandardItemModel(0,3)
        self.selected    = ''
        self.autoconnect = False
        
    def loadSettings(self):
        settings = QSettings()
        servers = settings.value("servers", None)
        self.servers.clear()
        if servers:
            for s in servers:
                self.servers.appendRow([QStandardItem(str(i)) for i in s])
        self.selected    =  str(settings.value("selected",    self.selected))
        self.autoconnect = int(settings.value("autoconnect", self.autoconnect))
        
    def saveSettings(self):
        settings = QSettings()
        servers = []
        if self.servers:
            for row in xrange(self.servers.rowCount()):
                entry = []
                for col in xrange(self.servers.columnCount()):
                    entry += [self.servers.item(row,col).text()]
                servers += [entry]
        if len(servers):
            settings.setValue("servers", servers)
        else:
            settings.setValue("servers", None)
        settings.setValue("selected",     str(self.selected))
        settings.setValue("autoconnect", int(self.autoconnect))

    def selectedServer(self):
        selectedServer = []
        if self.selected:
            result = self.servers.findItems( self.selected)
            if len(result):
                mi = result[0]
                row = mi.row()
                for col in xrange(self.servers.columnCount()):
                    selectedServer += [self.servers.item(row,col).text()]
        return selectedServer
Exemple #7
0
class GameListWidget(QListView):
    def __init__(self, parent=None):
        QListView.__init__(self, parent)
        self.game_list = []
        self.model = QStandardItemModel()
        self.setModel(self.model)
        self.setWordWrap(True)
        self.setUniformItemSizes(True)
        self.setGridSize(QSize(self.rect().width(), 30))
        self.setFont(QFont("Microsoft YaHei", 10))
        self.setEditTriggers(QAbstractItemView.NoEditTriggers)
        # self.setFocusPolicy(Qt.NoFocus)
        self.setSelectionMode(QAbstractItemView.SingleSelection)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        #self.setAcceptDrops(True)
        self.game_controller = GameController()
        self.game_controller.connector.connect(SIGNAL('game_list'),
                                               self.add_game_item)
        self.game_controller.connector.connect(SIGNAL('game_list_clear'),
                                               self.clear)

        self.clicked.connect(self.double_click_on_item)  ## ??????

    def double_click_on_item(self, idx):
        print('%d was clicked' % (idx))
        self.emit(SIGNAL("enter_room(QString, QString)"),
                  self.game_list[idx][0], self.game_list[idx][1])

    def add_game_item(self, txt, id):
        self.game_list.append((id, txt))
        item = QStandardItem(txt)
        item.setTextAlignment(Qt.AlignCenter)
        self.model.appendRow(item)

    def clear(self):
        self.model.clear()
class OWConfusionMatrix(widget.OWWidget):
    """Confusion matrix widget"""

    name = "Confusion Matrix"
    description = "Display a confusion matrix constructed from " \
                  "the results of classifier evaluations."
    icon = "icons/ConfusionMatrix.svg"
    priority = 1001

    inputs = [("Evaluation Results", Orange.evaluation.Results, "set_results")]
    outputs = [("Selected Data", Orange.data.Table)]

    quantities = [
        "Number of instances", "Proportion of predicted",
        "Proportion of actual"
    ]

    settingsHandler = settings.ClassValuesContextHandler()

    selected_learner = settings.Setting([0], schema_only=True)
    selection = settings.ContextSetting(set())
    selected_quantity = settings.Setting(0)
    append_predictions = settings.Setting(True)
    append_probabilities = settings.Setting(False)
    autocommit = settings.Setting(True)

    UserAdviceMessages = [
        widget.Message(
            "Clicking on cells or in headers outputs the corresponding "
            "data instances", "click_cell")
    ]

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

        self.data = None
        self.results = None
        self.learners = []
        self.headers = []

        box = gui.vBox(self.controlArea, "Learners")

        self.learners_box = gui.listBox(box,
                                        self,
                                        "selected_learner",
                                        "learners",
                                        callback=self._learner_changed)
        box = gui.vBox(self.controlArea, "Show")

        gui.comboBox(box,
                     self,
                     "selected_quantity",
                     items=self.quantities,
                     callback=self._update)

        box = gui.vBox(self.controlArea, "Select")

        gui.button(box,
                   self,
                   "Select Correct",
                   callback=self.select_correct,
                   autoDefault=False)
        gui.button(box,
                   self,
                   "Select Misclassified",
                   callback=self.select_wrong,
                   autoDefault=False)
        gui.button(box,
                   self,
                   "Clear Selection",
                   callback=self.select_none,
                   autoDefault=False)

        self.outputbox = box = gui.vBox(self.controlArea, "Output")
        gui.checkBox(box,
                     self,
                     "append_predictions",
                     "Predictions",
                     callback=self._invalidate)
        gui.checkBox(box,
                     self,
                     "append_probabilities",
                     "Probabilities",
                     callback=self._invalidate)

        gui.auto_commit(self.controlArea, self, "autocommit", "Send Selected",
                        "Send Automatically")

        grid = QGridLayout()

        self.tablemodel = QStandardItemModel(self)
        view = self.tableview = QTableView(
            editTriggers=QTableView.NoEditTriggers)
        view.setModel(self.tablemodel)
        view.horizontalHeader().hide()
        view.verticalHeader().hide()
        view.horizontalHeader().setMinimumSectionSize(60)
        view.selectionModel().selectionChanged.connect(self._invalidate)
        view.setShowGrid(False)
        view.setItemDelegate(BorderedItemDelegate(Qt.white))
        view.clicked.connect(self.cell_clicked)
        grid.addWidget(view, 0, 0)
        self.mainArea.layout().addLayout(grid)

    def sizeHint(self):
        """Initial size"""
        return QSize(750, 490)

    def _item(self, i, j):
        return self.tablemodel.item(i, j) or QStandardItem()

    def _set_item(self, i, j, item):
        self.tablemodel.setItem(i, j, item)

    def _init_table(self, nclasses):
        item = self._item(0, 2)
        item.setData("Predicted", Qt.DisplayRole)
        item.setTextAlignment(Qt.AlignCenter)
        item.setFlags(Qt.NoItemFlags)

        self._set_item(0, 2, item)
        item = self._item(2, 0)
        item.setData("Actual", Qt.DisplayRole)
        item.setTextAlignment(Qt.AlignHCenter | Qt.AlignBottom)
        item.setFlags(Qt.NoItemFlags)
        self.tableview.setItemDelegateForColumn(0, gui.VerticalItemDelegate())
        self._set_item(2, 0, item)
        self.tableview.setSpan(0, 2, 1, nclasses)
        self.tableview.setSpan(2, 0, nclasses, 1)

        font = self.tablemodel.invisibleRootItem().font()
        bold_font = QFont(font)
        bold_font.setBold(True)

        for i in (0, 1):
            for j in (0, 1):
                item = self._item(i, j)
                item.setFlags(Qt.NoItemFlags)
                self._set_item(i, j, item)

        for p, label in enumerate(self.headers):
            for i, j in ((1, p + 2), (p + 2, 1)):
                item = self._item(i, j)
                item.setData(label, Qt.DisplayRole)
                item.setFont(bold_font)
                item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                item.setFlags(Qt.ItemIsEnabled)
                if p < len(self.headers) - 1:
                    item.setData("br"[j == 1], BorderRole)
                    item.setData(QColor(192, 192, 192), BorderColorRole)
                self._set_item(i, j, item)

        hor_header = self.tableview.horizontalHeader()
        if len(' '.join(self.headers)) < 120:
            hor_header.setResizeMode(QHeaderView.ResizeToContents)
        else:
            hor_header.setDefaultSectionSize(60)
        self.tablemodel.setRowCount(nclasses + 3)
        self.tablemodel.setColumnCount(nclasses + 3)

    def set_results(self, results):
        """Set the input results."""

        prev_sel_learner = self.selected_learner.copy()
        self.clear()
        self.warning()
        self.closeContext()

        data = None
        if results is not None and results.data is not None:
            data = results.data

        if data is not None and not data.domain.has_discrete_class:
            self.warning("Confusion Matrix cannot show regression results.")

        self.results = results
        self.data = data

        if data is not None:
            class_values = data.domain.class_var.values
        elif results is not None:
            raise NotImplementedError

        if results is None:
            self.report_button.setDisabled(True)
        else:
            self.report_button.setDisabled(False)

            nmodels = results.predicted.shape[0]
            self.headers = class_values + \
                           [unicodedata.lookup("N-ARY SUMMATION")]

            # NOTE: The 'learner_names' is set in 'Test Learners' widget.
            if hasattr(results, "learner_names"):
                self.learners = results.learner_names
            else:
                self.learners = [
                    "Learner #{}".format(i + 1) for i in range(nmodels)
                ]

            self._init_table(len(class_values))
            self.openContext(data.domain.class_var)
            if not prev_sel_learner or prev_sel_learner[0] >= len(
                    self.learners):
                self.selected_learner[:] = [0]
            else:
                self.selected_learner[:] = prev_sel_learner
            self._update()
            self._set_selection()
            self.unconditional_commit()

    def clear(self):
        """Reset the widget, clear controls"""
        self.results = None
        self.data = None
        self.tablemodel.clear()
        self.headers = []
        # Clear learners last. This action will invoke `_learner_changed`
        self.learners = []

    def select_correct(self):
        """Select the diagonal elements of the matrix"""
        selection = QItemSelection()
        n = self.tablemodel.rowCount()
        for i in range(2, n):
            index = self.tablemodel.index(i, i)
            selection.select(index, index)
        self.tableview.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

    def select_wrong(self):
        """Select the off-diagonal elements of the matrix"""
        selection = QItemSelection()
        n = self.tablemodel.rowCount()
        for i in range(2, n):
            for j in range(i + 1, n):
                index = self.tablemodel.index(i, j)
                selection.select(index, index)
                index = self.tablemodel.index(j, i)
                selection.select(index, index)
        self.tableview.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

    def select_none(self):
        """Reset selection"""
        self.tableview.selectionModel().clear()

    def cell_clicked(self, model_index):
        """Handle cell click event"""
        i, j = model_index.row(), model_index.column()
        if not i or not j:
            return
        n = self.tablemodel.rowCount()
        index = self.tablemodel.index
        selection = None
        if i == j == 1 or i == j == n - 1:
            selection = QItemSelection(index(2, 2), index(n - 1, n - 1))
        elif i in (1, n - 1):
            selection = QItemSelection(index(2, j), index(n - 1, j))
        elif j in (1, n - 1):
            selection = QItemSelection(index(i, 2), index(i, n - 1))

        if selection is not None:
            self.tableview.selectionModel().select(
                selection, QItemSelectionModel.ClearAndSelect)

    def commit(self):
        """Output data instances corresponding to selected cells"""
        if self.results is not None and self.data is not None \
                and self.selected_learner:
            indices = self.tableview.selectedIndexes()
            indices = {(ind.row() - 2, ind.column() - 2) for ind in indices}
            actual = self.results.actual
            learner_name = self.learners[self.selected_learner[0]]
            predicted = self.results.predicted[self.selected_learner[0]]
            selected = [
                i for i, t in enumerate(zip(actual, predicted)) if t in indices
            ]
            row_indices = self.results.row_indices[selected]

            extra = []
            class_var = self.data.domain.class_var
            metas = self.data.domain.metas

            if self.append_predictions:
                predicted = numpy.array(predicted[selected], dtype=object)
                extra.append(predicted.reshape(-1, 1))
                var = Orange.data.DiscreteVariable(
                    "{}({})".format(class_var.name, learner_name),
                    class_var.values)
                metas = metas + (var, )

            if self.append_probabilities and \
                    self.results.probabilities is not None:
                probs = self.results.probabilities[self.selected_learner[0],
                                                   selected]
                extra.append(numpy.array(probs, dtype=object))
                pvars = [
                    Orange.data.ContinuousVariable("p({})".format(value))
                    for value in class_var.values
                ]
                metas = metas + tuple(pvars)

            X = self.data.X[row_indices]
            Y = self.data.Y[row_indices]
            M = self.data.metas[row_indices]
            row_ids = self.data.ids[row_indices]

            M = numpy.hstack((M, ) + tuple(extra))
            domain = Orange.data.Domain(self.data.domain.attributes,
                                        self.data.domain.class_vars, metas)
            data = Orange.data.Table.from_numpy(domain, X, Y, M)
            data.ids = row_ids
            data.name = learner_name

        else:
            data = None

        self.send("Selected Data", data)

    def _invalidate(self):
        indices = self.tableview.selectedIndexes()
        self.selection = {(ind.row() - 2, ind.column() - 2) for ind in indices}
        self.commit()

    def _set_selection(self):
        selection = QItemSelection()
        index = self.tableview.model().index
        for row, col in self.selection:
            sel = index(row + 2, col + 2)
            selection.select(sel, sel)
        self.tableview.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

    def _learner_changed(self):
        self._update()
        self._set_selection()
        self.commit()

    def _update(self):
        def _isinvalid(x):
            return isnan(x) or isinf(x)

        # Update the displayed confusion matrix
        if self.results is not None and self.selected_learner:
            cmatrix = confusion_matrix(self.results, self.selected_learner[0])
            colsum = cmatrix.sum(axis=0)
            rowsum = cmatrix.sum(axis=1)
            n = len(cmatrix)
            diag = numpy.diag_indices(n)

            colors = cmatrix.astype(numpy.double)
            colors[diag] = 0
            if self.selected_quantity == 0:
                normalized = cmatrix.astype(numpy.int)
                formatstr = "{}"
                div = numpy.array([colors.max()])
            else:
                if self.selected_quantity == 1:
                    normalized = 100 * cmatrix / colsum
                    div = colors.max(axis=0)
                else:
                    normalized = 100 * cmatrix / rowsum[:, numpy.newaxis]
                    div = colors.max(axis=1)[:, numpy.newaxis]
                formatstr = "{:2.1f} %"
            div[div == 0] = 1
            colors /= div
            colors[diag] = normalized[diag] / normalized[diag].max()

            for i in range(n):
                for j in range(n):
                    val = normalized[i, j]
                    col_val = colors[i, j]
                    item = self._item(i + 2, j + 2)
                    item.setData(
                        "NA" if _isinvalid(val) else formatstr.format(val),
                        Qt.DisplayRole)
                    bkcolor = QColor.fromHsl(
                        [0, 240][i == j], 160,
                        255 if _isinvalid(col_val) else int(255 -
                                                            30 * col_val))
                    item.setData(QBrush(bkcolor), Qt.BackgroundRole)
                    item.setData("trbl", BorderRole)
                    item.setToolTip("actual: {}\npredicted: {}".format(
                        self.headers[i], self.headers[j]))
                    item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                    item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
                    self._set_item(i + 2, j + 2, item)

            bold_font = self.tablemodel.invisibleRootItem().font()
            bold_font.setBold(True)

            def _sum_item(value, border=""):
                item = QStandardItem()
                item.setData(value, Qt.DisplayRole)
                item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                item.setFlags(Qt.ItemIsEnabled)
                item.setFont(bold_font)
                item.setData(border, BorderRole)
                item.setData(QColor(192, 192, 192), BorderColorRole)
                return item

            for i in range(n):
                self._set_item(n + 2, i + 2, _sum_item(int(colsum[i]), "t"))
                self._set_item(i + 2, n + 2, _sum_item(int(rowsum[i]), "l"))
            self._set_item(n + 2, n + 2, _sum_item(int(rowsum.sum())))

    def send_report(self):
        """Send report"""
        if self.results is not None and self.selected_learner:
            self.report_table(
                "Confusion matrix for {} (showing {})".format(
                    self.learners[self.selected_learner[0]],
                    self.quantities[self.selected_quantity].lower()),
                self.tableview)
Exemple #9
0
class SelectionSetsWidget(QFrame):
    """
    Widget for managing multiple stored item selections
    """
    selectionModified = Signal(bool)

    def __init__(self, parent):
        QFrame.__init__(self, parent)
        self.setContentsMargins(0, 0, 0, 0)
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(1)
        self._setNameLineEdit = QLineEdit(self)
        layout.addWidget(self._setNameLineEdit)

        self._setListView = QListView(self)
        self._listModel = QStandardItemModel(self)
        self._proxyModel = QSortFilterProxyModel(self)
        self._proxyModel.setSourceModel(self._listModel)

        self._setListView.setModel(self._proxyModel)
        self._setListView.setItemDelegate(ListItemDelegate(self))

        self._setNameLineEdit.textChanged.connect(
            self._proxyModel.setFilterFixedString)

        self._completer = QCompleter(self._listModel, self)

        self._setNameLineEdit.setCompleter(self._completer)

        self._listModel.itemChanged.connect(self._onSetNameChange)
        layout.addWidget(self._setListView)
        buttonLayout = QHBoxLayout()

        self._addAction = QAction(
            "+", self, toolTip="Add a new sort key")
        self._updateAction = QAction(
            "Update", self, toolTip="Update/save current selection")
        self._removeAction = QAction(
            "\u2212", self, toolTip="Remove selected sort key.")

        self._addToolButton = QToolButton(self)
        self._updateToolButton = QToolButton(self)
        self._removeToolButton = QToolButton(self)
        self._updateToolButton.setSizePolicy(
                QSizePolicy.MinimumExpanding, QSizePolicy.Minimum)

        self._addToolButton.setDefaultAction(self._addAction)
        self._updateToolButton.setDefaultAction(self._updateAction)
        self._removeToolButton.setDefaultAction(self._removeAction)

        buttonLayout.addWidget(self._addToolButton)
        buttonLayout.addWidget(self._updateToolButton)
        buttonLayout.addWidget(self._removeToolButton)

        layout.addLayout(buttonLayout)
        self.setLayout(layout)

        self._addAction.triggered.connect(self.addCurrentSelection)
        self._updateAction.triggered.connect(self.updateSelectedSelection)
        self._removeAction.triggered.connect(self.removeSelectedSelection)

        self._setListView.selectionModel().selectionChanged.connect(
            self._onListViewSelectionChanged)
        self.selectionModel = None
        self._selections = []

    def sizeHint(self):
        size = QFrame.sizeHint(self)
        return QSize(size.width(), 200)

    def _onSelectionChanged(self, selected, deselected):
        self.setSelectionModified(True)

    def _onListViewSelectionChanged(self, selected, deselected):
        try:
            index = self._setListView.selectedIndexes()[0]
        except IndexError:
            return
        self.commitSelection(self._proxyModel.mapToSource(index).row())

    def _onSetNameChange(self, item):
        self.selections[item.row()].name = str(item.text())

    def _setButtonStates(self, val):
        self._updateToolButton.setEnabled(val)

    def setSelectionModel(self, selectionModel):
        if self.selectionModel:
            self.selectionModel.selectionChanged.disconnect(
                self._onSelectionChanged)
        self.selectionModel = selectionModel
        self.selectionModel.selectionChanged.connect(self._onSelectionChanged)

    def addCurrentSelection(self):
        item = self.addSelection(
            SelectionByKey(self.selectionModel.selection(),
                           name="New selection",
                           key=(1, 2, 3, 10)))
        index = self._proxyModel.mapFromSource(item.index())
        self._setListView.setCurrentIndex(index)
        self._setListView.edit(index)
        self.setSelectionModified(False)

    def removeSelectedSelection(self):
        i = self._proxyModel.mapToSource(self._setListView.currentIndex()).row()
        self._listModel.takeRow(i)
        del self.selections[i]

    def updateCurentSelection(self):
        i = self._proxyModel.mapToSource(self._setListView.selectedIndex()).row()
        self.selections[i].setSelection(self.selectionModel.selection())
        self.setSelectionModified(False)

    def addSelection(self, selection, name=""):
        self._selections.append(selection)
        item = QStandardItem(selection.name)
        item.setFlags(item.flags() ^ Qt.ItemIsDropEnabled)
        self._listModel.appendRow(item)
        self.setSelectionModified(False)
        return item

    def updateSelectedSelection(self):
        i = self._proxyModel.mapToSource(self._setListView.currentIndex()).row()
        self.selections[i].setSelection(self.selectionModel.selection())
        self.setSelectionModified(False)

    def setSelectionModified(self, val):
        self._selectionModified = val
        self._setButtonStates(val)
        self.selectionModified.emit(bool(val))

    def commitSelection(self, index):
        selection = self.selections[index]
        selection.select(self.selectionModel)

    def setSelections(self, selections):
        self._listModel.clear()
        for selection in selections:
            self.addSelection(selection)

    def selections(self):
        return self._selections

    selections = property(selections, setSelections)
Exemple #10
0
class ListWidget(EditorWidget):
    widgettype = 'List'
    def __init__(self, *args):
        super(ListWidget, self).__init__(*args)
        self.listmodel = QStandardItemModel()
        self._bindvalue = None

    def createWidget(self, parent):
        return QComboBox(parent)

    def _buildfromlist(self, widget, listconfig):
        items = listconfig['items']
        for item in items:
            parts = item.split(';')
            data = parts[0]
            try:
                desc = parts[1]
            except IndexError:
                desc = data

            try:
                path = parts[2]
                icon = QIcon(path)
            except:
                icon = QIcon()

            item = QStandardItem(desc)
            item.setData(data, Qt.UserRole)
            item.setIcon(icon)
            self.listmodel.appendRow(item)

    def _buildfromlayer(self, widget, layerconfig):
        layername = layerconfig['layer']
        keyfield = layerconfig['key']
        valuefield = layerconfig['value']
        filterexp = layerconfig.get('filter', None)

        try:
            layer = QgsMapLayerRegistry.instance().mapLayersByName(layername)[0]
        except IndexError:
            roam.utils.warning("Can't find layer {} in project".format(layername))
            return

        keyfieldindex = layer.fieldNameIndex(keyfield)
        valuefieldindex = layer.fieldNameIndex(valuefield)
        if keyfieldindex == -1 or valuefieldindex == -1:
            roam.utils.warning("Can't find key or value column")
            return

        if self.allownulls:
            item = QStandardItem('(no selection)')
            item.setData(None, Qt.UserRole)
            self.listmodel.appendRow(item)

        attributes = {keyfieldindex, valuefieldindex}
        iconfieldindex =  layer.fieldNameIndex('icon')
        if iconfieldindex > -1:
            attributes.add(iconfieldindex)

        if not filterexp and valuefieldindex == keyfieldindex and iconfieldindex == -1:
            values = layer.uniqueValues(keyfieldindex)
            for value in values:
                value = nullconvert(value)
                item = QStandardItem(value)
                item.setData(value, Qt.UserRole)
                self.listmodel.appendRow(item)
            return

        flags = QgsFeatureRequest.NoGeometry

        expression = None
        if filterexp:
            expression = QgsExpression(filterexp)
            expression.prepare(layer.pendingFields())
            if expression.hasParserError():
                roam.utils.warning("Expression has parser error: {}".format(expression.parserErrorString()))
                return

            if expression.needsGeometry():
                flags = QgsFeatureRequest.NoFlags

            for field in expression.referencedColumns():
                index = layer.fieldNameIndex(field)
                attributes.add(index)

        request = QgsFeatureRequest().setFlags(flags).setSubsetOfAttributes(list(attributes))
        for feature in layer.getFeatures(request):
            if expression and not expression.evaluate(feature):
                continue

            keyvalue = nullconvert(feature[keyfieldindex])
            valuvalue = nullconvert(feature[valuefield])
            try:
                path = feature[iconfieldindex]
                icon = QIcon(path)
            except KeyError:
                icon = QIcon()

            item = QStandardItem(unicode(keyvalue))
            item.setData(unicode(valuvalue), Qt.UserRole)
            item.setIcon(icon)
            self.listmodel.appendRow(item)

    def initWidget(self, widget):
        if widget.isEditable():
            widget.editTextChanged.connect(self.emitvaluechanged)

        widget.currentIndexChanged.connect(self.emitvaluechanged)
        widget.setModel(self.listmodel)
        widget.showPopup = self.showpopup
        widget.setIconSize(QSize(24,24))
        widget.setStyleSheet("QComboBox::drop-down {border-width: 0px;} QComboBox::down-arrow {image: url(noimg); border-width: 0px;}")

    def showpopup(self):
        if self.listmodel.rowCount() == 0:
            return

        self.largewidgetrequest.emit(BigListWidget, self.widget.currentIndex(),
                                     self._biglistitem, dict(model=self.listmodel,
                                                             label=self.labeltext))

    def updatefromconfig(self):
        super(ListWidget, self).updatefromconfig()

        self.listmodel.clear()
        if 'list' in self.config:
            listconfig = self.config['list']
            self._buildfromlist(self.widget, listconfig)
        elif 'layer' in self.config:
            layerconfig = self.config['layer']
            self._buildfromlayer(self.widget, layerconfig)

        super(ListWidget, self).endupdatefromconfig()

    @property
    def allownulls(self):
        return self.config.get('allownull', False)

    def validate(self, *args):
        if (not self.widget.currentText() == '' and not self.widget.currentText() == "(no selection)"):
            return True
        else:
            return False

    def _biglistitem(self, index):
        self.widget.setCurrentIndex(index.row())

    def setvalue(self, value):
        self._bindvalue = value
        index = self.widget.findData(value)
        self.widget.setCurrentIndex(index)
        if index == -1 and self.widget.isEditable():
            if value is None and not self.config['allownull']:
                return

            self.widget.addItem(str(value))
            index = self.widget.count() - 1
            self.widget.setCurrentIndex(index)

    def value(self):
        index = self.widget.currentIndex()
        value = self.widget.itemData(index)
        text = self.widget.currentText()
        if value is None and self.widget.isEditable() and not text == '(no selection)':
            return self.widget.currentText()

        return value
Exemple #11
0
class VizRankDialog(QDialog, ProgressBarMixin, WidgetMessagesMixin):
    """
    Base class for VizRank dialogs, providing a GUI with a table and a button,
    and the skeleton for managing the evaluation of visualizations.

    Derived classes need to provide generators of combinations (e.g. pairs
    of attribtutes) and the scoring function. The widget stores the current
    upon pause, and restores it upon continuation.

    The class provides a table and a button. A widget constructs a single
    instance of this dialog in its `__init__`, like (in Sieve):

        self.vizrank = SieveRank(self)
        self.vizrank_button = gui.button(
            box, self, "Score Combinations", callback=self.vizrank.reshow)

    The widget (the argument `self`) above is stored in `VizRankDialog`'s
    attribute `master` since derived classes will need to interact with is.

    When the widget receives new data, it must call the VizRankDialog's
    method :obj:`VizRankDialog.initialize()` to clear the GUI and reset the
    state.

    Clicking the Start button calls method `run` (and renames the button to
    Pause). Run sets up a progress bar by getting the number of combinations
    from :obj:`VizRankDialog.state_count()`. It restores the paused state
    (if any) and calls generator :obj:`VizRankDialog.iterate_states()`. For
    each generated state, it calls :obj:`VizRankDialog.score(state)`, which
    must return the score (lower is better) for this state. If the returned
    state is not `None`, the data returned by `row_for_state` is inserted at
    the appropriate place in the table.

    Args:
        master (Orange.widget.OWWidget): widget to which the dialog belongs

    Attributes:
        master (Orange.widget.OWWidget): widget to which the dialog belongs
        captionTitle (str): the caption for the dialog. This can be a class
          attribute. `captionTitle` is used by the `ProgressBarMixin`.
    """

    captionTitle = ""

    processingStateChanged = Signal(int)
    progressBarValueChanged = Signal(float)
    messageActivated = Signal(Msg)
    messageDeactivated = Signal(Msg)

    def __init__(self, master):
        """Initialize the attributes and set up the interface"""
        QDialog.__init__(self, windowTitle=self.captionTitle)
        WidgetMessagesMixin.__init__(self)
        self.setLayout(QVBoxLayout())

        self.insert_message_bar()
        self.layout().insertWidget(0, self.message_bar)
        self.master = master

        self.keep_running = False
        self.saved_state = None
        self.saved_progress = 0
        self.scores = []

        self.rank_model = QStandardItemModel(self)
        self.rank_table = view = QTableView(
            selectionBehavior=QTableView.SelectRows,
            selectionMode=QTableView.SingleSelection,
            showGrid=False)
        view.setItemDelegate(HorizontalGridDelegate())
        view.setModel(self.rank_model)
        view.selectionModel().selectionChanged.connect(
            self.on_selection_changed)
        view.horizontalHeader().setStretchLastSection(True)
        view.horizontalHeader().hide()
        self.layout().addWidget(view)

        self.button = gui.button(self,
                                 self,
                                 "Start",
                                 callback=self.toggle,
                                 default=True)

    def reshow(self):
        """Put the widget on top of all windows
        """
        self.show()
        self.raise_()
        self.activateWindow()

    def initialize(self):
        """
        Clear and initialize the dialog.

        This method must be called by the widget when the data is reset,
        e.g. from `set_data` handler.
        """
        self.keep_running = False
        self.saved_state = None
        self.saved_progress = 0
        self.scores = []
        self.rank_model.clear()
        self.button.setText("Start")
        self.button.setEnabled(self.check_preconditions())

    def check_preconditions(self):
        """Check whether there is sufficient data for ranking."""
        return True

    def on_selection_changed(self, selected, deselected):
        """
        Set the new visualization in the widget when the user select a
        row in the table.

        If derived class does not reimplement this, the table gives the
        information but the user can't click it to select the visualization.

        Args:
            selected: the index of the selected item
            deselected: the index of the previously selected item
        """
        pass

    def iterate_states(self, initial_state):
        """
        Generate all possible states (e.g. attribute combinations) for the
        given data. The content of the generated states is specific to the
        visualization.

        This method must be defined in the derived classes.

        Args:
            initial_state: initial state; None if this is the first call
        """
        raise NotImplementedError

    def state_count(self):
        """
        Return the number of states for the progress bar.

        Derived classes should implement this to ensure the proper behaviour of
        the progress bar"""
        return 0

    def compute_score(self, state):
        """
        Abstract method for computing the score for the given state. Smaller
        scores are better.

        Args:
            state: the state, e.g. the combination of attributes as generated
                by :obj:`state_count`.
        """
        raise NotImplementedError

    def row_for_state(self, state, score):
        """
        Abstract method that return the items that are inserted into the table.

        Args:
            state: the state, e.g. combination of attributes
            score: score, computed by :obj:`compute_score`
            """
        raise NotImplementedError

    def _select_first_if_none(self):
        if not self.rank_table.selectedIndexes():
            self.rank_table.selectRow(0)

    def run(self):
        """Compute and show scores"""
        with self.progressBar(self.state_count()) as progress:
            progress.advance(self.saved_progress)
            for state in self.iterate_states(self.saved_state):
                if not self.keep_running:
                    self.saved_state = state
                    self.saved_progress = progress.count
                    self._select_first_if_none()
                    return
                score = self.compute_score(state)
                if score is not None:
                    pos = bisect_left(self.scores, score)
                    self.rank_model.insertRow(pos,
                                              self.row_for_state(score, state))
                    self.scores.insert(pos, score)
                progress.advance()
            self._select_first_if_none()
            self.button.setText("Finished")
            self.button.setEnabled(False)

    def toggle(self):
        """Start or pause the computation."""
        self.keep_running = not self.keep_running
        if self.keep_running:
            self.button.setText("Pause")
            self.run()
        else:
            self._select_first_if_none()
            self.button.setText("Continue")
Exemple #12
0
class ProgramInfoFiles(QWidget, Ui_ProgramInfoFiles):
    def __init__(self, context, update_main_ui_state, set_widget_enabled,
                 is_alive, show_upload_files_wizard, show_download_wizard):
        QWidget.__init__(self)

        self.setupUi(self)

        self.session = context.session
        self.script_manager = context.script_manager
        self.program = context.program
        self.update_main_ui_state = update_main_ui_state
        self.set_widget_enabled = set_widget_enabled
        self.is_alive = is_alive
        self.show_download_wizard = show_download_wizard
        self.bin_directory = posixpath.join(self.program.root_directory, 'bin')
        self.refresh_in_progress = False
        self.any_refresh_in_progress = False  # set from ProgramInfoMain.update_ui_state
        self.available_files = []
        self.available_directories = []
        self.folder_icon = QIcon(load_pixmap('folder-icon.png'))
        self.file_icon = QIcon(load_pixmap('file-icon.png'))
        self.tree_files_model = QStandardItemModel(self)
        self.tree_files_model_header = ['Name', 'Size', 'Last Modified']
        self.tree_files_proxy_model = FilesProxyModel(self)
        self.last_download_directory = get_home_path()

        self.tree_files_model.setHorizontalHeaderLabels(
            self.tree_files_model_header)
        self.tree_files_proxy_model.setSourceModel(self.tree_files_model)
        self.tree_files.setModel(self.tree_files_model)
        self.tree_files.setModel(self.tree_files_proxy_model)
        self.tree_files.setColumnWidth(0, 210)
        self.tree_files.setColumnWidth(1, 85)

        self.tree_files.selectionModel().selectionChanged.connect(
            self.update_ui_state)
        self.tree_files.activated.connect(self.rename_activated_file)
        self.button_upload_files.clicked.connect(show_upload_files_wizard)
        self.button_download_files.clicked.connect(
            self.download_selected_files)
        self.button_rename_file.clicked.connect(self.rename_selected_file)
        self.button_delete_files.clicked.connect(self.delete_selected_files)

        self.label_error.setVisible(False)

    def update_ui_state(self):
        selection_count = len(self.tree_files.selectionModel().selectedRows())

        self.set_widget_enabled(self.button_upload_files,
                                not self.any_refresh_in_progress)
        self.set_widget_enabled(
            self.button_download_files, not self.any_refresh_in_progress
            and selection_count > 0)
        self.set_widget_enabled(
            self.button_rename_file, not self.any_refresh_in_progress
            and selection_count == 1)
        self.set_widget_enabled(
            self.button_delete_files, not self.any_refresh_in_progress
            and selection_count > 0)

    def close_all_dialogs(self):
        pass

    def refresh_files_done(self):
        self.refresh_in_progress = False
        self.update_main_ui_state()

    def refresh_files(self):
        def cb_walk(result):
            okay, message = check_script_result(result, decode_stderr=True)

            if not okay:
                self.label_error.setText('<b>Error:</b> ' + Qt.escape(message))
                self.label_error.setVisible(True)
                self.refresh_files_done()
                return

            self.label_error.setVisible(False)

            def expand_async(data):
                try:
                    walk = json.loads(
                        zlib.decompress(buffer(data)).decode('utf-8'))
                except:
                    walk = None

                if walk == None or not isinstance(walk, dict):
                    available_files = []
                    available_directories = []
                    walk = None
                else:
                    available_files, available_directories = expand_walk_to_lists(
                        walk)

                return walk, available_files, available_directories

            def cb_expand_success(result):
                walk, available_files, available_directories = result

                self.available_files = available_files
                self.available_directories = available_directories

                if walk != None:
                    expand_walk_to_model(walk, self.tree_files_model,
                                         self.folder_icon, self.file_icon)
                else:
                    self.label_error.setText(
                        '<b>Error:</b> Received invalid data')
                    self.label_error.setVisible(True)

                self.tree_files.header().setSortIndicator(0, Qt.AscendingOrder)
                self.refresh_files_done()

            def cb_expand_error():
                self.label_error.setText('<b>Error:</b> Internal async error')
                self.label_error.setVisible(True)
                self.refresh_files_done()

            async_call(expand_async, result.stdout, cb_expand_success,
                       cb_expand_error)

        self.refresh_in_progress = True
        self.update_main_ui_state()

        width1 = self.tree_files.columnWidth(0)
        width2 = self.tree_files.columnWidth(1)

        self.tree_files_model.clear()
        self.tree_files_model.setHorizontalHeaderLabels(
            self.tree_files_model_header)
        self.tree_files.setColumnWidth(0, width1)
        self.tree_files.setColumnWidth(1, width2)

        self.script_manager.execute_script('walk',
                                           cb_walk, [self.bin_directory],
                                           max_length=1024 * 1024,
                                           decode_output_as_utf8=False)

    def get_directly_selected_name_items(self):
        selected_indexes = self.tree_files.selectedIndexes()
        selected_name_items = []

        for selected_index in selected_indexes:
            if selected_index.column() == 0:
                mapped_index = self.tree_files_proxy_model.mapToSource(
                    selected_index)
                selected_name_items.append(
                    self.tree_files_model.itemFromIndex(mapped_index))

        return selected_name_items

    def download_selected_files(self):
        selected_name_items = self.get_directly_selected_name_items()

        if len(selected_name_items) == 0:
            return

        downloads = []

        def expand(name_item):
            item_type = name_item.data(USER_ROLE_ITEM_TYPE)

            if item_type == ITEM_TYPE_DIRECTORY:
                for i in range(name_item.rowCount()):
                    expand(name_item.child(i, 0))
            elif item_type == ITEM_TYPE_FILE:
                filename = get_full_item_path(name_item)

                downloads.append(
                    Download(filename, QDir.toNativeSeparators(filename)))

        for selected_name_item in selected_name_items:
            expand(selected_name_item)

        if len(downloads) == 0:
            return

        download_directory = get_existing_directory(
            get_main_window(), 'Download Files', self.last_download_directory)

        if len(download_directory) == 0:
            return

        self.last_download_directory = download_directory

        self.show_download_wizard('files', download_directory, downloads)

    def rename_activated_file(self, index):
        if index.column() == 0 and not self.any_refresh_in_progress:
            mapped_index = self.tree_files_proxy_model.mapToSource(index)
            name_item = self.tree_files_model.itemFromIndex(mapped_index)
            item_type = name_item.data(USER_ROLE_ITEM_TYPE)

            # only rename files via activation, because directories are expanded
            if item_type == ITEM_TYPE_FILE:
                self.rename_item(name_item)

    def rename_selected_file(self):
        selection_count = len(self.tree_files.selectionModel().selectedRows())

        if selection_count != 1:
            return

        selected_name_items = self.get_directly_selected_name_items()

        if len(selected_name_items) != 1:
            return

        self.rename_item(selected_name_items[0])

    def rename_item(self, name_item):
        item_type = name_item.data(USER_ROLE_ITEM_TYPE)

        if item_type == ITEM_TYPE_FILE:
            title = 'Rename File'
            type_name = 'file'
        else:
            title = 'Rename Directory'
            type_name = 'directory'

        old_name = name_item.text()

        # get new name
        dialog = ExpandingInputDialog(get_main_window())
        dialog.setModal(True)
        dialog.setWindowTitle(title)
        dialog.setLabelText('Enter new {0} name:'.format(type_name))
        dialog.setInputMode(QInputDialog.TextInput)
        dialog.setTextValue(old_name)
        dialog.setOkButtonText('Rename')

        if dialog.exec_() != QDialog.Accepted:
            return

        new_name = dialog.textValue()

        if new_name == old_name:
            return

        # check that new name is valid
        if len(
                new_name
        ) == 0 or new_name == '.' or new_name == '..' or '/' in new_name:
            QMessageBox.critical(
                get_main_window(), title + ' Error',
                'A {0} name cannot be empty, cannot be one dot [.], cannot be two dots [..] and cannot contain a forward slash [/].'
                .format(type_name))
            return

        # check that new name is not already in use
        name_item_parent = name_item.parent()

        if name_item_parent == None:
            name_item_parent = self.tree_files_model.invisibleRootItem()

        for i in range(name_item_parent.rowCount()):
            if new_name == name_item_parent.child(i).text():
                QMessageBox.critical(
                    get_main_window(), title + ' Error',
                    'The new {0} name is already in use.'.format(type_name))
                return

        absolute_old_name = posixpath.join(self.bin_directory,
                                           get_full_item_path(name_item))
        absolute_new_name = posixpath.join(
            posixpath.split(absolute_old_name)[0], new_name)

        def cb_rename(result):
            if not report_script_result(
                    result, title + ' Error',
                    u'Could not rename {0}'.format(type_name)):
                return

            name_item.setText(new_name)

            if self.tree_files.header().sortIndicatorSection() == 0:
                self.tree_files.header().setSortIndicator(
                    0,
                    self.tree_files.header().sortIndicatorOrder())

        self.script_manager.execute_script(
            'rename', cb_rename, [absolute_old_name, absolute_new_name])

    def delete_selected_files(self):
        button = QMessageBox.question(
            get_main_window(), 'Delete Files',
            'Irreversibly deleting selected files and directories.',
            QMessageBox.Ok, QMessageBox.Cancel)

        if not self.is_alive() or button != QMessageBox.Ok:
            return

        selected_name_items = set(self.get_directly_selected_name_items())

        if len(selected_name_items) == 0:
            return

        script_instance_ref = [None]

        def progress_canceled():
            script_instance = script_instance_ref[0]

            if script_instance == None:
                return

            self.script_manager.abort_script(script_instance)

        progress = ExpandingProgressDialog(self)
        progress.set_progress_text_visible(False)
        progress.setModal(True)
        progress.setWindowTitle('Delete Files')
        progress.setLabelText('Collecting files and directories to delete')
        progress.setRange(0, 0)
        progress.canceled.connect(progress_canceled)
        progress.show()

        files_to_delete = []
        dirs_to_delete = []
        all_done = False

        while not all_done:
            all_done = True

            for selected_name_item in list(selected_name_items):
                item_done = False
                parent = selected_name_item.parent()

                while not item_done and parent != None:
                    if parent in selected_name_items:
                        selected_name_items.remove(selected_name_item)
                        item_done = True
                    else:
                        parent = parent.parent()

                if item_done:
                    all_done = False
                    break

        for selected_name_item in selected_name_items:
            path = get_full_item_path(selected_name_item)
            item_type = selected_name_item.data(USER_ROLE_ITEM_TYPE)

            if item_type == ITEM_TYPE_DIRECTORY:
                dirs_to_delete.append(posixpath.join(self.bin_directory, path))
            else:
                files_to_delete.append(posixpath.join(self.bin_directory,
                                                      path))

        message = 'Deleting '

        if len(files_to_delete) == 1:
            message += '1 file '
        elif len(files_to_delete) > 1:
            message += '{0} files '.format(len(files_to_delete))

        if len(dirs_to_delete) == 1:
            if len(files_to_delete) > 0:
                message += 'and '

            message += '1 directory'
        elif len(dirs_to_delete) > 1:
            if len(files_to_delete) > 0:
                message += 'and '

            message += '{0} directories'.format(len(dirs_to_delete))

        progress.setLabelText(message)

        def cb_delete(result):
            script_instance = script_instance_ref[0]

            if script_instance != None:
                aborted = script_instance.abort
            else:
                aborted = False

            script_instance_ref[0] = None

            progress.cancel()
            self.refresh_files()

            if aborted:
                QMessageBox.information(get_main_window(), 'Delete Files',
                                        u'Delete operation was aborted.')
                return

            report_script_result(
                result, 'Delete Files Error',
                'Could not delete selected files/directories:')

        script_instance_ref[0] = self.script_manager.execute_script(
            'delete',
            cb_delete,
            [json.dumps(files_to_delete),
             json.dumps(dirs_to_delete)],
            execute_as_user=True)
Exemple #13
0
class TreeView(QTreeView):
    def __init__(self, parent):
        QTreeView.__init__(self, parent)
        while not isinstance(parent, QDialog) and not isinstance(parent, QMainWindow):
            parent = parent.parent()
        self.setObjectName("TreeView" + str(len(parent.findChildren(TreeView))))

        # self.setObjectName("TreeViewWidget")
        # self.hLayout = QHBoxLayout(self)
        # self.hLayout.setObjectName("hLayout")
        # 
        # sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        # sizePolicy.setHorizontalStretch(0)
        # sizePolicy.setVerticalStretch(0)
        # sizePolicy.setHeightForWidth(self.sizePolicy().hasHeightForWidth())
        # self.setSizePolicy(sizePolicy)
        # 
        # # self.frame = Frame()
        # self = QTreeView(self)
        # self.hLayout.addWidget(self)

        self.stdModel = QStandardItemModel()
        self.setModel(self.stdModel)

        self.hasObject = False
        self.treeNodeList = []

        self.checkBoxList = []
        self.setHeaderHidden(True)
        # self.stdModel.appendRow(TreeNode("P"))
        # rootIndex = self.rootIndex()

        # rootItem = self.stdModel.itemFromIndex(rootIndex)
        # rootItem.setText("Root")

    def mouseMoveEvent(self, mouseEvent):
        pt = mouseEvent.pos()
        pass
    def Clear(self):
        self.stdModel.clear()
        self.hasObject = False
        self.treeNodeList = []
    def Add(self, caption):
        item = TreeNode(caption)
        if len(self.treeNodeList) > 0:
            item.PrevNode = self.treeNodeList[len(self.treeNodeList) - 1]
            item.PrevNode.NextNode = item
            item.Index = len(self.treeNodeList)
        # item.nodeIndex = len(self.treeNodeList)
        self.treeNodeList.append(item)
        self.stdModel.appendRow(item)
        return item
    def RemoveNode(self, i):
        self.stdModel.removeRow(i)
        self.treeNodeList.pop(i)
        for j in range(i, len(self.treeNodeList)):
            self.treeNodeList[j].nodeIndex -= 1
    def Remove(self, item):
        removedIndex = self.treeNodeList.index(item)
        if removedIndex == 0:
            self.treeNodeList[1].PrevNode = None
        elif removedIndex == len(self.treeNodeList) - 1:
            self.treeNodeList[len(self.treeNodeList) - 2].NextNode = None
        else:
            self.treeNodeList[removedIndex + 1].PrevNode = self.treeNodeList[removedIndex - 1]
            self.treeNodeList[removedIndex - 1].NextNode = self.treeNodeList[removedIndex + 1]
        self.treeNodeList.pop(removedIndex)
        self.stdModel.removeRow(removedIndex)
        i = 0
        for node in self.treeNodeList:
            node.Index = i
            node.LastNode = self.treeNodeList[len(self.treeNodeList) - 1]
            i += 1
    def Insert(self, index, text):
        if index == 0 and len(self.treeNodeList) == 0:
            self.Add(text)
            return
        node = TreeNode(text)
        node.Parent = self
        self.treeNodeList.insert(index, node)
        i = 0
        for node0 in self.treeNodeList:
            node0.Index = i
            i += 1
        if index == 0:
            self.treeNodeList[index].PrevNode = None
            if len(self.treeNodeList) == 1:
                self.treeNodeList[index].NextNode = None
                self.treeNodeList[index].LastNode = self.treeNodeList[index]
            else:
                self.treeNodeList[index].NextNode = self.treeNodeList[index + 1]
                self.treeNodeList[index].LastNode = self.treeNodeList[len(self.treeNodeList) - 1]
            
            self.treeNodeList[index + 1].PrevNode = self.treeNodeList[index]
            
        else:
            self.treeNodeList[index].PrevNode = self.treeNodeList[index - 1]
            self.treeNodeList[index].NextNode = self.treeNodeList[index + 1]
            self.treeNodeList[index].LastNode = self.treeNodeList[len(self.treeNodeList) - 1]
            
            self.treeNodeList[index + 1].PrevNode = self.treeNodeList[index]
            self.treeNodeList[index - 1].NextNode = self.treeNodeList[index]
            

        self.stdModel.insertRow(index, node)
        return node

    def get_Items(self):
        return self.treeNodeList
    Nodes = property(get_Items, None, None, None)

    def Node(self, index):
        if not self.stdModel.rowCount() > 0:
            return None
        return self.treeNodeList[index]

    def getSelectedNode(self):
        if not self.stdModel.rowCount() > 0:
            return None
        index = self.currentIndex()
        return self.stdModel.itemFromIndex(index)
    def setSelectedNode(self, node):
        if not self.stdModel.rowCount() > 0:
            return
        # self.s
        index = self.stdModel.indexFromItem(node)
        self.setCurrentIndex(index)
        # self.treeNodeList.pop(index)
        # self.treeNodeList.insert(index, node)
        # self.stdModel.setItem(index, node)

    SelectedNode = property(getSelectedNode, setSelectedNode, None, None)




    def get_Enabled(self):
        return self.isEnabled()
    def set_Enabled(self, bool):
        self.setEnabled(bool)
    Enabled = property(get_Enabled, set_Enabled, None, None)

    def get_Visible(self):
        return self.isVisible()
    def set_Visible(self, bool):
        self.setVisible(bool)
    Visible = property(get_Visible, set_Visible, None, None)
class OWConfusionMatrix(widget.OWWidget):
    name = "Confusion Matrix"
    description = "Display confusion matrix constructed from results " \
                  "of evaluation of classifiers."
    icon = "icons/ConfusionMatrix.svg"
    priority = 1001

    inputs = [("Evaluation Results", Orange.evaluation.Results, "set_results")]
    outputs = [("Selected Data", Orange.data.Table)]

    quantities = [
        "Number of instances", "Proportion of predicted",
        "Proportion of actual"
    ]

    selected_learner = settings.Setting([])
    selected_quantity = settings.Setting(0)
    append_predictions = settings.Setting(True)
    append_probabilities = settings.Setting(False)
    autocommit = settings.Setting(True)

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

        self.data = None
        self.results = None
        self.learners = []
        self.headers = []

        box = gui.widgetBox(self.controlArea, "Learners")

        self.learners_box = gui.listBox(box,
                                        self,
                                        "selected_learner",
                                        "learners",
                                        callback=self._learner_changed)
        box = gui.widgetBox(self.controlArea, "Show")

        gui.comboBox(box,
                     self,
                     "selected_quantity",
                     items=self.quantities,
                     callback=self._update)

        box = gui.widgetBox(self.controlArea, "Select")

        gui.button(box,
                   self,
                   "Correct",
                   callback=self.select_correct,
                   autoDefault=False)
        gui.button(box,
                   self,
                   "Misclassified",
                   callback=self.select_wrong,
                   autoDefault=False)
        gui.button(box,
                   self,
                   "None",
                   callback=self.select_none,
                   autoDefault=False)

        self.outputbox = box = gui.widgetBox(self.controlArea, "Output")
        gui.checkBox(box,
                     self,
                     "append_predictions",
                     "Predictions",
                     callback=self._invalidate)
        gui.checkBox(box,
                     self,
                     "append_probabilities",
                     "Probabilities",
                     callback=self._invalidate)

        gui.auto_commit(self.controlArea, self, "autocommit", "Send Data",
                        "Auto send is on")

        grid = QGridLayout()

        self.tablemodel = QStandardItemModel(self)
        view = self.tableview = QTableView(
            editTriggers=QTableView.NoEditTriggers)
        view.setModel(self.tablemodel)
        view.horizontalHeader().hide()
        view.verticalHeader().hide()
        view.horizontalHeader().setMinimumSectionSize(60)
        view.selectionModel().selectionChanged.connect(self._invalidate)
        view.setShowGrid(False)
        view.clicked.connect(self.cell_clicked)
        grid.addWidget(view, 0, 0)
        self.mainArea.layout().addLayout(grid)

    def sizeHint(self):
        return QSize(750, 490)

    def _item(self, i, j):
        return self.tablemodel.item(i, j) or QStandardItem()

    def _set_item(self, i, j, item):
        self.tablemodel.setItem(i, j, item)

    def set_results(self, results):
        """Set the input results."""

        self.clear()
        self.warning([0, 1])

        data = None
        if results is not None:
            if results.data is not None:
                data = results.data

        if data is not None and not data.domain.has_discrete_class:
            data = None
            results = None
            self.warning(
                0, "Confusion Matrix cannot be used for regression results.")

        self.results = results
        self.data = data

        if data is not None:
            class_values = data.domain.class_var.values
        elif results is not None:
            raise NotImplementedError

        if results is not None:
            nmodels, ntests = results.predicted.shape
            self.headers = class_values + \
                           [unicodedata.lookup("N-ARY SUMMATION")]

            # NOTE: The 'learner_names' is set in 'Test Learners' widget.
            if hasattr(results, "learner_names"):
                self.learners = results.learner_names
            else:
                self.learners = [
                    "Learner #%i" % (i + 1) for i in range(nmodels)
                ]

            item = self._item(0, 2)
            item.setData("Predicted", Qt.DisplayRole)
            item.setTextAlignment(Qt.AlignCenter)
            item.setFlags(Qt.NoItemFlags)

            self._set_item(0, 2, item)
            item = self._item(2, 0)
            item.setData("Actual", Qt.DisplayRole)
            item.setTextAlignment(Qt.AlignHCenter | Qt.AlignBottom)
            item.setFlags(Qt.NoItemFlags)
            self.tableview.setItemDelegateForColumn(0,
                                                    gui.VerticalItemDelegate())
            self._set_item(2, 0, item)
            self.tableview.setSpan(0, 2, 1, len(class_values))
            self.tableview.setSpan(2, 0, len(class_values), 1)

            for i in (0, 1):
                for j in (0, 1):
                    item = self._item(i, j)
                    item.setFlags(Qt.NoItemFlags)
                    self._set_item(i, j, item)

            for p, label in enumerate(self.headers):
                for i, j in ((1, p + 2), (p + 2, 1)):
                    item = self._item(i, j)
                    item.setData(label, Qt.DisplayRole)
                    item.setData(QBrush(QColor(208, 208, 208)),
                                 Qt.BackgroundColorRole)
                    item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                    item.setFlags(Qt.ItemIsEnabled)
                    self._set_item(i, j, item)

            hor_header = self.tableview.horizontalHeader()
            if len(' '.join(self.headers)) < 120:
                hor_header.setResizeMode(QHeaderView.ResizeToContents)
            else:
                hor_header.setDefaultSectionSize(60)
            self.tablemodel.setRowCount(len(class_values) + 3)
            self.tablemodel.setColumnCount(len(class_values) + 3)
            self.selected_learner = [0]
            self._update()

    def clear(self):
        self.results = None
        self.data = None
        self.tablemodel.clear()
        self.headers = []
        # Clear learners last. This action will invoke `_learner_changed`
        # method
        self.learners = []

    def select_correct(self):
        selection = QItemSelection()
        n = self.tablemodel.rowCount()
        for i in range(2, n):
            index = self.tablemodel.index(i, i)
            selection.select(index, index)

        self.tableview.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

    def select_wrong(self):
        selection = QItemSelection()
        n = self.tablemodel.rowCount()

        for i in range(2, n):
            for j in range(i + 1, n):
                index = self.tablemodel.index(i, j)
                selection.select(index, index)
                index = self.tablemodel.index(j, i)
                selection.select(index, index)

        self.tableview.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

    def select_none(self):
        self.tableview.selectionModel().clear()

    def cell_clicked(self, model_index):
        i, j = model_index.row(), model_index.column()
        if not i or not j:
            return
        n = self.tablemodel.rowCount()
        index = self.tablemodel.index
        selection = None
        if i == j == 1 or i == j == n - 1:
            selection = QItemSelection(index(2, 2), index(n - 1, n - 1))
        elif i in (1, n - 1):
            selection = QItemSelection(index(2, j), index(n - 1, j))
        elif j in (1, n - 1):
            selection = QItemSelection(index(i, 2), index(i, n - 1))

        if selection is not None:
            self.tableview.selectionModel().select(
                selection, QItemSelectionModel.ClearAndSelect)

    def commit(self):
        if self.results is not None and self.data is not None \
                and self.selected_learner:
            indices = self.tableview.selectedIndexes()
            indices = {(ind.row() - 2, ind.column() - 2) for ind in indices}
            actual = self.results.actual
            selected_learner = self.selected_learner[0]
            learner_name = self.learners[selected_learner]
            predicted = self.results.predicted[selected_learner]
            selected = [
                i for i, t in enumerate(zip(actual, predicted)) if t in indices
            ]
            row_indices = self.results.row_indices[selected]

            extra = []
            class_var = self.data.domain.class_var
            metas = self.data.domain.metas

            if self.append_predictions:
                predicted = numpy.array(predicted[selected], dtype=object)
                extra.append(predicted.reshape(-1, 1))
                var = Orange.data.DiscreteVariable(
                    "{}({})".format(class_var.name, learner_name),
                    class_var.values)
                metas = metas + (var, )

            if self.append_probabilities and \
                    self.results.probabilities is not None:
                probs = self.results.probabilities[selected_learner, selected]
                extra.append(numpy.array(probs, dtype=object))
                pvars = [
                    Orange.data.ContinuousVariable("p({})".format(value))
                    for value in class_var.values
                ]
                metas = metas + tuple(pvars)

            X = self.data.X[row_indices]
            Y = self.data.Y[row_indices]
            M = self.data.metas[row_indices]
            row_ids = self.data.ids[row_indices]

            M = numpy.hstack((M, ) + tuple(extra))
            domain = Orange.data.Domain(self.data.domain.attributes,
                                        self.data.domain.class_vars, metas)
            data = Orange.data.Table.from_numpy(domain, X, Y, M)
            data.ids = row_ids
            data.name = learner_name

        else:
            data = None

        self.send("Selected Data", data)

    def _invalidate(self):
        self.commit()

    def _learner_changed(self):
        # The selected learner has changed
        indices = self.tableview.selectedIndexes()
        self._update()
        selection = QItemSelection()
        for sel in indices:
            selection.select(sel, sel)
        self.tableview.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)
        self.commit()

    def _update(self):
        # Update the displayed confusion matrix
        if self.results is not None and self.selected_learner:
            index = self.selected_learner[0]
            cmatrix = confusion_matrix(self.results, index)
            colsum = cmatrix.sum(axis=0)
            rowsum = cmatrix.sum(axis=1)
            total = rowsum.sum()

            if self.selected_quantity == 0:
                value = lambda i, j: int(cmatrix[i, j])
            elif self.selected_quantity == 1:
                value = lambda i, j: \
                    ("{:2.1f} %".format(100 * cmatrix[i, j] / colsum[i])
                     if colsum[i] else "N/A")
            elif self.selected_quantity == 2:
                value = lambda i, j: \
                    ("{:2.1f} %".format(100 * cmatrix[i, j] / rowsum[i])
                     if colsum[i] else "N/A")
            else:
                assert False

            for i, row in enumerate(cmatrix):
                for j, _ in enumerate(row):
                    item = self._item(i + 2, j + 2)
                    item.setData(value(i, j), Qt.DisplayRole)
                    item.setToolTip("actual: {}\npredicted: {}".format(
                        self.headers[i], self.headers[j]))
                    item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                    item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
                    self._set_item(i + 2, j + 2, item)

            model = self.tablemodel
            font = model.invisibleRootItem().font()
            bold_font = QFont(font)
            bold_font.setBold(True)

            def sum_item(value):
                item = QStandardItem()
                item.setData(value, Qt.DisplayRole)
                item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                item.setFlags(Qt.ItemIsEnabled)
                item.setFont(bold_font)
                return item

            N = len(colsum)
            for i in range(N):
                model.setItem(N + 2, i + 2, sum_item(int(colsum[i])))
                model.setItem(i + 2, N + 2, sum_item(int(rowsum[i])))

            model.setItem(N + 2, N + 2, sum_item(int(total)))
class AbstractSTREnityListView(QListView):
    """
    A widget for listing and selecting one or more STR entities.
    .. versionadded:: 1.7
    """
    def __init__(self, parent=None, **kwargs):
        super(AbstractSTREnityListView, self).__init__(parent)

        self._model = QStandardItemModel(self)
        self._model.setColumnCount(1)
        self.setModel(self._model)
        self.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self._model.itemChanged.connect(self._on_item_changed)

        self._profile = kwargs.get('profile', None)
        self._social_tenure = kwargs.get('social_tenure', None)

        # Load appropriate entities to the view
        if not self._profile is None:
            self._load_profile_entities()

        # Load entities in the STR definition
        if not self._social_tenure is None:
            self._select_str_entities()

    def _on_item_changed(self, item):
        # Emit signals when an item has been (de)selected. To be
        # implemented by subclasses.
        pass

    @property
    def profile(self):
        """
        :return: Returns the current profile object in the configuration.
        :rtype: Profile
        """
        return self._profile

    @profile.setter
    def profile(self, profile):
        """
        Sets the current profile object in the configuration.
        :param profile: Profile object.
        :type profile: Profile
        """
        self._profile = profile
        self._load_profile_entities()

    @property
    def social_tenure(self):
        """
        :return: Returns the profile's social tenure entity.
        :rtype: SocialTenure
        """
        return self._social_tenure

    @social_tenure.setter
    def social_tenure(self, social_tenure):
        """
        Set the social_tenure entity.
        :param social_tenure: A profile's social tenure entity.
        :type social_tenure: SocialTenure
        """
        self._social_tenure = social_tenure
        self._select_str_entities()

    def _select_str_entities(self):
        """
        Select the entities defined in the STR. E.g. parties for party 
        entity and spatial units for spatial unit entity. Default 
        implementation does nothing, to be implemented by subclasses.
        """
        pass

    def _load_profile_entities(self):
        # Reset view
        self.clear()

        # Populate entity items in the view
        for e in self._profile.user_entities():
            self._add_entity(e)

    def _add_entity(self, entity):
        # Add entity item to view
        item = QStandardItem(
            QIcon(':/plugins/stdm/images/icons/table.png'),
            entity.short_name
        )
        item.setCheckable(True)
        item.setCheckState(Qt.Unchecked)

        self._model.appendRow(item)

    def select_entities(self, entities):
        """
        Checks STR entities in the view and emit the entity_selected
        signal for each item selected.
        :param entities: Collection of STR entities.
        :type entities: list
        """
        # Clear selection
        self.clear_selection()

        for e in entities:
            name = e.short_name
            self.select_entity(name)

    def selected_entities(self):
        """
        :return: Returns a list of selected entity short names.
        :rtype: list
        """
        selected_items = []

        for i in range(self._model.rowCount()):
            item = self._model.item(i)
            if item.checkState() == Qt.Checked:
                selected_items.append(item.text())

        return selected_items

    def clear(self):
        """
        Remove all party items in the view.
        """
        self._model.clear()
        self._model.setColumnCount(1)

    def clear_selection(self):
        """
        Uncheck all items in the view.
        """
        for i in range(self._model.rowCount()):
            item = self._model.item(i)
            if item.checkState() == Qt.Checked:
                item.setCheckState(Qt.Unchecked)

    def select_entity(self, name):
        """
        Selects a party entity with the given short name.
        :param name: Entity short name
        :type name: str
        """
        items = self._model.findItems(name)
        if len(items) > 0:
            item = items[0]
            if item.checkState() == Qt.Unchecked:
                item.setCheckState(Qt.Checked)

    def deselect_entity(self, name):
        """
        Deselects an entity with the given short name.
        :param name: Entity short name
        :type name: str
        """
        items = self._model.findItems(name)
        if len(items) > 0:
            item = items[0]
            if item.checkState() == Qt.Checked:
                item.setCheckState(Qt.Unchecked)
Exemple #16
0
class DlgSqlWindow(QWidget, Ui_Dialog):
    nameChanged = pyqtSignal(str)

    def __init__(self, iface, db, parent=None):
        QWidget.__init__(self, parent)
        self.iface = iface
        self.db = db
        self.allowMultiColumnPk = isinstance(
            db, PGDatabase
        )  # at the moment only PostGIS allows a primary key to span multiple columns, spatialite doesn't
        self.setupUi(self)
        self.setWindowTitle(
            u"%s - %s [%s]" %
            (self.windowTitle(), db.connection().connectionName(),
             db.connection().typeNameString()))

        self.defaultLayerName = 'QueryLayer'

        if self.allowMultiColumnPk:
            self.uniqueColumnCheck.setText(
                self.trUtf8("Column(s) with unique values"))
        else:
            self.uniqueColumnCheck.setText(
                self.trUtf8("Column with unique values"))

        self.editSql.setFocus()
        self.editSql.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self.initCompleter()

        # allow to copy results
        copyAction = QAction("copy", self)
        self.viewResult.addAction(copyAction)
        copyAction.setShortcuts(QKeySequence.Copy)

        copyAction.triggered.connect(self.copySelectedResults)

        self.btnExecute.clicked.connect(self.executeSql)
        self.btnClear.clicked.connect(self.clearSql)

        self.presetStore.clicked.connect(self.storePreset)
        self.presetDelete.clicked.connect(self.deletePreset)
        self.presetCombo.activated[str].connect(self.loadPreset)
        self.presetCombo.activated[str].connect(self.presetName.setText)

        self.updatePresetsCombobox()

        self.geomCombo.setEditable(True)
        self.geomCombo.lineEdit().setReadOnly(True)

        self.uniqueCombo.setEditable(True)
        self.uniqueCombo.lineEdit().setReadOnly(True)
        self.uniqueModel = QStandardItemModel(self.uniqueCombo)
        self.uniqueCombo.setModel(self.uniqueModel)
        if self.allowMultiColumnPk:
            self.uniqueCombo.setItemDelegate(QStyledItemDelegate())
            self.uniqueModel.itemChanged.connect(
                self.uniqueChanged)  # react to the (un)checking of an item
            self.uniqueCombo.lineEdit().textChanged.connect(
                self.uniqueTextChanged
            )  # there are other events that change the displayed text and some of them can not be caught directly

        # hide the load query as layer if feature is not supported
        self._loadAsLayerAvailable = self.db.connector.hasCustomQuerySupport()
        self.loadAsLayerGroup.setVisible(self._loadAsLayerAvailable)
        if self._loadAsLayerAvailable:
            self.layerTypeWidget.hide()  # show if load as raster is supported
            self.loadLayerBtn.clicked.connect(self.loadSqlLayer)
            self.getColumnsBtn.clicked.connect(self.fillColumnCombos)
            self.loadAsLayerGroup.toggled.connect(self.loadAsLayerToggled)
            self.loadAsLayerToggled(False)

        self._createViewAvailable = self.db.connector.hasCreateSpatialViewSupport(
        )
        self.btnCreateView.setVisible(self._createViewAvailable)
        if self._createViewAvailable:
            self.btnCreateView.clicked.connect(self.createView)

        self.queryBuilderFirst = True
        self.queryBuilderBtn.setIcon(QIcon(":/db_manager/icons/sql.gif"))
        self.queryBuilderBtn.clicked.connect(self.displayQueryBuilder)

        self.presetName.textChanged.connect(self.nameChanged)

    def updatePresetsCombobox(self):
        self.presetCombo.clear()

        names = []
        entries = QgsProject.instance().subkeyList('DBManager', 'savedQueries')
        for entry in entries:
            name = QgsProject.instance().readEntry(
                'DBManager', 'savedQueries/' + entry + '/name')[0]
            names.append(name)

        for name in sorted(names):
            self.presetCombo.addItem(name)
        self.presetCombo.setCurrentIndex(-1)

    def storePreset(self):
        query = self._getSqlQuery()
        if query == "":
            return
        name = self.presetName.text()
        QgsProject.instance().writeEntry(
            'DBManager', 'savedQueries/q' + unicode(name.__hash__()) + '/name',
            name)
        QgsProject.instance().writeEntry(
            'DBManager',
            'savedQueries/q' + unicode(name.__hash__()) + '/query', query)
        index = self.presetCombo.findText(name)
        if index == -1:
            self.presetCombo.addItem(name)
            self.presetCombo.setCurrentIndex(self.presetCombo.count() - 1)
        else:
            self.presetCombo.setCurrentIndex(index)

    def deletePreset(self):
        name = self.presetCombo.currentText()
        QgsProject.instance().removeEntry(
            'DBManager', 'savedQueries/q' + unicode(name.__hash__()))
        self.presetCombo.removeItem(self.presetCombo.findText(name))
        self.presetCombo.setCurrentIndex(-1)

    def loadPreset(self, name):
        query = QgsProject.instance().readEntry(
            'DBManager',
            'savedQueries/q' + unicode(name.__hash__()) + '/query')[0]
        name = QgsProject.instance().readEntry(
            'DBManager',
            'savedQueries/q' + unicode(name.__hash__()) + '/name')[0]
        self.editSql.setText(query)

    def loadAsLayerToggled(self, checked):
        self.loadAsLayerGroup.setChecked(checked)
        self.loadAsLayerWidget.setVisible(checked)
        if checked:
            self.fillColumnCombos()

    def clearSql(self):
        self.editSql.clear()
        self.editSql.setFocus()

    def executeSql(self):

        sql = self._getSqlQuery()
        if sql == "":
            return

        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))

        # delete the old model
        old_model = self.viewResult.model()
        self.viewResult.setModel(None)
        if old_model:
            old_model.deleteLater()

        cols = []
        quotedCols = []

        try:
            # set the new model
            model = self.db.sqlResultModel(sql, self)
            self.viewResult.setModel(model)
            self.lblResult.setText(
                self.tr("%d rows, %.1f seconds") %
                (model.affectedRows(), model.secs()))
            cols = self.viewResult.model().columnNames()
            for col in cols:
                quotedCols.append(self.db.connector.quoteId(col))

        except BaseError as e:
            QApplication.restoreOverrideCursor()
            DlgDbError.showError(e, self)
            self.uniqueModel.clear()
            self.geomCombo.clear()
            return

        self.setColumnCombos(cols, quotedCols)

        self.update()
        QApplication.restoreOverrideCursor()

    def loadSqlLayer(self):
        hasUniqueField = self.uniqueColumnCheck.checkState() == Qt.Checked
        if hasUniqueField:
            if self.allowMultiColumnPk:
                checkedCols = []
                for item in self.uniqueModel.findItems("*", Qt.MatchWildcard):
                    if item.checkState() == Qt.Checked:
                        checkedCols.append(item.data())
                uniqueFieldName = ",".join(checkedCols)
            elif self.uniqueCombo.currentIndex() >= 0:
                uniqueFieldName = self.uniqueModel.item(
                    self.uniqueCombo.currentIndex()).data()
            else:
                uniqueFieldName = None
        else:
            uniqueFieldName = None
        hasGeomCol = self.hasGeometryCol.checkState() == Qt.Checked
        if hasGeomCol:
            geomFieldName = self.geomCombo.currentText()
        else:
            geomFieldName = None

        query = self._getSqlQuery()
        if query == "":
            return

        # remove a trailing ';' from query if present
        if query.strip().endswith(';'):
            query = query.strip()[:-1]

        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))

        from qgis.core import QgsMapLayer, QgsMapLayerRegistry

        layerType = QgsMapLayer.VectorLayer if self.vectorRadio.isChecked(
        ) else QgsMapLayer.RasterLayer

        # get a new layer name
        names = []
        for layer in QgsMapLayerRegistry.instance().mapLayers().values():
            names.append(layer.name())

        layerName = self.layerNameEdit.text()
        if layerName == "":
            layerName = self.defaultLayerName
        newLayerName = layerName
        index = 1
        while newLayerName in names:
            index += 1
            newLayerName = u"%s_%d" % (layerName, index)

        # create the layer
        layer = self.db.toSqlLayer(query, geomFieldName, uniqueFieldName,
                                   newLayerName, layerType,
                                   self.avoidSelectById.isChecked())
        if layer.isValid():
            QgsMapLayerRegistry.instance().addMapLayers([layer], True)

        QApplication.restoreOverrideCursor()

    def fillColumnCombos(self):
        query = self._getSqlQuery()
        if query == "":
            return

        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))

        # get a new alias
        aliasIndex = 0
        while True:
            alias = "_%s__%d" % ("subQuery", aliasIndex)
            escaped = re.compile('\\b("?)' + re.escape(alias) + '\\1\\b')
            if not escaped.search(query):
                break
            aliasIndex += 1

        # remove a trailing ';' from query if present
        if query.strip().endswith(';'):
            query = query.strip()[:-1]

        # get all the columns
        cols = []
        quotedCols = []
        connector = self.db.connector
        sql = u"SELECT * FROM (%s\n) AS %s LIMIT 0" % (
            unicode(query), connector.quoteId(alias))

        c = None
        try:
            c = connector._execute(None, sql)
            cols = connector._get_cursor_columns(c)
            for col in cols:
                quotedCols.append(connector.quoteId(col))

        except BaseError as e:
            QApplication.restoreOverrideCursor()
            DlgDbError.showError(e, self)
            self.uniqueModel.clear()
            self.geomCombo.clear()
            return

        finally:
            if c:
                c.close()
                del c

        self.setColumnCombos(cols, quotedCols)

        QApplication.restoreOverrideCursor()

    def setColumnCombos(self, cols, quotedCols):
        # get sensible default columns. do this before sorting in case there's hints in the column order (eg, id is more likely to be first)
        try:
            defaultGeomCol = next(
                col for col in cols
                if col in ['geom', 'geometry', 'the_geom', 'way'])
        except:
            defaultGeomCol = None
        try:
            defaultUniqueCol = [col for col in cols if 'id' in col][0]
        except:
            defaultUniqueCol = None

        colNames = sorted(zip(cols, quotedCols))
        newItems = []
        uniqueIsFilled = False
        for (col, quotedCol) in colNames:
            item = QStandardItem(col)
            item.setData(quotedCol)
            item.setEnabled(True)
            item.setCheckable(self.allowMultiColumnPk)
            item.setSelectable(not self.allowMultiColumnPk)
            if self.allowMultiColumnPk:
                matchingItems = self.uniqueModel.findItems(col)
                if matchingItems:
                    item.setCheckState(matchingItems[0].checkState())
                    uniqueIsFilled = uniqueIsFilled or matchingItems[
                        0].checkState() == Qt.Checked
                else:
                    item.setCheckState(Qt.Unchecked)
            newItems.append(item)
        if self.allowMultiColumnPk:
            self.uniqueModel.clear()
            self.uniqueModel.appendColumn(newItems)
            self.uniqueChanged()
        else:
            previousUniqueColumn = self.uniqueCombo.currentText()
            self.uniqueModel.clear()
            self.uniqueModel.appendColumn(newItems)
            if self.uniqueModel.findItems(previousUniqueColumn):
                self.uniqueCombo.setEditText(previousUniqueColumn)
                uniqueIsFilled = True

        oldGeometryColumn = self.geomCombo.currentText()
        self.geomCombo.clear()
        self.geomCombo.addItems(cols)
        self.geomCombo.setCurrentIndex(
            self.geomCombo.findText(oldGeometryColumn, Qt.MatchExactly))

        # set sensible default columns if the columns are not already set
        try:
            if self.geomCombo.currentIndex() == -1:
                self.geomCombo.setCurrentIndex(cols.index(defaultGeomCol))
        except:
            pass
        items = self.uniqueModel.findItems(defaultUniqueCol)
        if items and not uniqueIsFilled:
            if self.allowMultiColumnPk:
                items[0].setCheckState(Qt.Checked)
            else:
                self.uniqueCombo.setEditText(defaultUniqueCol)
        try:
            pass
        except:
            pass

    def copySelectedResults(self):
        if len(self.viewResult.selectedIndexes()) <= 0:
            return
        model = self.viewResult.model()

        # convert to string using tab as separator
        text = model.headerToString("\t")
        for idx in self.viewResult.selectionModel().selectedRows():
            text += "\n" + model.rowToString(idx.row(), "\t")

        QApplication.clipboard().setText(text, QClipboard.Selection)
        QApplication.clipboard().setText(text, QClipboard.Clipboard)

    def initCompleter(self):
        dictionary = None
        if self.db:
            dictionary = self.db.connector.getSqlDictionary()
        if not dictionary:
            # use the generic sql dictionary
            from .sql_dictionary import getSqlDictionary

            dictionary = getSqlDictionary()

        wordlist = []
        for name, value in dictionary.iteritems():
            wordlist += value  # concat lists
        wordlist = list(set(wordlist))  # remove duplicates

        api = QsciAPIs(self.editSql.lexer())
        for word in wordlist:
            api.add(word)

        api.prepare()
        self.editSql.lexer().setAPIs(api)

    def displayQueryBuilder(self):
        dlg = QueryBuilderDlg(self.iface,
                              self.db,
                              self,
                              reset=self.queryBuilderFirst)
        self.queryBuilderFirst = False
        r = dlg.exec_()
        if r == QDialog.Accepted:
            self.editSql.setText(dlg.query)

    def createView(self):
        name, ok = QInputDialog.getText(None, "View name", "View name")
        if ok:
            try:
                self.db.connector.createSpatialView(name, self._getSqlQuery())
            except BaseError as e:
                DlgDbError.showError(e, self)

    def _getSqlQuery(self):
        sql = self.editSql.selectedText()
        if len(sql) == 0:
            sql = self.editSql.text()
        return sql

    def uniqueChanged(self):
        # when an item is (un)checked, simply trigger an update of the combobox text
        self.uniqueTextChanged(None)

    def uniqueTextChanged(self, text):
        # Whenever there is new text displayed in the combobox, check if it is the correct one and if not, display the correct one.
        checkedItems = []
        for item in self.uniqueModel.findItems("*", Qt.MatchWildcard):
            if item.checkState() == Qt.Checked:
                checkedItems.append(item.text())
        label = ", ".join(checkedItems)
        if text != label:
            self.uniqueCombo.setEditText(label)
Exemple #17
0
class SelectionSetsWidget(QFrame):
    """
    Widget for managing multiple stored item selections
    """
    selectionModified = Signal(bool)

    def __init__(self, parent):
        QFrame.__init__(self, parent)
        self.setContentsMargins(0, 0, 0, 0)
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(1)
        self._setNameLineEdit = QLineEdit(self)
        layout.addWidget(self._setNameLineEdit)

        self._setListView = QListView(self)
        self._listModel = QStandardItemModel(self)
        self._proxyModel = QSortFilterProxyModel(self)
        self._proxyModel.setSourceModel(self._listModel)

        self._setListView.setModel(self._proxyModel)
        self._setListView.setItemDelegate(ListItemDelegate(self))

        self._setNameLineEdit.textChanged.connect(
            self._proxyModel.setFilterFixedString)

        self._completer = QCompleter(self._listModel, self)

        self._setNameLineEdit.setCompleter(self._completer)

        self._listModel.itemChanged.connect(self._onSetNameChange)
        layout.addWidget(self._setListView)
        buttonLayout = QHBoxLayout()

        self._addAction = QAction("+", self, toolTip="Add a new sort key")
        self._updateAction = QAction("Update",
                                     self,
                                     toolTip="Update/save current selection")
        self._removeAction = QAction("\u2212",
                                     self,
                                     toolTip="Remove selected sort key.")

        self._addToolButton = QToolButton(self)
        self._updateToolButton = QToolButton(self)
        self._removeToolButton = QToolButton(self)
        self._updateToolButton.setSizePolicy(QSizePolicy.MinimumExpanding,
                                             QSizePolicy.Minimum)

        self._addToolButton.setDefaultAction(self._addAction)
        self._updateToolButton.setDefaultAction(self._updateAction)
        self._removeToolButton.setDefaultAction(self._removeAction)

        buttonLayout.addWidget(self._addToolButton)
        buttonLayout.addWidget(self._updateToolButton)
        buttonLayout.addWidget(self._removeToolButton)

        layout.addLayout(buttonLayout)
        self.setLayout(layout)

        self._addAction.triggered.connect(self.addCurrentSelection)
        self._updateAction.triggered.connect(self.updateSelectedSelection)
        self._removeAction.triggered.connect(self.removeSelectedSelection)

        self._setListView.selectionModel().selectionChanged.connect(
            self._onListViewSelectionChanged)
        self.selectionModel = None
        self._selections = []

    def sizeHint(self):
        size = QFrame.sizeHint(self)
        return QSize(size.width(), 150)

    def _onSelectionChanged(self, selected, deselected):
        self.setSelectionModified(True)

    def _onListViewSelectionChanged(self, selected, deselected):
        try:
            index = self._setListView.selectedIndexes()[0]
        except IndexError:
            return
        self.commitSelection(self._proxyModel.mapToSource(index).row())

    def _onSetNameChange(self, item):
        self.selections[item.row()].name = str(item.text())

    def _setButtonStates(self, val):
        self._updateToolButton.setEnabled(val)

    def setSelectionModel(self, selectionModel):
        if self.selectionModel:
            self.selectionModel.selectionChanged.disconnect(
                self._onSelectionChanged)
        self.selectionModel = selectionModel
        self.selectionModel.selectionChanged.connect(self._onSelectionChanged)

    def addCurrentSelection(self):
        item = self.addSelection(
            SelectionByKey(self.selectionModel.selection(),
                           name="New selection",
                           key=(1, 2, 3, 10)))
        index = self._proxyModel.mapFromSource(item.index())
        self._setListView.setCurrentIndex(index)
        self._setListView.edit(index)
        self.setSelectionModified(False)

    def removeSelectedSelection(self):
        i = self._proxyModel.mapToSource(
            self._setListView.currentIndex()).row()
        self._listModel.takeRow(i)
        del self.selections[i]

    def updateCurentSelection(self):
        i = self._proxyModel.mapToSource(
            self._setListView.selectedIndex()).row()
        self.selections[i].setSelection(self.selectionModel.selection())
        self.setSelectionModified(False)

    def addSelection(self, selection, name=""):
        self._selections.append(selection)
        item = QStandardItem(selection.name)
        item.setFlags(item.flags() ^ Qt.ItemIsDropEnabled)
        self._listModel.appendRow(item)
        self.setSelectionModified(False)
        return item

    def updateSelectedSelection(self):
        i = self._proxyModel.mapToSource(
            self._setListView.currentIndex()).row()
        self.selections[i].setSelection(self.selectionModel.selection())
        self.setSelectionModified(False)

    def setSelectionModified(self, val):
        self._selectionModified = val
        self._setButtonStates(val)
        self.selectionModified.emit(bool(val))

    def commitSelection(self, index):
        selection = self.selections[index]
        selection.select(self.selectionModel)

    def setSelections(self, selections):
        self._listModel.clear()
        for selection in selections:
            self.addSelection(selection)

    def selections(self):
        return self._selections

    selections = property(selections, setSelections)
class GeoDetermineDlg(FlightPlanBaseDlg):
    HistoryDataP = []
    HistoryDataBD = []
    HistoryDataMV = []

    def __init__(self, parent):
        FlightPlanBaseDlg.__init__(self, parent)
        self.setObjectName("HoldingRnavDlg")
        self.surfaceType = SurfaceTypes.GeoDetermine
        self.initParametersPan()
        self.setWindowTitle(SurfaceTypes.GeoDetermine)
        self.resize(540, 550)
        self.surfaceList = None

        self.date = QDate.currentDate()
        self.model = MagneticModel.WMM2010
        geo = Geo()

        self.resultLayerList = []
        self.resultPoint3d = None

        self.resultLat = None
        self.resultLon = None

    def uiStateInit(self):
        self.ui.grbMostCritical.setVisible(False)
        self.ui.grbResult_2.setVisible(False)
        self.ui.btnUpdateQA.setVisible(False)
        self.ui.btnUpdateQA_2.setVisible(False)
        self.ui.btnExportResult.setVisible(False)
        self.ui.btnEvaluate.setVisible(False)
        self.ui.btnPDTCheck.setVisible(False)
        self.ui.frm_cmbObstSurface.setVisible(False)
        self.ui.tabCtrlGeneral.removeTab(1)

        icon = QIcon()
        icon.addPixmap(QPixmap(("Resource/Calculator.bmp")), QIcon.Normal,
                       QIcon.Off)
        self.ui.btnConstruct.setIcon(icon)
        self.ui.btnConstruct.setToolTip("Calculate")

        return FlightPlanBaseDlg.uiStateInit(self)

    def tblHistory_Click(self, modelIndex):
        if modelIndex != None:
            if self.parametersPanel.tabGeneral.currentIndex() == 0:
                dataList = GeoDetermineDlg.HistoryDataP[modelIndex.row()]

                self.parametersPanel.pnlStartPosP.Point3d = Point3D(
                    float(dataList[1][1]), float(dataList[1][0]))
                self.parametersPanel.pnlVarStartP.Value = float(dataList[1][2])
                self.parametersPanel.txtForwardTP.Value = float(dataList[1][3])
                self.parametersPanel.txtForwardMP.Value = float(dataList[1][4])
                self.parametersPanel.txtDistanceP.Value = Distance(
                    float(dataList[1][5]), DistanceUnits.NM)
                self.parametersPanel.pnlVarFinishP.Value = float(
                    dataList[1][9])

                self.method_29_P()
                self.resultModelP.setItem(0, 0,
                                          QStandardItem(Captions.LATITUDE))
                self.resultModelP.setItem(0, 1, QStandardItem(dataList[1][7]))

                self.resultModelP.setItem(1, 0,
                                          QStandardItem(Captions.LONGITUDE))
                self.resultModelP.setItem(1, 1, QStandardItem(dataList[1][8]))

                self.resultModelP.setItem(
                    2, 0, QStandardItem(Captions.REVERSE_TRUE_BEARING))
                self.resultModelP.setItem(2, 1, QStandardItem(dataList[1][10]))

                self.resultModelP.setItem(
                    3, 0, QStandardItem(Captions.REVERSE_MAGNETIC_BEARING))
                self.resultModelP.setItem(3, 1, QStandardItem(dataList[1][11]))
            elif self.parametersPanel.tabGeneral.currentIndex() == 1:
                dataList = GeoDetermineDlg.HistoryDataBD[modelIndex.row()]

                self.parametersPanel.pnlStartPosBD.ID = dataList[1][0]
                self.parametersPanel.pnlStartPosBD.Point3d = Point3D(
                    float(dataList[1][2]), float(dataList[1][1]))
                self.parametersPanel.pnlVarStartBD.Value = float(
                    dataList[1][3])
                self.parametersPanel.pnlFinishPosBD.ID = dataList[1][4]
                self.parametersPanel.pnlFinishPosBD.Point3d = Point3D(
                    float(dataList[1][6]), float(dataList[1][5]))
                self.parametersPanel.pnlVarFinishBD.Value = float(
                    dataList[1][7])

                self.method_31_BD()
                self.resultModelBD.setItem(
                    0, 0, QStandardItem(Captions.FORWARD_TRUE_BEARING))
                self.resultModelBD.setItem(0, 1, QStandardItem(dataList[1][8]))

                self.resultModelBD.setItem(
                    1, 0, QStandardItem(Captions.FORWARD_MAGNETIC_BEARING))
                self.resultModelBD.setItem(1, 1, QStandardItem(dataList[1][9]))

                self.resultModelBD.setItem(
                    2, 0, QStandardItem(Captions.REVERSE_TRUE_BEARING))
                self.resultModelBD.setItem(2, 1,
                                           QStandardItem(dataList[1][10]))

                self.resultModelBD.setItem(
                    3, 0, QStandardItem(Captions.REVERSE_MAGNETIC_BEARING))
                self.resultModelBD.setItem(3, 1,
                                           QStandardItem(dataList[1][11]))

                self.resultModelBD.setItem(
                    2, 0, QStandardItem(Captions.DISTANCE_BETWEEN_POSITIONS))
                self.resultModelBD.setItem(2, 1,
                                           QStandardItem(dataList[1][12]))

                self.resultModelBD.setItem(
                    3, 0, QStandardItem(Captions.DISTANCE_BETWEEN_POSITIONS))
                self.resultModelBD.setItem(3, 1,
                                           QStandardItem(dataList[1][13]))
            else:
                dataList = GeoDetermineDlg.HistoryDataMV[modelIndex.row()]

                self.parametersPanel.pnlPositionMVD.Point3d = Point3D(
                    float(dataList[1][1]), float(dataList[1][0]))
                self.parametersPanel.pnlPositionMVD.txtAltitudeM.setText(
                    dataList[1][2])
                self.parametersPanel.txtResult.Value = dataList[1][6]

    def calculateP(self):
        degree = None
        degree1 = None
        degree2 = None
        degree3 = None
        num = None
        result, degree, degree1 = self.parametersPanel.pnlStartPosP.method_3()
        if (result):
            # self.pnlVarStart.Value.smethod_17();
            num1 = float(self.parametersPanel.pnlVarFinishP.Value)
            value = float(self.parametersPanel.txtForwardTP.Value)
            value1 = float(self.parametersPanel.txtForwardTP.Value)
            distance = self.parametersPanel.txtDistanceP.Value
            result, degree2, degree3, num = Geo.smethod_6(
                self.parametersPanel.cmbCalculationTypeP.SelectedItem, degree,
                degree1, value, distance)
            if (result):
                num2 = MathHelper.smethod_3(num - num1)
                self.method_29_P()
                self.resultPoint3d = Point3D(degree3, degree2)

                self.resultLat = degree2
                self.resultLon = degree3

                latStr = Degrees(degree2, None, None,
                                 DegreesType.Latitude).ToString()

                self.resultModelP.setItem(0, 0,
                                          QStandardItem(Captions.LATITUDE))
                self.resultModelP.setItem(0, 1, QStandardItem(latStr))

                lonStr = Degrees(degree3, None, None,
                                 DegreesType.Longitude).ToString()
                if String.Str2QString(lonStr).mid(0, 1) == "0":
                    lonStr = String.Str2QString(lonStr).mid(
                        1,
                        String.Str2QString(lonStr).length() - 1)

                self.resultModelP.setItem(1, 0,
                                          QStandardItem(Captions.LONGITUDE))
                self.resultModelP.setItem(1, 1, QStandardItem(lonStr))

                self.resultModelP.setItem(
                    2, 0, QStandardItem(Captions.REVERSE_TRUE_BEARING))
                self.resultModelP.setItem(2, 1,
                                          QStandardItem(str(round(num, 4))))

                self.resultModelP.setItem(
                    3, 0, QStandardItem(Captions.REVERSE_MAGNETIC_BEARING))
                self.resultModelP.setItem(3, 1,
                                          QStandardItem(str(round(num2, 4))))

                dataList = []
                dataList.append([
                    "Latitude (Start)", "Longitude (Start)",
                    "Variation (Start)", "Forward (° T)", "Forward (° M)",
                    "Distance (nm)", "Distance (km)", "Latitude (Finish)",
                    "Longitude (Finish)", "Variation (Finish)",
                    "Reverse (° T)", "Reverse (° M)"
                ])
                dataList.append([
                    str(degree),
                    str(degree1),
                    str(self.parametersPanel.pnlVarStartP.Value),
                    str(self.parametersPanel.txtForwardTP.Value),
                    str(self.parametersPanel.txtForwardMP.Value),
                    str(distance.NauticalMiles),
                    str(distance.Kilometres),
                    str(degree2),
                    str(degree3),
                    str(self.parametersPanel.pnlVarFinishP.Value),
                    str(round(num, 4)),
                    str(round(num2, 4))
                ])
                GeoDetermineDlg.HistoryDataP.append(dataList)
                self.setDataInHistoryModel(dataList)
                self.method_28_P(degree2, degree3)

    def calculateBD(self):
        degree = None
        degree1 = None
        degree2 = None
        degree3 = None
        num = None
        result, degree, degree1 = self.parametersPanel.pnlStartPosBD.method_3()
        if (result):
            num2 = self.parametersPanel.pnlVarStartBD.Value
            result1, degree2, degree3 = self.parametersPanel.pnlFinishPosBD.method_3(
            )
            if (result1):
                num3 = self.parametersPanel.pnlVarFinishBD.Value
                result2, distance, num, num1 = Geo.smethod_4(
                    self.parametersPanel.cmbCalculationTypeBD.SelectedItem,
                    degree, degree1, degree2, degree3)
                if define._units == QGis.Meters:
                    QgisHelper.convertMeasureUnits(QGis.Degrees)
                    distance = Distance(
                        MathHelper.calcDistance(
                            self.parametersPanel.pnlStartPosBD.Point3d,
                            self.parametersPanel.pnlFinishPosBD.Point3d))
                    QgisHelper.convertMeasureUnits(QGis.Meters)
                else:
                    distance = Distance(
                        MathHelper.calcDistance(
                            self.parametersPanel.pnlStartPosBD.Point3d,
                            self.parametersPanel.pnlFinishPosBD.Point3d))
                if result2:
                    num4 = MathHelper.smethod_3(num - num2)
                    num5 = MathHelper.smethod_3(num1 - num3)
                    self.method_31_BD()

                    self.resultModelBD.setItem(
                        0, 0, QStandardItem(Captions.FORWARD_TRUE_BEARING))
                    self.resultModelBD.setItem(
                        0, 1, QStandardItem(str(round(num, 4))))

                    self.resultModelBD.setItem(
                        1, 0, QStandardItem(Captions.FORWARD_MAGNETIC_BEARING))
                    self.resultModelBD.setItem(
                        1, 1, QStandardItem(str(round(num4, 4))))

                    self.resultModelBD.setItem(
                        2, 0, QStandardItem(Captions.REVERSE_TRUE_BEARING))
                    self.resultModelBD.setItem(
                        2, 1, QStandardItem(str(round(num1, 4))))

                    self.resultModelBD.setItem(
                        3, 0, QStandardItem(Captions.REVERSE_MAGNETIC_BEARING))
                    self.resultModelBD.setItem(
                        3, 1, QStandardItem(str(round(num5, 4))))

                    self.resultModelBD.setItem(
                        4, 0,
                        QStandardItem(Captions.DISTANCE_BETWEEN_POSITIONS))
                    self.resultModelBD.setItem(
                        4, 1,
                        QStandardItem(
                            str(round(distance.NauticalMiles, 4)) + " nm"))

                    self.resultModelBD.setItem(
                        5, 0,
                        QStandardItem(Captions.DISTANCE_BETWEEN_POSITIONS))
                    self.resultModelBD.setItem(
                        5, 1,
                        QStandardItem(
                            str(round(distance.Kilometres, 4)) + " km"))

                    dataList = []
                    dataList.append([
                        "ID (Start)", "Latitude (Start)", "Longitude (Start)",
                        "Variation (Start)", "ID (Finish)",
                        "Latitude (Finish)", "Longitude (Finish)",
                        "Variation (Finish)", "Forward (° T)", "Forward (° M)",
                        "Reverse (° T)", "Reverse (° M)", "Distance (nm)",
                        "Distance (km)"
                    ])
                    dataList.append([
                        self.parametersPanel.pnlStartPosBD.ID,
                        str(degree),
                        str(degree1),
                        str(self.parametersPanel.pnlVarStartBD.Value),
                        self.parametersPanel.pnlFinishPosBD.ID,
                        str(degree2),
                        str(degree3),
                        str(self.parametersPanel.pnlVarFinishBD.Value),
                        str(num),
                        str(num4),
                        str(round(num, 4)),
                        str(round(num2, 4)),
                        str(distance.NauticalMiles),
                        str(distance.Kilometres)
                    ])
                    GeoDetermineDlg.HistoryDataBD.append(dataList)
                    self.setDataInHistoryModel(dataList)
                    self.method_28_BD()

    def calculateMVD(self):
        degree = None
        degree1 = None
        degree2 = None

        result, degree, degree1 = self.parametersPanel.pnlPositionMVD.method_3(
        )
        # position.method_1(out degree, out degree1);
        altitude = self.parametersPanel.pnlPositionMVD.Altitude()
        result, degree2 = Geo.smethod_7(
            degree, degree1, altitude,
            self.parametersPanel.cmbModel.SelectedIndex,
            self.parametersPanel.dtpDate.date())
        if (result):
            self.parametersPanel.txtResult.Value = str(round(
                degree2, 4))  #.method_1(Formats.VariationFormat);
            date = self.parametersPanel.dtpDate.date()
            dataList = []
            dataList.append([
                "Latitude", "Longitude", "Altitude  (m)", "Altitude (ft)",
                "Date", "Magnetic Model", "Magnetic Variation"
            ])
            dataList.append([
                str(degree),
                str(degree1),
                str(altitude.Metres),
                str(altitude.Feet),
                date.toString(), self.parametersPanel.cmbModel.SelectedItem,
                self.parametersPanel.txtResult.Value
            ])
            GeoDetermineDlg.HistoryDataMV.append(dataList)
            self.setDataInHistoryModel(dataList)

    def autoCalcFinishMagVar(self):
        try:
            if self.parametersPanel.chbAutoFinishMagVar.Checked:
                finishPt = None
                finishLat = None
                finishLon = None
                degree3 = None
                num = None
                result, degree, degree1 = self.parametersPanel.pnlStartPosP.method_3(
                )
                if (result):
                    value = float(self.parametersPanel.txtForwardTP.Value)
                    value1 = float(self.parametersPanel.txtForwardTP.Value)
                    distance = self.parametersPanel.txtDistanceP.Value
                    result1, degree2, degree3, num = Geo.smethod_6(
                        self.parametersPanel.cmbCalculationTypeP.SelectedItem,
                        degree, degree1, value, distance)
                    if (result1):
                        finishPt = Point3D(degree3, degree2)

                        finishLat = degree2
                        finishLon = degree3
                if finishPt != None and self.model != None and self.date != None:
                    # result, degree, degree1 = self.parametersPanel.pnlStartPosP.method_3();
                    result2, degree2 = Geo.smethod_7(finishLat, finishLon,
                                                     Altitude(0), self.model,
                                                     self.date)
                    if (result2):
                        degree2 = round(degree2, 2)
                        self.parametersPanel.pnlVarFinishP.Value = degree2
        except:
            pass

    def btnConstruct_Click(self):
        # flag = FlightPlanBaseDlg.btnConstruct_Click(self)
        # if not flag:
        #     return
        if self.parametersPanel.tabGeneral.currentIndex() == 0:
            self.autoCalcFinishMagVar()
            self.calculateP()

        elif self.parametersPanel.tabGeneral.currentIndex() == 1:
            self.calculateBD()
        else:
            self.calculateMVD()

    def initParametersPan(self):
        ui = Ui_GeoDetermine()
        self.parametersPanel = ui
        FlightPlanBaseDlg.initParametersPan(self)

        self.parametersPanel.pnlStartPosP = PositionPanel(
            self.parametersPanel.gbStartPosP, None, None, "Degree")
        # self.parametersPanel.pnlStartPosP.degreeFormat = "ddmmss.ssssH"
        self.parametersPanel.pnlStartPosP.alwwaysShowString = "Degree"
        #         self.parametersPanel.pnlWaypoint.groupBox.setTitle("FAWP")
        self.parametersPanel.pnlStartPosP.btnCalculater.hide()
        self.parametersPanel.pnlStartPosP.hideframe_Altitude()
        # self.parametersPanel.pnlStartPosP.showframe_ID()
        self.parametersPanel.pnlStartPosP.setObjectName("pnlStartPosP")
        self.connect(self.parametersPanel.pnlStartPosP,
                     SIGNAL("positionChanged"), self.autoCalcFinishMagVar)

        ui.verticalLayout_gbStartPosP.addWidget(
            self.parametersPanel.pnlStartPosP)

        self.parametersPanel.pnlVarStartP = DegreesBoxPanel(self)
        self.parametersPanel.pnlVarStartP.CaptionLabel = "Magnetic Variation"
        self.connect(self.parametersPanel.pnlVarStartP,
                     SIGNAL("btnDegreeBoxPanel_clicked"), self.method_32_P)
        self.connect(self.parametersPanel.pnlVarStartP,
                     SIGNAL("txtDegreeBox_textChanged"),
                     self.txtDegreeBox_textChangedP)
        ui.verticalLayout_gbStartPosP.addWidget(
            self.parametersPanel.pnlVarStartP)

        self.parametersPanel.pnlVarFinishP = DegreesBoxPanel(self)
        self.parametersPanel.pnlVarFinishP.ButtonVisible = False
        self.parametersPanel.pnlVarFinishP.Enabled = False
        self.parametersPanel.pnlVarFinishP.CaptionLabel = "Magnetic Variation at Finish"
        ui.vLayout_grbParametersP.insertWidget(
            1, self.parametersPanel.pnlVarFinishP)
        # self.connect(self.parametersPanel.pnlVarStartP, SIGNAL("btnDegreeBoxPanel_clicked"), self.method_32_P)

        self.parametersPanel.pnlStartPosBD = PositionPanel(
            self.parametersPanel.gbFinishPosBD, None, None, "Degree")
        #         self.parametersPanel.pnlStartPosBD.groupBox.setTitle("FAWP")
        #         self.parametersPanel.pnlStartPosBD.degreeFormat = "ddmmss.ssssH"
        self.parametersPanel.pnlStartPosBD.alwwaysShowString = "Degree"
        self.parametersPanel.pnlStartPosBD.btnCalculater.hide()
        self.parametersPanel.pnlStartPosBD.hideframe_Altitude()
        self.parametersPanel.pnlStartPosBD.showframe_ID()
        self.parametersPanel.pnlStartPosBD.setObjectName("pnlStartPosBD")
        ui.verticalLayout_gbStartPosBD.insertWidget(
            0, self.parametersPanel.pnlStartPosBD)
        self.connect(self.parametersPanel.pnlStartPosBD,
                     SIGNAL("positionChanged"), self.positionChangedStartBD)

        self.parametersPanel.pnlVarStartBD = DegreesBoxPanel(self)
        self.parametersPanel.pnlVarStartBD.CaptionLabel = "Magnetic Variation"
        # self.parametersPanel.pnlVarStartBD.Enabled = False
        self.connect(self.parametersPanel.pnlVarStartBD,
                     SIGNAL("btnDegreeBoxPanel_clicked"), self.method_34_BD)
        # self.connect(self.parametersPanel.pnlVarStartBD, SIGNAL("txtDegreeBox_textChanged"), self.txtDegreeBox_textChangedP)
        ui.verticalLayout_gbStartPosBD.addWidget(
            self.parametersPanel.pnlVarStartBD)

        self.parametersPanel.pnlFinishPosBD = PositionPanel(
            self.parametersPanel.gbFinishPosBD, None, None, "Degree")
        #         self.parametersPanel.pnlStartPosBD.groupBox.setTitle("FAWP")
        #         self.parametersPanel.pnlFinishPosBD.degreeFormat = "ddmmss.ssssH"
        self.parametersPanel.pnlFinishPosBD.alwwaysShowString = "Degree"
        self.parametersPanel.pnlFinishPosBD.btnCalculater.hide()
        self.parametersPanel.pnlFinishPosBD.hideframe_Altitude()
        self.parametersPanel.pnlFinishPosBD.showframe_ID()
        self.parametersPanel.pnlFinishPosBD.setObjectName("pnlFinishPosBD")
        ui.verticalLayout_gbFinishPosBD.insertWidget(
            0, self.parametersPanel.pnlFinishPosBD)
        self.connect(self.parametersPanel.pnlFinishPosBD,
                     SIGNAL("positionChanged"), self.positionChangedFinishBD)

        self.parametersPanel.pnlVarFinishBD = DegreesBoxPanel(self)
        self.parametersPanel.pnlVarFinishBD.CaptionLabel = "Magnetic Variation"
        # self.parametersPanel.pnlVarFinishBD.Enabled = False
        self.connect(self.parametersPanel.pnlVarFinishBD,
                     SIGNAL("btnDegreeBoxPanel_clicked"), self.method_36_BD)
        # self.connect(self.parametersPanel.pnlVarFinishBD, SIGNAL("txtDegreeBox_textChanged"), self.txtDegreeBox_textChangedP)
        ui.verticalLayout_gbFinishPosBD.addWidget(
            self.parametersPanel.pnlVarFinishBD)

        self.parametersPanel.pnlPositionMVD = PositionPanel(
            self.parametersPanel.tabGeoDetermineMV, None, None, "Degree")
        self.parametersPanel.pnlPositionMVD.groupBox.setTitle("Position")
        self.parametersPanel.pnlPositionMVD.alwwaysShowString = "Degree"
        self.parametersPanel.pnlPositionMVD.btnCalculater.hide()
        self.parametersPanel.pnlPositionMVD.setObjectName("pnlPositionMVD")
        ui.verticalLayout_3.insertWidget(0,
                                         self.parametersPanel.pnlPositionMVD)
        self.connect(self.parametersPanel.pnlPositionMVD,
                     SIGNAL("positionChanged"), self.method_28_MVD)

        self.connect(self.parametersPanel.txtForwardTP, SIGNAL("Event_0"),
                     self.txtForwardTP_textChanged)
        self.connect(self.parametersPanel.chbAutoFinishMagVar,
                     SIGNAL("Event_0"), self.chbAutoFinishMagVar_clicked)
        self.connect(self.parametersPanel.txtDistanceP, SIGNAL("Event_0"),
                     self.autoCalcFinishMagVar)

        self.connect(self.parametersPanel.txtForwardMP, SIGNAL("Event_0"),
                     self.txtForwardMP_textChanged)
        self.parametersPanel.btnResultP.clicked.connect(
            self.btnResultP_clicked)
        self.parametersPanel.tabGeneral.currentChanged.connect(
            self.tabGeneral_CurrentChanged)
        self.parametersPanel.chbAutoVarBD.clicked.connect(
            self.chbAutoVarBD_clicked)
        self.parametersPanel.dtpDate.dateChanged.connect(self.method_28_MVD)
        self.connect(self.parametersPanel.cmbModel, SIGNAL("Event_0"),
                     self.method_28_MVD)
        self.parametersPanel.btnDtpDate.clicked.connect(
            self.btnDtpDate_clicked)

        self.resultModelP = QStandardItemModel()
        self.parametersPanel.tblResultP.setModel(self.resultModelP)

        self.resultModelBD = QStandardItemModel()
        self.parametersPanel.tblResultBD.setModel(self.resultModelBD)

        self.ttt = 0
        self.txtForwardTP_textChanged()

        self.setHistoryData()

        self.autoVarSet = False
        self.dateBD = QDate.currentDate()
        self.modelBD = MagneticModel.WMM2010
        self.parametersPanel.btnResultBD.setVisible(False)

        self.parametersPanel.dtpDate.setDate(QDate.currentDate())

        self.calendar = QCalendarWidget()
        self.calendar.clicked.connect(self.calendar_clicked)

        self.menu = QMenu()
        layout = QVBoxLayout(self.menu)
        layout.addWidget(self.calendar)

    def chbAutoFinishMagVar_clicked(self):
        if self.parametersPanel.chbAutoFinishMagVar.Checked:
            self.parametersPanel.pnlVarFinishP.Enabled = False
        else:
            self.parametersPanel.pnlVarFinishP.Enabled = True

    def calendar_clicked(self, date):
        self.parametersPanel.dtpDate.setDate(date)

    def btnDtpDate_clicked(self):
        rcRect = self.parametersPanel.btnDtpDate.geometry()
        ptPoint = rcRect.bottomLeft()
        self.menu.exec_(self.mapToGlobal(ptPoint))

    def positionChangedStartBD(self):
        if (self.parametersPanel.chbAutoVarBD.isChecked() and self.autoVarSet):
            self.method_29_BD()
        self.method_31_BD()

    def positionChangedFinishBD(self):
        if (self.parametersPanel.chbAutoVarBD.isChecked() and self.autoVarSet):
            self.method_30_BD()
        self.method_31_BD()

    def chbAutoVarBD_clicked(self):
        if (self.parametersPanel.chbAutoVarBD.isChecked()
                and not self.autoVarSet):
            result, self.dateBD, self.modelBD = DlgMagneticVariationParameters.smethod_0(
                self.dateBD, self.modelBD)
            self.parametersPanel.chbAutoVarBD.setChecked(result)
            self.autoVarSet = self.parametersPanel.chbAutoVarBD.isChecked()
        if (self.parametersPanel.chbAutoVarBD.isChecked()):
            self.method_29_BD()
            self.method_30_BD()
        self.method_31_BD()

    def tabGeneral_CurrentChanged(self):
        if self.parametersPanel.tabGeneral.currentIndex() == 0:
            self.stdItemModelHistory.clear()
            if len(GeoDetermineDlg.HistoryDataP) > 0:
                self.setDataInHistoryModel(GeoDetermineDlg.HistoryDataP, True)
                pass
        elif self.parametersPanel.tabGeneral.currentIndex() == 1:
            self.stdItemModelHistory.clear()
            if len(GeoDetermineDlg.HistoryDataBD) > 0:
                self.setDataInHistoryModel(GeoDetermineDlg.HistoryDataBD, True)
                pass
        else:
            self.stdItemModelHistory.clear()
            if len(GeoDetermineDlg.HistoryDataMV) > 0:
                self.setDataInHistoryModel(GeoDetermineDlg.HistoryDataMV, True)
                pass

    def setHistoryData(self):
        if len(self.HistoryDataP) > 0:
            self.setDataInHistoryModel(GeoDetermineDlg.HistoryDataP, True)
            pass

    def btnResultP_clicked(self):
        self.resultModelP.clear()
        self.parametersPanel.pnlVarStartP.Value = self.parametersPanel.pnlVarFinishP.Value
        self.parametersPanel.pnlVarFinishP.Value = 0.0
        self.parametersPanel.pnlStartPosP.Point3d = self.resultPoint3d

    def txtForwardTP_textChanged(self):
        if self.ttt == 0:
            self.ttt = 1
        if self.ttt == 2:
            self.ttt = 0
        if self.ttt == 1:
            try:
                value = float(self.parametersPanel.txtForwardTP.Value)
                self.parametersPanel.txtForwardMP.Value = MathHelper.smethod_3(
                    value - float(self.parametersPanel.pnlVarStartP.Value))
                self.autoCalcFinishMagVar()
            except:
                self.parametersPanel.txtForwardMP.Value = 0.0

    def txtForwardMP_textChanged(self):
        if self.ttt == 0:
            self.ttt = 2
        if self.ttt == 1:
            self.ttt = 0
        if self.ttt == 2:
            try:
                value = float(self.parametersPanel.txtForwardMP.Value)
                self.parametersPanel.txtForwardTP.Value = MathHelper.smethod_3(
                    value + float(self.parametersPanel.pnlVarStartP.Value))
            except:
                self.parametersPanel.txtForwardTP.Value = 0.0

    def txtDegreeBox_textChangedP(self):

        try:
            value = float(self.parametersPanel.txtForwardTP.Value)
            self.parametersPanel.txtForwardMP.Value = MathHelper.smethod_3(
                value - float(self.parametersPanel.pnlVarStartP.Value))
        except:
            self.parametersPanel.txtForwardMP.Value = 0.0

    def method_28_P(self, degrees_Lat, degrees_Lon):
        point3d = None
        point3d1 = None
        self.surfaceType = SurfaceTypes.GeoDeterminePosition
        flag = FlightPlanBaseDlg.btnConstruct_Click(self)
        if not flag:
            return
        if define._units == QGis.Meters:
            point3d0 = self.parametersPanel.pnlStartPosP.Point3d

            point3d = QgisHelper.CrsTransformPoint(point3d0.get_X(),
                                                   point3d0.get_Y(),
                                                   define._latLonCrs,
                                                   define._xyCrs)
            point3d1 = QgisHelper.CrsTransformPoint(degrees_Lon, degrees_Lat,
                                                    define._latLonCrs,
                                                    define._xyCrs)
        else:
            point3d = self.parametersPanel.pnlStartPosP.Point3d
            point3d1 = Point3D(degrees_Lon, degrees_Lat)

        constructionLayer = None
        mapUnits = define._canvas.mapUnits()
        if self.parametersPanel.chbMarkPointsP.isChecked():
            constructionLayer = AcadHelper.createVectorLayer(
                SurfaceTypes.GeoDeterminePosition + "_MarkPoint", QGis.Point)
            AcadHelper.setGeometryAndAttributesInLayer(constructionLayer,
                                                       point3d)
            AcadHelper.setGeometryAndAttributesInLayer(constructionLayer,
                                                       point3d1)

            # if mapUnits == QGis.Meters:
            #     constructionLayer = QgsVectorLayer("point?crs=%s"%define._xyCrs.authid (), SurfaceTypes.GeoDeterminePosition + "_MarkPoint", "memory")
            # else:
            #     constructionLayer = QgsVectorLayer("point?crs=%s"%define._latLonCrs.authid (), SurfaceTypes.GeoDeterminePosition + " MarkPoint", "memory")
            #
            # shpPath = ""
            # if define.obstaclePath != None:
            #     shpPath = define.obstaclePath
            # elif define.xmlPath != None:
            #     shpPath = define.xmlPath
            # else:
            #     shpPath = define.appPath
            # er = QgsVectorFileWriter.writeAsVectorFormat(constructionLayer, shpPath + "/" + SurfaceTypes.GeoDeterminePosition + "_MarkPoint" + ".shp", "utf-8", constructionLayer.crs())
            # constructionLayer = QgsVectorLayer(shpPath + "/" + SurfaceTypes.GeoDeterminePosition + "_MarkPoint" + ".shp", SurfaceTypes.GeoDeterminePosition + " MarkPoint", "ogr")
            #
            # constructionLayer.startEditing()
            #
            # feature = QgsFeature()
            # feature.setGeometry(QgsGeometry.fromPoint(point3d))
            # constructionLayer.addFeature(feature)
            # feature.setGeometry(QgsGeometry.fromPoint(point3d1))
            # constructionLayer.addFeature(feature)
            # constructionLayer.commitChanges()
            QgisHelper.appendToCanvas(define._canvas, [constructionLayer],
                                      SurfaceTypes.GeoDeterminePosition)

            self.resultLayerList.append(constructionLayer)
        if self.parametersPanel.chbDrawLineP.isChecked():
            constructionLayer1 = AcadHelper.createVectorLayer(
                SurfaceTypes.GeoDeterminePosition + "_Line", QGis.Line)
            AcadHelper.setGeometryAndAttributesInLayer(constructionLayer1,
                                                       [point3d, point3d1])
            QgisHelper.appendToCanvas(define._canvas, [constructionLayer1],
                                      SurfaceTypes.GeoDeterminePosition)
            self.resultLayerList.append(constructionLayer1)

    def method_28_BD(self):
        point3d = None
        point3d1 = None
        self.surfaceType = SurfaceTypes.GeoDetermineBD
        flag = FlightPlanBaseDlg.btnConstruct_Click(self)
        if not flag:
            return
        if define._units == QGis.Meters:
            point3d0 = self.parametersPanel.pnlStartPosBD.Point3d
            point3dF0 = self.parametersPanel.pnlFinishPosBD.Point3d

            point3d = QgisHelper.CrsTransformPoint(point3d0.get_X(),
                                                   point3d0.get_Y(),
                                                   define._latLonCrs,
                                                   define._xyCrs)
            point3d1 = QgisHelper.CrsTransformPoint(point3dF0.get_X(),
                                                    point3dF0.get_Y(),
                                                    define._latLonCrs,
                                                    define._xyCrs)
        else:
            point3d = self.parametersPanel.pnlStartPosBD.Point3d
            point3d1 = self.parametersPanel.pnlFinishPosBD.Point3d

        constructionLayer = None
        mapUnits = define._canvas.mapUnits()
        if self.parametersPanel.chbMarkPointsBD.isChecked():
            constructionLayer = AcadHelper.createVectorLayer(
                SurfaceTypes.GeoDetermineBD + "_MarkPoint", QGis.Point)
            AcadHelper.setGeometryAndAttributesInLayer(constructionLayer,
                                                       point3d)
            AcadHelper.setGeometryAndAttributesInLayer(constructionLayer,
                                                       point3d1)

            # if mapUnits == QGis.Meters:
            #     constructionLayer = QgsVectorLayer("point?crs=%s"%define._xyCrs.authid (), SurfaceTypes.GeoDetermineBD + "_MarkPoint", "memory")
            # else:
            #     constructionLayer = QgsVectorLayer("point?crs=%s"%define._latLonCrs.authid (), SurfaceTypes.GeoDetermineBD + " MarkPoint", "memory")
            #
            # shpPath = ""
            # if define.obstaclePath != None:
            #     shpPath = define.obstaclePath
            # elif define.xmlPath != None:
            #     shpPath = define.xmlPath
            # else:
            #     shpPath = define.appPath
            # er = QgsVectorFileWriter.writeAsVectorFormat(constructionLayer, shpPath + "/" + QString(SurfaceTypes.GeoDetermineBD).replace(" ", "") + "_MarkPoint" + ".shp", "utf-8", constructionLayer.crs())
            # constructionLayer = QgsVectorLayer(shpPath + "/" + QString(SurfaceTypes.GeoDetermineBD).replace(" ", "") + "_MarkPoint" + ".shp", SurfaceTypes.GeoDetermineBD + " MarkPoint", "ogr")
            #
            # constructionLayer.startEditing()
            #
            #
            # feature = QgsFeature()
            # feature.setGeometry(QgsGeometry.fromPoint(point3d))
            # constructionLayer.addFeature(feature)
            # feature.setGeometry(QgsGeometry.fromPoint(point3d1))
            # constructionLayer.addFeature(feature)
            # constructionLayer.commitChanges()
            QgisHelper.appendToCanvas(define._canvas, [constructionLayer],
                                      SurfaceTypes.GeoDetermineBD)
            self.resultLayerList.append(constructionLayer)
        if self.parametersPanel.chbDrawLineBD.isChecked():
            constructionLayer = AcadHelper.createVectorLayer(
                SurfaceTypes.GeoDetermineBD + "_Line", QGis.Line)
            AcadHelper.setGeometryAndAttributesInLayer(constructionLayer,
                                                       [point3d, point3d1])
            QgisHelper.appendToCanvas(define._canvas, [constructionLayer],
                                      SurfaceTypes.GeoDetermineBD)
            self.resultLayerList.append(constructionLayer)

    def method_28_MVD(self):
        self.parametersPanel.txtResult.Value = ""

    def method_29_P(self):
        self.resultModelP.clear()
        self.resultModelP.setHorizontalHeaderLabels(["Type", "Value"])

    def method_29_BD(self):
        degree = None
        degree1 = None
        degree2 = None
        if (self.parametersPanel.pnlStartPosBD.IsValid()):

            result, degree, degree1 = self.parametersPanel.pnlStartPosBD.method_3(
            )
            result, degree2 = Geo.smethod_7(degree, degree1, Altitude(0),
                                            self.modelBD, self.dateBD)
            if (result):
                degree2 = round(degree2, 2)
                self.parametersPanel.pnlVarStartBD.Value = degree2

    def method_30_BD(self):
        degree = None
        degree1 = None
        degree2 = None
        if (self.parametersPanel.pnlFinishPosBD.IsValid()):

            result, degree, degree1 = self.parametersPanel.pnlFinishPosBD.method_3(
            )
            result, degree2 = Geo.smethod_7(degree, degree1, Altitude(0),
                                            self.modelBD, self.dateBD)
            if (result):
                degree2 = round(degree2, 2)
                self.parametersPanel.pnlVarFinishBD.Value = degree2

    def method_31_BD(self):
        self.resultModelBD.clear()
        self.resultModelBD.setHorizontalHeaderLabels(["Type", "Value"])

    def method_32_P(self):
        startPos = self.parametersPanel.pnlStartPosP.Point3d
        result, self.date, self.model = DlgMagneticVariationParameters.smethod_0(
            self.date, self.model)
        if self.parametersPanel.pnlStartPosP.IsValid() and result:
            result, degree, degree1 = self.parametersPanel.pnlStartPosP.method_3(
            )
            result, degree2 = Geo.smethod_7(degree, degree1, Altitude(0),
                                            self.model, self.date)
            if (result):
                degree2 = round(degree2, 2)
                self.parametersPanel.pnlVarStartP.Value = degree2
                try:
                    self.parametersPanel.txtForwardMP.Value = MathHelper.smethod_3(
                        float(self.parametersPanel.txtForwardTP.Value) -
                        degree2)
                except:
                    self.parametersPanel.txtForwardMP.Value = 0.0
                self.method_29_P()

    def method_34_BD(self):
        startPos = self.parametersPanel.pnlStartPosBD.Point3d
        result, self.dateBD, self.modelBD = DlgMagneticVariationParameters.smethod_0(
            self.dateBD, self.modelBD)
        if self.parametersPanel.pnlStartPosBD.IsValid() and result:
            self.method_29_BD()
            self.method_31_BD()

    def method_36_BD(self):
        startPos = self.parametersPanel.pnlFinishPosBD.Point3d
        result, self.dateBD, self.modelBD = DlgMagneticVariationParameters.smethod_0(
            self.dateBD, self.modelBD)
        if self.parametersPanel.pnlFinishPosBD.IsValid() and result:
            self.method_30_BD()
            self.method_31_BD()
class DlgSqlLayerWindow(QWidget, Ui_Dialog):
    nameChanged = pyqtSignal(str)

    def __init__(self, iface, layer, parent=None):
        QWidget.__init__(self, parent)
        self.iface = iface
        self.layer = layer

        uri = QgsDataSourceUri(layer.source())
        dbplugin = None
        db = None
        if layer.dataProvider().name() == 'postgres':
            dbplugin = createDbPlugin('postgis', 'postgres')
        elif layer.dataProvider().name() == 'spatialite':
            dbplugin = createDbPlugin('spatialite', 'spatialite')
        elif layer.dataProvider().name() == 'oracle':
            dbplugin = createDbPlugin('oracle', 'oracle')
        elif layer.dataProvider().name() == 'virtual':
            dbplugin = createDbPlugin('vlayers', 'virtual')
        if dbplugin:
            dbplugin.connectToUri(uri)
            db = dbplugin.db

        self.dbplugin = dbplugin
        self.db = db
        self.filter = ""
        self.allowMultiColumnPk = isinstance(db, PGDatabase) # at the moment only PostgreSQL allows a primary key to span multiple columns, spatialite doesn't
        self.aliasSubQuery = isinstance(db, PGDatabase) # only PostgreSQL requires subqueries to be aliases
        self.setupUi(self)
        self.setWindowTitle(
            u"%s - %s [%s]" % (self.windowTitle(), db.connection().connectionName(), db.connection().typeNameString()))

        self.defaultLayerName = 'QueryLayer'

        if self.allowMultiColumnPk:
            self.uniqueColumnCheck.setText(self.trUtf8("Column(s) with unique values"))
        else:
            self.uniqueColumnCheck.setText(self.trUtf8("Column with unique values"))

        self.editSql.setFocus()
        self.editSql.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self.editSql.setMarginVisible(True)
        self.initCompleter()

        # allow copying results
        copyAction = QAction("copy", self)
        self.viewResult.addAction(copyAction)
        copyAction.setShortcuts(QKeySequence.Copy)

        copyAction.triggered.connect(self.copySelectedResults)

        self.btnExecute.clicked.connect(self.executeSql)
        self.btnSetFilter.clicked.connect(self.setFilter)
        self.btnClear.clicked.connect(self.clearSql)

        self.presetStore.clicked.connect(self.storePreset)
        self.presetDelete.clicked.connect(self.deletePreset)
        self.presetCombo.activated[str].connect(self.loadPreset)
        self.presetCombo.activated[str].connect(self.presetName.setText)

        self.updatePresetsCombobox()

        self.geomCombo.setEditable(True)
        self.geomCombo.lineEdit().setReadOnly(True)

        self.uniqueCombo.setEditable(True)
        self.uniqueCombo.lineEdit().setReadOnly(True)
        self.uniqueModel = QStandardItemModel(self.uniqueCombo)
        self.uniqueCombo.setModel(self.uniqueModel)
        if self.allowMultiColumnPk:
            self.uniqueCombo.setItemDelegate(QStyledItemDelegate())
            self.uniqueModel.itemChanged.connect(self.uniqueChanged)                # react to the (un)checking of an item
            self.uniqueCombo.lineEdit().textChanged.connect(self.uniqueTextChanged) # there are other events that change the displayed text and some of them can not be caught directly

        self.layerTypeWidget.hide()  # show if load as raster is supported
        #self.loadLayerBtn.clicked.connect(self.loadSqlLayer)
        self.updateLayerBtn.clicked.connect(self.updateSqlLayer)
        self.getColumnsBtn.clicked.connect(self.fillColumnCombos)

        self.queryBuilderFirst = True
        self.queryBuilderBtn.setIcon(QIcon(":/db_manager/icons/sql.gif"))
        self.queryBuilderBtn.clicked.connect(self.displayQueryBuilder)

        self.presetName.textChanged.connect(self.nameChanged)

        # Update from layer
        # Fisrtly the SQL from QgsDataSourceUri table
        sql = uri.table()
        if uri.keyColumn() == '_uid_':
            match = re.search('^\(SELECT .+ AS _uid_,\* FROM \((.*)\) AS _subq_.+_\s*\)$', sql, re.S)
            if match:
                sql = match.group(1)
        else:
            match = re.search('^\((SELECT .+ FROM .+)\)$', sql, re.S)
            if match:
                sql = match.group(1)
        self.editSql.setText(sql)
        self.executeSql()

        # Then the columns
        self.geomCombo.setCurrentIndex(self.geomCombo.findText(uri.geometryColumn(), Qt.MatchExactly))
        if uri.keyColumn() != '_uid_':
            self.uniqueColumnCheck.setCheckState(Qt.Checked)
            if self.allowMultiColumnPk:
                itemsData = uri.keyColumn().split(',')
                for item in self.uniqueModel.findItems("*", Qt.MatchWildcard):
                    if item.data() in itemsData:
                        item.setCheckState(Qt.Checked)
            else:
                keyColumn = uri.keyColumn()
                for item in self.uniqueModel.findItems("*", Qt.MatchWildcard):
                    if item.data() == keyColumn:
                        self.uniqueCombo.setCurrentIndex(self.uniqueModel.indexFromItem(item).row())

        # Finally layer name, filter and selectAtId
        self.layerNameEdit.setText(layer.name())
        self.filter = uri.sql()
        if uri.selectAtIdDisabled():
            self.avoidSelectById.setCheckState(Qt.Checked)

    def updatePresetsCombobox(self):
        self.presetCombo.clear()

        names = []
        entries = QgsProject.instance().subkeyList('DBManager', 'savedQueries')
        for entry in entries:
            name = QgsProject.instance().readEntry('DBManager', 'savedQueries/' + entry + '/name')[0]
            names.append(name)

        for name in sorted(names):
            self.presetCombo.addItem(name)
        self.presetCombo.setCurrentIndex(-1)

    def storePreset(self):
        query = self._getSqlQuery()
        if query == "":
            return
        name = self.presetName.text()
        QgsProject.instance().writeEntry('DBManager', 'savedQueries/q' + unicode(name.__hash__()) + '/name', name)
        QgsProject.instance().writeEntry('DBManager', 'savedQueries/q' + unicode(name.__hash__()) + '/query', query)
        index = self.presetCombo.findText(name)
        if index == -1:
            self.presetCombo.addItem(name)
            self.presetCombo.setCurrentIndex(self.presetCombo.count() - 1)
        else:
            self.presetCombo.setCurrentIndex(index)

    def deletePreset(self):
        name = self.presetCombo.currentText()
        QgsProject.instance().removeEntry('DBManager', 'savedQueries/q' + unicode(name.__hash__()))
        self.presetCombo.removeItem(self.presetCombo.findText(name))
        self.presetCombo.setCurrentIndex(-1)

    def loadPreset(self, name):
        query = QgsProject.instance().readEntry('DBManager', 'savedQueries/q' + unicode(name.__hash__()) + '/query')[0]
        name = QgsProject.instance().readEntry('DBManager', 'savedQueries/q' + unicode(name.__hash__()) + '/name')[0]
        self.editSql.setText(query)

    def clearSql(self):
        self.editSql.clear()
        self.editSql.setFocus()
        self.filter = ""

    def executeSql(self):

        sql = self._getSqlQuery()
        if sql == "":
            return

        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))

        # delete the old model
        old_model = self.viewResult.model()
        self.viewResult.setModel(None)
        if old_model:
            old_model.deleteLater()

        cols = []
        quotedCols = []

        try:
            # set the new model
            model = self.db.sqlResultModel(sql, self)
            self.viewResult.setModel(model)
            self.lblResult.setText(self.tr("%d rows, %.1f seconds") % (model.affectedRows(), model.secs()))
            cols = self.viewResult.model().columnNames()
            for col in cols:
                quotedCols.append(self.db.connector.quoteId(col))

        except BaseError as e:
            QApplication.restoreOverrideCursor()
            DlgDbError.showError(e, self)
            self.uniqueModel.clear()
            self.geomCombo.clear()
            return

        self.setColumnCombos(cols, quotedCols)

        self.update()
        QApplication.restoreOverrideCursor()

    def _getSqlLayer(self, _filter):
        hasUniqueField = self.uniqueColumnCheck.checkState() == Qt.Checked
        if hasUniqueField:
            if self.allowMultiColumnPk:
                checkedCols = []
                for item in self.uniqueModel.findItems("*", Qt.MatchWildcard):
                    if item.checkState() == Qt.Checked:
                        checkedCols.append(item.data())
                uniqueFieldName = ",".join(checkedCols)
            elif self.uniqueCombo.currentIndex() >= 0:
                uniqueFieldName = self.uniqueModel.item(self.uniqueCombo.currentIndex()).data()
            else:
                uniqueFieldName = None
        else:
            uniqueFieldName = None
        hasGeomCol = self.hasGeometryCol.checkState() == Qt.Checked
        if hasGeomCol:
            geomFieldName = self.geomCombo.currentText()
        else:
            geomFieldName = None

        query = self._getSqlQuery()
        if query == "":
            return None

        # remove a trailing ';' from query if present
        if query.strip().endswith(';'):
            query = query.strip()[:-1]

        from qgis.core import QgsMapLayer, QgsMapLayerRegistry

        layerType = QgsMapLayer.VectorLayer if self.vectorRadio.isChecked() else QgsMapLayer.RasterLayer

        # get a new layer name
        names = []
        for layer in QgsMapLayerRegistry.instance().mapLayers().values():
            names.append(layer.name())

        layerName = self.layerNameEdit.text()
        if layerName == "":
            layerName = self.defaultLayerName
        newLayerName = layerName
        index = 1
        while newLayerName in names:
            index += 1
            newLayerName = u"%s_%d" % (layerName, index)

        # create the layer
        layer = self.db.toSqlLayer(query, geomFieldName, uniqueFieldName, newLayerName, layerType,
                                   self.avoidSelectById.isChecked(), _filter)
        if layer.isValid():
            return layer
        else:
            return None

    def loadSqlLayer(self):
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        try:
            layer = self._getSqlLayer(self.filter)
            if layer == None:
                return

            from qgis.core import QgsMapLayerRegistry
            QgsMapLayerRegistry.instance().addMapLayers([layer], True)
        finally:
            QApplication.restoreOverrideCursor()

    def updateSqlLayer(self):
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        try:
            layer = self._getSqlLayer(self.filter)
            if layer == None:
                return

            #self.layer.dataProvider().setDataSourceUri(layer.dataProvider().dataSourceUri())
            #self.layer.dataProvider().reloadData()
            XMLDocument = QDomDocument("style")
            XMLMapLayers = XMLDocument.createElement("maplayers")
            XMLMapLayer = XMLDocument.createElement("maplayer")
            self.layer.writeLayerXML(XMLMapLayer, XMLDocument)
            XMLMapLayer.firstChildElement("datasource").firstChild().setNodeValue(layer.source())
            XMLMapLayers.appendChild(XMLMapLayer)
            XMLDocument.appendChild(XMLMapLayers)
            self.layer.readLayerXML(XMLMapLayer)
            self.layer.reload()
            self.iface.actionDraw().trigger()
            self.iface.mapCanvas().refresh()
            self.iface.legendInterface().refreshLayerSymbology(layer)
        finally:
            QApplication.restoreOverrideCursor()

    def fillColumnCombos(self):
        query = self._getSqlQuery()
        if query == "":
            return

        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))

        # remove a trailing ';' from query if present
        if query.strip().endswith(';'):
            query = query.strip()[:-1]

        # get all the columns
        cols = []
        quotedCols = []
        connector = self.db.connector
        if self.aliasSubQuery:
            # get a new alias
            aliasIndex = 0
            while True:
                alias = "_subQuery__%d" % aliasIndex
                escaped = re.compile('\\b("?)' + re.escape(alias) + '\\1\\b')
                if not escaped.search(query):
                    break
                aliasIndex += 1

            sql = u"SELECT * FROM (%s\n) AS %s LIMIT 0" % (unicode(query), connector.quoteId(alias))
        else:
            sql = u"SELECT * FROM (%s\n) WHERE 1=0" % unicode(query)

        c = None
        try:
            c = connector._execute(None, sql)
            cols = connector._get_cursor_columns(c)
            for col in cols:
                quotedCols.append(connector.quoteId(col))

        except BaseError as e:
            QApplication.restoreOverrideCursor()
            DlgDbError.showError(e, self)
            self.uniqueModel.clear()
            self.geomCombo.clear()
            return

        finally:
            if c:
                c.close()
                del c

        self.setColumnCombos(cols, quotedCols)

        QApplication.restoreOverrideCursor()

    def setColumnCombos(self, cols, quotedCols):
        # get sensible default columns. do this before sorting in case there's hints in the column order (eg, id is more likely to be first)
        try:
            defaultGeomCol = next(col for col in cols if col in ['geom', 'geometry', 'the_geom', 'way'])
        except:
            defaultGeomCol = None
        try:
            defaultUniqueCol = [col for col in cols if 'id' in col][0]
        except:
            defaultUniqueCol = None

        colNames = sorted(zip(cols, quotedCols))
        newItems = []
        uniqueIsFilled = False
        for (col, quotedCol) in colNames:
            item = QStandardItem(col)
            item.setData(quotedCol)
            item.setEnabled(True)
            item.setCheckable(self.allowMultiColumnPk)
            item.setSelectable(not self.allowMultiColumnPk)
            if self.allowMultiColumnPk:
                matchingItems = self.uniqueModel.findItems(col)
                if matchingItems:
                    item.setCheckState(matchingItems[0].checkState())
                    uniqueIsFilled = uniqueIsFilled or matchingItems[0].checkState() == Qt.Checked
                else:
                    item.setCheckState(Qt.Unchecked)
            newItems.append(item)
        if self.allowMultiColumnPk:
            self.uniqueModel.clear()
            self.uniqueModel.appendColumn(newItems)
            self.uniqueChanged()
        else:
            previousUniqueColumn = self.uniqueCombo.currentText()
            self.uniqueModel.clear()
            self.uniqueModel.appendColumn(newItems)
            if self.uniqueModel.findItems(previousUniqueColumn):
                self.uniqueCombo.setEditText(previousUniqueColumn)
                uniqueIsFilled = True

        oldGeometryColumn = self.geomCombo.currentText()
        self.geomCombo.clear()
        self.geomCombo.addItems(cols)
        self.geomCombo.setCurrentIndex(self.geomCombo.findText(oldGeometryColumn, Qt.MatchExactly))

        # set sensible default columns if the columns are not already set
        try:
            if self.geomCombo.currentIndex() == -1:
                self.geomCombo.setCurrentIndex(cols.index(defaultGeomCol))
        except:
            pass
        items = self.uniqueModel.findItems(defaultUniqueCol)
        if items and not uniqueIsFilled:
            if self.allowMultiColumnPk:
                items[0].setCheckState(Qt.Checked)
            else:
                self.uniqueCombo.setEditText(defaultUniqueCol)
        try:
            pass
        except:
            pass

    def copySelectedResults(self):
        if len(self.viewResult.selectedIndexes()) <= 0:
            return
        model = self.viewResult.model()

        # convert to string using tab as separator
        text = model.headerToString("\t")
        for idx in self.viewResult.selectionModel().selectedRows():
            text += "\n" + model.rowToString(idx.row(), "\t")

        QApplication.clipboard().setText(text, QClipboard.Selection)
        QApplication.clipboard().setText(text, QClipboard.Clipboard)

    def initCompleter(self):
        dictionary = None
        if self.db:
            dictionary = self.db.connector.getSqlDictionary()
        if not dictionary:
            # use the generic sql dictionary
            from .sql_dictionary import getSqlDictionary

            dictionary = getSqlDictionary()

        wordlist = []
        for name, value in dictionary.iteritems():
            wordlist += value  # concat lists
        wordlist = list(set(wordlist))  # remove duplicates

        api = QsciAPIs(self.editSql.lexer())
        for word in wordlist:
            api.add(word)

        api.prepare()
        self.editSql.lexer().setAPIs(api)

    def displayQueryBuilder(self):
        dlg = QueryBuilderDlg(self.iface, self.db, self, reset=self.queryBuilderFirst)
        self.queryBuilderFirst = False
        r = dlg.exec_()
        if r == QDialog.Accepted:
            self.editSql.setText(dlg.query)

    def _getSqlQuery(self):
        sql = self.editSql.selectedText()
        if len(sql) == 0:
            sql = self.editSql.text()
        return sql

    def uniqueChanged(self):
        # when an item is (un)checked, simply trigger an update of the combobox text
        self.uniqueTextChanged(None)

    def uniqueTextChanged(self, text):
        # Whenever there is new text displayed in the combobox, check if it is the correct one and if not, display the correct one.
        checkedItems = []
        for item in self.uniqueModel.findItems("*", Qt.MatchWildcard):
            if item.checkState() == Qt.Checked:
                checkedItems.append(item.text())
        label = ", ".join(checkedItems)
        if text != label:
            self.uniqueCombo.setEditText(label)

    def setFilter(self):
        from qgis.gui import QgsQueryBuilder
        layer = self._getSqlLayer("")
        if not layer:
            return

        dlg = QgsQueryBuilder(layer)
        dlg.setSql(self.filter)
        if dlg.exec_():
            self.filter = dlg.sql()
        layer.deleteLater()
class PluginRepositoriesGUI(QWidget):
    PATH_COLUMN = 1
    ACTIVE_COLUMN = 0
    AUTO_UPDATE_COLUMN = 2
    STATUS_COLUMN = 3
    
    addRepository = pyqtSignal()
    checkForUpdates = pyqtSignal()
    
    def __init__(self, logger, parent):
        super(PluginRepositoriesGUI, self).__init__(parent)
       
        self.logger = logger
        self._gitHandler = GitHandler(logger)
        
        layout = QVBoxLayout(self)
        
        self._reposTable = QTreeView(self) 
        self._reposTable.setIndentation(0)
        self._reposTable.header().setStretchLastSection(False)
                
        self._reposModel = QStandardItemModel(self._reposTable)
        self._initModel()
        self._reposTable.setModel(self._reposModel)
        
        self._reposTable.setSelectionMode(QTreeView.ExtendedSelection)
        self._reposTable.selectionModel().selectionChanged.connect(self._selectionChanged)
        
        layout.addWidget(self._reposTable)

        buttonLayout = QHBoxLayout()    
        addButton = QPushButton("Add...")
        addButton.clicked.connect(self.addRepository)
        self._removeButton = QPushButton("Remove")
        self._removeButton.setEnabled(False)
        self._removeButton.clicked.connect(self._removeSelected)
        refreshButton = QPushButton("Check Status")
        refreshButton.clicked.connect(self.checkForUpdates)
        buttonLayout.addWidget(addButton)
        buttonLayout.addWidget(self._removeButton)
        buttonLayout.addWidget(refreshButton)
        
        layout.addLayout(buttonLayout)
        
        self._statusWidget = QWidget(self) 
        self._statusWidget.setVisible(False)
        statusLayout = QHBoxLayout(self._statusWidget)
        statusLayout.setContentsMargins(0, 0, 0, 0)
        self._statusLabel = QLabel(self)
        statusLayout.addWidget(self._statusLabel)
        layout.addWidget(self._statusWidget)
        
    def clear(self):
        self._initModel()
        
    def _updateStatusItem(self, item, path, outdated=None, upToDate=None):
        # first check fresh results, then the ones from repositories
        if upToDate and path in upToDate:
            item.setText(upToDate[path])
            if upToDate[path] == GitHandler.UP_TO_DATE_REASON:
                item.setData(QColor(0, 255, 0), Qt.DecorationRole)
            else:
                item.setData(QColor(255, 0, 0), Qt.DecorationRole)
        elif outdated and path in outdated:
            item.setText("Repository can be updated")
            item.setData(QColor(255, 215, 0), Qt.DecorationRole)
        elif get_settings().get_plugin_repositories().isUpToDate(path):
            item.setText("No updates (for details, check status)")
            item.setData(QColor(0, 255, 0), Qt.DecorationRole)
        elif get_settings().get_plugin_repositories().isOutdated(path):
            item.setText("Repository can be updated")
            item.setData(QColor(255, 215, 0), Qt.DecorationRole)
        else:
            item.setData(None, Qt.DecorationRole)
        
    def updateStatusItems(self, outdated=None, upToDate=None):
        for row in xrange(self._reposModel.rowCount()):
            path = convert_string(self._reposModel.item(row, self.PATH_COLUMN).data(Qt.DisplayRole).toString())
            self._updateStatusItem(self._reposModel.item(row, self.STATUS_COLUMN), path, outdated, upToDate)
        
    def _initModel(self):
        from PyQt4.QtCore import QStringList
        self._reposModel.clear()
        stringList = QStringList([u"Active", u"Path", u"Auto Update", u"Status"])
        self._reposModel.setColumnCount(stringList.count())
        self._reposModel.setHorizontalHeaderLabels(stringList)
        
    def appendRepository(self, path, active, autoUpdate, canAutoUpdate = None):
        activeItem = QStandardItem()
        activeItem.setEditable(False)
        activeItem.setCheckState(Qt.Checked if active else Qt.Unchecked)
        activeItem.setCheckable(True)
        
        pathItem = QStandardItem()
        pathItem.setData(path, Qt.DisplayRole)
        pathItem.setEditable(False)
        
        autoUpdateItem = QStandardItem()
        autoUpdateItem.setEditable(False)
        if canAutoUpdate == None:
            canAutoUpdate = self._gitHandler.hasGit(path)
        if canAutoUpdate:
            autoUpdateItem.setCheckState(Qt.Checked if autoUpdate else Qt.Unchecked)
            autoUpdateItem.setCheckable(True)
                
        statusItem = QStandardItem()
        self._updateStatusItem(statusItem, path)
                
        self._reposModel.appendRow([activeItem, pathItem, autoUpdateItem, statusItem])
        
    def resizeColumns(self):
        self._reposTable.resizeColumnToContents(self.ACTIVE_COLUMN)
        self._reposTable.resizeColumnToContents(self.AUTO_UPDATE_COLUMN)
        self._reposTable.header().setResizeMode(self.PATH_COLUMN, QHeaderView.Stretch)
        self._reposTable.header().setResizeMode(self.STATUS_COLUMN, QHeaderView.ResizeToContents)
    
    def getTable(self):
        return self._reposTable
    
    @loggingSlot(QItemSelection, QItemSelection)
    def _selectionChanged(self, _sel, _desel):
        selection = self._reposTable.selectionModel().selectedRows()
        self._removeButton.setEnabled(len(selection) > 0)
        
    @loggingSlot()
    def _removeSelected(self):
        selection = self._reposTable.selectionModel().selectedRows()
        for index in selection:
            self._reposTable.model().removeRow(index.row())
        
    def setStatus(self, msg, _progress=False):
        if msg:
            self._statusLabel.setText(msg)
            self._statusWidget.setVisible(True)
        else:
            self._statusWidget.setVisible(False)
Exemple #21
0
class TreeLegend(QObject):

    toggledLegend = pyqtSignal(list)
    descriptionLegend = pyqtSignal(str)

    def __init__(self, treeView):
        def init():
            self.setHeader()
            self.tree.setModel(self.model)
            self.headerView.setMovable(False)
            self.headerView.setClickable(True)
            self.tree.setSelectionMode(0)  # no selection

        super(TreeLegend, self).__init__()
        self.tree = treeView
        #
        self.hasConnect = self.layer = self.legendItems = None
        self.visibleItems = []
        self.model = QStandardItemModel(0, 1)
        self.headerView = self.tree.header()
        #
        init()
        self._connect()

    def __del__(self):
        if self.hasConnect:
            self._connect(False)
        self.model.clear()
        self.layer.legendChanged.disconnect(self.updateLegendItems)

    def _connect(self, isConnect=True):
        ss = [{
            'signal': self.tree.clicked,
            'slot': self.toggleItem
        }, {
            'signal': self.headerView.sectionClicked,
            'slot': self.toggleHeader
        }, {
            'signal': self.headerView.sectionDoubleClicked,
            'slot': self.emitDescription
        }]
        if isConnect:
            self.hasConnect = True
            for item in ss:
                item['signal'].connect(item['slot'])
        else:
            self.hasConnect = False
            for item in ss:
                item['signal'].disconnect(item['slot'])

    def setHeader(self, data=None):
        if data is None:
            self.model.clear()
            nameHeader = 'Select Raster Layer(Palette)'
            font = QFont()
            font.setStrikeOut(False)
            headerModel = QStandardItem(nameHeader)
            headerModel.setData(font, Qt.FontRole)
            tip = "Raster with Palette(Single Band)"
            headerModel.setData(tip, Qt.ToolTipRole)
            self.model.setHorizontalHeaderItem(0, headerModel)
        else:
            headerModel = self.model.horizontalHeaderItem(0)
            label = "%s" % data['name']
            formatMgs = "Layer: %s\nSource: %s\nNumber Class: %d\nWidth: %d\nHeight: %d\nRes.X: %f\nRes.Y: %f\n\n* Double click copy to Clipboard"
            dataMsg = (data['name'], data['source'], data['num_class'],
                       data['width'], data['height'], data['resX'],
                       data['resY'])
            tip = formatMgs % dataMsg
            headerModel.setData(data, Qt.UserRole)
            headerModel.setData(label, Qt.DisplayRole)
            headerModel.setData(tip, Qt.ToolTipRole)

    def setLayer(self, layer):
        self.legendItems = layer.legendSymbologyItems()
        total = len(self.legendItems)
        self.visibleItems = [True for x in range(total)]
        data = {
            'name': layer.name(),
            'source': layer.source(),
            'num_class': total,
            'width': layer.width(),
            'height': layer.height(),
            'resX': layer.rasterUnitsPerPixelX(),
            'resY': layer.rasterUnitsPerPixelY()
        }
        self.setHeader(data)
        #
        if not self.layer is None:
            self.layer.legendChanged.disconnect(self.updateLegendItems)
        layer.legendChanged.connect(self.updateLegendItems)
        self.layer = layer

    def setLegend(self, values):
        def setHeader():
            headerModel = self.model.horizontalHeaderItem(0)
            data = headerModel.data(Qt.UserRole)
            data['num_class'] = len(values)
            self.setHeader(data)

        def createItem(item):
            (pixel, total) = item
            (legend, color) = self.legendItems[pixel]
            name = "[%d] %s" % (pixel, legend)
            tip = "Value pixel: %d\nTotal pixels: %d\nClass name: %s" % (
                pixel, total, legend)
            pix = QPixmap(16, 16)
            pix.fill(color)
            font.setStrikeOut(not self.visibleItems[pixel])
            #
            itemModel = QStandardItem(QIcon(pix), name)
            itemModel.setEditable(False)
            itemModel.setData(font, Qt.FontRole)
            itemModel.setData(tip, Qt.ToolTipRole)
            itemModel.setData(item, Qt.UserRole)
            #
            return itemModel

        setHeader()
        self.model.removeRows(0, self.model.rowCount())
        #
        font = QFont()
        for item in values:
            self.model.appendRow(createItem(item))

    def setEnabled(self, isEnable=True):
        self._connect(isEnable)
        self.tree.setEnabled(isEnable)

    def getLayerName(self):
        headerModel = self.model.horizontalHeaderItem(0)
        return headerModel.data(Qt.UserRole)['name']

    @pyqtSlot()
    def updateLegendItems(self):
        self.legendItems = self.layer.legendSymbologyItems()
        # Refresh legend
        rows = self.model.rowCount()
        row = 0
        while row < rows:
            index = self.model.index(row, 0)
            (pixel, total) = self.model.data(index, Qt.UserRole)
            (legend, color) = self.legendItems[pixel]
            pix = QPixmap(16, 16)
            pix.fill(color)
            self.model.setData(index, QIcon(pix), Qt.DecorationRole)
            row += 1

    @pyqtSlot('QModelIndex')
    def toggleItem(self, index):
        font = index.data(Qt.FontRole)
        strike = not font.strikeOut()
        font.setStrikeOut(strike)
        self.model.setData(index, font, Qt.FontRole)
        #
        (pixel, total) = index.data(Qt.UserRole)
        visible = not strike
        self.visibleItems[pixel] = visible
        #
        self.toggledLegend.emit(self.visibleItems)

    @pyqtSlot(int)
    def toggleHeader(self, logical):
        rowCount = self.model.rowCount()
        if rowCount == 0:
            return

        header = self.model.horizontalHeaderItem(0)
        font = header.data(Qt.FontRole)
        strike = not font.strikeOut()
        font.setStrikeOut(strike)
        header.setData(font, Qt.FontRole)
        #
        items = []
        row = 0
        while row < self.model.rowCount():
            index = self.model.index(row, 0)
            self.model.setData(index, font, Qt.FontRole)
            items.append(index.data(Qt.UserRole))
            row += 1

        visible = not strike
        for item in items:
            (pixel, total) = item
            self.visibleItems[pixel] = visible
        #
        self.toggledLegend.emit(self.visibleItems)

    @pyqtSlot(int)
    def emitDescription(self):
        def getDescription():
            data = self.model.horizontalHeaderItem(0).data(Qt.UserRole)
            formatMgs = "Layer: %s\nSource: %s\nNumber Class: %d\nWidth: %d\nHeight: %d\nRes.X: %f\nRes.Y: %f"
            dataMsg = (data['name'], data['source'], data['num_class'],
                       data['width'], data['height'], data['resX'],
                       data['resY'])
            descHeader = formatMgs % dataMsg
            #
            descItems = ["Value pixel;Total pixels;Class name"]
            rows = self.model.rowCount()
            row = 0
            while row < rows:
                index = self.model.index(row, 0)
                (pixel, total) = self.model.data(index, Qt.UserRole)
                (legend, color) = self.legendItems[pixel]
                descItems.append("%d;%d;%s" % (pixel, total, legend))
                row += 1

            return "%s\n\n%s" % (descHeader, '\n'.join(descItems))

        if self.model.rowCount() > 0:
            self.descriptionLegend.emit(getDescription())
Exemple #22
0
class MultipleSelectTreeView(QListView):
    """
    Custom QListView implementation that displays checkable items from a
    multiple select column type.
    """
    def __init__(self, column, parent=None):
        """
        Class constructor.
        :param column: Multiple select column object.
        :type column: MultipleSelectColumn
        :param parent: Parent widget for the control.
        :type parent: QWidget
        """
        QListView.__init__(self, parent)

        #Disable editing of lookup values
        self.setEditTriggers(QAbstractItemView.NoEditTriggers)

        self.column = column

        self._item_model = QStandardItemModel(self)

        self._value_list = self.column.value_list

        #Stores lookup objects based on primary keys
        self._lookup_cache = {}

        self._initialize()

        self._association = self.column.association

        self._first_parent_col = self._association.first_reference_column.name
        self._second_parent_col = self._association.second_reference_column.name

        #Association model
        self._assoc_cls = entity_model(self._association)

    def reset_model(self):
        """
        Resets the item model.
        """
        self._item_model.clear()
        self._item_model.setColumnCount(2)

    def clear(self):
        """
        Clears all items in the model.
        """
        self._item_model.clear()

    @property
    def association(self):
        """
        :return: Returns the association object corresponding to the column.
        :rtype: AssociationEntity
        """
        return self._association

    @property
    def value_list(self):
        """
        :return: Returns the ValueList object corresponding to the configured
        column object.
        :rtype: ValueList
        """
        return self._value_list

    @property
    def item_model(self):
        """
        :return: Returns the model corresponding to the checkable items.
        :rtype: QStandardItemModel
        """
        return self._item_model

    def _add_item(self, id, value):
        """
        Adds a row corresponding to id and corresponding value from a lookup
        table.
        :param id: Primary key of a lookup record.
        :type id: int
        :param value: Lookup value
        :type value: str
        """
        value_item = QStandardItem(value)
        value_item.setCheckable(True)
        id_item = QStandardItem(str(id))

        self._item_model.appendRow([value_item, id_item])

    def _initialize(self):
        #Populate list with lookup items
        self.reset_model()

        #Add all lookup values in the value list table
        vl_cls = entity_model(self._value_list)
        if not vl_cls is None:
            vl_obj = vl_cls()
            res = vl_obj.queryObject().all()
            for r in res:
                self._lookup_cache[r.id] = r
                self._add_item(r.id, r.value)

        self.setModel(self._item_model)

    def clear_selection(self):
        """
        Unchecks all items in the view.
        """
        for i in range(self._item_model.rowCount()):
            value_item = self._item_model.item(i, 0)

            if value_item.checkState() == Qt.Checked:
                value_item.setCheckState(Qt.Unchecked)

                if value_item.rowCount() > 0:
                    value_item.removeRow(0)

    def selection(self):
        """
        :return: Returns a list of selected items.
        :rtype: list
        """
        selection = []

        for i in range(self._item_model.rowCount()):
            value_item = self._item_model.item(i, 0)

            if value_item.checkState() == Qt.Checked:
                id_item = self._item_model.item(i, 1)
                id = int(id_item.text())

                #Get item from the lookup cache and append to selection
                if id in self._lookup_cache:
                    lookup_rec = self._lookup_cache[id]
                    selection.append(lookup_rec)

        return selection

    def set_selection(self, models):
        """
        Checks items corresponding to the specified models.
        :param models: List containing model values in the view for selection.
        :type models: list
        """
        for m in models:
            search_value = m.value
            v_items = self._item_model.findItems(search_value)

            #Loop through result and check items
            for vi in v_items:
                if vi.checkState() == Qt.Unchecked:
                    vi.setCheckState(Qt.Checked)
class InputDialog(GenericDialog):

    TBL_HEADER_LABEL=["Input Mesh", "Output group name"]

    def __init__(self, parent=None, name="InputDialog", modal=0):
        """
        This initializes a dialog windows to define the input data of
        the plugin function. The input data consist in a list of
        meshes characterizes each by a name, a pointer to the smesh
        servant object, a type and a group name (see data model in the
        inputdata.py).
        """
        GenericDialog.__init__(self, parent, name, modal)
        # Set up the user interface from Designer.
        self.__ui = Ui_InputFrame()
        # BE CAREFULL HERE, the ui form is NOT drawn in the global
        # dialog (already containing some generic widgets) but in the
        # center panel created in the GenericDialog as a void
        # container for the form. The InputFrame form is supposed
        # here to create only the widgets to be placed in the center
        # panel. Then, the setupUi function of this form draws itself
        # in the specified panel, i.e. the panel returned by
        # self.getPanel().
        self.__ui.setupUi(self.getPanel())

        self.setWindowTitle("Specification of input files")

        # The icon are supposed to be located in the plugin folder,
        # i.e. in the same folder than this python module file
        iconfolder=os.path.dirname(os.path.abspath(__file__))
        icon = QIcon()
        icon.addFile(os.path.join(iconfolder,"select.png"))
        self.__ui.btnSmeshObject.setIcon(icon)
        icon = QIcon()
        icon.addFile(os.path.join(iconfolder,"addinput.png"))
        self.__ui.btnAddInput.setIcon(icon)
        icon = QIcon()
        icon.addFile(os.path.join(iconfolder,"deleteinput.png"))
        self.__ui.btnDeleteInput.setIcon(icon)

        # We specify here the items in the combo box (even if already
        # defined in the designer) so that we can be sure of the item
        # indexation.
        self.MESHTYPE_ICONS = {}
        meshTypeIndex = InputData.MESHTYPES.CONCRETE
        self.__ui.cmbMeshType.setItemText(meshTypeIndex, "Béton")
        icon = QIcon()
        icon.addFile(os.path.join(iconfolder,"concrete.png"))
        self.__ui.cmbMeshType.setItemIcon(meshTypeIndex, icon)
        self.MESHTYPE_ICONS[meshTypeIndex] = icon

        meshTypeIndex = InputData.MESHTYPES.STEELBAR
        self.__ui.cmbMeshType.setItemText(meshTypeIndex, "Acier")
        icon = QIcon()
        icon.addFile(os.path.join(iconfolder,"steelbar.png"))
        self.__ui.cmbMeshType.setItemIcon(meshTypeIndex, icon)
        self.MESHTYPE_ICONS[meshTypeIndex] = icon
        
        # The click on btnSmeshObject (signal clicked() emitted by the
        # button btnSmeshObject) is connected to the slot
        # onSelectSmeshObject, etc ...
        self.connect(self.__ui.btnSmeshObject, SIGNAL('clicked()'), self.onSelectSmeshObject )
        self.connect(self.__ui.btnAddInput,    SIGNAL('clicked()'), self.onAddInput )
        self.connect(self.__ui.btnDeleteInput, SIGNAL('clicked()'), self.onDeleteInput )

        # Set up the model of the Qt table list
        self.__inputModel = QStandardItemModel(0,2)
        self.__inputModel.setHorizontalHeaderLabels(InputDialog.TBL_HEADER_LABEL)
        self.__ui.tblListInput.setModel(self.__inputModel)
        self.__ui.tblListInput.verticalHeader().hide()
        self.__ui.tblListInput.horizontalHeader().setStretchLastSection(True)
        # Note that the type is not display explicitly in the Qt table
        # because it is specified using an icon on the text of the
        # name item. 

        # Note that PADDER does not support group name longer than 8
        # characters. We apply then this limit in the gui field.
        self.__ui.txtGroupName.setMaxLength(GROUPNAME_MAXLENGTH)

        self.clear()

        self.smeshStudyTool = SMeshStudyTools()

    def clear(self):
        """
        This function clears the data gui area and associated values.
        """
        self.__ui.txtSmeshObject.setText("")
        self.__ui.txtGroupName.setText("")
        self.__inputModel.clear()
        self.__inputModel.setHorizontalHeaderLabels(InputDialog.TBL_HEADER_LABEL)
        if not DEBUG_MODE:
            self.__ui.txtSmeshObject.setEnabled(False)
            self.__ui.btnAddInput.setEnabled(False)
        self.__selectedMesh = None
        self.__dictInputData = {}
        self.__nbConcreteMesh = 0
        self.__nbSteelbarMesh = 0

    def accept(self):
        """
        This function is the slot connected to the button OK
        """
        # The dialog is raised in a non modal mode to get
        # interactivity with the parents windows. Then we have to emit
        # a signal to warn the parent observer that the dialog has
        # been validated so that it can process the event
        GenericDialog.accept(self)
        if self.wasOk():
            self.emit(SIGNAL('inputValidated()'))

    def onSelectSmeshObject(self):
        '''
        This function is the slot connected on the mesh selection
        button. It memorizes the selected mesh and put its name in the
        text field of the dialog box.
        '''
        mySObject, myEntry = guihelper.getSObjectSelected()
        if CORBA.is_nil(mySObject):
            self.__ui.txtSmeshObject.setText("You must choose a mesh")
            self.__ui.txtGroupName.setText("")
            self.__ui.txtSmeshObject.setEnabled(False)
            self.__ui.btnAddInput.setEnabled(False)
            self.__selectedMesh = None
            return

        self.smeshStudyTool.updateStudy(studyedit.getActiveStudyId())
        self.__selectedMesh = self.smeshStudyTool.getMeshObjectFromSObject(mySObject)
        if CORBA.is_nil(self.__selectedMesh):
            self.__ui.txtSmeshObject.setText("The selected object is not a mesh")
            self.__ui.txtGroupName.setText("")
            self.__ui.txtSmeshObject.setEnabled(False)
            self.__ui.btnAddInput.setEnabled(False)
            self.__selectedMesh = None
            return
        myName = mySObject.GetName()
        self.__ui.txtSmeshObject.setText(myName)
        self.__ui.txtSmeshObject.setEnabled(True)
        self.__ui.btnAddInput.setEnabled(True)

        # We can suggest a default group name from the mesh name
        self.__ui.txtGroupName.setText(myName)

    def onAddInput(self):
        """
        This function is the slot connected to the Add button. It
        creates a new entry in the list of input data, or updates this
        entry if it already exists.
        """
        meshName   = str(self.__ui.txtSmeshObject.text().trimmed())
        meshObject = self.__selectedMesh
        meshType   = self.__ui.cmbMeshType.currentIndex()
        groupName  = str(self.__ui.txtGroupName.text().trimmed())

        self.__addInputInGui(meshName, meshObject, meshType, groupName)
        self.__addInputInMap(meshName, meshObject, meshType, groupName)

    def __addInputInGui(self, meshName, meshObject, meshType, groupName):
        """
        This function adds an entry with the specified data int the
        GUI table (for data visualization purpose).
        """
        # The mesh name is used as the key index in the model. We have
        # to check first if this item already exists in the list.
        tblItems = self.__inputModel.findItems(meshName)
        row = self.__inputModel.rowCount()
        if not tblItems:
            tblItems = []
            tblItems.append(QStandardItem()) # input mesh name
            tblItems.append(QStandardItem()) # output group name
        else:
            row = tblItems[0].index().row()
            tblItems.append(self.__inputModel.item(row,1))

        tblItems[0].setText(meshName)
        tblItems[0].setIcon(self.MESHTYPE_ICONS[meshType])
        tblItems[1].setText(groupName)
        self.__inputModel.setItem(row,0,tblItems[0])
        self.__inputModel.setItem(row,1,tblItems[1])
        self.__ui.tblListInput.setCurrentIndex(tblItems[0].index())

    def __addInputInMap(self, meshName, meshObject, meshType, groupName):
        """
        This function adds an entry with the specified data in the
        internal map (for data management purpose).
        """
        # if the entry already exists, we remove it to replace by a
        # new one
        if self.__dictInputData.has_key(meshName):
            self.__delInputFromMap(meshName)
        
        inputData = InputData()
        inputData.meshName   = meshName
        inputData.meshObject = meshObject
        inputData.meshType   = meshType
        inputData.groupName  = groupName
        # The key of the map is the mesh name
        self.__dictInputData[meshName] = inputData
        if inputData.meshType == InputData.MESHTYPES.CONCRETE:
            self.__nbConcreteMesh += 1
        else:
            self.__nbSteelbarMesh += 1

        print inputData
        print "meshType = ",inputData.meshType
        print "nb concrete mesh ",self.__nbConcreteMesh
        print "nb steelbar mesh ",self.__nbSteelbarMesh
            

    def onDeleteInput(self):
        """
        This function is the slot connected to the Delete button. It
        remove from the data list the entry selected in the Qt table.
        """
        selectedIdx = self.__ui.tblListInput.selectedIndexes()
        if selectedIdx:
            row  = selectedIdx[0].row()
            tblItem  = self.__inputModel.item(row,0)
            meshName = str(tblItem.text())
            self.__inputModel.takeRow(row)
            # Don't forget to remove this entry from the mesh object
            # internal dictionnary
            self.__delInputFromMap(meshName)

    def __delInputFromMap(self, meshName):
        """
        This function removes the specified entry from the internal
        map (for data management purpose) 
        """
        inputData = self.__dictInputData.pop(meshName)
        if inputData.meshType == InputData.MESHTYPES.CONCRETE:
            self.__nbConcreteMesh -= 1
        else:
            self.__nbSteelbarMesh -= 1

        print inputData
        print "nb concrete mesh ",self.__nbConcreteMesh
        print "nb steelbar mesh ",self.__nbSteelbarMesh


    def setData(self, listInputData=[]):
        """
        This function fills the dialog widgets with values provided by
        the specified data list.
        """
        self.clear()
        for inputData in listInputData:

            meshName   = inputData.meshName
            meshObject = inputData.meshObject
            meshType   = inputData.meshType
            groupName  = inputData.groupName
            
            self.__addInputInGui(meshName, meshObject, meshType, groupName)
            self.__addInputInMap(meshName, meshObject, meshType, groupName)

            if not DEBUG_MODE:
                self.onSelectSmeshObject()

    def getData(self):
        """
        This function returns a list of InputData that corresponds to
        the data in the dialog widgets of the current dialog.
        """
        # Note that the values() function returns a copy of the list
        # of values.
        return self.__dictInputData.values()
        
    def checkData(self):
        """
        This function checks if the data are valid, from the dialog
        window point of view.
        """
        if self.__nbConcreteMesh == 0 and self.__nbSteelbarMesh == 0:
            self.checkDataMessage = "You must define at least one mesh (CONCRETE or STEELBAR)"
            return False        
        if self.__nbConcreteMesh > 1:
            self.checkDataMessage  = "You define multiple CONCRETE meshes."
            self.checkDataMessage += "You should verify first that your version of PADDER support this configuration."
            # just warn the user, but don't block
            QMessageBox.information(self, "Info", self.checkDataMessage)
            return True

        return True
class OWCorrelations(OWWidget):
    name = "Correlations"

    description = "Calculate correlation"
    icon = "icons/correlation.svg"

    inputs = [("Data", Table, 'set_data')]
    outputs = [("Correlations", Table), ("Variables", AttributeList)]

    def __init__(self):
        super().__init__()
        self.data = None

        self.pairwise_correlations = True
        self.correlations_type = 0
        self.selected_index = None
        self.changed_flag = False
        self.auto_commit = True
        self.splitter_state = None
        self.corr_graph = CorrelationsGraph(self)
        self.mainArea.layout().addWidget(self.corr_graph.plot_widget)
        self.resize(1000, 500)  # TODO better size handling

        gui.radioButtonsInBox(
            self.controlArea,
            self,
            "correlations_type",
            ("Pairwise Pearson correlation", "Pairwise Spearman correlation"),
            box="Correlations",
            callback=self.on_corr_type_change)

        self.corr_table = CorrelationsTableView()

        self.corr_model = QStandardItemModel()
        self.corr_table.setModel(self.corr_model)

        self.controlArea.layout().addWidget(self.corr_table)
        self.corr_table.selectionModel().selectionChanged.connect(
            self.on_table_selection_change)

    @property
    def target_variable(self):
        if self.data:
            return self.data.domain.class_var
        else:
            return None

    def on_corr_type_change(self):
        """Do necessary actions after correlation type change.

        Clear computed data, set selected by user variables and finally
        commit(_if) changes.
        """
        if self.data is not None:
            curr_selection = self.selected_vars
            self.clear_computed()
            self.run()

            if curr_selection:
                try:
                    self.set_selected_vars(*curr_selection)
                except Exception as ex:
                    import traceback
                    traceback.print_exc()

            self.commit_if()

    def on_table_selection_change(self, selected, deselected):
        indexes = self.corr_table.selectionModel().selectedIndexes()
        if indexes:
            index = indexes[0]
            i, j = index.row(), index.column()
            if self.correlations_type == 2 and \
                    is_continuous(self.target_variable):
                j = len(self.var_names) - 1

            vars = [self.cont_vars[i], self.cont_vars[j]]
            self.corr_graph.update_data(vars[0], vars[1], i, j)
        else:
            vars = None
        self.selected_vars = vars

        self.send("Variables", vars)

    def clear_computed(self):
        """Clear computed data."""
        self.corr_model.clear()
        self.set_all_pairwise_matrix(None)
        self.set_target_correlations(None, None)

    def set_selected_vars(self, x, y):
        """Set selected by user variable(s)."""
        x = self.cont_vars.index(x)
        y = self.cont_vars.index(y)
        if self.correlations_type == 2:
            y = 0

        model = self.corr_model
        sel_model = self.corr_table.selectionModel()
        sel_model.select(model.index(x, y), QItemSelectionModel.ClearAndSelect)

    def set_data(self, data):
        """
        Check if data has enough continuous variables.
        Update data, correlation type, correlation graph and commit changes.
        """
        self.clear()
        self.information()
        self.data = data
        if data is None:
            return
        if len(list(filter(lambda x: x.is_continuous, data.domain))) >= 2:
            self.set_variables_list(data)
            self.selected_index = None
            self.corr_graph.set_data(data)

            if self.selected_index is None or \
                    any(n in self.data.domain for n in self.selected_index):
                self.selected_index = self.var_names[:2]

            self.run()

        else:
            self.data = None
            self.information("Need data with at least 2 continuous variables.")

            self.commit_if()

        self.send("Correlations", Table(data))

    def clear(self):
        """ Clear all widget data. """
        self.data = None
        self.selected_vars = None
        self.clear_graph()

    def clear_graph(self):
        self.corr_graph._clear_plot_widget()
        self.corr_graph.set_data(None, None)
        self.corr_graph.replot()

    def set_variables_list(self, data):
        '''
        :param data: data
        :return: sets cont_vars and var_names
        '''
        vars = list(data.domain.variables)
        vars = [v for v in vars if v.is_continuous]
        self.cont_vars = vars
        self.var_names = [v.name for v in vars]

    def run(self):
        """ Start data matrix creation. """
        if self.correlations_type < 2:
            if self.correlations_type == 0:
                matrix = pairwise_pearson_correlations(self.data,
                                                       self.cont_vars)
            elif self.correlations_type == 1:
                matrix = pairwise_spearman_correlations(
                    self.data, self.cont_vars)
            self.set_all_pairwise_matrix(matrix)

        elif self.target_variable and self.target_variable.is_continuous:
            vars = [v for v in self.cont_vars if v != self.target_variable]
            p_corr = target_pearson_correlations(self.data, vars,
                                                 self.target_variable)
            s_corr = target_spearman_correlations(self.data, vars,
                                                  self.target_variable)
            correlations = [list(t) for t in zip(p_corr, s_corr)]
            self.set_target_correlations(correlations, vars)

    def set_all_pairwise_matrix(self, matrix):
        """ Set data matrix to correlations model and resize table. """
        self.matrix = matrix
        if matrix is not None:
            for i, row in enumerate(matrix):
                for j, e in enumerate(row):
                    item = QStandardItem()
                    if i != j:
                        item.setData(str(round(e, 5)), Qt.DisplayRole)
                    else:
                        item.setData(QColor(192, 192, 192), Qt.BackgroundRole)
                    self.corr_model.setItem(i, j, item)

            vars = self.cont_vars
            header = [v.name for v in vars]
            self.corr_model.setVerticalHeaderLabels(header)
            self.corr_model.setHorizontalHeaderLabels(header)

            self.corr_table.resizeColumnsToContents()
            self.corr_table.resizeRowsToContents()

            self.corr_table.updateGeometry()

    def set_target_correlations(self, correlations, vars=None):
        self.target_correlations = correlations
        if correlations is not None:
            for i, row in enumerate(correlations):
                for j, c in enumerate(row):
                    item = QStandardItem()
                    item.setData(c, Qt.DisplayRole)
                    self.corr_model.setItem(i, j, item)

            if vars is None:
                vars = self.cont_vars

            v_header = [v.name for v in vars]
            h_header = ["Pearson", "Spearman"]
            self.corr_model.setVerticalHeaderLabels(v_header)
            self.corr_model.setHorizontalHeaderLabels(h_header)

            self.corr_table.resizeColumnsToContents()
            self.corr_table.resizeRowsToContents()

            QTimer.singleShot(100, self.corr_table.updateGeometry)

    def commit_if(self):
        if self.auto_commit:
            self.commit()
        else:
            self.changed_flag = True

    def commit(self):
        table = None
        if self.data is not None:
            if self.correlations_type == 2 and self.target_variable and \
                    self.target_variable.is_continuous:
                pearson = ContinuousVariable.make("Pearson")
                spearman = ContinuousVariable.make("Spearman")
                row_name = StringVariable.make("Variable")

                domain = Orange.data.Domain([pearson, spearman],
                                            metas=[row_name])
                table = Orange.data.Table(domain, self.target_correlations)
                for inst, name in zip(table, self.var_names):
                    inst[row_name] = name
        self.send("Correlations", table)

    def selection_changed(self):  # TODO FIX IT
        pass
Exemple #25
0
class VizRankDialog(QDialog, ProgressBarMixin):
    """
    Base class for VizRank dialogs, providing a GUI with a table and a button,
    and the skeleton for managing the evaluation of visualizations.

    Derived classes need to provide generators of combinations (e.g. pairs
    of attribtutes) and the scoring function. The widget stores the current
    upon pause, and restores it upon continuation.

    The class provides a table and a button. A widget constructs a single
    instance of this dialog in its `__init__`, like (in Sieve):

        self.vizrank = SieveRank(self)
        self.vizrank_button = gui.button(
            box, self, "Score Combinations", callback=self.vizrank.reshow)

    The widget (the argument `self`) above is stored in `VizRankDialog`'s
    attribute `master` since derived classes will need to interact with is.

    When the widget receives new data, it must call the VizRankDialog's
    method :obj:`VizRankDialog.initialize()` to clear the GUI and reset the
    state.

    Clicking the Start button calls method `run` (and renames the button to
    Pause). Run sets up a progress bar by getting the number of combinations
    from :obj:`VizRankDialog.state_count()`. It restores the paused state
    (if any) and calls generator :obj:`VizRankDialog.iterate_states()`. For
    each generated state, it calls :obj:`VizRankDialog.score(state)`, which
    must return the score (lower is better) for this state. If the returned
    state is not `None`, the data returned by `row_for_state` is inserted at
    the appropriate place in the table.

    Args:
        master (Orange.widget.OWWidget): widget to which the dialog belongs

    Attributes:
        master (Orange.widget.OWWidget): widget to which the dialog belongs
        captionTitle (str): the caption for the dialog. This can be a class
          attribute. `captionTitle` is used by the `ProgressBarMixin`.
    """

    captionTitle = ""

    processingStateChanged = Signal(int)
    progressBarValueChanged = Signal(float)

    def __init__(self, master):
        """Initialize the attributes and set up the interface"""
        super().__init__(windowTitle=self.captionTitle)
        self.master = master

        self.keep_running = False
        self.saved_state = None
        self.saved_progress = 0
        self.scores = []

        self.setLayout(QVBoxLayout())
        self.rank_model = QStandardItemModel(self)
        self.rank_table = view = QTableView(
            selectionBehavior=QTableView.SelectRows,
            selectionMode=QTableView.SingleSelection,
            showGrid=False)
        view.setItemDelegate(HorizontalGridDelegate())
        view.setModel(self.rank_model)
        view.selectionModel().selectionChanged.connect(
            self.on_selection_changed)
        view.horizontalHeader().setStretchLastSection(True)
        view.horizontalHeader().hide()
        self.layout().addWidget(view)

        self.button = gui.button(
            self, self, "Start", callback=self.toggle, default=True)

    def reshow(self):
        """Put the widget on top of all windows
        """
        self.show()
        self.raise_()
        self.activateWindow()

    def initialize(self):
        """
        Clear and initialize the dialog.

        This method must be called by the widget when the data is reset,
        e.g. from `set_data` handler.
        """
        self.keep_running = False
        self.saved_state = None
        self.saved_progress = 0
        self.scores = []
        self.rank_model.clear()
        self.button.setText("Start")
        self.button.setEnabled(self.check_preconditions())

    def check_preconditions(self):
        """Check whether there is sufficient data for ranking."""
        return True

    def on_selection_changed(self, selected, deselected):
        """
        Set the new visualization in the widget when the user select a
        row in the table.

        If derived class does not reimplement this, the table gives the
        information but the user can't click it to select the visualization.

        Args:
            selected: the index of the selected item
            deselected: the index of the previously selected item
        """
        pass

    def iterate_states(self, initial_state):
        """
        Generate all possible states (e.g. attribute combinations) for the
        given data. The content of the generated states is specific to the
        visualization.

        This method must be defined in the derived classes.

        Args:
            initial_state: initial state; None if this is the first call
        """
        raise NotImplementedError

    def state_count(self):
        """
        Return the number of states for the progress bar.

        Derived classes should implement this to ensure the proper behaviour of
        the progress bar"""
        return 0

    def compute_score(self, state):
        """
        Abstract method for computing the score for the given state. Smaller
        scores are better.

        Args:
            state: the state, e.g. the combination of attributes as generated
                by :obj:`state_count`.
        """
        raise NotImplementedError

    def row_for_state(self, state, score):
        """
        Abstract method that return the items that are inserted into the table.

        Args:
            state: the state, e.g. combination of attributes
            score: score, computed by :obj:`compute_score`
            """
        raise NotImplementedError

    def _select_first_if_none(self):
        if not self.rank_table.selectedIndexes():
            self.rank_table.selectRow(0)

    def run(self):
        """Compute and show scores"""
        with self.progressBar(self.state_count()) as progress:
            progress.advance(self.saved_progress)
            for state in self.iterate_states(self.saved_state):
                if not self.keep_running:
                    self.saved_state = state
                    self.saved_progress = progress.count
                    self._select_first_if_none()
                    return
                score = self.compute_score(state)
                if score is not None:
                    pos = bisect_left(self.scores, score)
                    self.rank_model.insertRow(
                        pos, self.row_for_state(score, state))
                    self.scores.insert(pos, score)
                progress.advance()
            self._select_first_if_none()
            self.button.setText("Finished")
            self.button.setEnabled(False)

    def toggle(self):
        """Start or pause the computation."""
        self.keep_running = not self.keep_running
        if self.keep_running:
            self.button.setText("Pause")
            self.run()
        else:
            self._select_first_if_none()
            self.button.setText("Continue")
Exemple #26
0
class OWConfusionMatrix(widget.OWWidget):
    name = "Confusion Matrix"
    description = "Shows a confusion matrix."
    icon = "icons/ConfusionMatrix.svg"
    priority = 1001

    inputs = [{"name": "Evaluation Results",
               "type": Orange.evaluation.testing.Results,
               "handler": "set_results"}]
    outputs = [{"name": "Selected Data",
                "type": Orange.data.Table}]

    quantities = ["Number of instances",
                  "Observed and expected instances",
                  "Proportion of predicted",
                  "Proportion of true"]

    selected_learner = settings.Setting([])
    selected_quantity = settings.Setting(0)
    append_predictions = settings.Setting(True)
    append_probabilities = settings.Setting(False)
    autocommit = settings.Setting(True)

    def __init__(self, parent=None):
        super().__init__(parent)

        self.results = None
        self.learners = []
        self._invalidated = False

        box = gui.widgetBox(self.controlArea, "Learners")

        self.learners_box = gui.listBox(
            box, self, "selected_learner", "learners",
            callback=self._learner_changed
        )
        box = gui.widgetBox(self.controlArea, "Show")

        combo = gui.comboBox(box, self, "selected_quantity",
                             items=self.quantities,
                             callback=self._update)

        box = gui.widgetBox(self.controlArea, "Selection")

        gui.button(box, self, "Correct",
                   callback=self.select_correct, autoDefault=False)
        gui.button(box, self, "Misclassified",
                   callback=self.select_wrong, autoDefault=False)
        gui.button(box, self, "None",
                   callback=self.select_none, autoDefault=False)

        self.outputbox = box = gui.widgetBox(self.controlArea, "Output")
        gui.checkBox(box, self, "append_predictions",
                     "Append class predictions", callback=self._invalidate)
        gui.checkBox(box, self, "append_probabilities",
                     "Append predicted class probabilities",
                     callback=self._invalidate)

        b = gui.button(box, self, "Commit", callback=self.commit, default=True)
        cb = gui.checkBox(box, self, "autocommit", "Commit automatically")
        gui.setStopper(self, b, cb, "_invalidated", callback=self.commit)

        grid = QGridLayout()
        grid.setContentsMargins(0, 0, 0, 0)
        grid.addWidget(QLabel("Predicted"), 0, 1, Qt.AlignCenter)
        grid.addWidget(VerticalLabel("Correct Class"), 1, 0, Qt.AlignCenter)

        self.tablemodel = QStandardItemModel()
        self.tableview = QTableView(
            editTriggers=QTableView.NoEditTriggers,
        )
        self.tableview.setModel(self.tablemodel)
        self.tableview.selectionModel().selectionChanged.connect(
            self._invalidate
        )
        grid.addWidget(self.tableview, 1, 1)
        self.mainArea.layout().addLayout(grid)

    def set_results(self, results):
        """Set the input results."""

        self.clear()
        self.warning([0, 1])

        data = None
        if results is not None:
            if results.data is not None:
                data = results.data

        if data is not None and \
                not isinstance(data.domain.class_var,
                               Orange.data.DiscreteVariable):
            data = None
            results = None
            self.warning(
                0, "Confusion Matrix cannot be used for regression results.")

        self.results = results
        self.data = data

        if data is not None:
            class_values = data.domain.class_var.values
        elif results is not None:
            raise NotImplementedError

        if results is not None:
            nmodels, ntests = results.predicted.shape
            headers = class_values + [unicodedata.lookup("N-ARY SUMMATION")]

            # NOTE: The 'fitter_names' is set in 'Test Learners' widget.
            if hasattr(results, "fitter_names"):
                self.learners = results.fitter_names
            else:
                self.learners = ["L %i" % (i + 1) for i in range(nmodels)]

            self.tablemodel.setVerticalHeaderLabels(headers)
            self.tablemodel.setHorizontalHeaderLabels(headers)
            self.tablemodel.setRowCount(len(class_values) + 1)
            self.tablemodel.setColumnCount(len(class_values) + 1)
            self.selected_learner = [0]
            self._update()

    def clear(self):
        self.learners = []
        self.results = None
        self.data = None
        self.tablemodel.clear()

    def select_correct(self):
        selection = QItemSelection()
        n = self.tablemodel.rowCount()
        for i in range(n):
            index = self.tablemodel.index(i, i)
            selection.select(index, index)

        self.tableview.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect
        )

    def select_wrong(self):
        selection = QItemSelection()
        n = self.tablemodel.rowCount()

        for i in range(n):
            for j in range(i + 1, n):
                index = self.tablemodel.index(i, j)
                selection.select(index, index)
                index = self.tablemodel.index(j, i)
                selection.select(index, index)

        self.tableview.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect
        )

    def select_none(self):
        self.tableview.selectionModel().clear()

    def commit(self):
        if self.results and self.data:
            indices = self.tableview.selectedIndexes()
            indices = {(ind.row(), ind.column()) for ind in indices}
            actual = self.results.actual
            selected_learner = self.selected_learner[0]
            learner_name = self.learners[selected_learner]
            predicted = self.results.predicted[selected_learner]
            selected = [i for i, t in enumerate(zip(actual, predicted))
                        if t in indices]
            row_indices = self.results.row_indices[selected]

            extra = []
            class_var = self.data.domain.class_var
            metas = self.data.domain.metas

            if self.append_predictions:
                predicted = numpy.array(predicted[selected], dtype=object)
                extra.append(predicted.reshape(-1, 1))
                var = Orange.data.DiscreteVariable(
                    "{}({})".format(class_var.name, learner_name),
                    class_var.values
                )
                metas = metas + (var,)

            if self.append_probabilities and \
                    self.results.probabilities is not None:
                probs = self.results.probabilities[selected_learner, selected]
                extra.append(numpy.array(probs, dtype=object))
                pvars = [Orange.data.ContinuousVariable("p({})".format(value))
                         for value in class_var.values]
                metas = metas + tuple(pvars)

            X = self.data.X[row_indices]
            Y = self.data.Y[row_indices]
            M = self.data.metas[row_indices]

            M = numpy.hstack((M,) + tuple(extra))
            domain = Orange.data.Domain(
                self.data.domain.attributes,
                self.data.domain.class_vars,
                metas
            )

            data = Orange.data.Table.from_numpy(domain, X, Y, M)

        else:
            data = None

        self.send("Selected Data", data)
        self._invalidated = False

    def _invalidate(self):
        if self.autocommit:
            self.commit()
        else:
            self._invalidated = True

    def _learner_changed(self):
        # The selected learner has changed
        self._update()

    def _update(self):
        # Update the displayed confusion matrix
        if self.results is not None and self.selected_learner:
            index = self.selected_learner[0]
            cmatrix = confusion_matrix(self.results, index)
            colsum = cmatrix.sum(axis=0)
            rowsum = cmatrix.sum(axis=1)
            total = rowsum.sum()

            if self.selected_quantity == 0:
                value = lambda i, j: int(cmatrix[i, j])
            elif self.selected_quantity == 1:
                priors = numpy.outer(rowsum, colsum) / total
                value = lambda i, j: \
                    "{} / {:5.3f}".format(cmatrix[i, j], priors[i, j])
            elif self.selected_quantity == 2:
                value = lambda i, j: \
                    ("{:2.1f} %".format(100 * cmatrix[i, j] / colsum[i])
                     if colsum[i] else "N/A")
            elif self.selected_quantity == 3:
                value = lambda i, j: \
                    ("{:2.1f} %".format(100 * cmatrix[i, j] / rowsum[i])
                     if colsum[i] else "N/A")
            else:
                assert False

            model = self.tablemodel
            for i, row in enumerate(cmatrix):
                for j, _ in enumerate(row):
                    item = model.item(i, j)
                    if item is None:
                        item = QStandardItem()
                    item.setData(value(i, j), Qt.DisplayRole)
                    item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                    item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
                    model.setItem(i, j, item)

            font = model.invisibleRootItem().font()
            bold_font = QFont(font)
            bold_font.setBold(True)

            def sum_item(value):
                item = QStandardItem()
                item.setData(value, Qt.DisplayRole)
                item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                item.setFlags(Qt.ItemIsEnabled)
                item.setFont(bold_font)
                return item

            N = len(colsum)
            for i in range(N):
                model.setItem(N, i, sum_item(int(colsum[i])))
                model.setItem(i, N, sum_item(int(rowsum[i])))

            model.setItem(N, N, sum_item(int(total)))
Exemple #27
0
class OWConfusionMatrix(widget.OWWidget):
    name = "Confusion Matrix"
    description = "Shows a confusion matrix."
    icon = "icons/ConfusionMatrix.svg"
    priority = 1001

    inputs = [{
        "name": "Evaluation Results",
        "type": Orange.evaluation.Results,
        "handler": "set_results"
    }]
    outputs = [{"name": "Selected Data", "type": Orange.data.Table}]

    quantities = [
        "Number of instances", "Proportion of predicted",
        "Proportion of actual"
    ]

    selected_learner = settings.Setting([])
    selected_quantity = settings.Setting(0)
    append_predictions = settings.Setting(True)
    append_probabilities = settings.Setting(False)
    autocommit = settings.Setting(True)

    def __init__(self, parent=None):
        super().__init__(parent)

        self.data = None
        self.results = None
        self.learners = []

        box = gui.widgetBox(self.controlArea, "Learners")

        self.learners_box = gui.listBox(box,
                                        self,
                                        "selected_learner",
                                        "learners",
                                        callback=self._learner_changed)
        box = gui.widgetBox(self.controlArea, "Show")

        gui.comboBox(box,
                     self,
                     "selected_quantity",
                     items=self.quantities,
                     callback=self._update)

        box = gui.widgetBox(self.controlArea, "Select")

        gui.button(box,
                   self,
                   "Correct",
                   callback=self.select_correct,
                   autoDefault=False)
        gui.button(box,
                   self,
                   "Misclassified",
                   callback=self.select_wrong,
                   autoDefault=False)
        gui.button(box,
                   self,
                   "None",
                   callback=self.select_none,
                   autoDefault=False)

        self.outputbox = box = gui.widgetBox(self.controlArea, "Output")
        gui.checkBox(box,
                     self,
                     "append_predictions",
                     "Predictions",
                     callback=self._invalidate)
        gui.checkBox(box,
                     self,
                     "append_probabilities",
                     "Probabilities",
                     callback=self._invalidate)

        gui.auto_commit(self.controlArea, self, "autocommit", "Send Data",
                        "Auto send is on")

        grid = QGridLayout()
        grid.setContentsMargins(0, 0, 0, 0)
        grid.addWidget(QLabel("Predicted"), 0, 1, Qt.AlignCenter)
        grid.addWidget(VerticalLabel("Actual Class"), 1, 0, Qt.AlignCenter)

        self.tablemodel = QStandardItemModel()
        self.tableview = QTableView(editTriggers=QTableView.NoEditTriggers)
        self.tableview.setModel(self.tablemodel)
        self.tableview.selectionModel().selectionChanged.connect(
            self._invalidate)
        grid.addWidget(self.tableview, 1, 1)
        self.mainArea.layout().addLayout(grid)

    def set_results(self, results):
        """Set the input results."""

        self.clear()
        self.warning([0, 1])

        data = None
        if results is not None:
            if results.data is not None:
                data = results.data

        if data is not None and \
                not isinstance(data.domain.class_var,
                               Orange.data.DiscreteVariable):
            data = None
            results = None
            self.warning(
                0, "Confusion Matrix cannot be used for regression results.")

        self.results = results
        self.data = data

        if data is not None:
            class_values = data.domain.class_var.values
        elif results is not None:
            raise NotImplementedError

        if results is not None:
            nmodels, ntests = results.predicted.shape
            headers = class_values + [unicodedata.lookup("N-ARY SUMMATION")]

            # NOTE: The 'learner_names' is set in 'Test Learners' widget.
            if hasattr(results, "learner_names"):
                self.learners = results.learner_names
            else:
                self.learners = ["L %i" % (i + 1) for i in range(nmodels)]

            self.tablemodel.setVerticalHeaderLabels(headers)
            self.tablemodel.setHorizontalHeaderLabels(headers)
            self.tablemodel.setRowCount(len(class_values) + 1)
            self.tablemodel.setColumnCount(len(class_values) + 1)
            self.selected_learner = [0]
            self._update()

    def clear(self):
        self.results = None
        self.data = None
        self.tablemodel.clear()
        # Clear learners last. This action will invoke `_learner_changed`
        # method
        self.learners = []

    def select_correct(self):
        selection = QItemSelection()
        n = self.tablemodel.rowCount()
        for i in range(n):
            index = self.tablemodel.index(i, i)
            selection.select(index, index)

        self.tableview.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

    def select_wrong(self):
        selection = QItemSelection()
        n = self.tablemodel.rowCount()

        for i in range(n):
            for j in range(i + 1, n):
                index = self.tablemodel.index(i, j)
                selection.select(index, index)
                index = self.tablemodel.index(j, i)
                selection.select(index, index)

        self.tableview.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

    def select_none(self):
        self.tableview.selectionModel().clear()

    def commit(self):
        if self.results is not None and self.data is not None \
                and self.selected_learner:
            indices = self.tableview.selectedIndexes()
            indices = {(ind.row(), ind.column()) for ind in indices}
            actual = self.results.actual
            selected_learner = self.selected_learner[0]
            learner_name = self.learners[selected_learner]
            predicted = self.results.predicted[selected_learner]
            selected = [
                i for i, t in enumerate(zip(actual, predicted)) if t in indices
            ]
            row_indices = self.results.row_indices[selected]

            extra = []
            class_var = self.data.domain.class_var
            metas = self.data.domain.metas

            if self.append_predictions:
                predicted = numpy.array(predicted[selected], dtype=object)
                extra.append(predicted.reshape(-1, 1))
                var = Orange.data.DiscreteVariable(
                    "{}({})".format(class_var.name, learner_name),
                    class_var.values)
                metas = metas + (var, )

            if self.append_probabilities and \
                    self.results.probabilities is not None:
                probs = self.results.probabilities[selected_learner, selected]
                extra.append(numpy.array(probs, dtype=object))
                pvars = [
                    Orange.data.ContinuousVariable("p({})".format(value))
                    for value in class_var.values
                ]
                metas = metas + tuple(pvars)

            X = self.data.X[row_indices]
            Y = self.data.Y[row_indices]
            M = self.data.metas[row_indices]
            row_ids = self.data.ids[row_indices]

            M = numpy.hstack((M, ) + tuple(extra))
            domain = Orange.data.Domain(self.data.domain.attributes,
                                        self.data.domain.class_vars, metas)
            data = Orange.data.Table.from_numpy(domain, X, Y, M)
            data.ids = row_ids
            data.name = learner_name

        else:
            data = None

        self.send("Selected Data", data)

    def _invalidate(self):
        self.commit()

    def _learner_changed(self):
        # The selected learner has changed
        self._update()
        self._invalidate()

    def _update(self):
        # Update the displayed confusion matrix
        if self.results is not None and self.selected_learner:
            index = self.selected_learner[0]
            cmatrix = confusion_matrix(self.results, index)
            colsum = cmatrix.sum(axis=0)
            rowsum = cmatrix.sum(axis=1)
            total = rowsum.sum()

            if self.selected_quantity == 0:
                value = lambda i, j: int(cmatrix[i, j])
            elif self.selected_quantity == 1:
                value = lambda i, j: \
                    ("{:2.1f} %".format(100 * cmatrix[i, j] / colsum[i])
                     if colsum[i] else "N/A")
            elif self.selected_quantity == 2:
                value = lambda i, j: \
                    ("{:2.1f} %".format(100 * cmatrix[i, j] / rowsum[i])
                     if colsum[i] else "N/A")
            else:
                assert False

            model = self.tablemodel
            for i, row in enumerate(cmatrix):
                for j, _ in enumerate(row):
                    item = model.item(i, j)
                    if item is None:
                        item = QStandardItem()
                    item.setData(value(i, j), Qt.DisplayRole)
                    item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                    item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
                    model.setItem(i, j, item)

            font = model.invisibleRootItem().font()
            bold_font = QFont(font)
            bold_font.setBold(True)

            def sum_item(value):
                item = QStandardItem()
                item.setData(value, Qt.DisplayRole)
                item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                item.setFlags(Qt.ItemIsEnabled)
                item.setFont(bold_font)
                return item

            N = len(colsum)
            for i in range(N):
                model.setItem(N, i, sum_item(int(colsum[i])))
                model.setItem(i, N, sum_item(int(rowsum[i])))

            model.setItem(N, N, sum_item(int(total)))
    class VizRank(OWWidget):
        name = "Rank projections (Scatter Plot)"

        def __init__(self, parent_widget):
            super().__init__()
            self.parent_widget = parent_widget
            self.want_control_area = False
            self.running = False
            self.progress = None
            self.k = 10

            self.projectionTable = QTableView()
            self.mainArea.layout().addWidget(self.projectionTable)
            self.projectionTable.setSelectionBehavior(QTableView.SelectRows)
            self.projectionTable.setSelectionMode(QTableView.SingleSelection)
            self.projectionTable.setSortingEnabled(True)
            self.projectionTableModel = QStandardItemModel(self)
            self.projectionTable.setModel(self.projectionTableModel)
            self.projectionTable.selectionModel().selectionChanged.connect(
                self.on_selection_changed)

            self.button = gui.button(self.mainArea, self, "Start evaluation",
                                     callback=self.toggle, default=True)
            self.resize(380, 512)
            self._initialize()

        def _initialize(self):
            self.running = False
            self.projectionTableModel.clear()
            self.projectionTableModel.setHorizontalHeaderLabels(
                ["Score", "Feature 1", "Feature 2"])
            self.projectionTable.setColumnWidth(0, 60)
            self.projectionTable.setColumnWidth(1, 120)
            self.projectionTable.setColumnWidth(2, 120)
            self.button.setText("Start evaluation")
            self.button.setEnabled(False)
            self.pause = False
            self.data = None
            self.attrs = []
            self.scores = []
            self.i, self.j = 0, 0
            if self.progress:
                self.progress.finish()
            self.progress = None


            self.information(0)
            if self.parent_widget.data:
                if not self.parent_widget.data.domain.class_var:
                    self.information(
                        0, "Data with a class variable is required.")
                    return
                if len(self.parent_widget.data.domain.attributes) < 2:
                    self.information(
                        0, 'At least 2 unique features are needed.')
                    return
                self.button.setEnabled(True)

        def on_selection_changed(self, selected, deselected):
            """Called when the ranks view selection changes."""
            a1 = selected.indexes()[1].data()
            a2 = selected.indexes()[2].data()
            self.parent_widget.update_attr(attributes=(a1, a2))

        def toggle(self):
            self.running ^= 1
            if self.running:
                self.button.setText("Pause")
                self.run()
            else:
                self.button.setText("Continue")
                self.button.setEnabled(False)

        def run(self):
            graph = self.parent_widget.graph
            y_full = self.parent_widget.data.Y
            norm = 1 / (len(y_full) * self.k)
            if not self.attrs:
                self.attrs = self.score_heuristic()
            if not self.progress:
                self.progress = gui.ProgressBar(
                    self, len(self.attrs) * (len(self.attrs) - 1) / 2)
            for i in range(self.i, len(self.attrs)):
                ind1 = graph.attribute_name_index[self.attrs[i]]
                for j in range(self.j, i):
                    if not self.running:
                        self.i, self.j = i, j
                        if not self.projectionTable.selectedIndexes():
                            self.projectionTable.selectRow(0)
                        self.button.setEnabled(True)
                        return
                    ind2 = graph.attribute_name_index[self.attrs[j]]
                    X = graph.scaled_data[[ind1, ind2], :]
                    valid = graph.get_valid_list([ind1, ind2])
                    X = X[:, valid].T
                    y = y_full[valid]
                    knn = NearestNeighbors(n_neighbors=self.k).fit(X)
                    ind = knn.kneighbors(return_distance=False)
                    score = norm * np.sum(y[ind] == y.reshape(-1, 1))
                    pos = bisect_left(self.scores, score)
                    self.projectionTableModel.insertRow(
                        len(self.scores) - pos,
                        [QStandardItem("{:.4f}".format(score)),
                         QStandardItem(self.attrs[j]),
                         QStandardItem(self.attrs[i])])
                    self.scores.insert(pos, score)
                    self.progress.advance()
                self.j = 0
            self.progress.finish()
            if not self.projectionTable.selectedIndexes():
                self.projectionTable.selectRow(0)
            self.button.setText("Finished")
            self.button.setEnabled(False)

        def score_heuristic(self):
            X = self.parent_widget.graph.scaled_data.T
            Y = self.parent_widget.data.Y
            dom = Orange.data.Domain([ContinuousVariable(str(i))
                                      for i in range(X.shape[1])],
                                     self.parent_widget.data.domain.class_vars)
            data = Orange.data.Table(dom, X, Y)
            weights = ReliefF(n_iterations=100, k_nearest=self.k)(data)
            attrs = sorted(
                zip(weights,
                    (x.name for x in self.parent_widget.data.domain.attributes)
                    ),
                reverse=True)
            return [a for _, a in attrs]
class ModelAtrributesView(QListView):
    """
    Custom QListView implementation that displays checkable model attributes.
    """
    def __init__(self,parent=None,dataModel = None):
        QListView.__init__(self, parent)
        
        self._dataModel = dataModel
        self._selectedDisplayMapping = OrderedDict()
        self._modelDisplayMapping = OrderedDict()
        self._attrModel = QStandardItemModel(self)
        
    def dataModel(self):
        """
        Returns the data model instance.
        """
        return self._dataModel
    
    def setDataModel(self,dataModel):
        """
        Sets the data model. Should be a callable class rather than the class.
        instance.
        """
        if callable(dataModel):
            self._dataModel = dataModel
            
        else:
            self._dataModel = dataModel.__class__
    
    def modelDisplayMapping(self):
        """
        Returns the column name and display name collection.
        """
        return self._modelDisplayMapping
    
    def setModelDisplayMapping(self, dataMapping):
        """
        Sets the mapping dictionary for the table object
        """
        if dataMapping != None:
            self._modelDisplayMapping=dataMapping

    def load(self, sort=False):
        """
        Load the model's attributes into the list view.
        """
        if self._dataModel == None:
            return
        
        try:
            self._loadAttrs(self._dataModel.displayMapping(), sort)
        except AttributeError:
            #Ignore error if model does not contain the displayMapping static method
            pass

    def load_mapping(self, mapping, sort=False):
        """
        Load collection containing column name and corresponding display name.
        """
        self._modelDisplayMapping = mapping

        self._loadAttrs(mapping, sort)

    def sort(self):
        """
        Sorts display name in ascending order.
        """
        self._attrModel.sort(0)
        
    def _loadAttrs(self, attrMapping, sort=False):
        """
        Loads display mapping into the list view.
        Specify to sort display names in ascending order once items have been
        added to the model.
        """
        self._attrModel.clear()
        self._attrModel.setColumnCount(2)
        
        for attrName,displayName in attrMapping.iteritems():
            #Exclude row ID in the list, other unique identifier attributes in the model can be used
            if attrName != "id":
                displayNameItem = QStandardItem(displayName)
                displayNameItem.setCheckable(True)
                attrNameItem = QStandardItem(attrName)
                
                self._attrModel.appendRow([displayNameItem,attrNameItem])
            
        self.setModel(self._attrModel)

        if sort:
            self._attrModel.sort(0)
        
    def selectedMappings(self):
        """
        Return a dictionary of field names and their corresponding display values.
        """
        selectedAttrs = {}
        
        for i in range(self._attrModel.rowCount()):
            displayNameItem = self._attrModel.item(i,0)
            
            if displayNameItem.checkState() == Qt.Checked:
                attrNameItem = self._attrModel.item(i,1)  
                selectedAttrs[attrNameItem.text()] = displayNameItem.text()
        
        return selectedAttrs
Exemple #30
0
class DirDocumentTypeSelector(QDialog):
    """
    Dialog for selecting supporting documents from a given directory. Default
    filter searches for PDF files only.
    """
    def __init__(self, dir, doc_types, parent=None, filters=None):
        super(DirDocumentTypeSelector, self).__init__(parent)
        self.setWindowTitle(
            self.tr('Documents in Folder')
        )
        self._filters = filters
        # Use PDF as default filter
        if not self._filters:
            self._filters = ['*.pdf']

        self._init_ui()
        self._dir = QDir(dir)
        self._dir.setNameFilters(self._filters)
        self._doc_types = doc_types

        self._attr_model = QStandardItemModel(self)
        self._sel_doc_types = OrderedDict()

        # Notification bar
        self._notif_bar = NotificationBar(self.vl_notif)

        self.resize(320, 350)

        # Load documents
        self.load_document_types()

    @property
    def selected_document_types(self):
        """
        :return: Returns a dictionary of the document types and the
        corresponding file paths as selected by the user.
        :rtype: dict
        """
        return self._sel_doc_types

    def _init_ui(self):
        # Draw UI widgets
        layout = QVBoxLayout()

        # Add layout for notification bar
        self.vl_notif = QVBoxLayout()
        layout.addLayout(self.vl_notif)
        self.lbl_info = QLabel()
        self.lbl_info.setObjectName('lbl_info')
        self.lbl_info.setText(self.tr(
            'The selected document types have been found in the directory, '
            'check/uncheck to specify which ones to upload.'
        ))
        self.lbl_info.setWordWrap(True)
        layout.addWidget(self.lbl_info)
        self.lst_docs = QListView()
        layout.addWidget(self.lst_docs)
        self.lbl_warning = QLabel()
        self.lbl_warning.setTextFormat(Qt.RichText)
        self.lbl_warning.setText(self.tr(
            '<html><head/><body><p><span style=" font-style:italic;">'
            '* Previously uploaded documents will be replaced.</span></p>'
            '</body></html>'
        ))
        self.lbl_warning.setWordWrap(True)
        layout.addWidget(self.lbl_warning)
        self.btn_box = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel
        )
        layout.addWidget(self.btn_box)
        self.setLayout(layout)

        # Connect signals
        self.btn_box.accepted.connect(
            self.set_selected_document_types
        )
        self.btn_box.rejected.connect(
            self.reject
        )

    def set_selected_document_types(self):
        """
        Sets the collections of accepted document types and their
        corresponding file paths and accepts the dialog.
        """
        self._sel_doc_types = OrderedDict()
        for i in range(self._attr_model.rowCount()):
            doc_type_item = self._attr_model.item(i, 0)

            if doc_type_item.checkState() == Qt.Checked:
                path_item = self._attr_model.item(i, 1)
                self._sel_doc_types[doc_type_item.text()] = path_item.text()

        if len(self._sel_doc_types) == 0:
            self._notif_bar.clear()
            msg = self.tr('No matching documents found or selected.')
            self._notif_bar.insertWarningNotification(msg)

            return

        self.accept()

    def load_document_types(self):
        """
        Load all document types to the list view and enable/check the items
        for those types that have been found.
        """
        self._attr_model.clear()
        self._attr_model.setColumnCount(2)

        file_infos = self._dir.entryInfoList(
            QDir.Readable | QDir.Files,
            QDir.Name
        )

        # Index file info based on name
        idx_file_infos = {fi.completeBaseName().lower(): fi for fi in file_infos}

        for d in self._doc_types:
            doc_type_item = QStandardItem(d)
            doc_type_item.setCheckable(True)
            path_item = QStandardItem()

            item_enabled = False
            check_state = Qt.Unchecked
            dl = d.lower()
            if dl in idx_file_infos:
                item_enabled = True
                check_state = Qt.Checked
                path = idx_file_infos[dl].filePath()
                path_item.setText(path)
                doc_type_item.setToolTip(path)

            doc_type_item.setEnabled(item_enabled)
            doc_type_item.setCheckState(check_state)

            self._attr_model.appendRow([doc_type_item, path_item])

        self.lst_docs.setModel(self._attr_model)
class TreeLegendCRS(QObject):

  def __init__(self, iface):
    super(TreeLegendCRS, self).__init__()
    self.iface = iface
    self.model = QStandardItemModel( 0, 1 )
    #
    self.tree = QTreeView( None )
    self.tree.setAttribute(Qt.WA_DeleteOnClose)
    self.tree.setModel( self.model )
    self.tree.setSelectionMode( 0 ) # no selection
    self.tree.setHeaderHidden( True )
    #
    self.dock = QDockWidget( "CRS of Layers", iface.mainWindow() )
    self.dock.setWidget( self.tree )
    #
    self._connect()

  def __del__(self):
    self.model.clear()
    self._connect( False )

  def _connect(self, isConnect = True):
    ss = [
      { 'signal': self.tree.clicked, 'slot': self.setCurrentLayer },
      { 'signal': QgsMapLayerRegistry.instance().legendLayersAdded, 'slot': self.updateLegend },
      { 'signal': QgsMapLayerRegistry.instance().layersRemoved, 'slot': self.updateLegend }
    ]
    if isConnect:
      self.hasConnect = True
      for item in ss:
        item['signal'].connect( item['slot'] )  
    else:
      self.hasConnect = False
      for item in ss:
        item['signal'].disconnect( item['slot'] )

  def addLegend(self):
    self.updateLegend()
    self.iface.addDockWidget( Qt.LeftDockWidgetArea , self.dock )

  @pyqtSlot(list)
  def updateLegend(self, lst=None):
    def getCrs_Layers():
      layers = self.iface.legendInterface().layers()
      crs_layers = {}
      for layer in layers:
        crs = layer.crs().authid()
        if not crs_layers.has_key( crs):
          crs_layers[ crs ] = [ layer ]
        else:
          crs_layers[ crs ].append( layer )

      return crs_layers

    def createItem( data ):
      item = None
      if isinstance( data, QgsMapLayer):
        item = QStandardItem( data.name() )
        item.setData( data, Qt.UserRole )
      else:
        item = QStandardItem( data )
      item.setEditable( False )

      return item

    self.model.clear()
    for crs, layers in getCrs_Layers().iteritems():
      name = "%s (%d)" % ( crs, len( layers ) )
      itemCRS = createItem( name )
      self.model.appendRow( itemCRS ) 
      for layer in layers:
        itemLayer = createItem( layer )
        itemCRS.appendRow( itemLayer )

  @pyqtSlot('QModelIndex')
  def setCurrentLayer(self, index):
    data = index.data( Qt.UserRole )
    if not data is None:
      self.iface.setActiveLayer( data )
Exemple #32
0
class MainWindow(QMainWindow, Ui_MainWindow):
    qtcb_enumerate = pyqtSignal(str, str, 'char', type((0,)), type((0,)), int, int)
    qtcb_connected = pyqtSignal(int)
    qtcb_disconnected = pyqtSignal(int)

    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)
        self.setupUi(self)
        self.setWindowIcon(QIcon(os.path.join(get_program_path(), "brickv-icon.png")))
        signal.signal(signal.SIGINT, self.exit_brickv)
        signal.signal(signal.SIGTERM, self.exit_brickv)

        self.async_thread = async_start_thread(self)

        self.setWindowTitle("Brick Viewer " + config.BRICKV_VERSION)

        self.tree_view_model_labels = ['Name', 'UID', 'FW Version']
        self.tree_view_model = QStandardItemModel()
        self.tree_view.setModel(self.tree_view_model)
        self.tree_view.doubleClicked.connect(self.item_double_clicked)
        self.set_tree_view_defaults()

        # Remove dummy tab
        self.tab_widget.removeTab(1)
        self.last_tab = 0

        self.name = '<unknown>'
        self.uid = '<unknown>'
        self.version = (0, 0, 0)

        self.disconnect_times = []

        self.qtcb_enumerate.connect(self.cb_enumerate)
        self.qtcb_connected.connect(self.cb_connected)
        self.qtcb_disconnected.connect(self.cb_disconnected)

        self.ipcon = IPConnection()
        self.ipcon.set_auto_reauthenticate(False)
        self.ipcon.register_callback(IPConnection.CALLBACK_ENUMERATE,
                                     self.qtcb_enumerate.emit)
        self.ipcon.register_callback(IPConnection.CALLBACK_CONNECTED,
                                     self.qtcb_connected.emit)
        self.ipcon.register_callback(IPConnection.CALLBACK_DISCONNECTED,
                                     self.qtcb_disconnected.emit)

        self.flashing_window = None
        self.advanced_window = None
        self.delayed_refresh_updates_timer = QTimer()
        self.delayed_refresh_updates_timer.timeout.connect(self.delayed_refresh_updates)
        self.delayed_refresh_updates_timer.setInterval(500)
        self.reset_view()
        self.button_advanced.setDisabled(True)

        self.tab_widget.currentChanged.connect(self.tab_changed)
        self.button_connect.pressed.connect(self.connect_pressed)
        self.button_flashing.pressed.connect(self.flashing_pressed)
        self.button_advanced.pressed.connect(self.advanced_pressed)
        self.plugin_manager = PluginManager()

        self.combo_host.addItem(config.get_host())
        self.combo_host.addItems(config.get_host_history(HOST_HISTORY_SIZE - 1))
        self.spinbox_port.setValue(config.get_port())

        self.last_host = self.combo_host.currentText()
        self.last_port = self.spinbox_port.value()

        self.checkbox_authentication.stateChanged.connect(self.authentication_state_changed)

        self.label_secret.hide()

        self.edit_secret.hide()
        self.edit_secret.setEchoMode(QLineEdit.Password)

        self.checkbox_secret_show.hide()
        self.checkbox_secret_show.stateChanged.connect(self.secret_show_state_changed)

        self.checkbox_remember_secret.hide()

        if config.get_use_authentication():
            self.checkbox_authentication.setCheckState(Qt.Checked)

        if config.get_remember_secret():
            self.edit_secret.setText(config.get_secret())
            self.checkbox_remember_secret.setCheckState(Qt.Checked)

        self.label_auto_reconnects.hide()
        self.auto_reconnects = 0

    def closeEvent(self, event):
        self.exit_brickv()

    def exit_brickv(self, signl=None, frme=None):
        try:
            uid = self.tab_widget.widget(self.last_tab)._uid
            infos.infos[uid].plugin.stop()
        except:
            pass

        host = str(self.combo_host.currentText())
        history = []

        for i in range(self.combo_host.count()):
            h = str(self.combo_host.itemText(i))

            if h != host and h not in history:
                history.append(h)

        config.set_host(host)
        config.set_host_history(history[:HOST_HISTORY_SIZE - 1])
        config.set_port(self.spinbox_port.value())
        config.set_use_authentication(self.checkbox_authentication.isChecked())

        remember_secret = self.checkbox_remember_secret.isChecked()

        config.set_remember_secret(remember_secret)

        if remember_secret:
            config.set_secret(str(self.edit_secret.text()))
        else:
            config.set_secret(config.DEFAULT_SECRET)

        self.reset_view()

        try:
            self.ipcon.disconnect()
        except:
            pass

        if signl != None and frme != None:
            print "Received SIGINT or SIGTERM, shutting down."
            sys.exit()

    def start(self):
        pass

    def stop(self):
        pass

    def destroy(self):
        pass

    def authentication_state_changed(self, state):
        visible = state == Qt.Checked

        self.label_secret.setVisible(visible)
        self.edit_secret.setVisible(visible)
        self.checkbox_secret_show.setVisible(visible)
        self.checkbox_remember_secret.setVisible(visible)

    def secret_show_state_changed(self, state):
        if state == Qt.Checked:
            self.edit_secret.setEchoMode(QLineEdit.Normal)
        else:
            self.edit_secret.setEchoMode(QLineEdit.Password)

    def tab_changed(self, i):
        try:
            uid = self.tab_widget.widget(i)._uid
            infos.infos[uid].plugin.start()
        except:
            pass

        try:
            uid = self.tab_widget.widget(self.last_tab)._uid
            infos.infos[uid].plugin.stop()
        except:
            pass

        self.last_tab = i

    def reset_view(self):
        self.tab_widget.setCurrentIndex(0)

        keys_to_remove = []
        for key in infos.infos:
            if infos.infos[key].type in ('brick', 'bricklet'):
                try:
                    infos.infos[key].plugin.stop()
                except:
                    pass

                try:
                    infos.infos[key].plugin.destroy()
                except:
                    pass
                keys_to_remove.append(key)

        for key in keys_to_remove:
            try:
                infos.infos.pop(key)
            except:
                pass

        for i in reversed(range(1, self.tab_widget.count())):
            self.tab_widget.removeTab(i)

        self.update_tree_view()

    def do_disconnect(self):
        self.auto_reconnects = 0
        self.label_auto_reconnects.hide()

        self.reset_view()
        async_next_session()

        try:
            self.ipcon.disconnect()
        except:
            pass

    def do_authenticate(self, is_auto_reconnect):
        if not self.checkbox_authentication.isChecked():
            return True

        try:
            secret = str(self.edit_secret.text()).encode('ascii')
        except:
            self.do_disconnect()

            QMessageBox.critical(self, 'Connection',
                                 'Authentication secret cannot contain non-ASCII characters.',
                                 QMessageBox.Ok)
            return False

        self.ipcon.set_auto_reconnect(False) # don't auto-reconnect on authentication error

        try:
            self.ipcon.authenticate(secret)
        except:
            self.do_disconnect()

            if is_auto_reconnect:
                extra = ' after auto-reconnect'
            else:
                extra = ''

            QMessageBox.critical(self, 'Connection',
                                 'Could not authenticate' + extra + '. Check secret and ensure ' +
                                 'authentication for Brick Daemon is enabled.',
                                 QMessageBox.Ok)
            return False

        self.ipcon.set_auto_reconnect(True)

        return True

    def flashing_pressed(self):
        first = False

        if self.flashing_window is None:
            first = True
            self.flashing_window = FlashingWindow(self)

        self.update_flashing_window()
        self.flashing_window.show()
        self.flashing_window.refresh_updates_pressed()

    def advanced_pressed(self):
        if self.advanced_window is None:
            self.advanced_window = AdvancedWindow(self)

        self.update_advanced_window()
        self.advanced_window.show()

    def connect_pressed(self):
        if self.ipcon.get_connection_state() == IPConnection.CONNECTION_STATE_DISCONNECTED:
            try:
                self.last_host = self.combo_host.currentText()
                self.last_port = self.spinbox_port.value()
                self.button_connect.setDisabled(True)
                self.button_connect.setText("Connecting ...")
                self.button_connect.repaint()
                QApplication.processEvents()
                self.ipcon.connect(self.last_host, self.last_port)
            except:
                self.button_connect.setDisabled(False)
                self.button_connect.setText("Connect")
                QMessageBox.critical(self, 'Connection',
                                     'Could not connect. Please check host, check ' +
                                     'port and ensure that Brick Daemon is running.')
        else:
            self.do_disconnect()

    def item_double_clicked(self, index):
        text = str(index.data().toString())
        i = self.tab_for_uid(text)
        if i > 0:
            self.tab_widget.setCurrentIndex(i)

    def connected_uid_pressed(self, connected_uid):
        i = self.tab_for_uid(connected_uid)
        if i > 0:
            self.tab_widget.setCurrentIndex(i)

    def create_plugin_container(self, plugin, connected_uid, position):
        container = QWidget()
        container._uid = plugin.uid
        layout = QVBoxLayout(container)
        info = QHBoxLayout()

        # uid
        info.addWidget(QLabel('UID:'))
        label = QLabel('{0}'.format(plugin.uid))
        label.setTextInteractionFlags(Qt.TextSelectableByMouse |
                                      Qt.TextSelectableByKeyboard)
        info.addWidget(label)

        info.addSpacerItem(QSpacerItem(1, 1, QSizePolicy.Expanding))

        # connected uid
        if connected_uid != '0':
            info.addWidget(QLabel('Connected to:'))
            button = QToolButton()
            button.setText(connected_uid)
            button.pressed.connect(lambda: self.connected_uid_pressed(connected_uid))
            info.addWidget(button)

            info.addSpacerItem(QSpacerItem(1, 1, QSizePolicy.Expanding))

        # position
        info.addWidget(QLabel('Position:'))
        info.addWidget(QLabel('{0}'.format(position.upper())))

        info.addSpacerItem(QSpacerItem(1, 1, QSizePolicy.Expanding))

        # firmware version
        info.addWidget(QLabel('FW Version:'))
        info.addWidget(QLabel('{0}'.format(plugin.version_str)))

        info.addSpacerItem(QSpacerItem(1, 1, QSizePolicy.Expanding))

        # timeouts
        info.addWidget(QLabel('Timeouts:'))
        label_timeouts = QLabel('0')
        info.addWidget(label_timeouts)

        layout.addLayout(info)

        if plugin.is_brick():
            button = QPushButton('Reset')
            if plugin.has_reset_device():
                button.clicked.connect(plugin.reset_device)
            else:
                button.setDisabled(True)
            info.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Expanding))
            info.addWidget(button)

        line = QFrame()
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)

        plugin.label_timeouts = label_timeouts
        plugin.layout().setContentsMargins(0, 0, 0, 0)

        layout.addWidget(line)
        layout.addWidget(plugin)

        return container

    def tab_for_uid(self, uid):
        for i in range(1, self.tab_widget.count()):
            try:
                widget = self.tab_widget.widget(i)
                if widget._uid == uid:
                    return i
            except:
                pass

        return -1

    def cb_enumerate(self, uid, connected_uid, position,
                     hardware_version, firmware_version,
                     device_identifier, enumeration_type):
        if self.ipcon.get_connection_state() != IPConnection.CONNECTION_STATE_CONNECTED:
            # ignore enumerate callbacks that arrived after the connection got closed
            return

        if enumeration_type in [IPConnection.ENUMERATION_TYPE_AVAILABLE,
                                IPConnection.ENUMERATION_TYPE_CONNECTED]:
            if device_identifier == BrickMaster.DEVICE_IDENTIFIER:
                info = infos.BrickMasterInfo()
            elif position in ('a', 'b', 'c', 'd', 'A', 'B', 'C', 'D'):
                position = position.lower()
                info = infos.BrickletInfo()
            else:
                info = infos.BrickInfo()

            if uid in infos.infos:
                info = infos.infos[uid]
            else:
                infos.infos[uid] = info

            for device in infos.infos.values():
                if device.type == 'brick':
                    if info.type == 'bricklet':
                        if device.uid == connected_uid:
                            device.bricklets[position] = info
                if device.type == 'bricklet':
                    if info.type == 'brick':
                        if uid == device.connected_uid:
                            info.bricklets[device.position] = device

            info.uid = uid
            info.connected_uid = connected_uid
            info.position = position
            info.hardware_version = hardware_version
            info.firmware_version_installed = firmware_version
            info.device_identifier = device_identifier
            info.protocol_version = 2
            info.enumeration_type = enumeration_type

            for device in infos.infos.values():
                if device.type in ('brick', 'bricklet'):
                    if device.uid == uid and device.plugin != None:
                        return

            plugin = self.plugin_manager.get_plugin(device_identifier, self.ipcon,
                                                    uid, firmware_version)

            if plugin is not None:
                info.plugin = plugin
                if plugin.is_hardware_version_relevant(hardware_version):
                    info.name = '{0} {1}.{2}'.format(plugin.name,
                                                     hardware_version[0],
                                                     hardware_version[1])
                else:
                    info.name = plugin.name

                info.url_part = plugin.get_url_part()

                c = self.create_plugin_container(plugin, connected_uid, position)
                info.plugin_container = c
                self.tab_widget.addTab(c, info.name)
        elif enumeration_type == IPConnection.ENUMERATION_TYPE_DISCONNECTED:
            for device_info in infos.infos.values():
                if device_info.type in ('brick', 'bricklet'):
                    if device_info.uid == uid:
                        try:
                            self.tab_widget.setCurrentIndex(0)
                            if device_info.plugin:
                                try:
                                    device_info.plugin.stop()
                                except:
                                    pass

                                try:
                                    device_info.plugin.destroy()
                                except:
                                    pass

                            i = self.tab_for_uid(device_info.uid)
                            self.tab_widget.removeTab(i)
                        except:
                            pass

                if device_info.type == 'brick':
                    for port in device_info.bricklets:
                        if device_info.bricklets[port]:
                            if device_info.bricklets[port].uid == uid:
                                device_info.bricklets[port] = None

                try:
                    infos.infos.pop(uid)
                except:
                    pass

        self.update_tree_view()

    def cb_connected(self, connect_reason):
        self.disconnect_times = []

        self.update_ui_state()

        if connect_reason == IPConnection.CONNECT_REASON_REQUEST:
            self.auto_reconnects = 0
            self.label_auto_reconnects.hide()

            self.ipcon.set_auto_reconnect(True)

            index = self.combo_host.findText(self.last_host)
            if index >= 0:
                self.combo_host.removeItem(index)
            self.combo_host.insertItem(-1, self.last_host)
            self.combo_host.setCurrentIndex(0)

            while self.combo_host.count() > HOST_HISTORY_SIZE:
                self.combo_host.removeItem(self.combo_host.count() - 1)

            if not self.do_authenticate(False):
                return

            try:
                self.ipcon.enumerate()
            except:
                self.update_ui_state()
        elif connect_reason == IPConnection.CONNECT_REASON_AUTO_RECONNECT:
            self.auto_reconnects += 1
            self.label_auto_reconnects.setText('Auto-Reconnect Count: {0}'.format(self.auto_reconnects))
            self.label_auto_reconnects.show()

            if not self.do_authenticate(True):
                return

            try:
                self.ipcon.enumerate()
            except:
                self.update_ui_state()
        else:
            try:
                self.ipcon.enumerate()
            except:
                self.update_ui_state()

    def cb_disconnected(self, disconnect_reason):
        if disconnect_reason == IPConnection.DISCONNECT_REASON_REQUEST:
            self.auto_reconnects = 0
            self.label_auto_reconnects.hide()

        if disconnect_reason == IPConnection.DISCONNECT_REASON_REQUEST or not self.ipcon.get_auto_reconnect():
            self.update_ui_state()
        elif len(self.disconnect_times) >= 3 and self.disconnect_times[-3] < time.time() + 1:
            self.disconnect_times = []
            self.ipcon.set_auto_reconnect(False)
            self.update_ui_state()
            self.reset_view()

            QMessageBox.critical(self, 'Connection',
                                 'Stopped automatic reconnecting due to multiple connection errors in a row.',
                                 QMessageBox.Ok)
        else:
            self.disconnect_times.append(time.time())
            self.update_ui_state(IPConnection.CONNECTION_STATE_PENDING)

    def set_tree_view_defaults(self):
        self.tree_view_model.setHorizontalHeaderLabels(self.tree_view_model_labels)
        self.tree_view.expandAll()
        self.tree_view.setColumnWidth(0, 260)
        self.tree_view.setColumnWidth(1, 75)
        self.tree_view.setColumnWidth(2, 85)
        self.tree_view.setSortingEnabled(True)
        self.tree_view.header().setSortIndicator(0, Qt.AscendingOrder)

    def update_ui_state(self, connection_state=None):
        # FIXME: need to call processEvents() otherwise get_connection_state()
        #        might return the wrong value
        QApplication.processEvents()

        if connection_state is None:
            connection_state = self.ipcon.get_connection_state()

        self.button_connect.setDisabled(False)
        self.button_flashing.setDisabled(False)

        if connection_state == IPConnection.CONNECTION_STATE_DISCONNECTED:
            self.button_connect.setText('Connect')
            self.combo_host.setDisabled(False)
            self.spinbox_port.setDisabled(False)
            self.checkbox_authentication.setDisabled(False)
            self.edit_secret.setDisabled(False)
            self.button_advanced.setDisabled(True)
        elif connection_state == IPConnection.CONNECTION_STATE_CONNECTED:
            self.button_connect.setText("Disconnect")
            self.combo_host.setDisabled(True)
            self.spinbox_port.setDisabled(True)
            self.checkbox_authentication.setDisabled(True)
            self.edit_secret.setDisabled(True)
            self.update_advanced_window(False)
        elif connection_state == IPConnection.CONNECTION_STATE_PENDING:
            self.button_connect.setText('Abort Pending Automatic Reconnect')
            self.combo_host.setDisabled(True)
            self.spinbox_port.setDisabled(True)
            self.checkbox_authentication.setDisabled(True)
            self.edit_secret.setDisabled(True)
            self.button_advanced.setDisabled(True)
            self.button_flashing.setDisabled(True)

        enable = connection_state == IPConnection.CONNECTION_STATE_CONNECTED
        for i in range(1, self.tab_widget.count()):
            self.tab_widget.setTabEnabled(i, enable)

        QApplication.processEvents()

    def update_tree_view(self):
        self.tree_view_model.clear()

        for device_info in sorted(infos.infos.values(), cmp=lambda x, y: cmp(x.name, y.name)):
            if device_info.type == 'brick':
                parent = [QStandardItem(device_info.name),
                          QStandardItem(device_info.uid),
                          QStandardItem('.'.join(map(str, device_info.firmware_version_installed)))]
                for item in parent:
                    item.setFlags(item.flags() & ~Qt.ItemIsEditable)

                self.tree_view_model.appendRow(parent)
                for port in sorted(device_info.bricklets):
                    if device_info.bricklets[port] and device_info.bricklets[port].protocol_version == 2:
                        child = [QStandardItem(port.upper() + ': ' +device_info.bricklets[port].name),
                                 QStandardItem(device_info.bricklets[port].uid),
                                 QStandardItem('.'.join(map(str, device_info.bricklets[port].firmware_version_installed)))]
                        for item in child:
                            item.setFlags(item.flags() & ~Qt.ItemIsEditable)
                        parent[0].appendRow(child)

        self.set_tree_view_defaults()
        self.update_flashing_window()
        self.update_advanced_window()
        self.delayed_refresh_updates_timer.start()

    def update_flashing_window(self):
        if self.flashing_window is not None:
            self.flashing_window.update_bricks()

    def update_advanced_window(self, update_window=True):
        has_brick = False

        for info in infos.infos.values():
            if info.type == 'brick':
                has_brick = True

        self.button_advanced.setEnabled(has_brick)

        if self.advanced_window is not None and update_window:
            self.advanced_window.update_bricks()

    def delayed_refresh_updates(self):
        self.delayed_refresh_updates_timer.stop()

        if self.flashing_window is not None and self.flashing_window.isVisible():
            self.flashing_window.refresh_updates_pressed()
Exemple #33
0
class VizRankDialog(QDialog, ProgressBarMixin, WidgetMessagesMixin):
    """
    Base class for VizRank dialogs, providing a GUI with a table and a button,
    and the skeleton for managing the evaluation of visualizations.

    Derived classes must provide methods

    - `iterate_states` for generating combinations (e.g. pairs of attritutes),
    - `compute_score(state)` for computing the score of a combination,
    - `row_for_state(state)` that returns a list of items inserted into the
       table for the given state.

    and, optionally,

    - `state_count` that returns the number of combinations (used for progress
       bar)
    - `on_selection_changed` that handles event triggered when the user selects
      a table row. The method should emit signal
      `VizRankDialog.selectionChanged(object)`.

    The class provides a table and a button. A widget constructs a single
    instance of this dialog in its `__init__`, like (in Sieve) by using a
    convenience method :obj:`add_vizrank`::

        self.vizrank, self.vizrank_button = SieveRank.add_vizrank(
            box, self, "Score Combinations", self.set_attr)

    When the widget receives new data, it must call the VizRankDialog's
    method :obj:`VizRankDialog.initialize()` to clear the GUI and reset the
    state.

    Clicking the Start button calls method `run` (and renames the button to
    Pause). Run sets up a progress bar by getting the number of combinations
    from :obj:`VizRankDialog.state_count()`. It restores the paused state
    (if any) and calls generator :obj:`VizRankDialog.iterate_states()`. For
    each generated state, it calls :obj:`VizRankDialog.score(state)`, which
    must return the score (lower is better) for this state. If the returned
    state is not `None`, the data returned by `row_for_state` is inserted at
    the appropriate place in the table.

    Args:
        master (Orange.widget.OWWidget): widget to which the dialog belongs

    Attributes:
        master (Orange.widget.OWWidget): widget to which the dialog belongs
        captionTitle (str): the caption for the dialog. This can be a class
          attribute. `captionTitle` is used by the `ProgressBarMixin`.
    """

    captionTitle = ""

    processingStateChanged = Signal(int)
    progressBarValueChanged = Signal(float)
    messageActivated = Signal(Msg)
    messageDeactivated = Signal(Msg)
    selectionChanged = Signal(object)

    def __init__(self, master):
        """Initialize the attributes and set up the interface"""
        QDialog.__init__(self, windowTitle=self.captionTitle)
        WidgetMessagesMixin.__init__(self)
        self.setLayout(QVBoxLayout())

        self.insert_message_bar()
        self.layout().insertWidget(0, self.message_bar)
        self.master = master

        self.keep_running = False
        self.saved_state = None
        self.saved_progress = 0
        self.scores = []

        self.rank_model = QStandardItemModel(self)
        self.rank_table = view = QTableView(
            selectionBehavior=QTableView.SelectRows, selectionMode=QTableView.SingleSelection, showGrid=False
        )
        view.setItemDelegate(HorizontalGridDelegate())
        view.setModel(self.rank_model)
        view.selectionModel().selectionChanged.connect(self.on_selection_changed)
        view.horizontalHeader().setStretchLastSection(True)
        view.horizontalHeader().hide()
        self.layout().addWidget(view)

        self.button = gui.button(self, self, "Start", callback=self.toggle, default=True)

    @classmethod
    def add_vizrank(cls, widget, master, button_label, set_attr_callback):
        """
        Equip the widget with VizRank button and dialog, and monkey patch the
        widget's `closeEvent` and `hideEvent` to close/hide the vizrank, too.

        Args:
            widget (QWidget): the widget into whose layout to insert the button
            master (Orange.widgets.widget.OWWidget): the master widget
            button_label: the label for the button
            set_attr_callback: the callback for setting the projection chosen
                in the vizrank

        Returns:
            tuple with Vizrank dialog instance and push button
        """
        # Monkey patching could be avoided by mixing-in the class (not
        # necessarily a good idea since we can make a mess of multiple
        # defined/derived closeEvent and hideEvent methods). Furthermore,
        # per-class patching would be better than per-instance, but we don't
        # want to mess with meta-classes either.

        vizrank = cls(master)
        button = gui.button(widget, master, button_label, callback=vizrank.reshow, enabled=False)
        vizrank.selectionChanged.connect(lambda args: set_attr_callback(*args))

        master_close_event = master.closeEvent
        master_hide_event = master.hideEvent

        def closeEvent(event):
            vizrank.close()
            master_close_event(event)

        def hideEvent(event):
            vizrank.hide()
            master_hide_event(event)

        master.closeEvent = closeEvent
        master.hideEvent = hideEvent
        return vizrank, button

    def reshow(self):
        """Put the widget on top of all windows
        """
        self.show()
        self.raise_()
        self.activateWindow()

    def initialize(self):
        """
        Clear and initialize the dialog.

        This method must be called by the widget when the data is reset,
        e.g. from `set_data` handler.
        """
        self.keep_running = False
        self.saved_state = None
        self.saved_progress = 0
        self.scores = []
        self.rank_model.clear()
        self.button.setText("Start")
        self.button.setEnabled(self.check_preconditions())

    def check_preconditions(self):
        """Check whether there is sufficient data for ranking."""
        return True

    def on_selection_changed(self, selected, deselected):
        """
        Set the new visualization in the widget when the user select a
        row in the table.

        If derived class does not reimplement this, the table gives the
        information but the user can't click it to select the visualization.

        Args:
            selected: the index of the selected item
            deselected: the index of the previously selected item
        """
        pass

    def iterate_states(self, initial_state):
        """
        Generate all possible states (e.g. attribute combinations) for the
        given data. The content of the generated states is specific to the
        visualization.

        This method must be defined in the derived classes.

        Args:
            initial_state: initial state; None if this is the first call
        """
        raise NotImplementedError

    def state_count(self):
        """
        Return the number of states for the progress bar.

        Derived classes should implement this to ensure the proper behaviour of
        the progress bar"""
        return 0

    def compute_score(self, state):
        """
        Abstract method for computing the score for the given state. Smaller
        scores are better.

        Args:
            state: the state, e.g. the combination of attributes as generated
                by :obj:`state_count`.
        """
        raise NotImplementedError

    def row_for_state(self, state, score):
        """
        Abstract method that return the items that are inserted into the table.

        Args:
            state: the state, e.g. combination of attributes
            score: score, computed by :obj:`compute_score`
            """
        raise NotImplementedError

    def _select_first_if_none(self):
        if not self.rank_table.selectedIndexes():
            self.rank_table.selectRow(0)

    def run(self):
        """Compute and show scores"""
        with self.progressBar(self.state_count()) as progress:
            progress.advance(self.saved_progress)
            for state in self.iterate_states(self.saved_state):
                if not self.keep_running:
                    self.saved_state = state
                    self.saved_progress = progress.count
                    self._select_first_if_none()
                    return
                score = self.compute_score(state)
                if score is not None:
                    pos = bisect_left(self.scores, score)
                    self.rank_model.insertRow(pos, self.row_for_state(score, state))
                    self.scores.insert(pos, score)
                progress.advance()
            self._select_first_if_none()
            self.button.setText("Finished")
            self.button.setEnabled(False)

    def toggle(self):
        """Start or pause the computation."""
        self.keep_running = not self.keep_running
        if self.keep_running:
            self.button.setText("Pause")
            self.run()
        else:
            self._select_first_if_none()
            self.button.setText("Continue")
Exemple #34
0
class ConfigDialog(BASE, WIDGET):

    def __init__(self, toolbox):
        super(ConfigDialog, self).__init__(None)
        self.setupUi(self)

        self.toolbox = toolbox
        self.groupIcon = QIcon()
        self.groupIcon.addPixmap(self.style().standardPixmap(
            QStyle.SP_DirClosedIcon), QIcon.Normal, QIcon.Off)
        self.groupIcon.addPixmap(self.style().standardPixmap(
            QStyle.SP_DirOpenIcon), QIcon.Normal, QIcon.On)

        if hasattr(self.searchBox, 'setPlaceholderText'):
            self.searchBox.setPlaceholderText(self.tr('Search...'))

        self.model = QStandardItemModel()
        self.tree.setModel(self.model)

        self.delegate = SettingDelegate()
        self.tree.setItemDelegateForColumn(1, self.delegate)

        self.searchBox.textChanged.connect(self.fillTree)

        self.fillTree()

        self.tree.expanded.connect(self.adjustColumns)

    def fillTree(self):
        self.items = {}
        self.model.clear()
        self.model.setHorizontalHeaderLabels([self.tr('Setting'),
                                              self.tr('Value')])

        text = unicode(self.searchBox.text())
        settings = ProcessingConfig.getSettings()

        rootItem = self.model.invisibleRootItem()
        priorityKeys = [self.tr('General'), self.tr('Models'), self.tr('Scripts')]
        for group in priorityKeys:
            groupItem = QStandardItem(group)
            icon = ProcessingConfig.getGroupIcon(group)
            groupItem.setIcon(icon)
            groupItem.setEditable(False)
            emptyItem = QStandardItem()
            emptyItem.setEditable(False)
            rootItem.insertRow(0, [groupItem, emptyItem])
            for setting in settings[group]:
                if setting.hidden or setting.name.startswith("MENU_"):
                    continue

                if text == '' or text.lower() in setting.description.lower():
                    labelItem = QStandardItem(setting.description)
                    labelItem.setIcon(icon)
                    labelItem.setEditable(False)
                    self.items[setting] = SettingItem(setting)
                    groupItem.insertRow(0, [labelItem, self.items[setting]])

            if text != '':
                self.tree.expand(groupItem.index())

        providersItem = QStandardItem(self.tr('Providers'))
        icon = QIcon(os.path.join(pluginPath, 'images', 'alg.png'))
        providersItem.setIcon(icon)
        providersItem.setEditable(False)
        emptyItem = QStandardItem()
        emptyItem.setEditable(False)
        rootItem.insertRow(0, [providersItem, emptyItem])
        for group in settings.keys():
            if group in priorityKeys or group == menusSettingsGroup:
                continue

            groupItem = QStandardItem(group)
            icon = ProcessingConfig.getGroupIcon(group)
            groupItem.setIcon(icon)
            groupItem.setEditable(False)
            for setting in settings[group]:
                if setting.hidden:
                    continue

                if text == '' or text.lower() in setting.description.lower():
                    labelItem = QStandardItem(setting.description)
                    labelItem.setIcon(icon)
                    labelItem.setEditable(False)
                    self.items[setting] = SettingItem(setting)
                    groupItem.insertRow(0, [labelItem, self.items[setting]])

            emptyItem = QStandardItem()
            emptyItem.setEditable(False)
            providersItem.appendRow([groupItem, emptyItem])

        menusItem = QStandardItem(self.tr('Menus (requires restart)'))
        icon = QIcon(os.path.join(pluginPath, 'images', 'menu.png'))
        menusItem.setIcon(icon)
        menusItem.setEditable(False)
        emptyItem = QStandardItem()
        emptyItem.setEditable(False)
        rootItem.insertRow(0, [menusItem, emptyItem])
        providers = Processing.providers
        for provider in providers:
            groupItem = QStandardItem(provider.getDescription())
            icon = provider.getIcon()
            groupItem.setIcon(icon)
            groupItem.setEditable(False)
            for alg in provider.algs:
                labelItem = QStandardItem(alg.name)
                labelItem.setIcon(icon)
                labelItem.setEditable(False)
                try:
                    setting = ProcessingConfig.settings["MENU_" + alg.commandLineName()]
                except:
                    continue
                self.items[setting] = SettingItem(setting)
                groupItem.insertRow(0, [labelItem, self.items[setting]])
            emptyItem = QStandardItem()
            emptyItem.setEditable(False)
            menusItem.appendRow([groupItem, emptyItem])

        self.tree.sortByColumn(0, Qt.AscendingOrder)
        self.adjustColumns()

    def accept(self):
        for setting in self.items.keys():
            if isinstance(setting.value, bool):
                setting.setValue(self.items[setting].checkState() == Qt.Checked)
            else:
                try:
                    setting.setValue(unicode(self.items[setting].text()))
                except ValueError as e:
                    QMessageBox.warning(self, self.tr('Wrong value'),
                                        self.tr('Wrong value for parameter "%s":\n\n%s' % (setting.description, unicode(e))))
                    return
            setting.save()
        Processing.updateAlgsList()
        updateMenus()

        QDialog.accept(self)

    def adjustColumns(self):
        self.tree.resizeColumnToContents(0)
        self.tree.resizeColumnToContents(1)
class NeoNavigationDock(QDockWidget, Ui_neoNavigationDock):
    """ Implements a navigation dock for Neo hierarchies. Tightly coupled
    with :class:`main_window_neo.MainWindowNeo`, the main reason for this
    class to exist is to keep the dock out of the general ui file.
    """

    object_removed = pyqtSignal()  # Signal to remove an object

    def __init__(self, parent):
        QDockWidget.__init__(self, parent)
        self.parent = parent

        self.setupUi(self)

        self.block_model = QStandardItemModel()
        self.segment_model = QStandardItemModel()
        self.channelgroup_model = QStandardItemModel()
        self.channel_model = QStandardItemModel()
        self.unit_model = QStandardItemModel()

        self.neoBlockList.setModel(self.block_model)
        self.neoSegmentList.setModel(self.segment_model)
        self.neoChannelGroupList.setModel(self.channelgroup_model)
        self.neoChannelList.setModel(self.channel_model)
        self.neoUnitList.setModel(self.unit_model)

        self.neoBlockList.doubleClicked.connect(
            lambda x: self._edit_item_annotations(x, self.block_model))
        self.neoSegmentList.doubleClicked.connect(
            lambda x: self._edit_item_annotations(x, self.segment_model))
        self.neoChannelGroupList.doubleClicked.connect(
            lambda x: self._edit_item_annotations(x, self.channelgroup_model))
        self.neoChannelList.doubleClicked.connect(
            lambda x: self._edit_item_annotations(x, self.channel_model))
        self.neoUnitList.doubleClicked.connect(
            lambda x: self._edit_item_annotations(x, self.unit_model))

        self.neoBlockList.selectionModel().selectionChanged.connect(
            self.selected_blocks_changed)
        self.neoChannelGroupList.selectionModel().selectionChanged.connect(
            self.selected_channel_groups_changed)
        self.neoChannelList.selectionModel().selectionChanged.connect(
            self.selected_channels_changed)
        self.neoUnitList.selectionModel().selectionChanged.connect(
            self.selected_units_changed)
        self.neoSegmentList.selectionModel().selectionChanged.connect(
            self.selected_segments_changed)

    def clear(self):
        """ Clear all lists
        """
        self.neoBlockList.clearSelection()
        self.block_model.clear()

    def get_letter_id(self, id_, small=False):
        """ Return a name consisting of letters given an integer
        """
        if id_ < 0:
            return ''

        name = ''
        id_ += 1
        if small:
            start = ord('a')
        else:
            start = ord('A')

        while id_ > 26:
            id_ -= 1
            name += chr(start + (id_ % 26))
            id_ /= 26

        name += chr(start + id_ - 1)

        return name[::-1]

    def ensure_not_filtered(self, objects, all_objects, filters):
        """ Deactivates all filters that prevent the the given sequence
        of objects to be displayed. The passed filter tuple list is
        modified to only include valid filters.

        :param sequence objects: The objects that need to be visible.
        :param sequence all_objects: The whole object list to be filtered,
            including objects that are allowed to be hidden.
        :param sequence filters: A sequence of (Filter, name) tuples.
        """
        objset = set(objects)
        if not objset.issubset(
                set(self.parent.filter_list(all_objects, filters))):
            i = 1
            while i <= len(filters):
                test_filters = filters[:i]
                if objset.issubset(set(self.parent.filter_list(
                        all_objects, test_filters))):
                    i += 1
                else:
                    test_filters[-1][0].active = False
                    filters.pop(i - 1)

        for o in objects:
            i = 0
            while i < len(filters):
                if self.parent.is_filtered(o, [filters[i]]):
                    filters[i][0].active = False
                    filters.pop(i)
                else:
                    i += 1

    def filter_ordered(self, objects, filters):
        """ Filter a sequence of objects with a sequence of filters. Apply
        the filters in the order given by the sequence. Return the filtered
        list.
        """
        for f in filters:
            if f[0].combined:
                objects = self.parent.filter_list(objects, [f])
            else:
                objects = [o for o in objects
                           if not self.parent.is_filtered(o, [f])]
        return objects

    def populate_neo_block_list(self):
        """ Fill the block list with appropriate entries.
        Qt.UserRole: The :class:`neo.Block` object
        """
        self.block_model.clear()

        filters = self.parent.get_active_filters('Block')

        blocks = self.filter_ordered(
            self.parent.block_names.keys(), filters)
        for b in blocks:
            item = QStandardItem(self.parent.block_names[b])
            item.setData(b, Qt.UserRole)
            self.block_model.appendRow(item)

        self.neoBlockList.setCurrentIndex(self.block_model.index(0, 0))
        self.set_blocks_label()
        if not blocks:
            self.selected_blocks_changed()

    def populate_neo_segment_list(self):
        """ Fill the segment list with appropriate entries.
        Qt.UserRole: The :class:`neo.Segment` object
        """
        self.segment_model.clear()

        segments = []
        for b in self.blocks():
            segments.extend(b.segments)

        filters = self.parent.get_active_filters('Segment')
        segments = self.filter_ordered(segments, filters)
        for i, s in enumerate(segments):
            if s.name:
                name = s.name + ' (%s-%i)' % \
                    (self.parent.block_ids[s.block], i)
            else:
                name = '%s-%i' % (self.parent.block_ids[s.block], i)

            new_item = QStandardItem(name)
            new_item.setData(s, Qt.UserRole)
            self.segment_model.appendRow(new_item)

        self.neoSegmentList.setCurrentIndex(self.segment_model.index(0, 0))
        if api.config.autoselect_segments:
            self.neoSegmentList.selectAll()
        self.selected_segments_changed()

    def populate_neo_channel_group_list(self):
        """ Fill the channel group list with appropriate entries.
        Qt.UserRole: The :class:`neo.RecordingChannelGroup` object
        """
        self.neoChannelGroupList.clearSelection()
        self.channelgroup_model.clear()
        self.parent.channel_group_names.clear()

        rcgs = []
        for b in self.blocks():
            rcgs.extend(b.recordingchannelgroups)

        filters = self.parent.get_active_filters(
            'Recording Channel Group')
        rcgs = self.filter_ordered(rcgs, filters)

        for i, rcg in enumerate(rcgs):
            self.parent.channel_group_names[rcg] = '%s-%s' % (
                self.parent.block_ids[rcg.block],
                self.get_letter_id(i, True))
            if rcg.name:
                name = rcg.name + ' (%s)' % \
                                  self.parent.channel_group_names[rcg]
            else:
                name = self.parent.channel_group_names[rcg]
            new_item = QStandardItem(name)
            new_item.setData(rcg, Qt.UserRole)
            self.channelgroup_model.appendRow(new_item)

        self.neoChannelGroupList.setCurrentIndex(
            self.channelgroup_model.index(0, 0))
        if api.config.autoselect_channel_groups:
            self.neoChannelGroupList.selectAll()
        elif not rcgs:
            self.selected_channel_groups_changed()

    def populate_neo_channel_list(self):
        """ Fill the channel list with appropriate entries. Data slots:
        Qt.UserRole: The :class:`neo.RecordingChannel`
        Qt.UserRole+1: The channel index
        """
        self.channel_model.clear()
        channels = set()

        rcs = []
        rc_group_name = {}
        for rcg in self.recording_channel_groups():
            for rc in rcg.recordingchannels:
                if not api.config.duplicate_channels and rc in channels:
                    continue
                channels.add(rc)
                rcs.append(rc)
                rc_group_name[rc] = self.parent.channel_group_names[rcg]

        filters = self.parent.get_active_filters(
            'Recording Channel')
        rcs = self.filter_ordered(rcs, filters)

        for rc in rcs:
            identifier = '%s.%d' % \
                         (rc_group_name[rc],
                          rc.index)
            if rc.name:
                name = rc.name + ' (%s)' % identifier
            else:
                name = identifier

            new_item = QStandardItem(name)
            new_item.setData(rc, Qt.UserRole)
            new_item.setData(rc.index, Qt.UserRole + 1)
            self.channel_model.appendRow(new_item)

        if api.config.autoselect_channels:
            self.neoChannelList.selectAll()
        self.selected_channels_changed()

    def populate_neo_unit_list(self):
        """ Fill the unit list with appropriate entries.
        Qt.UserRole: The :class:`neo.Unit` object
        """
        self.unit_model.clear()

        units = []
        for rcg in self.recording_channel_groups():
            units.extend(rcg.units)

        filters = self.parent.get_active_filters('Unit')
        units = self.filter_ordered(units, filters)

        for i, u in enumerate(units):
            if self.parent.is_filtered(u, filters):
                continue
            if u.name:
                name = u.name + ' (%s-%d)' % \
                       (self.parent.channel_group_names[rcg], i)
            else:
                name = '%s-%d' % (self.parent.channel_group_names[rcg], i)
            new_item = QStandardItem(name)
            new_item.setData(u, Qt.UserRole)
            self.unit_model.appendRow(new_item)

        if api.config.autoselect_units:
            self.neoUnitList.selectAll()
        self.selected_units_changed()

    def set_blocks_label(self):
        self.blocksLabel.setText(
            'Blocks (%d/%d):' % (len(self.neoBlockList.selectedIndexes()),
                                 self.block_model.rowCount()))

    def set_channel_groups_label(self):
        self.channelGroupsLabel.setText(
            'Channel Groups (%d/%d):' % (
                len(self.neoChannelGroupList.selectedIndexes()),
                self.channelgroup_model.rowCount()))

    def selected_blocks_changed(self):
        self.set_blocks_label()
        self.populate_neo_channel_group_list()
        self.populate_neo_segment_list()

    def selected_channel_groups_changed(self):
        self.set_channel_groups_label()
        self.populate_neo_channel_list()
        self.populate_neo_unit_list()

    def selected_channels_changed(self):
        self.channelsLabel.setText(
            'Channels (%d/%d):' % (
                len(self.neoChannelList.selectedIndexes()),
                self.channel_model.rowCount()))

    def selected_units_changed(self):
        self.unitsLabel.setText(
            'Units (%d/%d):' % (
                len(self.neoUnitList.selectedIndexes()),
                self.unit_model.rowCount()))

    def selected_segments_changed(self):
        self.segmentsLabel.setText(
            'Segments (%d/%d):' % (
                len(self.neoSegmentList.selectedIndexes()),
                self.segment_model.rowCount()))

    def _edit_item_annotations(self, index, model):
        api.annotation_editor(model.data(index, Qt.UserRole))

    def remove_selected(self, list_widget):
        """ Remove all selected objects from the given list widget.
        """
        items = list_widget.selectedIndexes()
        if len(items) < 1:
            return
        model = list_widget.model()

        question = ('Do you really want to remove %d %s' %
                    (len(items),
                    type(model.data(items[0], Qt.UserRole)).__name__))
        if len(items) > 1:
            question += 's'
        question += '?'

        if QMessageBox.question(
                self, 'Please confirm', question,
                QMessageBox.Yes | QMessageBox.No) == QMessageBox.No:
            return

        for i in list_widget.selectedIndexes():
            data = model.data(i, Qt.UserRole)
            if isinstance(data, neo.Block):
                self.parent.block_names.pop(data)
            else:
                spykeutils.tools.remove_from_hierarchy(data)
            list_widget.selectionModel().select(i, QItemSelectionModel.Deselect)

        self.object_removed.emit()

    def _context_actions(self, list_widget):
        idx = list_widget.currentIndex()
        if not idx:
            return []

        data = list_widget.model().data(idx, Qt.UserRole)

        edit_action = QAction(get_icon('edit.png'),
                              'Edit annotations...', self)
        edit_action.triggered.connect(
            lambda x: self._edit_item_annotations(idx, list_widget.model()))

        delete_name = 'Delete %s' % type(data).__name__
        if len(list_widget.selectedIndexes()) > 1:
            delete_name += 's'
        delete_action = QAction(get_icon('editdelete.png'),
                                delete_name, self)
        delete_action.triggered.connect(
            lambda x:
            self.remove_selected(list_widget))

        return [edit_action, delete_action]

    def on_neoBlockList_customContextMenuRequested(self, pos):
        if not self.neoBlockList.selectedIndexes():
            return
        context_menu = QMenu(self)
        context_menu.addActions(self._context_actions(self.neoBlockList))
        context_menu.popup(self.neoBlockList.mapToGlobal(pos))

    def on_neoSegmentList_customContextMenuRequested(self, pos):
        if not self.neoSegmentList.selectedIndexes():
            return
        context_menu = QMenu(self)
        context_menu.addActions(self._context_actions(self.neoSegmentList))
        context_menu.popup(self.neoSegmentList.mapToGlobal(pos))

    def on_neoChannelGroupList_customContextMenuRequested(self, pos):
        if not self.neoChannelGroupList.selectedIndexes():
            return
        context_menu = QMenu(self)
        context_menu.addActions(self._context_actions(self.neoChannelGroupList))
        context_menu.popup(self.neoChannelGroupList.mapToGlobal(pos))

    def on_neoChannelList_customContextMenuRequested(self, pos):
        if not self.neoChannelList.selectedIndexes():
            return
        context_menu = QMenu(self)
        context_menu.addActions(self._context_actions(self.neoChannelList))
        context_menu.popup(self.neoChannelList.mapToGlobal(pos))

    def on_neoUnitList_customContextMenuRequested(self, pos):
        if not self.neoUnitList.selectedIndexes():
            return
        context_menu = QMenu(self)
        context_menu.addActions(self._context_actions(self.neoUnitList))
        context_menu.popup(self.neoUnitList.mapToGlobal(pos))

    def blocks(self):
        """ Return selected :class:`neo.Block` objects.
        """
        return [self.block_model.data(i, Qt.UserRole) for i in
                self.neoBlockList.selectedIndexes()]

    def segments(self):
        """ Return selected :class:`neo.Segment` objects.
        """
        return [self.segment_model.data(i, Qt.UserRole) for i in
                self.neoSegmentList.selectedIndexes()]

    def recording_channel_groups(self):
        """ Return selected :class:`neo.RecordingChannelGroup` objects.
        """
        return [self.channelgroup_model.data(i, Qt.UserRole) for i in
                self.neoChannelGroupList.selectedIndexes()]

    def recording_channels(self):
        """ Return selected :class:`neo.RecordingChannel` objects.
        """
        return [self.channel_model.data(i, Qt.UserRole) for i in
                self.neoChannelList.selectedIndexes()]

    def units(self):
        """ Return selected :class:`neo.Unit` objects.
        """
        return [self.unit_model.data(i, Qt.UserRole) for i in
                self.neoUnitList.selectedIndexes()]

    def set_selection(self, data):
        """ Set the selected data.
        """
        block_list = []
        for b in data['blocks']:
            cl = None
            rp = None
            if len(b) > 2:
                cl = NeoDataProvider.find_io_class(b[2])
            if len(b) > 3:
                rp = b[3]
            loaded = NeoDataProvider.get_block(
                b[1], b[0], force_io=cl, read_params=rp)
            if loaded is None:
                raise IOError('One of the files contained in the '
                              'selection could not be loaded!')
            block_list.append(loaded)

        block_set = set([(b[0], b[1]) for b in data['blocks']])
        # Select blocks
        self.ensure_not_filtered(block_list, self.parent.block_names.keys(),
                                 self.parent.get_active_filters('Block'))
        self.populate_neo_block_list()
        selection = QItemSelection()
        for i in self.block_model.findItems(
                '*', Qt.MatchWrap | Qt.MatchWildcard):
            block = i.data(Qt.UserRole)
            t = (NeoDataProvider.block_indices[block],
                 self.parent.block_files[block])

            if t in block_set:
                selection.append(QItemSelectionRange(
                    self.block_model.indexFromItem(i)))
        self.neoBlockList.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

        # Select segments
        seg_list = [block_list[idx[1]].segments[idx[0]]
                    for idx in data['segments']]
        all_segs = []
        for b in self.blocks():
            all_segs.extend(b.segments)
        self.ensure_not_filtered(seg_list, all_segs,
                                 self.parent.get_active_filters('Segment'))
        self.populate_neo_segment_list()

        selection = QItemSelection()
        for i in self.segment_model.findItems(
                '*', Qt.MatchWrap | Qt.MatchWildcard):
            segment = i.data(Qt.UserRole)
            if not segment.block in block_list:
                continue

            seg_idx = segment.block.segments.index(segment)
            block_idx = block_list.index(segment.block)
            if [seg_idx, block_idx] in data['segments']:
                selection.append(QItemSelectionRange(
                    self.segment_model.indexFromItem(i)))
        self.neoSegmentList.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

        # Select recording channel groups
        rcg_list = [block_list[rcg[1]].recordingchannelgroups[rcg[0]]
                    for rcg in data['channel_groups']]
        all_rcgs = []
        for b in self.blocks():
            all_rcgs.extend(b.recordingchannelgroups)
        self.ensure_not_filtered(
            rcg_list, all_rcgs,
            self.parent.get_active_filters('Recording Channel Group'))
        self.populate_neo_channel_group_list()

        selection = QItemSelection()
        for i in self.channelgroup_model.findItems(
                '*', Qt.MatchWrap | Qt.MatchWildcard):
            rcg = i.data(Qt.UserRole)
            if not rcg.block in block_list:
                continue

            rcg_idx = rcg.block.recordingchannelgroups.index(rcg)
            block_idx = block_list.index(rcg.block)
            if [rcg_idx, block_idx] in data['channel_groups']:
                selection.append(QItemSelectionRange(
                    self.channelgroup_model.indexFromItem(i)))
        self.neoChannelGroupList.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

        # Select channels
        rc_list = [rcg_list[rc[1]].recordingchannels[rc[0]]
                   for rc in data['channels']]
        all_rcs = []
        for rcg in self.recording_channel_groups():
            for rc in rcg.recordingchannels:
                if not api.config.duplicate_channels and rc in all_rcs:
                    continue
                all_rcs.append(rc)
        self.ensure_not_filtered(
            rc_list, all_rcs,
            self.parent.get_active_filters('Recording Channel'))
        self.populate_neo_channel_list()

        selection = QItemSelection()
        rcg_set = set(rcg_list)
        for i in self.channel_model.findItems(
                '*', Qt.MatchWrap | Qt.MatchWildcard):
            channel = i.data(Qt.UserRole)
            if not set(channel.recordingchannelgroups).intersection(rcg_set):
                continue

            for rcg in channel.recordingchannelgroups:
                if [rcg.recordingchannels.index(channel),
                        rcg_list.index(rcg)] in data['channels']:
                    selection.append(QItemSelectionRange(
                        self.channel_model.indexFromItem(i)))
                    break
        self.neoChannelList.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

        # Select units
        unit_list = [rcg_list[u[1]].units[u[0]]
                     for u in data['units']]
        all_units = []
        for rcg in self.recording_channel_groups():
            all_units.extend(rcg.units)
        self.ensure_not_filtered(
            unit_list, all_units,
            self.parent.get_active_filters('Unit'))
        self.populate_neo_unit_list()

        selection = QItemSelection()
        for i in self.unit_model.findItems(
                '*', Qt.MatchWrap | Qt.MatchWildcard):
            unit = i.data(Qt.UserRole)
            if unit.recordingchannelgroup not in rcg_list:
                continue

            rcg_idx = rcg_list.index(unit.recordingchannelgroup)
            unit_idx = unit.recordingchannelgroup.units.index(unit)
            if [unit_idx, rcg_idx] in data['units']:
                selection.append(QItemSelectionRange(
                    self.unit_model.indexFromItem(i)))
        self.neoUnitList.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

        self.parent.refresh_filters()
class CheckedListBox(QWidget):
    def __init__(self, parent):
        QWidget.__init__(self, parent)
        while not isinstance(parent, QDialog):
            parent = parent.parent()
        self.setObjectName("CheckedListBox" +
                           str(len(parent.findChildren(CheckedListBox))))

        self.setObjectName("checkBoxWidget")
        self.hLayout = QHBoxLayout(self)
        self.hLayout.setObjectName("hLayout")

        sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.sizePolicy().hasHeightForWidth())
        self.setSizePolicy(sizePolicy)

        # self.frame = Frame()
        self.listView = QListView(self)
        self.hLayout.addWidget(self.listView)

        self.stdModel = QStandardItemModel()
        self.listView.setModel(self.stdModel)

        self.hasObject = False
        self.objList = []

        self.checkBoxList = []

        self.listView.pressed.connect(self.listView_clicked)

    def listView_clicked(self, index):
        self.emit(SIGNAL("ItemCheck"), self.stdModel.itemFromIndex(index))

    def mouseMoveEvent(self, mouseEvent):
        pt = mouseEvent.pos()
        pass

    def Clear(self):
        self.stdModel.clear()
        self.hasObject = False
        self.objList = []

    def Add(self, caption, isCheckedFlag=False):
        captionStr = ""
        if isinstance(caption, str) or isinstance(caption, QString):
            captionStr = caption
        else:
            captionStr = caption.ToString()
            self.hasObject = True
            self.objList.append([self.stdModel.rowCount(), caption])
        item = QStandardItem(captionStr)
        item.setCheckable(True)
        if isCheckedFlag:
            item.setCheckState(Qt.Checked)
        else:
            item.setCheckState(Qt.Unchecked)
        self.stdModel.setItem(self.stdModel.rowCount(), item)

        # checkBox = QCheckBox(self)
        # checkBox.setText(captionStr)
        # checkBox.setChecked(isCheckedFlag)
        # checkBox.clicked.connect(self.checkBox_clicked)
        # self.hLayout.addWidget(checkBox)

    def GetItemChecked(self, index):
        if self.stdModel.rowCount() > 0:
            item = self.stdModel.item(index)
            if item.checkState() == Qt.Unchecked:
                return False
            return True
        return False

    def get_CheckedItems(self):
        if not self.stdModel.rowCount() > 0:
            return []
        resultCheckedItems = []
        for i in range(self.stdModel.rowCount()):
            item = self.stdModel.item(i)
            if item.checkState() == Qt.Checked:
                flag = False
                if self.hasObject:
                    for obj in self.objList:
                        if obj[0] == i:
                            resultCheckedItems.append(obj)
                            flag = True
                            break
                if not flag:
                    resultCheckedItems.append(item)
        return resultCheckedItems

    CheckedItems = property(get_CheckedItems, None, None, None)

    def get_Items(self):
        if not self.stdModel.rowCount() > 0:
            return None
        resultItems = []
        for i in range(self.stdModel.rowCount()):
            item = self.stdModel.item(i)
            flag = False
            if self.hasObject:
                for obj in self.objList:
                    if obj[0] == i:
                        resultItems.append(obj)
                        flag = True
                        break
            if not flag:
                resultItems.append(item.text())
        return resultItems

    Items = property(get_Items, None, None, None)

    def get_Enabled(self):
        return self.isEnabled()

    def set_Enabled(self, bool):
        self.setEnabled(bool)

    Enabled = property(get_Enabled, set_Enabled, None, None)

    def get_Visible(self):
        return self.isVisible()

    def set_Visible(self, bool):
        self.setVisible(bool)

    Visible = property(get_Visible, set_Visible, None, None)
Exemple #37
0
class MainWindow(mainwindow_widget, mainwindow_base):
    """
    Main application window
    """

    def __init__(self, settings):
        super(MainWindow, self).__init__()
        self.setupUi(self)
        self.settings = settings
        roam.featureform.settings = settings.settings
        self.canvaslayers = []
        self.layerbuttons = []
        self.project = None
        self.selectionbands = defaultdict(partial(QgsRubberBand, self.canvas))
        self.canvas.setCanvasColor(Qt.white)
        self.canvas.enableAntiAliasing(True)
        self.canvas.setWheelAction(QgsMapCanvas.WheelZoomToMouseCursor)
        self.bar = roam.messagebaritems.MessageBar(self)

        self.actionMap.setVisible(False)

        pal = QgsPalLabeling()
        self.canvas.mapRenderer().setLabelingEngine(pal)
        self.canvas.setFrameStyle(QFrame.NoFrame)
        self.menuGroup = QActionGroup(self)
        self.menuGroup.setExclusive(True)

        self.menuGroup.addAction(self.actionMap)
        self.menuGroup.addAction(self.actionDataEntry)
        self.menuGroup.addAction(self.actionProject)
        self.menuGroup.addAction(self.actionSync)
        self.menuGroup.addAction(self.actionSettings)
        self.menuGroup.triggered.connect(self.updatePage)

        self.editgroup = QActionGroup(self)
        self.editgroup.setExclusive(True)
        self.editgroup.addAction(self.actionPan)
        self.editgroup.addAction(self.actionZoom_In)
        self.editgroup.addAction(self.actionZoom_Out)
        self.editgroup.addAction(self.actionInfo)

        #TODO Extract GPS out into a service and remove UI stuff
        self.actionGPS = GPSAction(":/icons/gps", self.canvas, self.settings, self)
        self.projecttoolbar.addAction(self.actionGPS)

        self.projectwidget = ProjectsWidget(self)
        self.projectwidget.requestOpenProject.connect(self.loadProject)
        QgsProject.instance().readProject.connect(self._readProject)
        self.project_page.layout().addWidget(self.projectwidget)

        self.syncwidget = SyncWidget()
        self.syncpage.layout().addWidget(self.syncwidget)

        self.settingswidget = SettingsWidget(settings, self)
        self.settings_page.layout().addWidget(self.settingswidget)
        self.actionSettings.toggled.connect(self.settingswidget.populateControls)
        self.actionSettings.toggled.connect(self.settingswidget.readSettings)
        self.settingswidget.settingsupdated.connect(self.settingsupdated)

        self.dataentrywidget = DataEntryWidget(self.canvas, self.bar)
        self.widgetpage.layout().addWidget(self.dataentrywidget)

        self.dataentrywidget.rejected.connect(self.formrejected)
        self.dataentrywidget.featuresaved.connect(self.featureSaved)
        self.dataentrywidget.featuredeleted.connect(self.featuredeleted)
        self.dataentrywidget.failedsave.connect(self.failSave)
        self.dataentrywidget.helprequest.connect(self.showhelp)
        self.dataentrywidget.openimage.connect(self.openimage)

        def createSpacer(width=0, height=0):
            widget = QWidget()
            widget.setMinimumWidth(width)
            widget.setMinimumHeight(height)
            return widget

        gpsspacewidget = createSpacer(30)
        sidespacewidget = createSpacer(30)
        sidespacewidget2 = createSpacer(height=20)

        sidespacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        sidespacewidget2.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        gpsspacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

        self.topspaceraction = self.projecttoolbar.insertWidget(self.actionGPS, gpsspacewidget)

        def createlabel(text):
            style = """
                QLabel {
                        color: #706565;
                        font: 14px "Calibri" ;
                        }"""
            label = QLabel(text)
            label.setStyleSheet(style)

            return label

        self.projectlabel = createlabel("Project: {project}")
        self.userlabel = createlabel("User: {user}".format(user=getpass.getuser()))
        self.positionlabel = createlabel('')
        self.statusbar.addWidget(self.projectlabel)
        self.statusbar.addWidget(self.userlabel)
        spacer = createSpacer()
        spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        self.statusbar.addWidget(spacer)
        self.statusbar.addWidget(self.positionlabel)

        self.menutoolbar.insertWidget(self.actionQuit, sidespacewidget2)
        self.menutoolbar.insertWidget(self.actionProject, sidespacewidget)
        self.stackedWidget.currentChanged.connect(self.updateUIState)

        self.panels = []

        self.connectButtons()

        self.band = QgsRubberBand(self.canvas)
        self.band.setIconSize(20)
        self.band.setWidth(10)
        self.band.setColor(QColor(186, 93, 212, 76))

        self.canvas_page.layout().insertWidget(0, self.projecttoolbar)
        self.dataentrymodel = QStandardItemModel(self)
        self.dataentrycombo = QComboBox(self.projecttoolbar)
        self.dataentrycombo.setIconSize(QSize(48,48))
        self.dataentrycombo.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
        self.dataentrycombo.setSizeAdjustPolicy(QComboBox.AdjustToContents)
        self.dataentrycombo.setModel(self.dataentrymodel)
        self.dataentrycomboaction = self.projecttoolbar.insertWidget(self.topspaceraction, self.dataentrycombo)

        self.dataentrycombo.showPopup = self.selectdataentry

        self.biglist = BigList(self.canvas)
        self.biglist.setlabel("Select data entry form")
        self.biglist.setmodel(self.dataentrymodel)
        self.biglist.itemselected.connect(self.dataentrychanged)
        self.biglist.hide()

        self.centralwidget.layout().addWidget(self.statusbar)

        self.actionGPSFeature.setProperty('dataentry', True)

        self.infodock = InfoDock(self.canvas)
        self.infodock.requestopenform.connect(self.openForm)
        self.infodock.featureupdated.connect(self.highlightfeature)
        self.infodock.resultscleared.connect(self.clearselection)
        self.infodock.openurl.connect(self.viewurl)
        self.infodock.hide()
        self.hidedataentry()
        self.canvas.extentsChanged.connect(self.updatestatuslabel)
        self.projecttoolbar.toolButtonStyleChanged.connect(self.updatecombo)

    def selectdataentry(self, ):
        if self.dataentrycombo.count() == 0:
            return

        self.biglist.show()

    def viewurl(self, url):
        """
        Open a URL in Roam
        :param url:
        :return:
        """
        key = url.toString().lstrip('file://')
        try:
            # Hack. Eww fix me.
            data, imagetype = roam.htmlviewer.images[os.path.basename(key)]
        except KeyError:
            # It's not a image so lets just pass it of as a normal
            # URL
            QDesktopServices.openUrl(url)
            return

        pix = QPixmap()
        if imagetype == 'base64':
            pix.loadFromData(data)
        else:
            pix.load(data)

        self.openimage(pix)

    def openimage(self, pixmap):
        viewer = ImageViewer(self.stackedWidget)
        viewer.resize(self.stackedWidget.size())
        viewer.openimage(pixmap)

    def updatecombo(self, *args):
        self.dataentrycombo.setMinimumHeight(0)

    def settingsupdated(self, settings):
        settings.save()
        self.show()
        self.actionGPS.updateGPSPort()
        # eww!
        roam.featureform.settings = settings.settings

    def updatestatuslabel(self):
        extent = self.canvas.extent()
        self.positionlabel.setText("Map Center: {}".format(extent.center().toString()))

    def highlightselection(self, results):
        for layer, features in results.iteritems():
            band = self.selectionbands[layer]
            band.setColor(QColor(255, 0, 0, 150))
            band.setIconSize(20)
            band.setWidth(2)
            band.setBrushStyle(Qt.NoBrush)
            band.reset(layer.geometryType())
            for feature in features:
                band.addGeometry(feature.geometry(), layer)

    def clearselection(self):
        # Clear the main selection rubber band
        self.band.reset()
        # Clear the rest
        for band in self.selectionbands.itervalues():
            band.reset()

    def highlightfeature(self, layer, feature, features):
        self.clearselection()
        self.highlightselection({layer: features})
        self.band.setToGeometry(feature.geometry(), layer)

    def showmap(self):
        self.actionMap.setVisible(True)
        self.actionMap.trigger()

    def hidedataentry(self):
        self.actionDataEntry.setVisible(False)

    def showdataentry(self):
        self.actionDataEntry.setVisible(True)
        self.actionDataEntry.trigger()

    def dataentrychanged(self, index):
        wasactive = self.clearCapatureTools()

        if not index.isValid():
            return

        modelindex = index
        # modelindex = self.dataentrymodel.index(index, 0)
        form = modelindex.data(Qt.UserRole + 1)
        self.dataentrycombo.setCurrentIndex(index.row())
        self.createCaptureButtons(form, wasactive)

    def raiseerror(self, *exinfo):
        info = traceback.format_exception(*exinfo)
        item = self.bar.pushError('Seems something has gone wrong. Press for more details',
                                  info)

    def setMapTool(self, tool, *args):
        self.canvas.setMapTool(tool)

    def homeview(self):
        """
        Zoom the mapview canvas to the extents the project was opened at i.e. the
        default extent.
        """
        self.canvas.setExtent(self.defaultextent)
        self.canvas.refresh()

    def connectButtons(self):
        def connectAction(action, tool):
            action.toggled.connect(partial(self.setMapTool, tool))

        def cursor(name):
            pix = QPixmap(name)
            pix = pix.scaled(QSize(24,24))
            return QCursor(pix)

        self.zoomInTool = QgsMapToolZoom(self.canvas, False)
        self.zoomOutTool = QgsMapToolZoom(self.canvas, True)
        self.panTool = TouchMapTool(self.canvas)
        self.moveTool = MoveTool(self.canvas, [])
        self.infoTool = InfoTool(self.canvas)

        connectAction(self.actionZoom_In, self.zoomInTool)
        connectAction(self.actionZoom_Out, self.zoomOutTool)
        connectAction(self.actionPan, self.panTool)
        connectAction(self.actionMove, self.moveTool)
        connectAction(self.actionInfo, self.infoTool)

        self.zoomInTool.setCursor(cursor(':/icons/in'))
        self.zoomOutTool.setCursor(cursor(':/icons/out'))
        self.infoTool.setCursor(cursor(':/icons/info'))

        self.actionRaster.triggered.connect(self.toggleRasterLayers)

        self.infoTool.infoResults.connect(self.showInfoResults)

        # The edit toolbutton is currently not being used but leaving it for feature.
        self.moveTool.layersupdated.connect(self.actionMove.setEnabled)
        self.moveTool.layersupdated.connect(self.actionEdit_Tools.setEnabled)

        self.actionGPSFeature.triggered.connect(self.addFeatureAtGPS)
        self.actionGPSFeature.setEnabled(self.actionGPS.isConnected)
        self.actionGPS.gpsfixed.connect(self.actionGPSFeature.setEnabled)

        self.actionHome.triggered.connect(self.homeview)
        self.actionQuit.triggered.connect(self.exit)

    def showToolError(self, label, message):
        self.bar.pushMessage(label, message, QgsMessageBar.WARNING)

    def clearCapatureTools(self):
        captureselected = False
        for action in self.projecttoolbar.actions():
            if action.objectName() == "capture" and action.isChecked():
                captureselected = True

            if action.property('dataentry'):
                self.projecttoolbar.removeAction(action)
        return captureselected

    def createCaptureButtons(self, form, wasselected):
        tool = form.getMaptool()(self.canvas)
        for action in tool.actions:
            # Create the action here.
            if action.ismaptool:
                action.toggled.connect(partial(self.setMapTool, tool))

            # Set the action as a data entry button so we can remove it later.
            action.setProperty("dataentry", True)
            self.editgroup.addAction(action)
            self.layerbuttons.append(action)
            self.projecttoolbar.insertAction(self.topspaceraction, action)

            if action.isdefault:
                action.setChecked(wasselected)

        if hasattr(tool, 'geometryComplete'):
            add = partial(self.addNewFeature, form)
            tool.geometryComplete.connect(add)
        else:
            tool.finished.connect(self.openForm)
            tool.error.connect(partial(self.showToolError, form.label))

        self.projecttoolbar.insertAction(self.topspaceraction, self.actionGPSFeature)
        self.actionGPSFeature.setVisible(not tool.isEditTool())

    def createFormButtons(self, forms):
        """
            Create buttons for each form that is defined
        """
        self.dataentrymodel.clear()
        self.clearCapatureTools()


        def captureFeature(form):
            item = QStandardItem(QIcon(form.icon), form.icontext)
            item.setData(form, Qt.UserRole + 1)
            item.setSizeHint(QSize(item.sizeHint().width(), self.projecttoolbar.height()))
            self.dataentrymodel.appendRow(item)

        capabilitityhandlers = {"capture": captureFeature}

        failedforms = []
        for form in forms:
            valid, reasons = form.valid
            if not valid:
                roam.utils.log("Form is invalid for data entry because {}".format(reasons))
                failedforms.append((form, reasons))
                continue

            for capability in form.capabilities:
                try:
                    capabilitityhandlers[capability](form)
                except KeyError:
                    # Just ignore capabilities we don't support yet.
                    continue

        if failedforms:
            for form, reasons in failedforms:
                html = "<h3>{}</h3><br>{}".format(form.label,
                                             "<br>".join(reasons))
            self.bar.pushMessage("Form errors",
                                 "Looks like some forms couldn't be loaded",
                                 level=QgsMessageBar.WARNING, extrainfo=html)

        visible = self.dataentrymodel.rowCount() > 0
        self.dataentrycomboaction.setVisible(visible)
        self.dataentrycombo.setMinimumHeight(self.projecttoolbar.height())

        index = self.dataentrymodel.index(0, 0)
        self.dataentrychanged(index)

    def addFeatureAtGPS(self):
        """
        Add a record at the current GPS location.
        """
        index = self.dataentrycombo.currentIndex()
        modelindex = self.dataentrymodel.index(index, 0)
        form = modelindex.data(Qt.UserRole + 1)
        point = self.actionGPS.position
        point = QgsGeometry.fromPoint(point)
        self.addNewFeature(form=form, geometry=point)

    def clearToolRubberBand(self):
        """
        Clear the rubber band of the active tool if it has one
        """
        tool = self.canvas.mapTool()
        try:
            tool.clearBand()
        except AttributeError:
            # No clearBand method found, but that's cool.
            pass

    def showhelp(self, url):
        help = HelpPage(self.stackedWidget)
        help.setHelpPage(url)
        help.show()

    def dataentryfinished(self):
        self.hidedataentry()
        self.showmap()
        self.cleartempobjects()
        self.infodock.refreshcurrent()

    def featuredeleted(self):
        self.dataentryfinished()
        self.bar.pushMessage("Deleted", "Feature Deleted", QgsMessageBar.INFO, 1)
        self.canvas.refresh()

    def featureSaved(self):
        self.dataentryfinished()
        self.canvas.refresh()

    def failSave(self, messages):
        self.bar.pushError("Error when saving changes.", messages)

    def cleartempobjects(self):
        self.band.reset()
        self.clearToolRubberBand()

    def formrejected(self, message, level):
        self.dataentryfinished()
        if message:
            self.bar.pushMessage("Form Message", message, level, duration=2)

        self.cleartempobjects()

    def openForm(self, form, feature):
        """
        Open the form that is assigned to the layer
        """
        self.band.setToGeometry(feature.geometry(), form.QGISLayer)
        self.showdataentry()
        self.dataentrywidget.openform(feature=feature, form=form, project=self.project)

    def addNewFeature(self, form, geometry):
        """
        Add a new new feature to the given layer
        """
        layer = form.QGISLayer
        fields = layer.pendingFields()

        feature = QgsFeature(fields)
        feature.setGeometry(geometry)

        for index in xrange(fields.count()):
            pkindexes = layer.dataProvider().pkAttributeIndexes()
            if index in pkindexes and layer.dataProvider().name() == 'spatialite':
                continue

            value = layer.dataProvider().defaultValue(index)
            feature[index] = value

        self.openForm(form, feature)

    def exit(self):
        """
        Exit the application.
        """
        QApplication.exit(0)

    def showInfoResults(self, results):
        self.infodock.clearResults()
        forms = {}
        for layer in results.keys():
            layername = layer.name()
            if not layername in forms:
                forms[layername] = list(self.project.formsforlayer(layername))

        self.infodock.setResults(results, forms)
        self.infodock.show()

    def toggleRasterLayers(self):
        """
        Toggle all raster layers on or off.
        """
        if not self.canvaslayers:
            return

        #Freeze the canvas to save on UI refresh
        self.canvas.freeze()
        for layer in self.canvaslayers:
            if layer.layer().type() == QgsMapLayer.RasterLayer:
                layer.setVisible(not layer.isVisible())
            # Really!? We have to reload the whole layer set every time?
        # WAT?
        self.canvas.setLayerSet(self.canvaslayers)
        self.canvas.freeze(False)
        self.canvas.refresh()

    def missingLayers(self, layers):
        """
        Called when layers have failed to load from the current project
        """
        roam.utils.warning("Missing layers")
        map(roam.utils.warning, layers)

        missinglayers = roam.messagebaritems.MissingLayerItem(layers,
                                                              parent=self.bar)
        self.bar.pushItem(missinglayers)

    def loadprojects(self, projects):
        """
        Load the given projects into the project
        list
        """
        projects = list(projects)
        self.projectwidget.loadProjectList(projects)
        self.syncwidget.loadprojects(projects)

    def updatePage(self, action):
        """
        Update the current stack page based on the current selected
        action
        """
        page = action.property("page")
        self.stackedWidget.setCurrentIndex(page)

    def show(self):
        """
        Override show method. Handles showing the app in fullscreen
        mode or just maximized
        """
        fullscreen = self.settings.settings.get("fullscreen", False)
        if fullscreen:
            self.showFullScreen()
        else:
            self.showMaximized()

    def viewprojects(self):
        self.stackedWidget.setCurrentIndex(1)

    def updateUIState(self, page):
        """
        Update the UI state to reflect the currently selected
        page in the stacked widget
        """
        pass

    @roam.utils.timeit
    def _readProject(self, doc):
        """
        readProject is called by QgsProject once the map layer has been
        populated with all the layers
        """
        parser = ProjectParser(doc)
        canvasnode = parser.canvasnode
        self.canvas.freeze()
        self.canvas.mapRenderer().readXML(canvasnode)
        self.canvaslayers = parser.canvaslayers()
        self.canvas.setLayerSet(self.canvaslayers)
        self.canvas.updateScale()
        self.projectOpened()
        self.canvas.freeze(False)
        self.canvas.refresh()
        self.showmap()

    @roam.utils.timeit
    def projectOpened(self):
        """
            Called when a new project is opened in QGIS.
        """
        projectpath = QgsProject.instance().fileName()
        self.project = Project.from_folder(os.path.dirname(projectpath))
        self.projectlabel.setText("Project: {}".format(self.project.name))
        self.createFormButtons(forms=self.project.forms)

        # Enable the raster layers button only if the project contains a raster layer.
        layers = QgsMapLayerRegistry.instance().mapLayers().values()
        hasrasters = any(layer.type() == QgsMapLayer.RasterLayer for layer in layers)
        self.actionRaster.setEnabled(hasrasters)
        self.defaultextent = self.canvas.extent()
        roam.utils.info("Extent: {}".format(self.defaultextent.toString()))

        # Show panels
        for panel in self.project.getPanels():
            self.mainwindow.addDockWidget(Qt.BottomDockWidgetArea, panel)
            self.panels.append(panel)

        # TODO Abstract this out
        if not self.project.selectlayers:
            selectionlayers = QgsMapLayerRegistry.instance().mapLayers().values()
        else:
            selectionlayers = []
            for layername in self.project.selectlayers:
                try:
                    layer = QgsMapLayerRegistry.instance().mapLayersByName(layername)[0]
                except IndexError:
                    roam.utils.warning("Can't find QGIS layer for select layer {}".format(layername))
                    continue
                selectionlayers.append(layer)

        self.infoTool.selectionlayers = selectionlayers
        self.actionPan.trigger()

    #noinspection PyArgumentList
    @roam.utils.timeit
    def loadProject(self, project):
        """
        Load a project into the application .
        """
        roam.utils.log(project)
        roam.utils.log(project.name)
        roam.utils.log(project.projectfile)
        roam.utils.log(project.valid)

        (passed, message) = project.onProjectLoad()

        if not passed:
            self.bar.pushMessage("Project load rejected", "Sorry this project couldn't"
                                                          "be loaded.  Click for me details.",
                                 QgsMessageBar.WARNING, extrainfo=message)
            return

        self.actionMap.trigger()

        self.closeProject()

        self.canvas.refresh()
        self.canvas.repaint()

        self.infodock.clearResults()

        # No idea why we have to set this each time.  Maybe QGIS deletes it for
        # some reason.
        self.badLayerHandler = BadLayerHandler(callback=self.missingLayers)
        QgsProject.instance().setBadLayerHandler(self.badLayerHandler)

        self.stackedWidget.setCurrentIndex(3)
        self.projectloading_label.setText("Project {} Loading".format(project.name))
        pixmap = QPixmap(project.splash)
        w = self.projectimage.width()
        h = self.projectimage.height()
        self.projectimage.setPixmap(pixmap.scaled(w,h, Qt.KeepAspectRatio))
        QApplication.processEvents()

        QDir.setCurrent(os.path.dirname(project.projectfile))
        fileinfo = QFileInfo(project.projectfile)
        QgsProject.instance().read(fileinfo)

    def closeProject(self):
        """
        Close the current open project
        """
        self.canvas.freeze()
        QgsMapLayerRegistry.instance().removeAllMapLayers()
        self.canvas.clear()
        self.canvas.freeze(False)
        for panel in self.panels:
            self.removeDockWidget(panel)
            del panel
            # Remove all the old buttons
        for action in self.layerbuttons:
            self.editgroup.removeAction(action)

        self.dataentrymodel.clear()
        self.panels = []
        self.project = None
        self.dataentrywidget.clear()
        self.hidedataentry()
        self.infodock.close()
class OWConfusionMatrix(widget.OWWidget):
    """Confusion matrix widget"""

    name = "Confusion Matrix"
    description = "Display a confusion matrix constructed from " \
                  "the results of classifier evaluations."
    icon = "icons/ConfusionMatrix.svg"
    priority = 1001

    inputs = [("Evaluation Results", Orange.evaluation.Results, "set_results")]
    outputs = [("Selected Data", Orange.data.Table)]

    quantities = ["Number of instances",
                  "Proportion of predicted",
                  "Proportion of actual"]

    settingsHandler = settings.ClassValuesContextHandler()

    selected_learner = settings.Setting([0], schema_only=True)
    selection = settings.ContextSetting(set())
    selected_quantity = settings.Setting(0)
    append_predictions = settings.Setting(True)
    append_probabilities = settings.Setting(False)
    autocommit = settings.Setting(True)

    UserAdviceMessages = [
        widget.Message(
            "Clicking on cells or in headers outputs the corresponding "
            "data instances",
            "click_cell")]

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

        self.data = None
        self.results = None
        self.learners = []
        self.headers = []

        box = gui.vBox(self.controlArea, "Learners")

        self.learners_box = gui.listBox(
            box, self, "selected_learner", "learners",
            callback=self._learner_changed
        )
        box = gui.vBox(self.controlArea, "Show")

        gui.comboBox(box, self, "selected_quantity", items=self.quantities,
                     callback=self._update)

        box = gui.vBox(self.controlArea, "Select")

        gui.button(box, self, "Select Correct",
                   callback=self.select_correct, autoDefault=False)
        gui.button(box, self, "Select Misclassified",
                   callback=self.select_wrong, autoDefault=False)
        gui.button(box, self, "Clear Selection",
                   callback=self.select_none, autoDefault=False)

        self.outputbox = box = gui.vBox(self.controlArea, "Output")
        gui.checkBox(box, self, "append_predictions",
                     "Predictions", callback=self._invalidate)
        gui.checkBox(box, self, "append_probabilities",
                     "Probabilities",
                     callback=self._invalidate)

        gui.auto_commit(self.controlArea, self, "autocommit",
                        "Send Selected", "Send Automatically")

        grid = QGridLayout()

        self.tablemodel = QStandardItemModel(self)
        view = self.tableview = QTableView(
            editTriggers=QTableView.NoEditTriggers)
        view.setModel(self.tablemodel)
        view.horizontalHeader().hide()
        view.verticalHeader().hide()
        view.horizontalHeader().setMinimumSectionSize(60)
        view.selectionModel().selectionChanged.connect(self._invalidate)
        view.setShowGrid(False)
        view.setItemDelegate(BorderedItemDelegate(Qt.white))
        view.clicked.connect(self.cell_clicked)
        grid.addWidget(view, 0, 0)
        self.mainArea.layout().addLayout(grid)

    def sizeHint(self):
        """Initial size"""
        return QSize(750, 490)

    def _item(self, i, j):
        return self.tablemodel.item(i, j) or QStandardItem()

    def _set_item(self, i, j, item):
        self.tablemodel.setItem(i, j, item)

    def _init_table(self, nclasses):
        item = self._item(0, 2)
        item.setData("Predicted", Qt.DisplayRole)
        item.setTextAlignment(Qt.AlignCenter)
        item.setFlags(Qt.NoItemFlags)

        self._set_item(0, 2, item)
        item = self._item(2, 0)
        item.setData("Actual", Qt.DisplayRole)
        item.setTextAlignment(Qt.AlignHCenter | Qt.AlignBottom)
        item.setFlags(Qt.NoItemFlags)
        self.tableview.setItemDelegateForColumn(0, gui.VerticalItemDelegate())
        self._set_item(2, 0, item)
        self.tableview.setSpan(0, 2, 1, nclasses)
        self.tableview.setSpan(2, 0, nclasses, 1)

        font = self.tablemodel.invisibleRootItem().font()
        bold_font = QFont(font)
        bold_font.setBold(True)

        for i in (0, 1):
            for j in (0, 1):
                item = self._item(i, j)
                item.setFlags(Qt.NoItemFlags)
                self._set_item(i, j, item)

        for p, label in enumerate(self.headers):
            for i, j in ((1, p + 2), (p + 2, 1)):
                item = self._item(i, j)
                item.setData(label, Qt.DisplayRole)
                item.setFont(bold_font)
                item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                item.setFlags(Qt.ItemIsEnabled)
                if p < len(self.headers) - 1:
                    item.setData("br"[j == 1], BorderRole)
                    item.setData(QColor(192, 192, 192), BorderColorRole)
                self._set_item(i, j, item)

        hor_header = self.tableview.horizontalHeader()
        if len(' '.join(self.headers)) < 120:
            hor_header.setResizeMode(QHeaderView.ResizeToContents)
        else:
            hor_header.setDefaultSectionSize(60)
        self.tablemodel.setRowCount(nclasses + 3)
        self.tablemodel.setColumnCount(nclasses + 3)

    def set_results(self, results):
        """Set the input results."""

        prev_sel_learner = self.selected_learner.copy()
        self.clear()
        self.warning()
        self.closeContext()

        data = None
        if results is not None and results.data is not None:
            data = results.data

        if data is not None and not data.domain.has_discrete_class:
            self.warning("Confusion Matrix cannot show regression results.")

        self.results = results
        self.data = data

        if data is not None:
            class_values = data.domain.class_var.values
        elif results is not None:
            raise NotImplementedError

        if results is None:
            self.report_button.setDisabled(True)
        else:
            self.report_button.setDisabled(False)

            nmodels = results.predicted.shape[0]
            self.headers = class_values + \
                           [unicodedata.lookup("N-ARY SUMMATION")]

            # NOTE: The 'learner_names' is set in 'Test Learners' widget.
            if hasattr(results, "learner_names"):
                self.learners = results.learner_names
            else:
                self.learners = ["Learner #{}".format(i + 1)
                                 for i in range(nmodels)]

            self._init_table(len(class_values))
            self.openContext(data.domain.class_var)
            if not prev_sel_learner or prev_sel_learner[0] >= len(self.learners):
                self.selected_learner[:] = [0]
            else:
                self.selected_learner[:] = prev_sel_learner
            self._update()
            self._set_selection()
            self.unconditional_commit()

    def clear(self):
        """Reset the widget, clear controls"""
        self.results = None
        self.data = None
        self.tablemodel.clear()
        self.headers = []
        # Clear learners last. This action will invoke `_learner_changed`
        self.learners = []

    def select_correct(self):
        """Select the diagonal elements of the matrix"""
        selection = QItemSelection()
        n = self.tablemodel.rowCount()
        for i in range(2, n):
            index = self.tablemodel.index(i, i)
            selection.select(index, index)
        self.tableview.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

    def select_wrong(self):
        """Select the off-diagonal elements of the matrix"""
        selection = QItemSelection()
        n = self.tablemodel.rowCount()
        for i in range(2, n):
            for j in range(i + 1, n):
                index = self.tablemodel.index(i, j)
                selection.select(index, index)
                index = self.tablemodel.index(j, i)
                selection.select(index, index)
        self.tableview.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

    def select_none(self):
        """Reset selection"""
        self.tableview.selectionModel().clear()

    def cell_clicked(self, model_index):
        """Handle cell click event"""
        i, j = model_index.row(), model_index.column()
        if not i or not j:
            return
        n = self.tablemodel.rowCount()
        index = self.tablemodel.index
        selection = None
        if i == j == 1 or i == j == n - 1:
            selection = QItemSelection(index(2, 2), index(n - 1, n - 1))
        elif i in (1, n - 1):
            selection = QItemSelection(index(2, j), index(n - 1, j))
        elif j in (1, n - 1):
            selection = QItemSelection(index(i, 2), index(i, n - 1))

        if selection is not None:
            self.tableview.selectionModel().select(
                selection, QItemSelectionModel.ClearAndSelect)

    def commit(self):
        """Output data instances corresponding to selected cells"""
        if self.results is not None and self.data is not None \
                and self.selected_learner:
            indices = self.tableview.selectedIndexes()
            indices = {(ind.row() - 2, ind.column() - 2) for ind in indices}
            actual = self.results.actual
            learner_name = self.learners[self.selected_learner[0]]
            predicted = self.results.predicted[self.selected_learner[0]]
            selected = [i for i, t in enumerate(zip(actual, predicted))
                        if t in indices]
            row_indices = self.results.row_indices[selected]

            extra = []
            class_var = self.data.domain.class_var
            metas = self.data.domain.metas

            if self.append_predictions:
                predicted = numpy.array(predicted[selected], dtype=object)
                extra.append(predicted.reshape(-1, 1))
                var = Orange.data.DiscreteVariable(
                    "{}({})".format(class_var.name, learner_name),
                    class_var.values
                )
                metas = metas + (var,)

            if self.append_probabilities and \
                    self.results.probabilities is not None:
                probs = self.results.probabilities[self.selected_learner[0],
                                                   selected]
                extra.append(numpy.array(probs, dtype=object))
                pvars = [Orange.data.ContinuousVariable("p({})".format(value))
                         for value in class_var.values]
                metas = metas + tuple(pvars)

            X = self.data.X[row_indices]
            Y = self.data.Y[row_indices]
            M = self.data.metas[row_indices]
            row_ids = self.data.ids[row_indices]

            M = numpy.hstack((M,) + tuple(extra))
            domain = Orange.data.Domain(
                self.data.domain.attributes,
                self.data.domain.class_vars,
                metas
            )
            data = Orange.data.Table.from_numpy(domain, X, Y, M)
            data.ids = row_ids
            data.name = learner_name

        else:
            data = None

        self.send("Selected Data", data)

    def _invalidate(self):
        indices = self.tableview.selectedIndexes()
        self.selection = {(ind.row() - 2, ind.column() - 2) for ind in indices}
        self.commit()

    def _set_selection(self):
        selection = QItemSelection()
        index = self.tableview.model().index
        for row, col in self.selection:
            sel = index(row + 2, col + 2)
            selection.select(sel, sel)
        self.tableview.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

    def _learner_changed(self):
        self._update()
        self._set_selection()
        self.commit()

    def _update(self):
        def _isinvalid(x):
            return isnan(x) or isinf(x)

        # Update the displayed confusion matrix
        if self.results is not None and self.selected_learner:
            cmatrix = confusion_matrix(self.results, self.selected_learner[0])
            colsum = cmatrix.sum(axis=0)
            rowsum = cmatrix.sum(axis=1)
            n = len(cmatrix)
            diag = numpy.diag_indices(n)

            colors = cmatrix.astype(numpy.double)
            colors[diag] = 0
            if self.selected_quantity == 0:
                normalized = cmatrix.astype(numpy.int)
                formatstr = "{}"
                div = numpy.array([colors.max()])
            else:
                if self.selected_quantity == 1:
                    normalized = 100 * cmatrix / colsum
                    div = colors.max(axis=0)
                else:
                    normalized = 100 * cmatrix / rowsum[:, numpy.newaxis]
                    div = colors.max(axis=1)[:, numpy.newaxis]
                formatstr = "{:2.1f} %"
            div[div == 0] = 1
            colors /= div
            colors[diag] = normalized[diag] / normalized[diag].max()

            for i in range(n):
                for j in range(n):
                    val = normalized[i, j]
                    col_val = colors[i, j]
                    item = self._item(i + 2, j + 2)
                    item.setData(
                        "NA" if _isinvalid(val) else formatstr.format(val),
                        Qt.DisplayRole)
                    bkcolor = QColor.fromHsl(
                        [0, 240][i == j], 160,
                        255 if _isinvalid(col_val) else int(255 - 30 * col_val))
                    item.setData(QBrush(bkcolor), Qt.BackgroundRole)
                    item.setData("trbl", BorderRole)
                    item.setToolTip("actual: {}\npredicted: {}".format(
                        self.headers[i], self.headers[j]))
                    item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                    item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
                    self._set_item(i + 2, j + 2, item)

            bold_font = self.tablemodel.invisibleRootItem().font()
            bold_font.setBold(True)

            def _sum_item(value, border=""):
                item = QStandardItem()
                item.setData(value, Qt.DisplayRole)
                item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                item.setFlags(Qt.ItemIsEnabled)
                item.setFont(bold_font)
                item.setData(border, BorderRole)
                item.setData(QColor(192, 192, 192), BorderColorRole)
                return item

            for i in range(n):
                self._set_item(n + 2, i + 2, _sum_item(int(colsum[i]), "t"))
                self._set_item(i + 2, n + 2, _sum_item(int(rowsum[i]), "l"))
            self._set_item(n + 2, n + 2, _sum_item(int(rowsum.sum())))

    def send_report(self):
        """Send report"""
        if self.results is not None and self.selected_learner:
            self.report_table(
                "Confusion matrix for {} (showing {})".
                format(self.learners[self.selected_learner[0]],
                       self.quantities[self.selected_quantity].lower()),
                self.tableview)
Exemple #39
0
class MainWindow(QMainWindow, Ui_MainWindow):
    qtcb_enumerate = pyqtSignal(str, str, 'char', type((0,)), type((0,)), int, int)
    qtcb_connected = pyqtSignal(int)
    qtcb_disconnected = pyqtSignal(int)

    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)

        self.setupUi(self)

        signal.signal(signal.SIGINT, self.exit_brickv)
        signal.signal(signal.SIGTERM, self.exit_brickv)

        self.async_thread = async_start_thread(self)

        self.setWindowTitle("Brick Viewer " + config.BRICKV_VERSION)

        self.tree_view_model_labels = ['Name', 'UID', 'Position', 'FW Version']
        self.tree_view_model = QStandardItemModel(self)
        self.tree_view.setModel(self.tree_view_model)
        self.tree_view.doubleClicked.connect(self.item_double_clicked)
        self.set_tree_view_defaults()

        # Remove dummy tab
        self.tab_widget.removeTab(1)

        self.name = '<unknown>'
        self.uid = '<unknown>'
        self.version = (0, 0, 0)

        self.disconnect_times = []

        self.qtcb_enumerate.connect(self.cb_enumerate)
        self.qtcb_connected.connect(self.cb_connected)
        self.qtcb_disconnected.connect(self.cb_disconnected)

        self.ipcon = IPConnection()
        self.ipcon.register_callback(IPConnection.CALLBACK_ENUMERATE,
                                     self.qtcb_enumerate.emit)
        self.ipcon.register_callback(IPConnection.CALLBACK_CONNECTED,
                                     self.qtcb_connected.emit)
        self.ipcon.register_callback(IPConnection.CALLBACK_DISCONNECTED,
                                     self.qtcb_disconnected.emit)

        self.current_device_info = None
        self.flashing_window = None
        self.advanced_window = None
        self.delayed_refresh_updates_timer = QTimer()
        self.delayed_refresh_updates_timer.timeout.connect(self.delayed_refresh_updates)
        self.delayed_refresh_updates_timer.setInterval(500)
        self.reset_view()
        self.button_advanced.setDisabled(True)

        self.tab_widget.currentChanged.connect(self.tab_changed)
        self.tab_widget.setMovable(True)
        self.tab_widget.tabBar().installEventFilter(self)

        self.button_connect.clicked.connect(self.connect_clicked)
        self.button_flashing.clicked.connect(self.flashing_clicked)
        self.button_advanced.clicked.connect(self.advanced_clicked)
        self.plugin_manager = PluginManager()

        # host info
        self.host_infos = config.get_host_infos(config.HOST_INFO_COUNT)
        self.host_index_changing = True

        for host_info in self.host_infos:
            self.combo_host.addItem(host_info.host)

        self.last_host = None
        self.combo_host.currentIndexChanged.connect(self.host_index_changed)

        self.spinbox_port.setValue(self.host_infos[0].port)
        self.spinbox_port.valueChanged.connect(self.port_changed)

        self.checkbox_authentication.stateChanged.connect(self.authentication_state_changed)

        self.label_secret.hide()
        self.edit_secret.hide()
        self.edit_secret.setEchoMode(QLineEdit.Password)
        self.edit_secret.textEdited.connect(self.secret_changed)

        self.checkbox_secret_show.hide()
        self.checkbox_secret_show.stateChanged.connect(self.secret_show_state_changed)

        self.checkbox_remember_secret.hide()
        self.checkbox_remember_secret.stateChanged.connect(self.remember_secret_state_changed)

        self.checkbox_authentication.setChecked(self.host_infos[0].use_authentication)
        self.edit_secret.setText(self.host_infos[0].secret)
        self.checkbox_remember_secret.setChecked(self.host_infos[0].remember_secret)

        self.host_index_changing = False

        # auto-reconnect
        self.label_auto_reconnects.hide()
        self.auto_reconnects = 0

        # RED Session losts
        self.label_red_session_losts.hide()
        self.red_session_losts = 0

    # override QMainWindow.closeEvent
    def closeEvent(self, event):
        self.exit_brickv()

    def exit_brickv(self, signal=None, frame=None):
        if self.current_device_info is not None:
            self.current_device_info.plugin.stop_plugin()
            self.current_device_info.plugin.destroy_plugin()

        self.update_current_host_info()
        config.set_host_infos(self.host_infos)

        self.do_disconnect()

        if signal != None and frame != None:
            print("Received SIGINT or SIGTERM, shutting down.")
            sys.exit()

    def host_index_changed(self, i):
        if i < 0:
            return

        self.host_index_changing = True

        self.spinbox_port.setValue(self.host_infos[i].port)
        self.checkbox_authentication.setChecked(self.host_infos[i].use_authentication)
        self.edit_secret.setText(self.host_infos[i].secret)
        self.checkbox_remember_secret.setChecked(self.host_infos[i].remember_secret)

        self.host_index_changing = False

    def port_changed(self, value):
        self.update_current_host_info()

    def authentication_state_changed(self, state):
        visible = state == Qt.Checked

        self.label_secret.setVisible(visible)
        self.edit_secret.setVisible(visible)
        self.checkbox_secret_show.setVisible(visible)
        self.checkbox_remember_secret.setVisible(visible)

        self.update_current_host_info()

    def secret_changed(self):
        self.update_current_host_info()

    def secret_show_state_changed(self, state):
        if state == Qt.Checked:
            self.edit_secret.setEchoMode(QLineEdit.Normal)
        else:
            self.edit_secret.setEchoMode(QLineEdit.Password)

        self.update_current_host_info()

    def remember_secret_state_changed(self, state):
        self.update_current_host_info()

    def tab_changed(self, i):
        if not hasattr(self.tab_widget.widget(i), '_info'):
            new_current_device_info = None
        else:
            new_current_device_info = self.tab_widget.widget(i)._info
            new_current_device_info.plugin.start_plugin()

        # stop the now deselected plugin, if there is one that's running
        if self.current_device_info is not None:
            self.current_device_info.plugin.stop_plugin()

        self.current_device_info = new_current_device_info

    def update_current_host_info(self):
        if self.host_index_changing:
            return

        i = self.combo_host.currentIndex()

        if i < 0:
            return

        #self.host_infos[i].host = self.combo_host.currentText()
        self.host_infos[i].port = self.spinbox_port.value()
        self.host_infos[i].use_authentication = self.checkbox_authentication.isChecked()
        self.host_infos[i].secret = self.edit_secret.text()
        self.host_infos[i].remember_secret = self.checkbox_remember_secret.isChecked()

    def remove_all_device_infos(self):
        for device_info in infos.get_device_infos():
            self.remove_device_info(device_info.uid)

    def remove_device_info(self, uid):
        tab_id = self.tab_for_uid(uid)
        device_info = infos.get_info(uid)

        device_info.plugin.stop_plugin()
        device_info.plugin.destroy_plugin()

        if tab_id >= 0:
            self.tab_widget.removeTab(tab_id)

        # ensure that the widget gets correctly destroyed. otherwise QWidgets
        # tend to leak as Python is not able to collect their PyQt object
        tab_window = device_info.tab_window
        device_info.tab_window = None

        # If we reboot the RED Brick, the tab_window sometimes is
        # already None here
        if tab_window != None:
            tab_window.hide()
            tab_window.setParent(None)

        plugin = device_info.plugin
        device_info.plugin = None

        if plugin != None:
            plugin.hide()
            plugin.setParent(None)

        infos.remove_info(uid)

    def reset_view(self):
        self.tab_widget.setCurrentIndex(0)
        self.remove_all_device_infos()
        self.update_tree_view()

    def do_disconnect(self):
        self.auto_reconnects = 0
        self.label_auto_reconnects.hide()

        self.red_session_losts = 0
        self.label_red_session_losts.hide()

        self.reset_view()
        async_next_session()

        # force garbage collection, to ensure that all plugin related objects
        # got destroyed before disconnect is called. this is especially
        # important for the RED Brick plugin because its relies on releasing
        # the the RED Brick API objects in the __del__ method as a last resort
        # to avoid leaking object references. but this only works if garbage
        # collection is done before disconnect is called
        gc.collect()

        try:
            self.ipcon.disconnect()
        except:
            pass

    def do_authenticate(self, is_auto_reconnect):
        if not self.checkbox_authentication.isChecked():
            return True

        try:
            secret = self.edit_secret.text().encode('ascii')
        except:
            self.do_disconnect()

            QMessageBox.critical(self, 'Connection',
                                 'Authentication secret cannot contain non-ASCII characters.',
                                 QMessageBox.Ok)
            return False

        self.ipcon.set_auto_reconnect(False) # don't auto-reconnect on authentication error

        try:
            self.ipcon.authenticate(secret)
        except:
            self.do_disconnect()

            if is_auto_reconnect:
                extra = ' after auto-reconnect'
            else:
                extra = ''

            QMessageBox.critical(self, 'Connection',
                                 'Could not authenticate' + extra + '. Check secret and ensure ' +
                                 'authentication for Brick Daemon is enabled.',
                                 QMessageBox.Ok)
            return False

        self.ipcon.set_auto_reconnect(True)

        return True

    def flashing_clicked(self):
        if self.flashing_window is None:
            self.flashing_window = FlashingWindow(self)

        self.flashing_window.show()
        self.flashing_window.refresh_updates_clicked()

    def advanced_clicked(self):
        if self.advanced_window is None:
            self.advanced_window = AdvancedWindow(self)

        self.advanced_window.show()

    def connect_clicked(self):
        if self.ipcon.get_connection_state() == IPConnection.CONNECTION_STATE_DISCONNECTED:
            try:
                self.last_host = self.combo_host.currentText()
                self.button_connect.setDisabled(True)
                self.button_connect.setText("Connecting ...")
                self.button_connect.repaint()
                QApplication.processEvents()
                self.ipcon.connect(self.last_host, self.spinbox_port.value())
            except:
                self.button_connect.setDisabled(False)
                self.button_connect.setText("Connect")
                QMessageBox.critical(self, 'Connection',
                                     'Could not connect. Please check host, check ' +
                                     'port and ensure that Brick Daemon is running.')
        else:
            self.do_disconnect()

    def item_double_clicked(self, index):
        uid_index = index.sibling(index.row(), 1)

        if uid_index.isValid():
            uid_text = uid_index.data()
            self.show_plugin(uid_text)

    def create_tab_window(self, device_info, connected_uid, position):
        tab_window = TabWindow(self.tab_widget, device_info.name, self.untab)
        tab_window._info = device_info
        tab_window.set_callback_on_tab(lambda index:
            self.ipcon.get_connection_state() == IPConnection.CONNECTION_STATE_PENDING and \
                self.tab_widget.setTabEnabled(index, False))

        layout = QVBoxLayout(tab_window)
        info_bar = QHBoxLayout()

        # uid
        info_bar.addWidget(QLabel('UID:'))

        label = QLabel('{0}'.format(device_info.uid))
        label.setTextInteractionFlags(Qt.TextSelectableByMouse |
                                      Qt.TextSelectableByKeyboard)

        info_bar.addWidget(label)
        info_bar.addSpacerItem(QSpacerItem(1, 1, QSizePolicy.Expanding))

        # connected uid
        if connected_uid != '0':
            info_bar.addWidget(QLabel('Connected to:'))

            button = QToolButton()
            button.setText(connected_uid)
            button.clicked.connect(lambda: self.show_plugin(connected_uid))

            info_bar.addWidget(button)
            info_bar.addSpacerItem(QSpacerItem(1, 1, QSizePolicy.Expanding))

        # position
        info_bar.addWidget(QLabel('Position:'))
        info_bar.addWidget(QLabel('{0}'.format(position.upper())))

        info_bar.addSpacerItem(QSpacerItem(1, 1, QSizePolicy.Expanding))

        # firmware version
        label_version_name = QLabel('Version:')
        label_version = QLabel('...')

        if not device_info.plugin.has_custom_version(label_version_name, label_version):
            label_version_name.setText('FW Version:')
            label_version.setText(infos.get_version_string(device_info.plugin.firmware_version))

        info_bar.addWidget(label_version_name)
        info_bar.addWidget(label_version)

        info_bar.addSpacerItem(QSpacerItem(1, 1, QSizePolicy.Expanding))

        # timeouts
        info_bar.addWidget(QLabel('Timeouts:'))
        label_timeouts = QLabel('0')
        info_bar.addWidget(label_timeouts)

        layout.addLayout(info_bar)

        # actions
        actions = device_info.plugin.get_actions()

        if actions != None:
            if type(actions) == QAction:
                button = QPushButton(actions.text())
                button.clicked.connect(actions.trigger)
            else:
                button = QToolButton()
                button.setText(actions[0])
                button.setPopupMode(QToolButton.InstantPopup)
                button.setToolButtonStyle(Qt.ToolButtonTextOnly)
                button.setArrowType(Qt.DownArrow)
                button.setAutoRaise(True)

                menu = QMenu(actions[0])
                button.setMenu(menu)

                for action in actions[1]:
                    menu.addAction(action)

            info_bar.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Expanding))
            info_bar.addWidget(button)

        line = QFrame()
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)

        device_info.plugin.label_timeouts = label_timeouts
        device_info.plugin.layout().setContentsMargins(0, 0, 0, 0)

        layout.addWidget(line)
        layout.addWidget(device_info.plugin)

        return tab_window

    def tab_move(self, event):
        # visualize rearranging of tabs (if allowed by tab_widget)
        if self.tab_widget.isMovable():
            if event.type() == QEvent.MouseButtonPress and event.button() & Qt.LeftButton:
                QApplication.setOverrideCursor(QCursor(Qt.SizeHorCursor))

            elif event.type() == QEvent.MouseButtonRelease and event.button() & Qt.LeftButton:
                QApplication.restoreOverrideCursor()

        return False

    def untab(self, tab_index):
        tab = self.tab_widget.widget(tab_index)
        tab.untab()
        tab._info.plugin.start_plugin()
        self.tab_widget.setCurrentIndex(0)

    def eventFilter(self, source, event):
        if source is self.tab_widget.tabBar():
            return self.tab_move(event)

        return False

    def tab_for_uid(self, uid):
        for i in range(1, self.tab_widget.count()):
            try:
                if self.tab_widget.widget(i)._info.uid == uid:
                    return i
            except:
                pass

        return -1

    def show_plugin(self, uid):
        i = self.tab_for_uid(uid)
        tab_window = infos.get_info(uid).tab_window

        if i > 0 and self.tab_widget.isTabEnabled(i):
            self.tab_widget.setCurrentIndex(i)

        QApplication.setActiveWindow(tab_window)

        tab_window.show()
        tab_window.activateWindow()
        tab_window.raise_()

    def cb_enumerate(self, uid, connected_uid, position,
                     hardware_version, firmware_version,
                     device_identifier, enumeration_type):
        if self.ipcon.get_connection_state() != IPConnection.CONNECTION_STATE_CONNECTED:
            # ignore enumerate callbacks that arrived after the connection got closed
            return

        if enumeration_type in [IPConnection.ENUMERATION_TYPE_AVAILABLE,
                                IPConnection.ENUMERATION_TYPE_CONNECTED]:
            device_info = infos.get_info(uid)
            something_changed_ref = [False]

            if device_info == None:
                if device_identifier == BrickMaster.DEVICE_IDENTIFIER:
                    device_info = infos.BrickMasterInfo()
                elif device_identifier == BrickRED.DEVICE_IDENTIFIER:
                    device_info = infos.BrickREDInfo()
                elif position in ('a', 'b', 'c', 'd', 'A', 'B', 'C', 'D'):
                    position = position.lower()
                    device_info = infos.BrickletInfo()
                else:
                    device_info = infos.BrickInfo()
                    something_changed_ref[0] = True

            def set_device_info_value(name, value):
                if getattr(device_info, name) != value:
                    setattr(device_info, name, value)
                    something_changed_ref[0] = True

            set_device_info_value('uid', uid)
            set_device_info_value('connected_uid', connected_uid)
            set_device_info_value('position', position)
            set_device_info_value('hardware_version', hardware_version)
            set_device_info_value('firmware_version_installed', firmware_version)
            set_device_info_value('device_identifier', device_identifier)
            set_device_info_value('protocol_version', 2)
            set_device_info_value('enumeration_type', enumeration_type)

            if device_info.type == 'bricklet':
                for brick_info in infos.get_brick_infos():
                    if brick_info.uid == device_info.connected_uid:
                        if brick_info.bricklets[position] != device_info:
                            brick_info.bricklets[position] = device_info
                            something_changed_ref[0] = True
            elif device_info.type == 'brick':
                for bricklet_info in infos.get_bricklet_infos():
                    if bricklet_info.connected_uid == device_info.uid:
                        if device_info.bricklets[bricklet_info.position] != bricklet_info:
                            device_info.bricklets[bricklet_info.position] = bricklet_info
                            something_changed_ref[0] = True

            if device_info.plugin == None:
                plugin = self.plugin_manager.get_plugin(device_identifier, self.ipcon,
                                                        uid, hardware_version, firmware_version)

                device_info.plugin = plugin
                device_info.name = plugin.name
                device_info.url_part = plugin.get_url_part()

                infos.add_info(device_info)

                device_info.tab_window = self.create_tab_window(device_info, connected_uid, position)
                device_info.tab_window.setWindowFlags(Qt.Widget)
                device_info.tab_window.tab()

                something_changed_ref[0] = True

            if something_changed_ref[0]:
                self.update_tree_view()
        elif enumeration_type == IPConnection.ENUMERATION_TYPE_DISCONNECTED:
            for device_info in infos.get_device_infos():
                if device_info.uid == uid:
                    self.tab_widget.setCurrentIndex(0)
                    self.remove_device_info(device_info.uid)

                if device_info.type == 'brick':
                    for port in device_info.bricklets:
                        if device_info.bricklets[port] and device_info.bricklets[port].uid == uid:
                            device_info.bricklets[port] = None

            self.update_tree_view()

    def hack_to_remove_red_brick_tab(self, red_brick_uid):
        for device_info in infos.get_device_infos():
            if device_info.uid == red_brick_uid:
                self.tab_widget.setCurrentIndex(0)
                self.remove_device_info(device_info.uid)

                self.red_session_losts += 1
                self.label_red_session_losts.setText('RED Brick Session Loss Count: {0}'.format(self.red_session_losts))
                self.label_red_session_losts.show()

                break

        self.update_tree_view()

    def cb_connected(self, connect_reason):
        self.disconnect_times = []

        self.update_ui_state()

        if connect_reason == IPConnection.CONNECT_REASON_REQUEST:
            self.auto_reconnects = 0
            self.label_auto_reconnects.hide()

            self.red_session_losts = 0
            self.label_red_session_losts.hide()

            self.ipcon.set_auto_reconnect(True)

            index = self.combo_host.findText(self.last_host)

            if index >= 0:
                self.combo_host.removeItem(index)

                host_info = self.host_infos[index]

                del self.host_infos[index]
                self.host_infos.insert(0, host_info)
            else:
                index = self.combo_host.currentIndex()

                host_info = self.host_infos[index].duplicate()
                host_info.host = self.last_host

                self.host_infos.insert(0, host_info)

            self.combo_host.insertItem(-1, self.last_host)
            self.combo_host.setCurrentIndex(0)

            while self.combo_host.count() > config.HOST_INFO_COUNT:
                self.combo_host.removeItem(self.combo_host.count() - 1)

            if not self.do_authenticate(False):
                return

            try:
                self.ipcon.enumerate()
            except:
                self.update_ui_state()
        elif connect_reason == IPConnection.CONNECT_REASON_AUTO_RECONNECT:
            self.auto_reconnects += 1
            self.label_auto_reconnects.setText('Auto-Reconnect Count: {0}'.format(self.auto_reconnects))
            self.label_auto_reconnects.show()

            if not self.do_authenticate(True):
                return

            try:
                self.ipcon.enumerate()
            except:
                self.update_ui_state()
        else:
            try:
                self.ipcon.enumerate()
            except:
                self.update_ui_state()

    def cb_disconnected(self, disconnect_reason):
        if disconnect_reason == IPConnection.DISCONNECT_REASON_REQUEST:
            self.auto_reconnects = 0
            self.label_auto_reconnects.hide()

            self.red_session_losts = 0
            self.label_red_session_losts.hide()

        if disconnect_reason == IPConnection.DISCONNECT_REASON_REQUEST or not self.ipcon.get_auto_reconnect():
            self.update_ui_state()
        elif len(self.disconnect_times) >= 3 and self.disconnect_times[-3] < time.time() + 1:
            self.disconnect_times = []
            self.ipcon.set_auto_reconnect(False)
            self.update_ui_state()
            self.reset_view()

            QMessageBox.critical(self, 'Connection',
                                 'Stopped automatic reconnecting due to multiple connection errors in a row.')
        else:
            self.disconnect_times.append(time.time())
            self.update_ui_state(IPConnection.CONNECTION_STATE_PENDING)

    def set_tree_view_defaults(self):
        self.tree_view_model.setHorizontalHeaderLabels(self.tree_view_model_labels)
        self.tree_view.expandAll()
        self.tree_view.setColumnWidth(0, 250)
        self.tree_view.setColumnWidth(1, 85)
        self.tree_view.setColumnWidth(2, 85)
        self.tree_view.setColumnWidth(3, 90)
        self.tree_view.setExpandsOnDoubleClick(False)
        self.tree_view.setSortingEnabled(True)
        self.tree_view.header().setSortIndicator(0, Qt.AscendingOrder)

    def update_ui_state(self, connection_state=None):
        # FIXME: need to call processEvents() otherwise get_connection_state()
        #        might return the wrong value
        QApplication.processEvents()

        if connection_state is None:
            connection_state = self.ipcon.get_connection_state()

        self.button_connect.setDisabled(False)
        self.button_flashing.setDisabled(False)

        if connection_state == IPConnection.CONNECTION_STATE_DISCONNECTED:
            self.button_connect.setText('Connect')
            self.combo_host.setDisabled(False)
            self.spinbox_port.setDisabled(False)
            self.checkbox_authentication.setDisabled(False)
            self.edit_secret.setDisabled(False)
            self.button_advanced.setDisabled(True)
        elif connection_state == IPConnection.CONNECTION_STATE_CONNECTED:
            self.button_connect.setText("Disconnect")
            self.combo_host.setDisabled(True)
            self.spinbox_port.setDisabled(True)
            self.checkbox_authentication.setDisabled(True)
            self.edit_secret.setDisabled(True)
            self.update_advanced_window()

            # restart all pause plugins
            for info in infos.get_device_infos():
                info.plugin.resume_plugin()
        elif connection_state == IPConnection.CONNECTION_STATE_PENDING:
            self.button_connect.setText('Abort Pending Automatic Reconnect')
            self.combo_host.setDisabled(True)
            self.spinbox_port.setDisabled(True)
            self.checkbox_authentication.setDisabled(True)
            self.edit_secret.setDisabled(True)
            self.button_advanced.setDisabled(True)
            self.button_flashing.setDisabled(True)

            # pause all running plugins
            for info in infos.get_device_infos():
                info.plugin.pause_plugin()

        enable = connection_state == IPConnection.CONNECTION_STATE_CONNECTED

        for i in range(1, self.tab_widget.count()):
            self.tab_widget.setTabEnabled(i, enable)

        for device_info in infos.get_device_infos():
            device_info.tab_window.setEnabled(enable)

        QApplication.processEvents()

    def update_tree_view(self):
        self.tree_view_model.clear()

        for info in infos.get_brick_infos():
            parent = [QStandardItem(info.name),
                      QStandardItem(info.uid),
                      QStandardItem(info.position.upper()),
                      QStandardItem('.'.join(map(str, info.firmware_version_installed)))]

            for item in parent:
                item.setFlags(item.flags() & ~Qt.ItemIsEditable)

            self.tree_view_model.appendRow(parent)

            for port in sorted(info.bricklets):
                if info.bricklets[port] and info.bricklets[port].protocol_version == 2:
                    child = [QStandardItem(port.upper() + ': ' + info.bricklets[port].name),
                             QStandardItem(info.bricklets[port].uid),
                             QStandardItem(info.bricklets[port].position.upper()),
                             QStandardItem('.'.join(map(str, info.bricklets[port].firmware_version_installed)))]
                    for item in child:
                        item.setFlags(item.flags() & ~Qt.ItemIsEditable)
                    parent[0].appendRow(child)

        self.set_tree_view_defaults()
        self.update_advanced_window()
        self.delayed_refresh_updates_timer.start()

    def update_advanced_window(self):
        self.button_advanced.setEnabled(len(infos.get_brick_infos()) > 0)

    def delayed_refresh_updates(self):
        self.delayed_refresh_updates_timer.stop()

        if self.flashing_window is not None and self.flashing_window.isVisible():
            self.flashing_window.refresh_updates_clicked()
Exemple #40
0
class EkdSaveDialog(QDialog):
    '''
    EkdSaveDialog : Classe représentant la boite de dialogue utiliser lors de
                    l'enregistrement des modification sur un fichier donné
        attributs : suffix   - Suffix utilisé en filtre
                    filter   - Filtre (déduit à partir du suffix)
                    chemin   - Chemin du dernier fichier enregistré
                    multiple - va-t-on enregistrer plus d'un fichier ?
                                (ex: extraction d'image d'une vidéo)
        méthodes  : getFile - Invoque la boite de dialogue et retourne
                    le fichier saisi
    '''
    ### Pourquoi avoir réimplémenté cette classe au lieu de passer par
    ### QFileDialog ?
    ## Correction du bug de consommation mémoire
    ##
    ##  Explication du problème :
    ##   Par défaut un QFileDialog utilise un QFileSystemModel qui lui
    ##   crée un QWatchFileSystem
    ##   Hors QWatchFileSystem scan régulièrement les changement
    ##   dans le répertoire courant
    ##   Ce phénomène provoque une réaction en chaine :
    ##     1 - je choisi mon répertoire de destination d'enregistrement
    ##         de mes images
    ##     2 - ffmpeg se lance
    ##     3 - ffmpeg crée un fichier dans l'arborescence
    ##     4 - QWatchFileSystem (est toujours dans le répertoire courant)
    ##         détecte un changement
    ##         et recharge l'ensemble du contenue du répertoire
    ##     5 - goto 3 jusqu'à plus de mémoire ou fin du process ffmpeg
    ##
    ##

    def __init__(self, parent, path = None, suffix = '', title = u"Sauver",
                                            multiple = False, mode = None):

        if type(suffix) == tuple or type(suffix) == list :
            sfilter=""
            for s in suffix :
                sfilter += "*"+s+" "
            self.filter=sfilter[:-1]
            # Si on a plusieur suffix, on prend le premier par défaut pour
            # la sauvegarde
            self.suffix = suffix[0]
        else :
            self.suffix = suffix
            self.filter = "*" + self.suffix

        QDialog.__init__(self, parent)

        self.setWindowTitle(title)
        self.multiple = multiple
        self.mode = mode

        if not path:
            if self.mode == "image" :
                path = EkdConfig.get("general", "image_output_path")
            elif self.mode == "video" :
                path = EkdConfig.get("general", "video_output_path")
            elif self.mode == "audio" :
                path = EkdConfig.get("general", "sound_output_path")
            else :
                path = unicode(QDir.homePath())

        # Nom du répertoire courant
        self.location = QLabel("<b>%s</b>" % path)
        # Variable permettant de savoir à tout moment le répertoire courant.
        self.currentDir = path
        self.mkdirButton = QPushButton(u"  Créer un répertoire")
        self.mkdirButton.setIcon(QIcon("Icones" + os.sep + "add_sub_task.png"))
        if int(EkdConfig.get("general", "show_hidden_files")) :
            #print "hidden shown"
	    EkdPrint(u"hidden shown")
            shf = QDir.Hidden
        else : shf = QDir.Readable
        # Liste des fichiers
        self.dirList = QListView()
        sorting = QDir.DirsFirst
        if int(EkdConfig.get("general", "ignore_case")):
            sorting |= QDir.IgnoreCase
        self.sorting = sorting
        self.flags = QDir.Files | QDir.Readable | shf
        self.dirModel = QStandardItemModel()
        self.dirList.setModel(self.dirModel)
        self.updateDir(path)
        self.dirList.setWrapping(True)

        #panneau latéral
        self.dirTree = QTreeView()
        self.dirModelLight = QDirModel(QStringList(""), QDir.AllDirs |
                                    QDir.NoDotAndDotDot | shf, QDir.DirsFirst |
                                    QDir.Name | QDir.IgnoreCase)
        self.dirTree.setModel(self.dirModelLight)
        self.dirTree.setColumnHidden(1,True)
        self.dirTree.setColumnHidden(2,True)
        self.dirTree.setColumnHidden(3,True)
        self.dirTree.setMaximumWidth(200)
        self.dirTree.setMinimumWidth(150)
        self.dirTree.setCurrentIndex(self.dirModelLight.index(path))
        self.dirTree.resizeColumnToContents(0)
        self.connect(self.dirTree, SIGNAL("pressed(QModelIndex)"),
                                                    self.updateLatDir)
        self.connect(self.dirTree, SIGNAL("expanded(QModelIndex)"),
                                                        self.treeMAJ)
        self.connect(self.dirTree, SIGNAL("collapsed(QModelIndex)"),
                                                        self.treeMAJ)

        # Nom du fichier
        self.fileField = QLineEdit()

        # Nom du filtre
        self.filterField = QComboBox()
        self.filterField.addItems(QStringList(self.filter))

        # Bouton de sauvegarde et d'annulation
        self.saveButton = QPushButton(_(u"  Enregistrer"))
        self.saveButton.setIcon(QIcon("Icones" + os.sep + "action.png"))
        self.cancelButton = QPushButton(_(u"  Annuler"))
        self.cancelButton.setIcon(QIcon("Icones" + os.sep + "annuler.png"))

        # Organisation des différents objets de la boite de dialogue
        self.layout = QHBoxLayout(self)
        self.layout.addWidget(self.dirTree)
        self.filelinelayout = QGridLayout()
        self.filelinelayout.addWidget(self.location, 0, 0, 1, 0)
        self.filelinelayout.addWidget(self.mkdirButton, 0, 2)
        self.filelinelayout.addWidget(self.dirList, 1, 0, 1, 0)
        self.filelinelayout.addWidget(QLabel(_("Nom de fichier : ")), 2, 0)
        self.filelinelayout.addWidget(self.fileField, 2, 1)
        self.filelinelayout.addWidget(self.saveButton, 2, 2)
        self.filelinelayout.addWidget(QLabel(_("Filtre extension : ")), 3, 0)
        self.filelinelayout.addWidget(self.filterField, 3, 1)
        self.filelinelayout.addWidget(self.cancelButton, 3, 2)

        self.layout.addLayout(self.filelinelayout)

        # Connexion des différents objets
        self.connect(self.dirList, SIGNAL("clicked(QModelIndex)"),
                                                        self.updateFile)
        self.connect(self.saveButton, SIGNAL("clicked()"), self.accept)
        self.connect(self.cancelButton, SIGNAL("clicked()"), self.reject)
        self.connect(self.mkdirButton, SIGNAL("clicked()"), self.mkdir)
        self.connect(self.dirList,
                    SIGNAL("indexesMoved (const QModelIndexList&)"),
                    self.updateFile)
        self.connect(self.fileField, SIGNAL("textChanged (const QString&)"),
                    self.activate)
        self.connect(self.fileField, SIGNAL("returnPressed()"), self.accept)

        # Taille minimum
        self.setMinimumSize(700, 480)

        # Par défaut, on désactive
        self.deactivate()

        # Completion des fichiers
        self.completion = QCompleter(self.dirModel, self.dirList)

    def updateLatDir(self, item) :
        """ Fonction permettant de naviguer dans la listes des répertoires """
        self.updateDir(self.dirModelLight.filePath(item))

    def treeMAJ(self, item) :
        self.dirTree.resizeColumnToContents(0)

    def activate(self, filename=None):
        """ Activation des boutton de sauvegarde """
        self.dirList.clearSelection()
        if filename != "":
            self.saveButton.setEnabled(True)
        else:
            self.saveButton.setEnabled(False)

    def deactivate(self):
        """ Désactivation des boutton de sauvegarde """
        self.saveButton.setEnabled(False)

    def updateDir(self, path = None):
        """ Fonction permettant de naviguer dans la listes des répertoires """

        if path :
            self.currentDir = path
            self.location.setText("<b>%s</b>" % path)
        self.dirModel.clear()
        self.tmpdir = QDir()
        self.tmpdir.setPath(self.currentDir)
        self.tmpdir.setNameFilters(QStringList(self.filter))

        # Une icône pour les images, un autre icône pour les vidéos, et
        # une pour audio
        if self.mode == "image" :
            icone = QIcon("Icones" + os.sep + "image_image.png")
        elif self.mode == "video" :
            icone = QIcon("Icones" + os.sep + "image_video.png")
        elif self.mode == "audio" :
            icone = QIcon("Icones" + os.sep + "image_audio.png")
        else:
            icone = QIcon("Icones" + os.sep + "image_default.png")

        for wlfile in self.tmpdir.entryList(QDir.Files):
            if self.mode == "image" :
                icone = QIcon(EkdPreview("Icones" + os.sep + "image_default.png").get_preview())
            item = QStandardItem(icone, QString(wlfile))
            item.setToolTip(wlfile)
            item.setData(QVariant(self.tmpdir.absolutePath() + os.sep + wlfile), Qt.UserRole + 2)
            item.setData(QVariant(wlfile), Qt.UserRole + 3)
            self.dirModel.appendRow(item)


    def updateFile(self, item):
        """ Fonction appelée par la listView lors d'un changement de repertoire"""
        # On récupère le QModel parent du QModelItem
        path = "%s" % item.data(Qt.UserRole + 2).toString()
        name = os.path.basename(path)
        self.fileField.selectAll()
        self.fileField.setFocus()
        self.fileField.setText(name)
        self.activate(name)

    def selectedFile(self):
        """ Renvoi le fichier selectionné pour la sauvegarde"""
        # Récupération du fichier selectionné
        fichier = self.fileField.text()
        output = "%s%s%s" % (self.currentDir, os.sep, fichier)
        info = QFileInfo(output)
        # Si l'utilisateur n'a pas spécifié de suffix, on l'ajoute
        if info.suffix() != self.suffix[1:]:
            output = "%s%s" % (output, self.suffix)
        return output

    def mkdir(self):
        """ Crée un répertoire dans le répertoire courant """
        (dirname, ok) = QInputDialog.getText(self, _(u"Nouveau répertoire"), _(u"Nom du répertoire"), QLineEdit.Normal, _(u"Nouveau répertoire"))
        if ok :
            try :
                os.mkdir("%s%s%s" % (self.currentDir, os.sep,dirname))
                #print u"Création de : %s%s%s" % (self.currentDir, os.sep, dirname)
		EkdPrint(u"Création de : %s%s%s" % (self.currentDir, os.sep, dirname))
                self.updateDir()
                self.dirModelLight.refresh()
            except Exception, e:
                #print _(u"Impossible de crée un nouveau répertoire : %s" % e)
		EkdPrint(_(u"Impossible de crée un nouveau répertoire : %s" % e))
Exemple #41
0
class STRPartyListView(QListView):
    """
    A widget for listing and selecting STR party entities.
    .. versionadded:: 1.5
    """
    party_selected = pyqtSignal(QStandardItem)
    party_deselected = pyqtSignal(QStandardItem)

    def __init__(self, parent=None, profile=None, social_tenure=None):
        super(STRPartyListView, self).__init__(parent)

        self._model = QStandardItemModel(self)
        self._model.setColumnCount(1)
        self.setModel(self._model)
        self.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self._model.itemChanged.connect(self._on_item_changed)

        self._profile = profile
        if not self._profile is None:
            self._load_profile_entities()

        self._social_tenure = social_tenure
        if not self._social_tenure is None:
            self.select_parties(self._social_tenure.parties)

    def _on_item_changed(self, item):
        # Emit signals when an item has been (de)selected.
        if item.checkState() == Qt.Checked:
            self.party_selected.emit(item)
        elif item.checkState() == Qt.Unchecked:
            self.party_deselected.emit(item)

    @property
    def profile(self):
        """
        :return: Returns the current profile object in the configuration.
        :rtype: Profile
        """
        return self._profile

    @profile.setter
    def profile(self, profile):
        """
        Sets the current profile object in the configuration.
        :param profile: Profile object.
        :type profile: Profile
        """
        self._profile = profile
        self._load_profile_entities()

    @property
    def social_tenure(self):
        """
        :return: Returns the profile's social tenure entity.
        :rtype: SocialTenure
        """
        return self._social_tenure

    @social_tenure.setter
    def social_tenure(self, social_tenure):
        """
        Set the social_tenure entity.
        :param social_tenure: A profile's social tenure entity.
        :type social_tenure: SocialTenure
        """
        self._social_tenure = social_tenure
        self.select_parties(self._social_tenure.parties)

    def _load_profile_entities(self):
        # Reset view
        self.clear()

        # Populate entity items in the view
        for e in self._profile.user_entities():
            self._add_entity(e)

    def _add_entity(self, party):
        # Add entity item to view
        item = QStandardItem(QIcon(':/plugins/stdm/images/icons/table.png'),
                             party.short_name)
        item.setCheckable(True)
        item.setCheckState(Qt.Unchecked)

        self._model.appendRow(item)

    def select_parties(self, parties):
        """
        Checks party entities in the view and emit the party_selected
        signal for each item selected.
        :param parties: Collection of STR party entities.
        :type parties: list
        """
        # Clear selection
        self.clear_selection()

        for p in parties:
            name = p.short_name
            self.select_party(name)

    def parties(self):
        """
        :return: Returns a list of selected party names.
        :rtype: list
        """
        selected_items = []

        for i in range(self._model.rowCount()):
            item = self._model.item(i)
            if item.checkState() == Qt.Checked:
                selected_items.append(item.text())

        return selected_items

    def clear(self):
        """
        Remove all party items in the view.
        """
        self._model.clear()
        self._model.setColumnCount(1)

    def clear_selection(self):
        """
        Uncheck all items in the view.
        """
        for i in range(self._model.rowCount()):
            item = self._model.item(i)
            if item.checkState() == Qt.Checked:
                item.setCheckState(Qt.Unchecked)

    def select_party(self, name):
        """
        Selects a party entity with the given short name.
        :param name: Entity short name
        :type name: str
        """
        items = self._model.findItems(name)
        if len(items) > 0:
            item = items[0]
            if item.checkState() == Qt.Unchecked:
                item.setCheckState(Qt.Checked)

    def deselect_party(self, name):
        """
        Deselects a party entity with the given short name.
        :param name: Entity short name
        :type name: str
        """
        items = self._model.findItems(name)
        if len(items) > 0:
            item = items[0]
            if item.checkState() == Qt.Checked:
                item.setCheckState(Qt.Unchecked)
Exemple #42
0
    class VizRank(OWWidget):
        name = "Rank projections (Scatter Plot)"

        want_control_area = False

        def __init__(self, parent_widget):
            super().__init__()
            self.parent_widget = parent_widget
            self.running = False
            self.progress = None
            self.k = 10

            self.projectionTable = QTableView()
            self.mainArea.layout().addWidget(self.projectionTable)
            self.projectionTable.setSelectionBehavior(QTableView.SelectRows)
            self.projectionTable.setSelectionMode(QTableView.SingleSelection)
            self.projectionTable.setSortingEnabled(True)
            self.projectionTableModel = QStandardItemModel(self)
            self.projectionTable.setModel(self.projectionTableModel)
            self.projectionTable.selectionModel().selectionChanged.connect(
                self.on_selection_changed)

            self.button = gui.button(self.mainArea,
                                     self,
                                     "Start evaluation",
                                     callback=self.toggle,
                                     default=True)
            self.resize(380, 512)
            self._initialize()

        def _initialize(self):
            self.running = False
            self.projectionTableModel.clear()
            self.projectionTableModel.setHorizontalHeaderLabels(
                ["Score", "Feature 1", "Feature 2"])
            self.projectionTable.setColumnWidth(0, 60)
            self.projectionTable.setColumnWidth(1, 120)
            self.projectionTable.setColumnWidth(2, 120)
            self.button.setText("Start evaluation")
            self.button.setEnabled(False)
            self.pause = False
            self.data = None
            self.attrs = []
            self.scores = []
            self.i, self.j = 0, 0
            if self.progress:
                self.progress.finish()
            self.progress = None

            self.information(0)
            if self.parent_widget.data:
                if not self.parent_widget.data.domain.class_var:
                    self.information(
                        0, "Data with a class variable is required.")
                    return
                if len(self.parent_widget.data.domain.attributes) < 2:
                    self.information(0,
                                     'At least 2 unique features are needed.')
                    return
                if len(self.parent_widget.data) < 2:
                    self.information(0, 'At least 2 instances are needed.')
                    return
                self.button.setEnabled(True)

        def on_selection_changed(self, selected, deselected):
            """Called when the ranks view selection changes."""
            a1 = selected.indexes()[1].data()
            a2 = selected.indexes()[2].data()
            self.parent_widget.update_attr(attributes=(a1, a2))

        def toggle(self):
            self.running ^= 1
            if self.running:
                self.button.setText("Pause")
                self.run()
            else:
                self.button.setText("Continue")
                self.button.setEnabled(False)

        def run(self):
            graph = self.parent_widget.graph
            y_full = self.parent_widget.data.Y
            if not self.attrs:
                self.attrs = self.score_heuristic()
            if not self.progress:
                self.progress = gui.ProgressBar(
                    self,
                    len(self.attrs) * (len(self.attrs) - 1) / 2)
            for i in range(self.i, len(self.attrs)):
                ind1 = graph.attribute_name_index[self.attrs[i]]
                for j in range(self.j, i):
                    if not self.running:
                        self.i, self.j = i, j
                        if not self.projectionTable.selectedIndexes():
                            self.projectionTable.selectRow(0)
                        self.button.setEnabled(True)
                        return
                    ind2 = graph.attribute_name_index[self.attrs[j]]
                    X = graph.scaled_data[[ind1, ind2], :]
                    valid = graph.get_valid_list([ind1, ind2])
                    X = X[:, valid].T
                    if X.shape[0] < self.k:
                        self.progress.advance()
                        continue
                    y = y_full[valid]
                    n_neighbors = min(self.k, len(X) - 1)
                    knn = NearestNeighbors(n_neighbors=n_neighbors).fit(X)
                    ind = knn.kneighbors(return_distance=False)
                    if self.parent_widget.data.domain.has_discrete_class:
                        score = np.sum(y[ind] == y.reshape(-1, 1)) / (
                            len(y_full) * n_neighbors)
                    else:
                        score = r2_score(y, np.mean(
                            y[ind], axis=1)) * (len(y) / len(y_full))
                    pos = bisect_left(self.scores, score)
                    self.projectionTableModel.insertRow(
                        len(self.scores) - pos, [
                            QStandardItem("{:.4f}".format(score)),
                            QStandardItem(self.attrs[j]),
                            QStandardItem(self.attrs[i])
                        ])
                    self.scores.insert(pos, score)
                    self.progress.advance()
                self.j = 0
            self.progress.finish()
            if not self.projectionTable.selectedIndexes():
                self.projectionTable.selectRow(0)
            self.button.setText("Finished")
            self.button.setEnabled(False)

        def score_heuristic(self):
            X = self.parent_widget.graph.scaled_data.T
            Y = self.parent_widget.data.Y
            dom = Domain(
                [ContinuousVariable(str(i)) for i in range(X.shape[1])],
                self.parent_widget.data.domain.class_vars)
            data = Table(dom, X, Y)
            relief = ReliefF if isinstance(dom.class_var,
                                           DiscreteVariable) else RReliefF
            weights = relief(n_iterations=100, k_nearest=self.k)(data)
            attrs = sorted(zip(
                weights,
                (x.name for x in self.parent_widget.data.domain.attributes)),
                           reverse=True)
            return [a for _, a in attrs]
class dbmanagerUI(QMainWindow, ui_dbmanager.Ui_dbmanagerUI):
    """Main UI for MASAR database manager."""
    def __init__(self):
        """"""
        super(dbmanagerUI, self).__init__()
        self.setupUi(self)
        self.statusbar.showMessage("Ready")

        exitAction = QtGui.QAction(QtGui.QIcon('exit.png'), '&Exit', self)
        exitAction.setShortcut('Ctrl+Q')
        exitAction.setStatusTip('Exit Masar Configuration Manager.')
        exitAction.triggered.connect(QtGui.qApp.quit)

        self.groupdatabasemenubar()

        #default database source, could be 0: SQLite, 1: MongoDB, and 2: MySQL
        self.dbsource = None

        self.defaultdbinfo = self._loadmasarconfig()
        self.usedefaultdb = True

        self.comboxboxSignalMapper = QtCore.QSignalMapper(self)
        self.comboxboxSignalMapper.mapped[QtGui.QWidget].connect(self.comboboxSignalMapperMapped)

        self.pushbuttonSignalMapper = QtCore.QSignalMapper(self)
        self.pushbuttonSignalMapper.mapped[QtGui.QWidget].connect(self.pushbuttonSignalMapperMapped)

        self.showpvbuttonSignalMapper = QtCore.QSignalMapper(self)
        self.showpvbuttonSignalMapper.mapped[QtGui.QWidget].connect(self.showpvbuttonSignalMapperMapped)

        self.choosepvbuttonSignalMapper = QtCore.QSignalMapper(self)
        self.choosepvbuttonSignalMapper.mapped[QtGui.QWidget].connect(self.choosepvbuttonSignalMapperMapped)

        self.currentselectedrow4config = -1

        self.selectedsystem = "Others"

        # self.pvgrouptreeview = QTreeView()
        self.pvGroupTreeView.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.pvgroupmodel = QStandardItemModel()
        # self.pvgroupmodel.setHorizontalHeaderLabels(['id', 'date', 'version', "description"])
        self.pvgroupmodel.setHorizontalHeaderLabels(["PV Groups"])
        self.pvGroupTreeView.setModel(self.pvgroupmodel)
        self.pvGroupTreeView.setUniformRowHeights(True)

    @QtCore.pyqtSlot(QtGui.QWidget)
    def comboboxSignalMapperMapped(self, comboBox):
        if self.masarConfigTableWidget.cellWidget(comboBox.row, comboBox.column + 1).isEnabled():
            self.masarConfigTableWidget.cellWidget(comboBox.row, comboBox.column + 1).setEnabled(False)
            button = self.masarConfigTableWidget.cellWidget(comboBox.row, comboBox.column + 1)
            palette = QtGui.QPalette(button.palette())  # make a copy of the palette
            palette.setColor(QtGui.QPalette.ButtonText, QtGui.QColor('grey'))
            button.setPalette(palette)
        else:
            self.masarConfigTableWidget.cellWidget(comboBox.row, comboBox.column + 1).setEnabled(True)
            button = self.masarConfigTableWidget.cellWidget(comboBox.row, comboBox.column + 1)
            palette = QtGui.QPalette(button.palette())  # make a copy of the palette
            palette.setColor(QtGui.QPalette.ButtonText, QtGui.QColor('red'))
            button.setPalette(palette)

    @QtCore.pyqtSlot(QtGui.QWidget)
    def pushbuttonSignalMapperMapped(self, pushbutton):
        configstatus = str(self.masarConfigTableWidget.cellWidget(pushbutton.row, pushbutton.column-1).currentText())
        cid = str(self.masarConfigTableWidget.item(pushbutton.row, 0).text())
        cname = str(self.masarConfigTableWidget.item(pushbutton.row, 1).text())

        if self.updatemasarconfigstatus(configstatus, cid, configname=cname):
            palette = QtGui.QPalette(pushbutton.palette())  # make a copy of the palette
            palette.setColor(QtGui.QPalette.ButtonText, QtGui.QColor('grey'))
            pushbutton.setPalette(palette)
            pushbutton.setEnabled(False)

    @QtCore.pyqtSlot(QtGui.QWidget)
    def showpvbuttonSignalMapperMapped(self, showpvbutton):
        if self.newConfigTableWidget.item(showpvbutton.row, showpvbutton.column + 2) is None:
            raise RuntimeError("Unknown pv file")
        pvfilename = self.newConfigTableWidget.item(showpvbutton.row, showpvbutton.column + 2).text()
        if not os.path.isfile(pvfilename):
            raise RuntimeError("Invalid pv file name for (row, col): (%s, %s)" % (showpvbutton.row,
                                                                                  showpvbutton.column + 2))

        text = ", ".join(np.loadtxt(str(pvfilename), dtype=str, comments="#"))
        if self.newConfigTableWidget.item(showpvbutton.row, 0) is not None:
            head = self.newConfigTableWidget.item(showpvbutton.row, 0).text()
        else:
            head = ""
        msg = QMessageBox(self,
                          windowTitle='PVs for group %s' % head,
                          text="The following PVs to be added:")
        msg.setDetailedText(text)
        msg.exec_()

    @QtCore.pyqtSlot(QtGui.QWidget)
    def choosepvbuttonSignalMapperMapped(self, chhosepvbutton):
        self.newConfigTableWidget.setItem(chhosepvbutton.row,
                                          chhosepvbutton.column+1,
                                          QTableWidgetItem(QFileDialog.getOpenFileName(self, "Open File")))

    def _loadmasarconfig(self):
        cf = ConfigParser.SafeConfigParser()
        cf.read([
            os.path.expanduser('~/.masarservice.conf'),
            '/etc/masarservice.conf',
            'masarservice.conf',
            "%s/masarservice.conf" % os.path.abspath(os.path.dirname(__file__))
        ])
        return cf

    def groupdatabasemenubar(self):
        """Group 3 Databases menu together to make selection exclusive."""
        group = QtGui.QActionGroup(self)
        self.actionSQLite.setActionGroup(group)
        self.actionMongoDB.setActionGroup(group)
        self.actionMySQL.setActionGroup(group)

    def actionsqlitemenu(self):
        """Answer action when SQLite is selected."""
        if self.actionSQLite.isChecked():
            self.dbsource = 0
            self.defaultsqlitedb()
            self.listPvGroupPushButton.setEnabled(True)

    def actionmongodbmenu(self):
        """Answer action when MongoDB is selected."""
        if self.actionMongoDB.isChecked():
            self.dbsource = 1
            self.defaultmongodb()
            self.listPvGroupPushButton.setEnabled(False)

    def actionmysqlmenu(self):
        """Answer action when MySQL is selected."""
        if self.actionMySQL.isChecked():
            QMessageBox.warning(self, 'Warning', "MySQL support not implemented yet.")
            self.actionMySQL.setChecked(False)
            if self.dbsource == 0:
                self.actionSQLite.setChecked(True)
            elif self.dbsource == 1:
                self.actionMongoDB.setChecked(True)

    def showdefaultdbinfo(self):
        """"""
        if self.dbsource == 0:
            self.defaultsqlitedb()
        elif self.dbsource == 1:
            self.defaultmongodb()
        elif self.dbsource == 2:
            QMessageBox.warning(self, 'Warning', "MySQL support not implemented yet.")

    def defaultsqlitedb(self):
        """"""
        if self.defaultdbinfo.has_section("sqlite"):
            self.databaseDefault.setText(self.defaultdbinfo.get("sqlite", "database"))
            self.hostDefault.clear()
            self.portDefault.clear()
            self.userDefault.clear()

    def defaultmongodb(self):
        """"""
        if self.defaultdbinfo.has_section("mongodb"):
            self.databaseDefault.setText(self.defaultdbinfo.get("mongodb", "database"))
            self.hostDefault.setText(self.defaultdbinfo.get("mongodb", "host"))
            self.portDefault.setText(self.defaultdbinfo.get("mongodb", "port"))
            self.userDefault.setText(self.defaultdbinfo.get("mongodb", "username"))

    def getdatabasename(self):
        """"""
        self.database = self.databaseLineEdit.text()

    def getdatabaseport(self):
        """"""
        self.databaseport = self.databaseportLineEdit.text()

    def getdatabasehost(self):
        """"""
        self.databasehost = self.databaseHostLineEdit.text()

    def getdatabasepw(self):
        """"""
        self.databasepw = self.databasePwLineEdit.text()

    def showmasarconfigs(self):
        """"""
        result = None
        if self.dbsource == 0:
            # get data from sqlite
            if self.usedefaultdb:
                # masardb = str(self.databaseDefault.text())
                masardb = str(self.databaseDefault.toPlainText())
            else:
                masardb = str(self.databaseLineEdit.text())

            if masardb != "":
                os.environ["MASAR_SQLITE_DB"] = masardb
            else:
                raise RuntimeError("Cannot find MASAR SQLite Database")

            import pymasarsqlite
            conn = pymasarsqlite.utils.connect()
            result = pymasarsqlite.service.retrieveServiceConfigs(conn, servicename="masar")
            pymasarsqlite.utils.close(conn)

        elif self.dbsource == 1:
            # get data from mongodb
            if self.usedefaultdb:
                database = str(self.databaseDefault.toPlainText())
                host = str(self.hostDefault.toPlainText())
                port = str(self.portDefault.toPlainText())
            else:
                database = str(self.databaseLineEdit.text())
                host = str(self.databaseHostLineEdit.text())
                port = str(self.databasePortLineEdit.text())

            import pymasarmongo
            mongoconn, collection = pymasarmongo.db.utils.conn(host=host, port=port, db=database)
            resultdict = pymasarmongo.pymasarmongo.pymasar.retrieveconfig(mongoconn, collection)
            pymasarmongo.db.utils.close(mongoconn)

            result = [['id', 'name', 'desc', 'date', 'version', 'status']]
            for res in resultdict:
                result.append([res['configidx'],
                               res['name'],
                               res['desc'],
                               res['created_on'],
                               res['version'],
                               res['status']])
                               # res['system']

        if result is not None:
            self._setconfigtable(result)

    def choosedbsrc(self, bool):
        """Choose DB source"""
        if bool:
            self.usedefaultdb = True
        else:
            self.usedefaultdb = False

    def _setconfigtable(self, content):
        """"""
        # head = self.masarConfigTableWidget.horizontalHeader()
        self.masarConfigTableWidget.clearContents()
        # self.masarConfigTableWidget.setHorizontalHeaderLabels(head)

        if len(content) > 1:
            self.masarConfigTableWidget.setRowCount(len(content)-1)

            n = 0
            data = sorted(content[1:], key=itemgetter(0), reverse=True)
            for res in data:
                m = 0
                for item in res:
                    if not isinstance(item, basestring):
                        item = str(item)
                    if item:
                        if m == 5:
                            newitem = QtGui.QComboBox()
                            newitem.addItem("active")
                            newitem.addItem("inactive")
                            if item == "active":
                                newitem.setCurrentIndex(0)
                            else:
                                newitem.setCurrentIndex(1)

                            newitem.row = n
                            newitem.column = m
                            self.masarConfigTableWidget.setCellWidget(n, m, newitem)
                            self.comboxboxSignalMapper.setMapping(newitem, newitem)
                            newitem.currentIndexChanged.connect(self.comboxboxSignalMapper.map)

                            updatebutton = QtGui.QPushButton()
                            updatebutton.setText("Update")
                            updatebutton.setEnabled(False)
                            updatebutton.row = n
                            updatebutton.column = m+1
                            self.pushbuttonSignalMapper.setMapping(updatebutton, updatebutton)
                            self.masarConfigTableWidget.setCellWidget(n, m+1, updatebutton)
                            updatebutton.clicked.connect(self.pushbuttonSignalMapper.map)
                        else:
                            newitem = QTableWidgetItem(item)
                            newitem.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
                            self.masarConfigTableWidget.setItem(n, m, newitem)
                    m += 1
                n += 1

            self.masarConfigTableWidget.resizeColumnsToContents()

    def updatemasarconfigstatus(self, configstatus, cid, configname=None):
        if self.dbsource == 0:
            # get data from sqlite
            if self.usedefaultdb:
                masardb = str(self.databaseDefault.toPlainText())
            else:
                masardb = str(self.databaseLineEdit.text())

            if masardb != "":
                os.environ["MASAR_SQLITE_DB"] = masardb
            else:
                raise RuntimeError("Cannot find MASAR SQLite Database")

            import pymasarsqlite

            conn = pymasarsqlite.utils.connect()
            pymasarsqlite.service.updateServiceConfigStatus(conn, cid, status=configstatus)
            pymasarsqlite.utils.save(conn)
            pymasarsqlite.utils.close(conn)

        elif self.dbsource == 1:
            # get data from mongodb
            if self.usedefaultdb:
                database = str(self.databaseDefault.toPlainText())
                host = str(self.hostDefault.toPlainText())
                port = str(self.portDefault.toPlainText())
            else:
                database = str(self.databaseLineEdit.text())
                host = str(self.databaseHostLineEdit.text())
                port = str(self.databasePortLineEdit.text())

            import pymasarmongo

            mongoconn, collection = pymasarmongo.db.utils.conn(host=host, port=port, db=database)
            pymasarmongo.pymasarmongo.pymasar.updateconfig(mongoconn, collection, configname,
                                                           configidx=int(cid), status=configstatus)
            pymasarmongo.db.utils.close(mongoconn)

        return True

    def addnewpvgrouprow(self):
        """Add a new row to add pv group to MASAR configuration"""
        print ("""Add a new row to add pv group to MASAR configuration""")
        currowcount = self.newConfigTableWidget.rowCount()
        self.newConfigTableWidget.setRowCount(currowcount + 1)

        showpvbutton = QtGui.QPushButton()
        showpvbutton.setText("Show PVs")
        showpvbutton.setEnabled(True)
        showpvbutton.row = currowcount
        showpvbutton.column = 2
        self.showpvbuttonSignalMapper.setMapping(showpvbutton, showpvbutton)
        self.newConfigTableWidget.setCellWidget(currowcount, showpvbutton.column, showpvbutton)
        showpvbutton.clicked.connect(self.showpvbuttonSignalMapper.map)

        choosepvbutton = QtGui.QPushButton()
        choosepvbutton.setText("PV File")
        choosepvbutton.setEnabled(True)
        choosepvbutton.row = currowcount
        choosepvbutton.column = 3
        self.choosepvbuttonSignalMapper.setMapping(choosepvbutton, choosepvbutton)
        self.newConfigTableWidget.setCellWidget(currowcount, choosepvbutton.column, choosepvbutton)
        choosepvbutton.clicked.connect(self.choosepvbuttonSignalMapper.map)

    def removepvgrouprow(self):
        """Remove selected pv group from the configuration to be added into MASAR database"""
        if self.currentselectedrow4config == -1:
            raise RuntimeError("No pv group selected.")
        rownametobedelete = self.newConfigTableWidget.item(self.currentselectedrow4config, 0)
        if rownametobedelete is not None:
            rownametobedelete = rownametobedelete.text()
        self.newConfigTableWidget.removeRow(self.currentselectedrow4config)
        if rownametobedelete is not None:
            print ("Successfully delete pv group: ", rownametobedelete)
        else:
            print ("Successfully delete row: ", self.currentselectedrow4config)
        self.currentselectedrow4config = -1

    def savemasarsqlite(self):
        """"""
        # get data from sqlite
        if self.usedefaultdb:
            masardb = str(self.databaseDefault.toPlainText())
        else:
            masardb = str(self.databaseLineEdit.text())

        if masardb != "":
            os.environ["MASAR_SQLITE_DB"] = masardb
        else:
            QMessageBox.warning(self, "Warning",
                                "Cannot find MASAR SQLite Database")
            return

        import pymasarsqlite
        conn = pymasarsqlite.utils.connect()
        existedresult = pymasarsqlite.service.retrieveServiceConfigs(conn, servicename="masar")

        newcfgdata = self._getnewconfigurationdata(existedresult)
        if newcfgdata is None:
            QMessageBox.warning(self, "Warning",
                                "Not enough information for a new configuration.")
            return
        newcfgname = newcfgdata[0]
        desc = newcfgdata[1]
        msystem = newcfgdata[2]
        # config data format: [[name], [desc], [pv files]]
        cfgdata = newcfgdata[3]

        if newcfgname is None or msystem is None or newcfgdata is None:
            # Nothing to be added.
            QMessageBox.warning(self, "Warning",
                                "No name or system is given, or empty configuration data.")
            return

        for i in range(len(cfgdata[0])):
            if cfgdata[0][i] is None:
                QMessageBox.warning(self, "Warning", "Wrong PV group name")
                return

            if cfgdata[2][i] is not None and os.path.isfile(cfgdata[2][i]):
                pvs = list(np.loadtxt(cfgdata[2][i], dtype=str, comments="#"))
                if len(pvs) > 0:
                    for j, pv in enumerate(pvs):
                        pvs[j] = pv.strip()
                    pymasarsqlite.pvgroup.savePvGroup(conn, cfgdata[0][i], func=cfgdata[1][i])
                    pymasarsqlite.pvgroup.saveGroupPvs(conn, cfgdata[0][i], pvs)

        try:
            pymasarsqlite.service.saveServiceConfig(conn, "masar", newcfgname, configdesc=desc, system=msystem)
            pymasarsqlite.service.saveServicePvGroup(conn, newcfgname, cfgdata[0])
        except Exception as e:
            QMessageBox.warning(self, "Error", e.message)
            return

        pymasarsqlite.utils.save(conn)
        QMessageBox.information(self, "Congratulation", "A new configuration has been added successfully.")
        pymasarsqlite.utils.close(conn)

    def savemasarmongodb(self):
        """"""
        # get data from mongodb
        if self.usedefaultdb:
            database = str(self.databaseDefault.toPlainText())
            host = str(self.hostDefault.toPlainText())
            port = str(self.portDefault.toPlainText())
        else:
            database = str(self.databaseLineEdit.text())
            host = str(self.databaseHostLineEdit.text())
            port = str(self.databasePortLineEdit.text())

        import pymasarmongo

        mongoconn, collection = pymasarmongo.db.utils.conn(host=host, port=port, db=database)
        existedresult = pymasarmongo.pymasarmongo.pymasar.retrieveconfig(mongoconn, collection)
        existedcfg = []
        for res in existedresult:
            existedcfg.append([res['configidx'],
                               res['name'],
                               res['desc'],
                               res['created_on'],
                               res['version'],
                               res['status']])
        newcfgdata = self._getnewconfigurationdata(existedcfg)
        if newcfgdata is None:
            # Nothing to be added.
            raise ValueError("Empty configuration.")

        newcfgname = newcfgdata[0]
        desc = newcfgdata[1]
        msystem = newcfgdata[2]
        # config data format: [[name], [desc], [pv files]]
        cfgdata = newcfgdata[3]
        pvs = []
        for pvf in cfgdata[2]:
            if pvf is not None and os.path.isfile(pvf):
                pvs += list(np.loadtxt(pvf, dtype=str, comments="#"))
        if pvs:
            for i, pv in enumerate(pvs):
                pvs[i] = pv.strip()
            pymasarmongo.pymasarmongo.pymasar.saveconfig(mongoconn, collection, newcfgname,
                                                         desc=desc,
                                                         system=msystem,
                                                         pvlist={"names": pvs})
            QMessageBox.information(self, "Congratulation", "A new configuration has been added successfully.")
        else:
            QMessageBox.warning(self, "Warning", "No PVs available for the new configuration.")

        pymasarmongo.db.utils.close(mongoconn)

    def submitmasarconfig(self):
        """submit a new configuration to MASAR database"""
        if self.dbsource is None:
            QMessageBox.warning(self, "Warning",
                                "Unknown database source.\nPlease select which database should be use.")
            return
        if self.dbsource == 0:
            self.savemasarsqlite()
        elif self.dbsource == 1:
            self.savemasarmongodb()

    def _getnewconfigurationdata(self, existedresult):
        """"""
        newcfgname = self.newConfigurationLineEdit.text()
        if newcfgname is None or str(newcfgname) == "":
            QMessageBox.warning(self, "Warning",
                                "Name of configuration is empty.")
            return None
        elif str(newcfgname) in np.array(existedresult)[:, 1]:
            QMessageBox.warning(self, "Warning",
                                "Configuration (%s) exists already." % str(newcfgname))
            return None
        else:
            newcfgname = str(newcfgname)
        desc = self.newConfigurationDescription.text()
        if str(desc) == "":
            desc = None
        else:
            desc = str(desc)

        msystem = str(self.systemComboBox.currentText())
        if msystem == "Others":
            msystem = self.systemLineEdit.text()
            if msystem is None or str(msystem) == "":
                QMessageBox.warning(self, "Warning",
                                    "System for configuration (%s) not specified yet." % str(newcfgname))
                return None
            else:
                msystem = str(msystem)

        pvgroups = self.newConfigTableWidget.rowCount()
        if pvgroups == 0:
            QMessageBox.warning(self, "Warning",
                                "No PV founded for new configuration (%s)." % str(newcfgname))
            return None

        pvgroupnames = []
        pvgroupdescs = []
        pvgroupfiles = []
        for count in range(pvgroups):
            # Collect PV group name information
            if self.newConfigTableWidget.item(count, 0) is not None and str(self.newConfigTableWidget.item(count, 0).text()) != "":
                pvgname = str(self.newConfigTableWidget.item(count, 0).text())
                if pvgname in pvgroupnames:
                    QMessageBox.warning(self, "Warning",
                                        "Duplicated pv group name: %s." % str(pvgname))
                    return None
                pvgroupnames.append(pvgname)
            elif self.newConfigTableWidget.item(count, 4) is None or str(self.newConfigTableWidget.item(count, 4)) == "":
                continue
            elif self.dbsource == 1:
                pvgroupnames.append(None)
            else:
                QMessageBox.warning(self, "Warning",
                                    "Empty pv group name.")
                return None
                # reply = QMessageBox.question(self, "Message",
                #                              "pv group name not specified for row {}.\nContinue?".format(count),
                #                              QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
                # if reply == QMessageBox.Yes:
                #     pvgroupnames.append(None)
                # else:
                #     return None

            # Collection PV group descriptions
            if self.newConfigTableWidget.item(count, 1) is not None:
                pvgroupdescs.append(str(self.newConfigTableWidget.item(count, 1).text()))
            else:
                pvgroupdescs.append(None)

            # PV files for the pv group.
            if self.newConfigTableWidget.item(count, 4) is not None:
                pvgroupfiles.append(str(self.newConfigTableWidget.item(count, 4).text()))
            else:
                pvgroupfiles.append(None)

        if pvgroupfiles:
            return newcfgname, desc, msystem, [pvgroupnames, pvgroupdescs, pvgroupfiles]
        else:
            return None

    def currentselectedrow(self, row, col):
        """Cache current selected row in MASAR configuration Table Widget."""
        self.currentselectedrow4config = row

    def updateselectedsystem(self, system):
        """Update selected system if value changed"""
        self.selectedsystem = str(system)

    def updatesystemcombobox(self):
        """Update selected system if value changed"""
        self.systemComboBox.clear()
        if self.dbsource == 0:
            # get data from sqlite
            if self.usedefaultdb:
                # masardb = str(self.databaseDefault.text())
                masardb = str(self.databaseDefault.toPlainText())
            else:
                masardb = str(self.databaseLineEdit.text())

            if masardb != "":
                os.environ["MASAR_SQLITE_DB"] = masardb
            else:
                raise RuntimeError("Cannot find MASAR SQLite Database")

            import pymasarsqlite

            conn = pymasarsqlite.utils.connect()
            result = pymasarsqlite.service.retrieveServiceConfigProps(conn, propname="system", servicename="masar")
            index = 0
            if len(result) > 1:
                res = sorted(set(list(np.array(result[1:])[:, 3])))
                #for res in result[1:]:
                self.systemComboBox.addItems(res)
                index = len(res)
            self.systemComboBox.addItem("Others")
            self.systemComboBox.setCurrentIndex(index)
            pymasarsqlite.utils.close(conn)

        elif self.dbsource == 1:
            # get data from mongodb
            if self.usedefaultdb:
                database = str(self.databaseDefault.toPlainText())
                host = str(self.hostDefault.toPlainText())
                port = str(self.portDefault.toPlainText())
            else:
                database = str(self.databaseLineEdit.text())
                host = str(self.databaseHostLineEdit.text())
                port = str(self.databasePortLineEdit.text())

            import pymasarmongo

            mongoconn, collection = pymasarmongo.db.utils.conn(host=host, port=port, db=database)

            result = pymasarmongo.pymasarmongo.pymasar.retrieveconfig(mongoconn, collection)
            pymasarmongo.db.utils.close(mongoconn)

            results = []
            for res in result:
                if res["system"] not in results:
                    results.append(res["system"])
            res = sorted(set(results))
            self.systemComboBox.addItems(res)
            index = len(res)
            self.systemComboBox.addItem("Others")
            self.systemComboBox.setCurrentIndex(index)

    def listpvgroups(self):
        """"""
        self.pvgroupmodel.clear()
        self.pvgroupmodel.setHorizontalHeaderLabels(["PV Groups"])
        if self.dbsource == 0:
            # get data from sqlite
            if self.usedefaultdb:
                # masardb = str(self.databaseDefault.text())
                masardb = str(self.databaseDefault.toPlainText())
            else:
                masardb = str(self.databaseLineEdit.text())

            if masardb != "":
                os.environ["MASAR_SQLITE_DB"] = masardb
            else:
                raise RuntimeError("Cannot find MASAR SQLite Database")

            import pymasarsqlite

            conn = pymasarsqlite.utils.connect()
            result = pymasarsqlite.pvgroup.retrievePvGroups(conn)
            if len(result) > 0:
                result = sorted(result, key=itemgetter(0), reverse=True)
            for res in result:
                parent1 = QStandardItem(res[1])
                child1 = QStandardItem('id: {}'.format(res[0]))
                child2 = QStandardItem('description: {}'.format(res[2]))
                child3 = QStandardItem('date: {}'.format(res[3]))
                child4 = QStandardItem('version: {}'.format(res[4]))
                parent1.appendColumn([child1, child2, child3, child4])
                self.pvgroupmodel.appendRow(parent1)
            selmod = self.pvGroupTreeView.selectionModel()
            index2 = self.pvgroupmodel.indexFromItem(child3)
            selmod.select(index2, QItemSelectionModel.Select | QItemSelectionModel.Rows)
            pymasarsqlite.utils.close(conn)

            self.pvGroupTreeView.setContextMenuPolicy(Qt.CustomContextMenu)
            self.pvGroupTreeView.clicked.connect(self.showpvsinpvgroup)
            # self.connect(self.pvGroupTreeView,
            #              QtCore.SIGNAL("clicked(QModelIndex)"),
            #              #QtCore.SIGNAL("customContextMenuRequested(const QPoint &)"),
            #              self.doMenu)

        elif self.dbsource == 1:
            # get data from mongodb
            if self.usedefaultdb:
                database = str(self.databaseDefault.toPlainText())
                host = str(self.hostDefault.toPlainText())
                port = str(self.portDefault.toPlainText())
            else:
                database = str(self.databaseLineEdit.text())
                host = str(self.databaseHostLineEdit.text())
                port = str(self.databasePortLineEdit.text())

            import pymasarmongo

    def showpvsinpvgroup(self, point):
        if point.model().itemFromIndex(point).child(0) is None:
            return
        reply = QMessageBox.question(self, 'Message',
                                     "show all pvs belong to group {} ?".format(point.model().
                                                                                itemFromIndex(point).
                                                                                text()),
                                     QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if reply == QMessageBox.Yes:
            pvgroupidx = int(str(point.model().itemFromIndex(point).child(0).text().split(":")[1]).strip())

            # msg = QMessageBox(self)
            msg = ShowPvMessageBox()
            msg.setWindowTitle('PVs for group {}'.format(point.model().itemFromIndex(point).text()))
            msg.setText("Click show details to see all pvs.")
            # windowTitle = 'PVs for group {}'.format(point.model().itemFromIndex(point).text())

            if self.dbsource == 0:
                # get data from sqlite
                if self.usedefaultdb:
                    # masardb = str(self.databaseDefault.text())
                    masardb = str(self.databaseDefault.toPlainText())
                else:
                    masardb = str(self.databaseLineEdit.text())

                if masardb != "":
                    os.environ["MASAR_SQLITE_DB"] = masardb
                else:
                    raise RuntimeError("Cannot find MASAR SQLite Database")

                import pymasarsqlite

                conn = pymasarsqlite.utils.connect()
                result = pymasarsqlite.pvgroup.retrieveGroupPvs(conn, pvgroupidx)
                text = "\n".join(list(np.array(result)[:, 0]))
                msg.setDetailedText(text)
                msg.exec_()

            elif self.dbsource == 1:
                # get data from mongodb
                if self.usedefaultdb:
                    database = str(self.databaseDefault.toPlainText())
                    host = str(self.hostDefault.toPlainText())
                    port = str(self.portDefault.toPlainText())
                else:
                    database = str(self.databaseLineEdit.text())
                    host = str(self.databaseHostLineEdit.text())
                    port = str(self.databasePortLineEdit.text())

                import pymasarmongo
class ObstacleTable(QSortFilterProxyModel):

    MocMultiplier = 1
    SelectionMode = SelectionModeType.Automatic

    def __init__(self, surfacesList, fileWriter=None):
        QSortFilterProxyModel.__init__(self)
        ObstacleTable.SelectionMode = SelectionModeType.Automatic
        self.manualPolygon = None
        self.surfacesList = surfacesList
        self.surfaceType = None
        self.source = QStandardItemModel()
        self.setSourceModel(self.source)
        #         tableView.hideColumn(self.IndexObjectId)
        #         tableView.hideColumn(self.IndexLayerId)
        #         tableView.hideColumn(self.IndexX)
        #         tableView.hideColumn(self.IndexY)
        #         tableView.hideColumn(self.IndexLat)
        #         tableView.hideColumn(self.IndexLon)
        #         tableView.hideColumn(self.IndexSurface)
        self.hideColumnLabels = [
            ObstacleTableColumnType.ObjectId, ObstacleTableColumnType.LayerId,
            ObstacleTableColumnType.X, ObstacleTableColumnType.Y,
            ObstacleTableColumnType.Lat, ObstacleTableColumnType.Lon,
            ObstacleTableColumnType.Surface
        ]

        self.fixedColumnLabels = [
            ObstacleTableColumnType.ObjectId, ObstacleTableColumnType.LayerId,
            ObstacleTableColumnType.Name, ObstacleTableColumnType.X,
            ObstacleTableColumnType.Y, ObstacleTableColumnType.Lat,
            ObstacleTableColumnType.Lon, ObstacleTableColumnType.AltM,
            ObstacleTableColumnType.AltFt, ObstacleTableColumnType.TreesM,
            ObstacleTableColumnType.TreesFt
        ]

        self.IndexObjectId = 0
        self.IndexLayerId = 1
        self.IndexName = 2
        self.IndexX = 3
        self.IndexY = 4
        self.IndexLat = 5
        self.IndexLon = 6
        self.IndexAltM = 7
        self.IndexAltFt = 8
        self.IndexTreesM = 9
        self.IndexTreesFt = 10
        self.IndexOcaM = -1
        self.IndexOcaFt = -1
        self.IndexOchM = -1
        self.IndexOchFt = -1
        self.IndexObstArea = -1
        self.IndexDistInSecM = -1
        self.IndexMocAppliedM = -1
        self.IndexMocAppliedFt = -1
        self.IndexMocMultiplier = -1
        self.IndexMocReqM = -1
        self.IndexMocReqFt = -1
        self.IndexDoM = -1
        self.IndexDrM = -1
        self.IndexDzM = -1
        self.IndexDxM = -1
        self.IndexDsocM = -1
        self.IndexHeightLossM = -1
        self.IndexHeightLossFt = -1
        self.IndexAcAltM = -1
        self.IndexAcAltFt = -1
        self.IndexAltReqM = -1
        self.IndexAltReqFt = -1
        self.IndexCritical = -1
        self.IndexMACG = -1
        self.IndexPDG = -1
        self.IndexSurfAltM = -1
        self.IndexSurfAltFt = -1
        self.IndexDifferenceM = -1
        self.IndexDifferenceFt = -1
        self.IndexIlsX = -1
        self.IndexIlsY = -1
        self.IndexEqAltM = -1
        self.IndexEqAltFt = -1
        self.IndexSurfaceName = -1
        self.IndexDisregardable = -1
        self.IndexCloseIn = -1
        self.IndexTag = -1
        self.IndexSurface = -1
        self.IndexArea = -1
        self.IndexHLAppliedM = -1
        self.setHeaderLabels()
        self.setFilterKeyColumn(self.IndexSurface)
        self.setSortRole(Qt.UserRole + 1)
        self.layoutChanged.connect(self.setVerticalHeader)
        self.btnLocate = None
        self.tblObstacles = None

    def FilterDisregardableObstacles(self, state):
        if state:
            self.setFilterKeyColumn(self.IndexDisregardable)
            self.setFilterFixedString("Yes")
            self.setFilterKeyColumn(self.IndexSurface)

    def setSurfaceType(self, surfaceType):
        self.surfaceType = surfaceType

    def setFilterFixedString(self, filterString):
        QSortFilterProxyModel.setFilterFixedString(self, filterString)
        self.setVerticalHeader()
        if self.btnLocate != None and self.tblObstacles != None:
            selectedIndexes = self.tblObstacles.selectedIndexes()
            if len(selectedIndexes) == 0:
                self.btnLocate.setEnabled(False)
            else:
                self.btnLocate.setEnabled(True)

    def setLocateBtn(self, btnLocate):
        self.btnLocate = btnLocate
        self.btnLocate.setEnabled(False)
        self.btnLocate.clicked.connect(self.btnLocateClicked)

    def btnLocateClicked(self):
        if self.tblObstacles == None:
            return
        selectedIndexes = self.tblObstacles.selectedIndexes()
        self.locate(selectedIndexes)

    def tblObstaclesClicked(self, idx):
        if len(self.tblObstacles.selectedIndexes()) > 0:
            self.btnLocate.setEnabled(True)

    def setTableView(self, tblObstacles):
        self.tblObstacles = tblObstacles
        self.tblObstacles.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.tblObstacles.setSortingEnabled(True)
        self.tblObstacles.clicked.connect(self.tblObstaclesClicked)
        self.tblObstacles.verticalHeader().sectionClicked.connect(
            self.tblObstaclesClicked)
        pass

    def setHeaderLabels(self):
        #         print self.setHeaderData(1, Qt.Vertical, 1, Qt.DisplayRole)
        pass

    def setVerticalHeader(self):
        for i in range(self.rowCount()):
            self.setHeaderData(i, Qt.Vertical, i + 1, Qt.DisplayRole)

    def setHiddenColumns(self, tableView):
        tableView.hideColumn(self.IndexObjectId)
        tableView.hideColumn(self.IndexLayerId)
        tableView.hideColumn(self.IndexX)
        tableView.hideColumn(self.IndexY)
        tableView.hideColumn(self.IndexLat)
        tableView.hideColumn(self.IndexLon)
        tableView.hideColumn(self.IndexSurface)

    def getExtentForLocate(self, sourceRow):
        extent = None
        surfaceType = None
        if self.IndexSurface < 0:
            surfaceType = self.surfaceType
        else:
            surfaceType = self.source.item(sourceRow, self.IndexSurface).text()
        surfaceLayers = QgisHelper.getSurfaceLayers(self.surfaceType)
        for sfLayer in surfaceLayers:
            lId = sfLayer.name()
            if lId.contains(surfaceType):
                extent = sfLayer.extent()
                break
        return extent

    def clear(self):
        self.source.clear()
        self.source.setHorizontalHeaderLabels(self.fixedColumnLabels)
#         self.setHeaderLabels()

    def locate(self, selectedRowIndexes):
        if selectedRowIndexes == None or len(selectedRowIndexes) <= 0:
            return
        sourceRow = self.mapToSource(selectedRowIndexes[0]).row()
        objectId = int(self.source.item(sourceRow, self.IndexObjectId).text())
        layerId = self.source.item(sourceRow, self.IndexLayerId).text()
        QgisHelper.selectFeature(layerId, objectId)
        layer = QgsMapLayerRegistry.instance().mapLayer(layerId)
        crs = define._canvas.mapSettings().destinationCrs()
        if crs.mapUnits() == QGis.Meters:
            x = float(self.source.item(sourceRow, self.IndexX).text())
            y = float(self.source.item(sourceRow, self.IndexY).text())
            extent = QgsRectangle(x - 350, y - 350, x + 350, y + 350)
        else:
            x, result1 = self.source.item(sourceRow,
                                          self.IndexLon).data().toDouble()
            y, result2 = self.source.item(sourceRow,
                                          self.IndexLat).data().toDouble()
            extent = QgsRectangle(x - 0.005, y - 0.005, x + 0.005, y + 0.005)
        point = QgsPoint(x, y)
        # extent = self.getExtentForLocate(sourceRow)

        if extent is None:
            return

        QgisHelper.zoomExtent(point, extent, 2)
        pass

    def loadObstacles(self, surfaceLayers):
        if self.source.rowCount() > 0:
            self.source.clear()
            self.source.setHorizontalHeaderLabels(self.fixedColumnLabels)
        demEvaluateAg = None
        existingDemFlag = False
        obstacleLayersDEM = QgisHelper.getSurfaceLayers(SurfaceTypes.DEM)
        obstacleLayers = QgisHelper.getSurfaceLayers(SurfaceTypes.Obstacles)
        if obstacleLayersDEM != None and len(obstacleLayersDEM) > 0:
            if QMessageBox.question(
                    None, "Question",
                    "Do you want to use DEM for evaluating Obstacle?",
                    QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes:
                self.loadObstaclesDEM(obstacleLayersDEM, surfaceLayers)
        if obstacleLayers != None and len(obstacleLayers) > 0:
            #             if QMessageBox.question(None, "Question", "Do you want to use DEM for evaluating Obstacle?", QMessageBox.Yes | QMessageBox.No) == QMessageBox.No:
            self.loadObstaclesVector(obstacleLayers, surfaceLayers)
        return True

    def loadObstaclesDEM(self, obstacleLayersDEM, surfaceLayers):
        progressMessageBar = define._messagBar.createMessage(
            "Loading DEM Obstacles...")
        self.progress = QProgressBar()
        self.progress.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
        progressMessageBar.layout().addWidget(self.progress)
        define._messagBar.pushWidget(progressMessageBar,
                                     define._messagBar.INFO)
        maxium = 0
        offset = 0.0
        self.progress.setValue(0)
        wCount = 0
        hCount = 0
        for ly in obstacleLayersDEM:
            demDataProvider = ly.dataProvider()
            boundDem = demDataProvider.extent()
            xMin = boundDem.xMinimum()
            xMax = boundDem.xMaximum()
            yMin = boundDem.yMinimum()
            yMax = boundDem.yMaximum()
            bound = QgisHelper.getIntersectExtent(
                ly, QgsGeometry.fromRect(boundDem), surfaceLayers)
            #             boundGeom = QgsGeometry.fromRect(bound)
            if bound == None:
                continue
            block = ly.dataProvider().block(0, ly.extent(), ly.width(),
                                            ly.height())
            xMinimum = ly.dataProvider().extent().xMinimum()
            yMaximum = ly.dataProvider().extent().yMaximum()
            yMinimum = ly.dataProvider().extent().yMinimum()
            xMaximum = ly.dataProvider().extent().xMaximum()

            xOffSet = ly.extent().width() / ly.width()
            yOffSet = ly.extent().height() / ly.height()
            offset = xOffSet
            if bound.xMinimum() < xMinimum:
                wStartNumber = 0
                xStartValue = xMinimum
            else:
                wStartNumber = int((bound.xMinimum() - xMinimum) / xOffSet)
                xStartValue = bound.xMinimum()
            if yMaximum < bound.yMaximum():
                hStartNumber = 0
                yStartValue = yMaximum
            else:
                hStartNumber = int((yMaximum - bound.yMaximum()) / yOffSet)
                yStartValue = bound.yMaximum()

            if bound.xMaximum() > xMaximum:
                xEndValue = xMaximum
            else:
                xEndValue = bound.xMaximum()
            if yMinimum > bound.yMinimum():
                yEndValue = yMinimum
            else:
                yEndValue = bound.yMinimum()
            wCount = int(math.fabs(xEndValue - xStartValue) / xOffSet)
            hCount = int(math.fabs(yEndValue - yStartValue) / yOffSet)

            pixelCount = hCount
            maxium += pixelCount

        cellSizeWnd = cellsizeWnd(offset, wCount * hCount, maxium * 0.04)
        cellSizeWnd.setWindowTitle("Input Cell Size")
        result = cellSizeWnd.exec_()
        cellRate = 1
        if result == 1:
            offset = cellSizeWnd.cellsize
            maxium = cellSizeWnd.cellCount + 2
            cellRate = cellSizeWnd.cellRate


#             print cellSizeWnd.textedit1.text()

        if maxium == 0:
            return False
        self.progress.setMaximum(maxium)

        trees = define._treesDEM
        tolerance = define._toleranceDEM

        for obstacleLayer in obstacleLayersDEM:
            obstacleUnits = obstacleLayer.crs().mapUnits()
            demDataProvider = obstacleLayer.dataProvider()
            boundDem = demDataProvider.extent()
            bound = QgisHelper.getIntersectExtent(
                obstacleLayer, QgsGeometry.fromRect(boundDem), surfaceLayers)
            if bound == None:
                continue
            boundGeom = QgsGeometry.fromRect(bound)
            if not boundGeom.intersects(QgsGeometry.fromRect(boundDem)):
                continue
            block = obstacleLayer.dataProvider().block(1,
                                                       obstacleLayer.extent(),
                                                       obstacleLayer.width(),
                                                       obstacleLayer.height())
            xMinimum = obstacleLayer.extent().xMinimum()
            yMaximum = obstacleLayer.extent().yMaximum()
            yMinimum = obstacleLayer.extent().yMinimum()
            xMaximum = obstacleLayer.extent().xMaximum()

            xOffSet = obstacleLayer.extent().width() / obstacleLayer.width()
            yOffSet = obstacleLayer.extent().height() / obstacleLayer.height()

            if bound.xMinimum() < xMinimum:
                hStartNumber = 0
                xStartValue = xMinimum
            else:
                hStartNumber = int((bound.xMinimum() - xMinimum) / xOffSet)
                xStartValue = bound.xMinimum()
            if yMaximum < bound.yMaximum():
                wStartNumber = 0
                yStartValue = yMaximum
            else:
                wStartNumber = int((yMaximum - bound.yMaximum()) / yOffSet)
                yStartValue = bound.yMaximum()

            if bound.xMaximum() > xMaximum:
                xEndValue = xMaximum
            else:
                xEndValue = bound.xMaximum()
            if yMinimum > bound.yMinimum():
                yEndValue = yMinimum
            else:
                yEndValue = bound.yMinimum()
            wCount = int(math.fabs(xEndValue - xStartValue) / offset)
            hCount = int(math.fabs(yEndValue - yStartValue) / offset)

            xPixelWidth = 0.0
            yPixelWidth = 0.0
            featureID = 0
            # i = 0
            # k = 0
            # altitudeList = []
            # self.progress.setValue(0)
            # while i  <=  hCount - 1:
            #     j = 0
            #     while j <= wCount - 1:
            #         if block.isNoData(j * int(cellRate) + wStartNumber, i* int(cellRate) + hStartNumber):
            #             self.progress.setValue(k)
            #             QApplication.processEvents()
            #             j += 1
            #             k += 1
            #             altitudeList.append(None)
            #             continue
            #         altitude = block.value(j * int(cellRate) + wStartNumber, i * int(cellRate) + hStartNumber)
            #         altitudeList.append(altitude)
            #         self.progress.setValue(k)
            #         QApplication.processEvents()
            #         j += 1
            #         k += 1
            #     i += 1

            i = 0
            k = 0
            name = "DEM"
            semiXoffset = xOffSet / 2
            semiYoffset = yOffSet / 2
            while i <= hCount - 1:
                j = 0
                while j <= wCount - 1:
                    if block.isNoData(j * cellRate + wStartNumber,
                                      i * cellRate + hStartNumber):
                        self.progress.setValue(k)
                        QApplication.processEvents()
                        j += 1
                        k += 1
                        continue
                    altitude = block.value(j * cellRate + wStartNumber,
                                           i * cellRate + hStartNumber)
                    # # if block.isNoData(j * int(cellRate) + wStartNumber, i* int(cellRate) + hStartNumber):
                    # #     j += 1
                    # #     self.progress.setValue(self.progress.value() + 1)
                    # #     QApplication.processEvents()
                    # #     continue
                    # # altitude = block.value(j* int(cellRate) + wStartNumber, i* int(cellRate)+ hStartNumber)
                    # altitude = altitudeList[k]
                    # if altitude == None:
                    #     self.progress.setValue(k)
                    #     QApplication.processEvents()
                    #     j += 1
                    #     k += 1
                    #     continue

                    point = QgsPoint(
                        xStartValue + (i * cellRate) * xOffSet + semiXoffset,
                        yStartValue - (j * cellRate - 1) * yOffSet -
                        semiYoffset)
                    position = Point3D()
                    positionDegree = Point3D()

                    if define._canvas.mapUnits() == QGis.Meters:
                        if define._canvas.mapSettings().destinationCrs(
                        ) != obstacleLayer.crs():
                            position = QgisHelper.CrsTransformPoint(
                                point.x(), point.y(), obstacleLayer.crs(),
                                define._canvas.mapSettings().destinationCrs(),
                                altitude)
                        else:
                            position = Point3D(point.x(), point.y(), altitude)
                    else:
                        if define._canvas.mapSettings().destinationCrs(
                        ) != obstacleLayer.crs():
                            positionDegree = QgisHelper.CrsTransformPoint(
                                point.x(), point.y(), obstacleLayer.crs(),
                                define._canvas.mapSettings().destinationCrs(),
                                altitude)
                        else:
                            positionDegree = Point3D(point.x(), point.y(),
                                                     altitude)
                    obstacle = Obstacle(name, position, obstacleLayer.id(),
                                        featureID, None, trees,
                                        ObstacleTable.MocMultiplier, tolerance)
                    obstacle.positionDegree = positionDegree
                    if self.manualPolygon != None:
                        if not self.manualPolygon.contains(obstacle.Position):
                            continue
                    self.checkObstacle(obstacle)
                    self.progress.setValue(k)
                    QApplication.processEvents()
                    j += 1
                    featureID += 1
                    k += 1
                i += 1
        self.progress.setValue(maxium)
        define._messagBar.hide()
        self.manualPolygon = None

    def loadObstaclesVector(self, obstacleLayers, surfaceLayers):
        progressMessageBar = define._messagBar.createMessage(
            "Loading Vector Obstacles...")
        self.progress = QProgressBar()
        self.progress.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
        progressMessageBar.layout().addWidget(self.progress)
        define._messagBar.pushWidget(progressMessageBar,
                                     define._messagBar.INFO)
        maxium = 0
        self.progress.setValue(0)
        for ly in obstacleLayers:
            maxium += ly.featureCount()

        if maxium == 0:
            return False
        self.progress.setMaximum(maxium)

        for obstacleLayer in obstacleLayers:
            obstacleUnits = obstacleLayer.crs().mapUnits()
            features = QgisHelper.getFeaturesInLayerExtent(
                define._canvas, obstacleLayer, surfaceLayers,
                SelectionModeType.Automatic)
            #             print len(features)
            for feature in features:
                name = feature.attribute("Name").toString()
                altitude = feature.attribute("Altitude").toFloat()[0]
                trees = define._trees
                tolerance = define._tolerance
                point = feature.geometry().asPoint()
                position = Point3D()
                positionDegree = Point3D()
                if define._canvas.mapUnits() == QGis.Meters:
                    if define._canvas.mapSettings().destinationCrs(
                    ) != obstacleLayer.crs():
                        position = QgisHelper.CrsTransformPoint(
                            point.x(), point.y(), obstacleLayer.crs(),
                            define._canvas.mapSettings().destinationCrs(),
                            altitude)
                    else:
                        position = Point3D(point.x(), point.y(), altitude)
                else:
                    if define._canvas.mapSettings().destinationCrs(
                    ) != obstacleLayer.crs():
                        positionDegree = QgisHelper.CrsTransformPoint(
                            point.x(), point.y(), obstacleLayer.crs(),
                            define._canvas.mapSettings().destinationCrs(),
                            altitude)
                    else:
                        positionDegree = Point3D(point.x(), point.y(),
                                                 altitude)
                featureId = feature.id()
                layerId = obstacleLayer.id()
                obstacle = Obstacle(name, position, layerId, featureId, None,
                                    trees, ObstacleTable.MocMultiplier,
                                    tolerance)
                obstacle.positionDegree = positionDegree
                #                 obstacle.positionDegree = positionDegree
                self.checkObstacle(obstacle)
                self.progress.setValue(self.progress.value() + 1)
                QApplication.processEvents()
            QApplication.processEvents()
        self.progress.setValue(maxium)
        define._messagBar.hide()
        self.manualPolygon = None

    def addObstacleToModel(self, obstacle, checkResult=None):
        standardItemList = []
        # obstacle.positionDegree = QgisHelper.Meter2Degree(obstacle.Position.x(), obstacle.Position.y())
        standardItem = QStandardItem(str(obstacle.featureId))
        standardItem.setData(obstacle.featureId)
        standardItemList.append(standardItem)

        standardItem = QStandardItem(str(obstacle.layerId))
        standardItem.setData(obstacle.layerId)
        standardItemList.append(standardItem)

        standardItem = QStandardItem(str(obstacle.name))
        standardItem.setData(obstacle.name)
        standardItemList.append(standardItem)

        standardItem = QStandardItem(str(obstacle.Position.x()))
        standardItem.setData(obstacle.Position.x())
        standardItemList.append(standardItem)

        standardItem = QStandardItem(str(obstacle.Position.y()))
        standardItem.setData(obstacle.Position.y())
        standardItemList.append(standardItem)

        value = QVariant(QgisHelper.strDegree(obstacle.positionDegree.y()))
        standardItem = QStandardItem(value.toString())
        standardItem.setData(obstacle.positionDegree.y())
        standardItemList.append(standardItem)
        strV = QgisHelper.strDegree(obstacle.positionDegree.y())

        value = QVariant(QgisHelper.strDegree(obstacle.positionDegree.x()))
        standardItem = QStandardItem(value.toString())
        standardItem.setData(obstacle.positionDegree.x())
        standardItemList.append(standardItem)

        standardItem = QStandardItem(str(obstacle.Position.z()))
        standardItem.setData(obstacle.Position.z())
        standardItemList.append(standardItem)

        standardItem = QStandardItem(
            str(Unit.ConvertMeterToFeet(obstacle.Position.z())))
        standardItem.setData(Unit.ConvertMeterToFeet(obstacle.Position.z()))
        standardItemList.append(standardItem)

        standardItem = QStandardItem(str(obstacle.trees))
        standardItem.setData(obstacle.trees)
        standardItemList.append(standardItem)

        standardItem = QStandardItem(
            str(Unit.ConvertMeterToFeet(obstacle.trees)))
        standardItem.setData(Unit.ConvertMeterToFeet(obstacle.trees))
        standardItemList.append(standardItem)

        #         for i in range(len(standardItemList), self.source.columnCount()):
        #             standardItemList.append(QStandardItem("None"))

        self.source.appendRow(standardItemList)

        standardItem = QStandardItem(str(obstacle.mocMultiplier))
        standardItem.setData(obstacle.mocMultiplier)
        self.source.setItem(self.source.rowCount() - 1,
                            self.IndexMocMultiplier, standardItem)

    def checkObstacle(self, obstacle):
        pass

    def CompareObstacleRows(self, newRow, row, ignore):
        pass

    def method_0(self, obstacle_0):
        colCount = self.columnCount()
        objectId = range(colCount)

        objectId[0] = (obstacle_0.featureId)
        objectId[1] = (obstacle_0.name)
        objectId[2] = (obstacle_0.position.x())
        objectId[3] = (obstacle_0.position.y())
        objectId[4] = (obstacle_0.Position.z())
        position = obstacle_0.position
        objectId[6] = (Unit.ConvertMeterToFeet(position.z()))
        objectId[7] = (obstacle_0.trees)
        objectId[8] = (Unit.ConvertMeterToFeet(obstacle_0.Trees))
        if (self.IndexMocMultiplier > -1):
            objectId[self.IndexMocMultiplier] = obstacle_0.MocMultiplier

        return objectId

    def method_1(self, object_0):
        pass
Exemple #45
0
class ConfigDialog(BASE, WIDGET):

    def __init__(self, toolbox):
        super(ConfigDialog, self).__init__(None)
        self.setupUi(self)

        self.toolbox = toolbox
        self.groupIcon = QIcon()
        self.groupIcon.addPixmap(self.style().standardPixmap(
            QStyle.SP_DirClosedIcon), QIcon.Normal, QIcon.Off)
        self.groupIcon.addPixmap(self.style().standardPixmap(
            QStyle.SP_DirOpenIcon), QIcon.Normal, QIcon.On)

        if hasattr(self.searchBox, 'setPlaceholderText'):
            self.searchBox.setPlaceholderText(self.tr('Search...'))

        self.model = QStandardItemModel()
        self.tree.setModel(self.model)

        self.delegate = SettingDelegate()
        self.tree.setItemDelegateForColumn(1, self.delegate)

        self.searchBox.textChanged.connect(self.fillTree)

        self.fillTree()

        self.tree.expanded.connect(self.adjustColumns)

    def fillTree(self):
        self.items = {}
        self.model.clear()
        self.model.setHorizontalHeaderLabels([self.tr('Setting'),
                                              self.tr('Value')])

        text = unicode(self.searchBox.text())
        settings = ProcessingConfig.getSettings()

        rootItem = self.model.invisibleRootItem()
        priorityKeys = [self.tr('General'), self.tr('Models'), self.tr('Scripts')]
        for group in priorityKeys:
            groupItem = QStandardItem(group)
            icon = ProcessingConfig.getGroupIcon(group)
            groupItem.setIcon(icon)
            groupItem.setEditable(False)
            emptyItem = QStandardItem()
            emptyItem.setEditable(False)
            rootItem.insertRow(0, [groupItem, emptyItem])
            for setting in settings[group]:
                if setting.hidden:
                    continue

                if text == '' or text.lower() in setting.description.lower():
                    labelItem = QStandardItem(setting.description)
                    labelItem.setIcon(icon)
                    labelItem.setEditable(False)
                    self.items[setting] = SettingItem(setting)
                    groupItem.insertRow(0, [labelItem, self.items[setting]])

            if text != '':
                self.tree.expand(groupItem.index())

        providersItem = QStandardItem(self.tr('Providers'))
        icon = QIcon(os.path.join(pluginPath, 'images', 'alg.png'))
        providersItem.setIcon(icon)
        providersItem.setEditable(False)
        emptyItem = QStandardItem()
        emptyItem.setEditable(False)
        rootItem.insertRow(0, [providersItem, emptyItem])
        for group in settings.keys():
            if group in priorityKeys:
                continue

            groupItem = QStandardItem(group)
            icon = ProcessingConfig.getGroupIcon(group)
            groupItem.setIcon(icon)
            groupItem.setEditable(False)
            for setting in settings[group]:
                if setting.hidden:
                    continue

                if text == '' or text.lower() in setting.description.lower():
                    labelItem = QStandardItem(setting.description)
                    labelItem.setIcon(icon)
                    labelItem.setEditable(False)
                    self.items[setting] = SettingItem(setting)
                    groupItem.insertRow(0, [labelItem, self.items[setting]])

            emptyItem = QStandardItem()
            emptyItem.setEditable(False)
            providersItem.appendRow([groupItem, emptyItem])

        self.tree.sortByColumn(0, Qt.AscendingOrder)
        self.adjustColumns()

    def accept(self):
        for setting in self.items.keys():
            if isinstance(setting.value, bool):
                setting.value = self.items[setting].checkState() == Qt.Checked
            elif isinstance(setting.value, (float, int, long)):
                value = unicode(self.items[setting].text())
                try:
                    value = float(value)
                    setting.value = value
                except ValueError:
                    QMessageBox.critical(self, self.tr('Wrong value'),
                                         self.tr('Wrong parameter value:\n%1') % value)
                    return
            else:
                setting.value = unicode(self.items[setting].text())
            setting.save()
        Processing.updateAlgsList()

        QDialog.accept(self)

    def adjustColumns(self):
        self.tree.resizeColumnToContents(0)
        self.tree.resizeColumnToContents(1)
Exemple #46
0
class FlashingWindow(QDialog, Ui_Flashing):
    def __init__(self, parent):
        QDialog.__init__(self, parent)

        self.setupUi(self)

        self.tool_infos = {}
        self.firmware_infos = {}
        self.plugin_infos = {}
        self.brick_infos = []
        self.refresh_updates_pending = False

        self.parent = parent
        self.tab_widget.currentChanged.connect(self.tab_changed)
        self.button_serial_port_refresh.clicked.connect(self.refresh_serial_ports)
        self.combo_firmware.currentIndexChanged.connect(self.firmware_changed)
        self.button_firmware_save.clicked.connect(self.firmware_save_clicked)
        self.button_firmware_browse.clicked.connect(self.firmware_browse_clicked)
        self.button_uid_load.clicked.connect(self.uid_load_clicked)
        self.button_uid_save.clicked.connect(self.uid_save_clicked)
        self.combo_brick.currentIndexChanged.connect(self.brick_changed)
        self.combo_port.currentIndexChanged.connect(self.port_changed)
        self.combo_plugin.currentIndexChanged.connect(self.plugin_changed)
        self.button_plugin_save.clicked.connect(self.plugin_save_clicked)
        self.button_plugin_browse.clicked.connect(self.plugin_browse_clicked)

        infos.get_infos_changed_signal().connect(self.update_bricks)

        self.update_tool_label.hide()
        self.no_connection_label.hide()

        self.refresh_serial_ports()

        self.combo_firmware.addItem(CUSTOM)
        self.combo_firmware.setDisabled(True)
        self.firmware_changed(0)

        self.combo_plugin.addItem(CUSTOM)
        self.combo_plugin.setDisabled(True)
        self.plugin_changed(0)

        self.brick_changed(0)

        self.update_tree_view_model_labels = ['Name', 'UID', 'Installed', 'Latest']
        self.update_tree_view_model = QStandardItemModel(self)
        self.update_tree_view.setModel(self.update_tree_view_model)
        self.update_tree_view.setSortingEnabled(True)
        self.update_tree_view.header().setSortIndicator(0, Qt.AscendingOrder)

        self.update_button_refresh.clicked.connect(self.refresh_updates_clicked)
        self.update_button_bricklets.clicked.connect(self.auto_update_bricklets_clicked)

        self.update_ui_state()
        self.update_bricks()

    def refresh_latest_version_info(self, progress):
        self.tool_infos = {}
        self.firmware_infos = {}
        self.plugin_infos = {}

        self.combo_firmware.clear()
        self.combo_plugin.clear()
        self.combo_firmware.setDisabled(False)
        self.combo_plugin.setDisabled(False)

        progress.setLabelText('Discovering latest versions on tinkerforge.com')
        progress.setMaximum(0)
        progress.setValue(0)
        progress.show()

        okay = True

        try:
            response = urllib2.urlopen(LATEST_VERSIONS_URL, timeout=10)
            latest_versions_data = response.read()
            response.close()
        except urllib2.URLError:
            okay = False
            progress.cancel()
            self.combo_firmware.setDisabled(True)
            self.combo_plugin.setDisabled(True)
            self.popup_fail('Updates / Flashing', 'Latest version information on tinkerforge.com is not available (error code 1). Please report this to [email protected].\n\nFirmwares and plugins can be flashed from local files only.')

        if okay:
            def report_malformed(error_code):
                progress.cancel()
                self.combo_firmware.setDisabled(True)
                self.combo_plugin.setDisabled(True)
                self.popup_fail('Updates / Flashing', 'Latest version information on tinkerforge.com is malformed (error code {0}). Please report this to [email protected].\n\nFirmwares and plugins can be flashed from local files only.'.format(error_code))

            for line in latest_versions_data.split('\n'):
                line = line.strip()

                if len(line) < 1:
                    continue

                parts = line.split(':')

                if len(parts) != 3:
                    okay = False
                    report_malformed(2)
                    break

                latest_version_parts = parts[2].split('.')

                if len(latest_version_parts) != 3:
                    okay = False
                    report_malformed(3)
                    break

                try:
                    latest_version = int(latest_version_parts[0]), int(latest_version_parts[1]), int(latest_version_parts[2])
                except:
                    okay = False
                    report_malformed(4)
                    break

                if parts[0] == 'tools':
                    tool_info = infos.ToolInfo()
                    tool_info.firmware_version_latest = latest_version

                    self.tool_infos[parts[1]] = tool_info
                elif parts[0] == 'bricks':
                    self.refresh_firmware_info(parts[1], latest_version)
                elif parts[0] == 'bricklets':
                    self.refresh_plugin_info(parts[1], latest_version)

        if okay:
            # update combo_firmware
            if len(self.firmware_infos) > 0:
                self.combo_firmware.addItem(SELECT)
                self.combo_firmware.insertSeparator(self.combo_firmware.count())

            for firmware_info in sorted(self.firmware_infos.values(), key=lambda x: x.name):
                name = '{0} ({1}.{2}.{3})'.format(firmware_info.name, *firmware_info.firmware_version_latest)
                self.combo_firmware.addItem(name, firmware_info.url_part)

            if self.combo_firmware.count() > 0:
                self.combo_firmware.insertSeparator(self.combo_firmware.count())

            # update combo_plugin
            if len(self.plugin_infos) > 0:
                self.combo_plugin.addItem(SELECT)
                self.combo_plugin.insertSeparator(self.combo_plugin.count())

            for plugin_info in sorted(self.plugin_infos.values(), key=lambda x: x.name):
                name = '{0} ({1}.{2}.{3})'.format(plugin_info.name, *plugin_info.firmware_version_latest)
                self.combo_plugin.addItem(name, plugin_info.url_part)

            if self.combo_plugin.count() > 0:
                self.combo_plugin.insertSeparator(self.combo_plugin.count())

        self.combo_firmware.addItem(CUSTOM)
        self.firmware_changed(0)

        self.combo_plugin.addItem(CUSTOM)
        self.plugin_changed(0)

        self.update_ui_state()

    def refresh_firmware_info(self, url_part, latest_version):
        name = url_part

        if name in ['dc', 'imu']:
            name = name.upper()
        else:
            words = name.split('_')
            parts = []
            for word in words:
                parts.append(word[0].upper() + word[1:])
            name = ' '.join(parts)

        firmware_info = infos.FirmwareInfo()
        firmware_info.name = name
        firmware_info.url_part = url_part
        firmware_info.firmware_version_latest = latest_version

        self.firmware_infos[url_part] = firmware_info

    def refresh_plugin_info(self, url_part, latest_version):
        name = url_part

        if name in ['gps', 'ptc']:
            name = name.upper()
        elif name.startswith('lcd_'):
            name = name.replace('lcd_', 'LCD_')
            if url_part.startswith('lcd_20x4_'):
                name = name.replace('_v11', '_1.1').replace('_v12', '_1.2')
        elif name.startswith('io'):
            name = name.replace('io', 'IO-')
        elif name.endswith('_ir'):
            name = name.replace('_ir', '_IR')
        elif name.endswith('_us'):
            name = name.replace('_us', '_US')
        elif name.startswith('led_'):
            name = name.replace('led_', 'LED_')
        elif name.endswith('_v2'):
            name = name.replace('_v2', '_2.0')

        words = name.split('_')
        parts = []

        for word in words:
            parts.append(word[0].upper() + word[1:])

        name = ' '.join(parts)
        name = name.replace('Voltage Current', 'Voltage/Current')
        name = name.replace('Nfc Rfid', 'NFC/RFID')
        name = name.replace('0 20ma', '0-20mA')

        plugin_info = infos.PluginInfo()
        plugin_info.name = name
        plugin_info.url_part = url_part
        plugin_info.firmware_version_latest = latest_version

        self.plugin_infos[url_part] = plugin_info

    def update_bricks(self):
        self.brick_infos = []
        self.combo_brick.clear()
        items = {}

        for info in infos.get_brick_infos():
            items[info.get_combo_item()] = info

        for item in sorted(items.keys()):
            self.brick_infos.append(items[item])
            self.combo_brick.addItem(item)

        if self.combo_brick.count() == 0:
            self.combo_brick.addItem(NO_BRICK)

        self.update_ui_state()

    def create_progress_bar(self, title):
        progress = QProgressDialog(self)
        progress.setAutoClose(False)
        progress.setWindowTitle(title)
        progress.setCancelButton(None)
        progress.setWindowModality(Qt.WindowModal)
        return progress

    def popup_ok(self, title, message):
        QMessageBox.information(self, title, message, QMessageBox.Ok)

    def popup_fail(self, title, message):
        QMessageBox.critical(self, title, message, QMessageBox.Ok)

    def refresh_serial_ports(self):
        progress = self.create_progress_bar('Discovering')
        current_text = self.combo_serial_port.currentText()
        self.combo_serial_port.clear()

        try:
            progress.setLabelText('Discovering serial ports')
            progress.setMaximum(0)
            progress.setValue(0)
            progress.show()

            ports = get_serial_ports()
        except:
            progress.cancel()
            self.combo_serial_port.addItem(NO_BOOTLOADER)
            self.update_ui_state()
            self.popup_fail('Brick', 'Could not discover serial ports')
        else:
            preferred_index = None

            for port in ports:
                if preferred_index is None:
                    if 'ttyACM' in port[0] or \
                       'ttyUSB' in port[0] or \
                       'usbmodemfd' in port[0] or \
                       'AT91 USB to Serial Converter' in port[1] or \
                       'GPS Camera Detect' in port[1]:
                        preferred_index = self.combo_serial_port.count()

                if len(port[1]) > 0 and port[0] != port[1]:
                    self.combo_serial_port.addItem(u'{0} - {1}'.format(port[0], port[1]), port[0])
                else:
                    self.combo_serial_port.addItem(port[0], port[0])

            if self.combo_serial_port.count() == 0:
                self.combo_serial_port.addItem(NO_BOOTLOADER)
            elif preferred_index is not None:
                self.combo_serial_port.setCurrentIndex(preferred_index)
            else:
                index = self.combo_serial_port.findText(current_text)
                if index >= 0:
                    self.combo_serial_port.setCurrentIndex(index)

            self.update_ui_state()

        progress.cancel()

    def update_ui_state(self):
        is_firmware_select = self.combo_firmware.currentText() == SELECT
        is_firmware_custom = self.combo_firmware.currentText() == CUSTOM
        is_no_bootloader = self.combo_serial_port.currentText() == NO_BOOTLOADER
        has_bricklet_ports = self.combo_port.count() > 0
        self.combo_serial_port.setEnabled(not is_no_bootloader)
        self.combo_port.setEnabled(has_bricklet_ports)
        self.combo_plugin.setEnabled(has_bricklet_ports and self.combo_plugin.count() > 1)
        self.button_firmware_save.setEnabled(not is_firmware_select and not is_no_bootloader)
        self.edit_custom_firmware.setEnabled(is_firmware_custom)
        self.button_firmware_browse.setEnabled(is_firmware_custom)
        self.edit_uid.setEnabled(has_bricklet_ports)
        self.button_uid_load.setEnabled(has_bricklet_ports)
        self.button_uid_save.setEnabled(has_bricklet_ports)

        is_plugin_select = self.combo_plugin.currentText() == SELECT
        is_plugin_custom = self.combo_plugin.currentText() == CUSTOM
        is_no_brick = self.combo_brick.currentText() == NO_BRICK
        self.combo_brick.setEnabled(not is_no_brick)
        self.button_plugin_save.setEnabled(not is_plugin_select and not is_no_brick)
        self.edit_custom_plugin.setEnabled(is_plugin_custom)
        self.button_plugin_browse.setEnabled(is_plugin_custom)

        self.tab_widget.setTabEnabled(2, len(self.brick_infos) > 0)

    def firmware_changed(self, index):
        self.update_ui_state()

    def firmware_browse_clicked(self):
        if len(self.edit_custom_firmware.text()) > 0:
            last_dir = os.path.dirname(os.path.realpath(self.edit_custom_firmware.text()))
        else:
            last_dir = get_home_path()

        filename = get_open_file_name(get_main_window(), 'Open Firmware', last_dir, '*.bin')

        if len(filename) > 0:
            self.edit_custom_firmware.setText(filename)

    def firmware_save_clicked(self):
        port_name = self.combo_serial_port.itemData(self.combo_serial_port.currentIndex())

        try:
            samba = SAMBA(port_name)
        except SAMBAException as e:
            self.refresh_serial_ports()
            self.popup_fail('Brick', 'Could not connect to Brick: {0}'.format(str(e)))
            return
        except SerialException as e:
            self.refresh_serial_ports()
            self.popup_fail('Brick', str(e)[0].upper() + str(e)[1:])
            return
        except:
            self.refresh_serial_ports()
            self.popup_fail('Brick', 'Could not connect to Brick')
            return

        progress = ProgressWrapper(self.create_progress_bar('Flashing'))
        samba.progress = progress
        current_text = self.combo_firmware.currentText()

        # Get firmware
        name = None
        version = None

        if current_text == SELECT:
            return
        elif current_text == CUSTOM:
            firmware_file_name = self.edit_custom_firmware.text()

            try:
                with open(firmware_file_name, 'rb') as f:
                    firmware = f.read()
            except IOError:
                progress.cancel()
                self.popup_fail('Brick', 'Could not read firmware file')
                return
        else:
            url_part = self.combo_firmware.itemData(self.combo_firmware.currentIndex())
            name = self.firmware_infos[url_part].name
            version = self.firmware_infos[url_part].firmware_version_latest

            progress.reset('Downloading {0} Brick firmware {1}.{2}.{3}'.format(name, *version), 0)

            response = None

            try:
                response = urllib2.urlopen(FIRMWARE_URL + 'bricks/{0}/brick_{0}_firmware_{1}_{2}_{3}.bin'.format(url_part, *version), timeout=10)
            except urllib2.URLError:
                pass

            beta = 5

            while response is None and beta > 0:
                try:
                    response = urllib2.urlopen(FIRMWARE_URL + 'bricks/{0}/brick_{0}_firmware_{2}_{3}_{4}_beta{1}.bin'.format(url_part, beta, *version), timeout=10)
                except urllib2.URLError:
                    beta -= 1

            if response is None:
                progress.cancel()
                self.popup_fail('Brick', 'Could not download {0} Brick firmware {1}.{2}.{3}'.format(name, *version))
                return

            try:
                length = int(response.headers['Content-Length'])
                progress.setMaximum(length)
                progress.update(0)
                QApplication.processEvents()
                firmware = ''
                chunk = response.read(1024)

                while len(chunk) > 0:
                    firmware += chunk
                    progress.update(len(firmware))
                    chunk = response.read(1024)

                response.close()
            except urllib2.URLError:
                progress.cancel()
                self.popup_fail('Brick', 'Could not download {0} Brick firmware {1}.{2}.{3}'.format(name, *version))
                return

        # Get IMU UID
        imu_uid = None
        imu_calibration = None
        lock_imu_calibration_pages = False

        if name == 'IMU':
            # IMU 1.0.9 and earlier have a bug in their flash locking that makes
            # them unlook the wrong pages. Therefore, the calibration pages
            # must not be locked for this versions
            if version[1] > 0 or (version[1] == 0 and version[2] > 9):
                lock_imu_calibration_pages = True

            try:
                imu_uid = base58encode(uid64_to_uid32(samba.read_uid64()))
            except SerialException as e:
                progress.cancel()
                self.popup_fail('Brick', 'Could read UID of IMU Brick: {0}'.format(str(e)))
                return
            except:
                progress.cancel()
                self.popup_fail('Brick', 'Could read UID of IMU Brick')
                return

            result = QMessageBox.question(self, 'IMU Brick',
                                          'Restore factory calibration for IMU Brick [{0}] from tinkerforge.com?'.format(imu_uid),
                                          QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)

            # Download IMU calibration
            if result == QMessageBox.Yes:
                progress.reset('Downloading factory calibration for IMU Brick', 0)

                try:
                    imu_calibration_text = ''
                    response = urllib2.urlopen(IMU_CALIBRATION_URL + '{0}.txt'.format(imu_uid), timeout=10)
                    chunk = response.read(1024)

                    while len(chunk) > 0:
                        imu_calibration_text += chunk
                        chunk = response.read(1024)

                    response.close()
                except urllib2.HTTPError as e:
                    if e.code == 404:
                        imu_calibration_text = None
                        self.popup_ok('IMU Brick', 'No factory calibration for IMU Brick [{0}] available'.format(imu_uid))
                    else:
                        progress.cancel()
                        self.popup_fail('IMU Brick', 'Could not download factory calibration for IMU Brick [{0}]'.format(imu_uid))
                        return
                except urllib2.URLError:
                    progress.cancel()
                    self.popup_fail('IMU Brick', 'Could not download factory calibration for IMU Brick [{0}]'.format(imu_uid))
                    return

                if imu_calibration_text is not None:
                    if len(imu_calibration_text) == 0:
                        progress.cancel()
                        self.popup_fail('IMU Brick', 'Could not download factory calibration for IMU Brick [{0}]'.format(imu_uid))
                        return

                    try:
                        imu_calibration_matrix = parse_imu_calibration(imu_calibration_text)

                        # Ensure proper temperature relation
                        if imu_calibration_matrix[5][1][7] <= imu_calibration_matrix[5][1][3]:
                            imu_calibration_matrix[5][1][7] = imu_calibration_matrix[5][1][3] + 1

                        imu_calibration_array = imu_calibration_matrix[0][1][:6] + \
                                                imu_calibration_matrix[1][1][:3] + \
                                                imu_calibration_matrix[2][1][:6] + \
                                                imu_calibration_matrix[3][1][:3] + \
                                                imu_calibration_matrix[4][1][:6] + \
                                                imu_calibration_matrix[5][1][:8]

                        imu_calibration = struct.pack('<32h', *imu_calibration_array)
                    except:
                        progress.cancel()
                        self.popup_fail('IMU Brick', 'Could not parse factory calibration for IMU Brick [{0}]'.format(imu_uid))
                        return

        # Flash firmware
        def report_result(reboot_okay):
            if current_text == CUSTOM:
                if reboot_okay:
                    message = 'Successfully restarted Brick!'
                else:
                    message = 'Manual restart of Brick required!'
            else:
                if reboot_okay:
                    message = 'Successfully restarted {0} Brick!'.format(name)
                else:
                    message = 'Manual restart of {0} Brick required!'.format(name)

            if current_text == CUSTOM:
                self.popup_ok('Brick', 'Successfully flashed firmware.\n' + message)
            elif imu_calibration is not None:
                self.popup_ok('Brick', 'Successfully flashed {0} Brick firmware {1}.{2}.{3}.\n'.format(name, *version) +
                                       'Successfully restored factory calibration.\n' + message)
            else:
                self.popup_ok('Brick', 'Successfully flashed {0} Brick firmware {1}.{2}.{3}.\n'.format(name, *version) +
                                       message)

        try:
            samba.flash(firmware, imu_calibration, lock_imu_calibration_pages)
            # close serial device before showing dialog, otherwise exchanging
            # the brick while the dialog is open will force it to show up as ttyACM1
            samba = None
            progress.cancel()
            report_result(True)
        except SAMBARebootError as e:
            samba = None
            progress.cancel()
            self.refresh_serial_ports()
            report_result(False)
        except SAMBAException as e:
            samba = None
            progress.cancel()
            self.refresh_serial_ports()
            self.popup_fail('Brick', 'Could not flash Brick: {0}'.format(str(e)))
        except SerialException as e:
            samba = None
            progress.cancel()
            self.refresh_serial_ports()
            self.popup_fail('Brick', 'Could not flash Brick: {0}'.format(str(e)))
        except:
            samba = None
            progress.cancel()
            self.refresh_serial_ports()
            self.popup_fail('Brick', 'Could not flash Brick')

    def uid_save_clicked(self):
        device, port = self.current_device_and_port()
        uid = self.edit_uid.text()

        if len(uid) == 0:
            self.popup_fail('Bricklet', 'UID cannot be empty')
            return

        for c in uid:
            if c not in BASE58:
                self.popup_fail('Bricklet', "UID cannot contain '{0}'".format(c))
                return

        try:
            if base58decode(uid) > 0xFFFFFFFF:
                self.popup_fail('Bricklet', 'UID is too long')
                return
        except:
            self.popup_fail('Bricklet', 'UID is invalid')
            return

        try:
            self.parent.ipcon.write_bricklet_uid(device, port, uid)
        except Error as e:
            self.popup_fail('Bricklet', 'Could not write UID: ' + error_to_name(e))
            return

        try:
            uid_read = self.parent.ipcon.read_bricklet_uid(device, port)
        except Error as e:
            self.popup_fail('Bricklet', 'Could not read written UID: ' + error_to_name(e))
            return

        if uid == uid_read:
            self.popup_ok('Bricklet', 'Successfully wrote UID.\nNew UID will be used after reset of the connected Brick.')
        else:
            self.popup_fail('Bricklet', 'Could not write UID: Verification failed')

    def uid_load_clicked(self):
        device, port = self.current_device_and_port()
        try:
            uid = self.parent.ipcon.read_bricklet_uid(device, port)
        except Error as e:
            self.edit_uid.setText('')
            self.popup_fail('Bricklet', 'Could not read UID: ' + error_to_name(e))
            return

        self.edit_uid.setText(uid)

    def brick_changed(self, index):
        self.combo_port.clear()

        if index < 0 or len(self.brick_infos) == 0:
            self.combo_port.addItems(['A', 'B', 'C', 'D'])
            return

        brick_info = self.brick_infos[index]

        for key in sorted(brick_info.bricklets.keys()):
            bricklet_info = brick_info.bricklets[key]

            if bricklet_info is None:
                self.combo_port.addItem(key.upper())
            else:
                name = '{0}: {1}'.format(key.upper(), bricklet_info.get_combo_item())
                self.combo_port.addItem(name, bricklet_info.url_part)

        self.update_ui_state()

    def port_changed(self, index):
        self.edit_uid.setText('')

        if index < 0:
            self.combo_plugin.setCurrentIndex(0)
            return

        url_part = self.combo_port.itemData(index)

        if url_part == None or len(url_part) == 0:
            self.combo_plugin.setCurrentIndex(0)
            return

        i = self.combo_plugin.findData(url_part)

        if i < 0:
            self.combo_plugin.setCurrentIndex(0)
        else:
            self.combo_plugin.setCurrentIndex(i)

        b = self.combo_brick.currentIndex()
        p = self.combo_port.currentIndex()

        if b < 0 or p < 0:
            return

        self.edit_uid.setText(self.brick_infos[b].bricklets[('a', 'b', 'c', 'd')[p]].uid)

    def plugin_changed(self, index):
        self.update_ui_state()

    def download_bricklet_plugin(self, progress, url_part, name, version, popup=False):
        progress.setLabelText('Downloading {0} Bricklet plugin {1}.{2}.{3}'.format(name, *version))
        progress.setMaximum(0)
        progress.show()

        response = None

        try:
            response = urllib2.urlopen(FIRMWARE_URL + 'bricklets/{0}/bricklet_{0}_firmware_{1}_{2}_{3}.bin'.format(url_part, *version), timeout=10)
        except urllib2.URLError:
            pass

        beta = 5

        while response is None and beta > 0:
            try:
                response = urllib2.urlopen(FIRMWARE_URL + 'bricklets/{0}/bricklet_{0}_firmware_{2}_{3}_{4}_beta{1}.bin'.format(url_part, beta, *version), timeout=10)
            except urllib2.URLError:
                beta -= 1

        if response is None:
            progress.cancel()
            if popup:
                self.popup_fail('Bricklet', 'Could not download {0} Bricklet plugin {1}.{2}.{3}'.format(name, *version))
            return None

        try:
            length = int(response.headers['Content-Length'])
            progress.setMaximum(length)
            progress.setValue(0)
            QApplication.processEvents()
            plugin = []
            chunk = response.read(256)

            while len(chunk) > 0:
                plugin += map(ord, chunk) # Convert plugin to list of bytes
                progress.setValue(len(plugin))
                chunk = response.read(256)

            response.close()
        except urllib2.URLError:
            progress.cancel()
            if popup:
                self.popup_fail('Bricklet', 'Could not download {0} Bricklet plugin {1}.{2}.{3}'.format(name, *version))
            return None

        return plugin

    def write_bricklet_plugin(self, plugin, device, port, name, progress, popup=True):
        # Write
        progress.setLabelText('Writing plugin: ' + name)
        progress.setMaximum(0)
        progress.setValue(0)
        progress.show()

        plugin_chunks = []
        offset = 0

        while offset < len(plugin):
            chunk = plugin[offset:offset + IPConnection.PLUGIN_CHUNK_SIZE]

            if len(chunk) < IPConnection.PLUGIN_CHUNK_SIZE:
                chunk += [0] * (IPConnection.PLUGIN_CHUNK_SIZE - len(chunk))

            plugin_chunks.append(chunk)
            offset += IPConnection.PLUGIN_CHUNK_SIZE

        progress.setMaximum(len(plugin_chunks))

        position = 0

        for chunk in plugin_chunks:
            try:
                self.parent.ipcon.write_bricklet_plugin(device, port, position, chunk)
            except Error as e:
                progress.cancel()
                if popup:
                    self.popup_fail('Bricklet', 'Could not write Bricklet plugin: ' + error_to_name(e))
                return False

            position += 1
            progress.setValue(position)

            time.sleep(0.015)
            QApplication.processEvents()

        time.sleep(0.1)

        # Verify
        progress.setLabelText('Verifying written plugin: ' + name)
        progress.setMaximum(len(plugin_chunks))
        progress.setValue(0)
        progress.show()

        time.sleep(0.1)
        position = 0

        for chunk in plugin_chunks:
            try:
                read_chunk = list(self.parent.ipcon.read_bricklet_plugin(device, port, position))
            except Error as e:
                progress.cancel()
                if popup:
                    self.popup_fail('Bricklet', 'Could not read Bricklet plugin back for verification: ' + error_to_name(e))
                return False

            if read_chunk != chunk:
                progress.cancel()
                if popup:
                    self.popup_fail('Bricklet', 'Could not flash Bricklet plugin: Verification error')
                return False

            position += 1
            progress.setValue(position)

            time.sleep(0.015)
            QApplication.processEvents()

        return True

    def plugin_save_clicked(self):
        progress = self.create_progress_bar('Flashing')
        current_text = self.combo_plugin.currentText()

        # Get plugin
        if current_text == SELECT:
            return
        elif current_text == CUSTOM:
            plugin_file_name = self.edit_custom_plugin.text()

            try:
                with open(plugin_file_name, 'rb') as f:
                    plugin = map(ord, f.read()) # Convert plugin to list of bytes
            except IOError:
                progress.cancel()
                self.popup_fail('Bricklet', 'Could not read plugin file')
                return
        else:
            url_part = self.combo_plugin.itemData(self.combo_plugin.currentIndex())
            name = self.plugin_infos[url_part].name
            version = self.plugin_infos[url_part].firmware_version_latest
            plugin = self.download_bricklet_plugin(progress, url_part, name, version)

            if not plugin:
                return

        # Flash plugin
        device, port = self.current_device_and_port()

        if current_text == CUSTOM:
            if not self.write_bricklet_plugin(plugin, device, port, os.path.split(plugin_file_name)[-1], progress):
                return
        else:
            if not self.write_bricklet_plugin(plugin, device, port, name, progress):
                return

        progress.cancel()

        if current_text == CUSTOM:
            self.popup_ok('Bricklet', 'Successfully flashed plugin.\nNew plugin will be used after reset of the connected Brick.')
        else:
            self.popup_ok('Bricklet', 'Successfully flashed {0} Bricklet plugin {1}.{2}.{3}.\nNew plugin will be used after reset of the connected Brick.'.format(name, *version))

    def current_device_and_port(self):
        port_names = ['a', 'b', 'c', 'd']

        return (self.current_device(),
                port_names[self.combo_port.currentIndex()])

    def current_device(self):
        try:
            return self.brick_infos[self.combo_brick.currentIndex()].plugin.device
        except:
            return None

    def plugin_browse_clicked(self):
        last_dir = get_home_path()

        if len(self.edit_custom_plugin.text()) > 0:
            last_dir = os.path.dirname(os.path.realpath(self.edit_custom_plugin.text()))

        filename = get_open_file_name(get_main_window(), 'Open Plugin', last_dir, '*.bin')

        if len(filename) > 0:
            self.edit_custom_plugin.setText(filename)

    def auto_update_bricklets_clicked(self):
        def brick_for_bricklet(bricklet):
            for device_info in infos.get_brick_infos():
                if bricklet.position in device_info.bricklets and \
                   device_info.bricklets[bricklet.position] == bricklet:
                    return device_info

        progress = self.create_progress_bar('Auto-Updating Bricklets')

        bricks_to_reset = set()

        for device_info in infos.get_device_infos():
            if device_info.type == 'bricklet':
                if device_info.protocol_version == 2 and device_info.firmware_version_installed < device_info.firmware_version_latest:
                    plugin = self.download_bricklet_plugin(progress, device_info.url_part, device_info.name, device_info.firmware_version_latest)

                    if not plugin:
                        progress.cancel()
                        self.refresh_updates_clicked()
                        return

                    brick = brick_for_bricklet(device_info)
                    if self.write_bricklet_plugin(plugin, brick.plugin.device, device_info.position, device_info.name, progress):
                        bricks_to_reset.add(brick)
                    else:
                        progress.cancel()
                        self.refresh_updates_clicked()
                        return
            elif device_info.type == 'brick':
                for port in device_info.bricklets:
                    if not device_info.bricklets[port]:
                        continue

                    if device_info.bricklets[port].protocol_version == 1 and \
                       device_info.bricklets[port].firmware_version_installed < device_info.bricklets[port].firmware_version_latest:
                        plugin = self.download_bricklet_plugin(progress, device_info.bricklets[port].url_part,
                                                               device_info.bricklets[port].name,
                                                               device_info.bricklets[port].firmware_version_latest)

                        if not plugin:
                            progress.cancel()
                            self.refresh_updates_clicked()
                            return

                        brick = brick_for_bricklet(device_info.bricklets[port])
                        if self.write_bricklet_plugin(plugin, brick.plugin.device, port, device_info.bricklets[port].name, progress):
                            bricks_to_reset.add(brick)
                        else:
                            progress.cancel()
                            self.refresh_updates_clicked()
                            return

        for brick in bricks_to_reset:
            try:
                brick.plugin.device.reset()
            except:
                pass

        progress.setLabelText('Waiting for Bricks to reset')
        progress.setMaximum(400)
        progress.setValue(0)

        for i in range(400):
            time.sleep(0.03)
            progress.setValue(i)

        progress.cancel()

    def tab_changed(self, i):
        if i == 0 and self.refresh_updates_pending:
            self.refresh_updates_clicked()
        elif i == 2:
            self.brick_changed(self.combo_brick.currentIndex())
            self.port_changed(self.combo_port.currentIndex())

    def refresh_updates_clicked(self):
        if self.tab_widget.currentIndex() != 0:
            self.refresh_updates_pending = True
            return

        self.update_button_refresh.setDisabled(True)

        self.refresh_updates_pending = False

        url_part_proto1_map = {
            # 'name': 'url_part'
            'Ambient Light Bricklet': 'ambient_light',
            'Analog In Bricklet': 'analog_in',
            'Analog Out Bricklet': 'analog_out',
            'Barometer Bricklet': 'barometer',
            'Current12 Bricklet': 'current12',
            'Current25 Bricklet': 'current25',
            'Distance IR Bricklet': 'distance_ir',
            'Dual Relay Bricklet': 'dual_relay',
            'GPS Bricklet': 'gps',
            'Humidity Bricklet': 'humidity',
            'Industrial Digital In 4 Bricklet': 'industrial_digital_in_4',
            'Industrial Digital Out 4 Bricklet': 'industrial_digital_out_4',
            'Industrial Quad Relay Bricklet': 'industrial_quad_relay',
            'IO-16 Bricklet': 'io16',
            'IO-4 Bricklet': 'io4',
            'Joystick Bricklet': 'joystick',
            'LCD 16x2 Bricklet': 'lcd_16x2',
            'LCD 20x4 Bricklet': 'lcd_20x4_v11',
            'Linear Poti Bricklet': 'linear_poti',
            'Piezo Buzzer Bricklet': 'piezo_buzzer',
            'Rotary Poti Bricklet': 'rotary_poti',
            'Temperature Bricklet': 'temperature',
            'Temperature-IR Bricklet': 'temperature_ir',
            'Voltage Bricklet': 'voltage',
            'Voltage/Current Bricklet': 'voltage_current',
        }

        progress = self.create_progress_bar('Discovering')
        okay = True

        try:
            urllib2.urlopen(FIRMWARE_URL, timeout=10).read()
            self.no_connection_label.hide()
        except urllib2.URLError:
            okay = False
            progress.cancel()
            self.no_connection_label.show()
            return

        if okay:
            self.refresh_latest_version_info(progress)

        def get_color_for_device(device):
            if device.firmware_version_installed >= device.firmware_version_latest:
                return None, False

            if device.firmware_version_installed[0] <= 1:
                return QBrush(Qt.red), True

            return QBrush(QColor(255, 160, 55)), True

        try:
            infos.get_info(infos.UID_BRICKV).firmware_version_latest = self.tool_infos['brickv'].firmware_version_latest
        except:
            infos.get_info(infos.UID_BRICKV).firmware_version_latest = (0, 0, 0)

        for device_info in infos.get_device_infos():
            if device_info.type == 'brick':
                try:
                    device_info.firmware_version_latest = self.firmware_infos[device_info.url_part].firmware_version_latest
                except:
                    device_info.firmware_version_latest = (0, 0, 0)
            elif device_info.type == 'bricklet':
                try:
                    device_info.firmware_version_latest = self.plugin_infos[device_info.url_part].firmware_version_latest
                except:
                    device_info.firmware_version_latest = (0, 0, 0)

        progress.cancel()

        self.update_tree_view_model.clear()
        self.update_tree_view_model.setHorizontalHeaderLabels(self.update_tree_view_model_labels)

        is_update = False
        protocol1_errors = set()
        items = []

        for device_info in infos.get_infos():
            if device_info.type == 'brick':
                parent = [QStandardItem(device_info.name),
                          QStandardItem(device_info.uid),
                          QStandardItem(get_version_string(device_info.firmware_version_installed)),
                          QStandardItem(get_version_string(device_info.firmware_version_latest))]

                color, update = get_color_for_device(device_info)
                if update:
                    is_update = True
                for item in parent:
                    item.setFlags(item.flags() & ~Qt.ItemIsEditable)
                    item.setData(color, Qt.BackgroundRole)
                parent[0].setData(device_info.uid, Qt.UserRole)
                items.append(parent)

                for port in device_info.bricklets:
                    if not device_info.bricklets[port] or device_info.bricklets[port].protocol_version == 1:
                        try:
                            protv, fw, name = device_info.plugin.device.get_protocol1_bricklet_name(port)
                        except:
                            protocol1_errors.add(device_info.uid)
                            child = [QStandardItem(port.upper() + ': Protocol 1.0 Bricklet with Error'),
                                     QStandardItem(''),
                                     QStandardItem(''),
                                     QStandardItem('')]

                            for item in child:
                                item.setFlags(item.flags() & ~Qt.ItemIsEditable)
                                item.setData(QBrush(Qt.magenta), Qt.BackgroundRole)
                            parent[0].appendRow(child)
                            continue

                        if protv == 1:
                            # Hack for LCD 20x4 Bricklet (name is not set early enough in firmware)
                            if fw == (1, 1, 1) and name == '':
                                name = 'LCD 20x4 Bricklet'
                            bricklet_info = infos.BrickletInfo()
                            bricklet_info.protocol_version = 1
                            bricklet_info.name = name
                            bricklet_info.position = port
                            bricklet_info.firmware_version_installed = tuple(fw)

                            device_info.bricklets[port] = bricklet_info
                            for key in url_part_proto1_map:
                                if key in device_info.bricklets[port].name:
                                    bricklet_info.url_part = url_part_proto1_map[key]
                                    break

                            try:
                                bricklet_info.firmware_version_latest = self.plugin_infos[bricklet_info.url_part].firmware_version_latest
                            except KeyError:
                                pass

                    if device_info.bricklets[port]:
                        child = [QStandardItem(port.upper() + ': ' + device_info.bricklets[port].name),
                                 QStandardItem(device_info.bricklets[port].uid),
                                 QStandardItem(get_version_string(device_info.bricklets[port].firmware_version_installed)),
                                 QStandardItem(get_version_string(device_info.bricklets[port].firmware_version_latest))]

                        color, update = get_color_for_device(device_info.bricklets[port])
                        if update:
                            is_update = True
                        for item in child:
                            item.setFlags(item.flags() & ~Qt.ItemIsEditable)
                            item.setData(color, Qt.BackgroundRole)
                        parent[0].appendRow(child)

            elif device_info.type == 'tool' and 'Brick Viewer' in device_info.name:
                parent = [QStandardItem(device_info.name),
                          QStandardItem(''),
                          QStandardItem(get_version_string(device_info.firmware_version_installed)),
                          QStandardItem(get_version_string(device_info.firmware_version_latest))]

                color, update = get_color_for_device(device_info)
                if update:
                    self.update_tool_label.show()
                else:
                    self.update_tool_label.hide()

                for item in parent:
                    item.setFlags(item.flags() & ~Qt.ItemIsEditable)
                    item.setData(color, Qt.BackgroundRole)
                items.append(parent)

        t = 0
        if len(protocol1_errors) > 0:
            # if there were protocol1 errors give the enumerate callback a
            # chance to update the infos to have correct information to filter
            # out false-positive protocol1 errors that were detected due to
            # fast USB unplug
            t = 200
        QTimer.singleShot(t, lambda: self.refresh_updates_clicked_second_step(is_update, items, protocol1_errors))

    def refresh_updates_clicked_second_step(self, is_update, items, protocol1_errors):
        protocol1_error_still_there = False

        # filter out false-positive protocol1 errors
        for device_uid in protocol1_errors:
            if infos.get_info(device_uid) != None:
                protocol1_error_still_there = True
                continue

            for i in range(len(items)):
                if items[i][0].data(Qt.UserRole) == device_uid:
                    del items[i]
                    break

        for item in items:
            self.update_tree_view_model.appendRow(item)

        self.update_tree_view.expandAll()
        self.update_tree_view.setColumnWidth(0, 260)
        self.update_tree_view.setColumnWidth(1, 75)
        self.update_tree_view.setColumnWidth(2, 75)
        self.update_tree_view.setColumnWidth(3, 75)
        self.update_tree_view.setSortingEnabled(True)
        self.update_tree_view.header().setSortIndicator(0, Qt.AscendingOrder)

        if is_update:
            self.update_button_bricklets.setEnabled(True)
        else:
            self.update_button_bricklets.setEnabled(False)

        self.brick_changed(self.combo_brick.currentIndex())

        self.update_button_refresh.setDisabled(False)

        if protocol1_error_still_there:
            message = """
There was an error during the auto-detection of Bricklets with Protocol 1.0 plugins. Those cannot be updated automatically, but you can update them manually:

- Disconnect the affected Bricklets from their Brick and restart the Brick without the Bricklets.
- Ensure that the Brick shows up correctly.
- Connect the Bricklet to the Brick again, while the Brick is already running.
- Select the "Bricklet" tab and update the plugin manually.
"""
            QMessageBox.critical(self, "Bricklet with Error", message, QMessageBox.Ok)
Exemple #47
0
class MonthTab(QWidget):
    """ MonthTab class for the Financeager application. """
    def __init__(self, parent=None, month=None, filled=True):
        """
        Loads the ui file and sets several attributes.
        Pass filled=True if you want to load the widget with default
        categories.

        :param      parent | FinanceagerWindow 
                    month | str 
                    filled | bool 
        :attrib     __mainWindow | FinanceagerWindow 
                    __month | str 
                    __monthIndex | int 
                    __expendituresModel | BalanceModel 
                    __receiptsModel | BalanceModel 
                    __categoriesModel | QStandardItemModel 
        """
        super(MonthTab, self).__init__(parent)
        loadUi(__file__, self)
        self.__mainWindow = parent
        self.__month = month
        self.__monthIndex = _MONTHS_.index(month)
        self.__expendituresModel = None
        self.__receiptsModel = None
        self.__categoriesModel = QStandardItemModel()
        self.setModels(filled)
        self.setViews()

    def categoriesModel(self):
        """
        Fills the categoriesModel with the month's categories 
        (both expenditures and receipts). 
        This model can be set to the ComboBoxes in NewEntryDialog and the 
        CategoriesTab of the SettingsDialog. 

        :return     QStandardItemModel 
        """
        self.__categoriesModel.clear()
        for r in range(self.__expendituresModel.rowCount()):
            item = QStandardItem(
                unicode(self.__expendituresModel.item(r).text()))
            self.__categoriesModel.appendRow(item)
        for r in range(self.__receiptsModel.rowCount()):
            item = QStandardItem(unicode(self.__receiptsModel.item(r).text()))
            self.__categoriesModel.appendRow(item)
        return self.__categoriesModel

    def categoriesStringList(self):
        """
        Returns all the names of all the child categories in a string list. 
        Called from CategoriesTab in SettingsDialog to fill the 
        removeCategoryCombo.

        :return     list[str]
        """
        categoriesModel = self.categoriesModel()
        return [
            unicode(categoriesModel.item(r).text())
            for r in range(categoriesModel.rowCount())
        ]

    def entriesStringList(self):
        """
        Returns names of all entries of the current month in a string list. To
        avoid duplication, a set is created first and then converted to a list.
        This is used to feed the QCompleter of NewEntryDialog.

        :return     list[str]
        """
        entries = set()
        for r in range(self.__expendituresModel.rowCount()):
            category = self.__expendituresModel.item(r, 0)
            for e in range(category.rowCount()):
                entries.add(unicode(category.child(e, 0).text()))
        for r in range(self.__receiptsModel.rowCount()):
            category = self.__receiptsModel.item(r, 0)
            for e in range(category.rowCount()):
                entries.add(unicode(category.child(e, 0).text()))
        return list(entries)

    def expendituresModel(self):
        return self.__expendituresModel

    def month(self):
        """
        Name of the tab's month.

        :return     str 
        """
        return self.__month

    def monthIndex(self):
        """
        Index of the tab's month. January has 0, February has 1 etc.

        :return     int 
        """
        return self.__monthIndex

    def parseXMLtoModel(self, childList, appender):
        """
        Recursive function. Takes child from childList, creates
        appropriate item and appends it to the appender. 
        At the initial call, appender is a MonthTab's model.
        Later, appender is a CategoryItem. 

        :param      childList | list of xml children 
                    appender | items.Item
        """
        if issubclass(appender.__class__, QStandardItemModel):
            appender.clear()
            appender.setHorizontalHeaderLabels(_HEADERLABELS_)
        for child in childList:
            name = unicode(child.get('name'))
            value = str(child.get('value'))
            if child.tag == 'model':
                appender.setValueItem(value)
                self.parseXMLtoModel(child.getchildren(), appender)
            elif child.tag == 'category':
                catItem = CategoryItem(name)
                appender.appendRow([catItem, SumItem(value), EmptyItem()])
                self.parseXMLtoModel(child.getchildren(), catItem)
            else:
                day = unicode(child.get('date'))
                dateItem = DateItem(day,
                                    self.monthIndex() + 1,
                                    self.__mainWindow.year())
                appender.appendRow(
                    [EntryItem(name),
                     ExpenseItem(value), dateItem], False)

    def receiptsModel(self):
        return self.__receiptsModel

    def setModels(self, filled):
        """ 
        Sets up the models for the expendituresView and receiptsView.
        Does not set the categoriesModel because expendituresModel and 
        receiptsModel are not yet filled with data when being loaded from 
        xml file (flag filled=False).
        """
        self.__expendituresModel = BalanceModel(self.__mainWindow,
                                                _EXPCATEGORIES_, filled)
        self.__receiptsModel = BalanceModel(self.__mainWindow, _RECCATEGORIES_,
                                            filled)

    def setViews(self):
        """
        Connects the tab's models to the  respective views. 
        Does some layout adjustments and sets up connections.
        Only called at initialization of MonthTab. 
        """
        self.expendituresView.setModel(self.__expendituresModel)
        self.receiptsView.setModel(self.__receiptsModel)
        for view in [self.expendituresView, self.receiptsView]:
            view.header().setResizeMode(QHeaderView.ResizeToContents)
            view.clicked.connect(self.__mainWindow.enableRemoveEntry)

    def writeToXML(self, xmlWriter, name, value, item):
        """
        Recursive function. Converts item and its children to XML.
        The initial call is from FinanceagerWindow.saveToXML(). 
        In this case, a MonthTab is passed as item. 
        In the next recursion run, the MonthTab's models are passed as items.
        Always pass the pointer to xmlWriter during recursion.
        The recursion is stopped when the EntryItem depth is reached.

        :param      xmlWriter | QtCore.QXmlStreamWriter
                    name, value | str 
                    item | items.Item 
        """
        xmlWriter.writeStartElement(item.xmlTag())
        xmlWriter.writeAttribute('name', name)
        xmlWriter.writeAttribute('value', unicode(value))
        if isinstance(item, EntryItem):
            xmlWriter.writeAttribute(
                'date', unicode(item.parent().child(item.row(), 2).text()))
        elif isinstance(item, MonthTab):
            self.writeToXML(xmlWriter, 'expenditures',
                            self.expendituresModel().value(),
                            self.expendituresModel())
            self.writeToXML(xmlWriter, 'receipts',
                            self.receiptsModel().value(), self.receiptsModel())
        elif not isinstance(item, EntryItem):
            for r in range(item.rowCount()):
                name = unicode(item.child(r, 0).text())
                value = str(item.child(r, 1).text())
                self.writeToXML(xmlWriter, name, value, item.child(r, 0))
        xmlWriter.writeEndElement()

    def xmlTag(self):
        return 'tab'
Exemple #48
0
class EntryList:
    def __init__(self, filename=None):
        self.scopes = {}
        self.topLevelEntries = []
        self.model = QStandardItemModel()
        self.model.setHorizontalHeaderLabels(["Name", "File", "Line", "Type"])
        
        if filename:
            self.readFromFile(filename)
        
    def readFromFile(self, filename):
        self.scopes = {}
        self.topLevelEntries = []
        self.model.clear()
        self.model.setHorizontalHeaderLabels(["Name", "File", "Line", "Type"])
        
        e = ctags.TagEntry()
        tf = ctags.CTags(filename)
        while tf.next(e):
            self.addFromTagEntry(e)
    
    def getScope(self, scope):
        hier = scope.split("::")
        for s in range(len(hier)):
            scopename = "::".join(hier[0:s+1])
            if scopename not in self.scopes:
                self.scopes[scopename] = Struct(self)
        return self.scopes[scope]

    def addFromTagEntry(self, e):
        kind = e['kind']
        name = e['name']
        file_ = e['file']
        lineNumber = int(e['lineNumber'])
        if e['struct']:
            scope = e['struct']
        elif e['class']:
            scope = e['class']
        else:
            scope = None
        
        # Only add a class/struct if it wasn't present in the file before; this
        # can happen if there are template specializations such as myclass and
        # myclass<T>, which are both represented as myclass in the file. If the
        # entry is present, it will have a line number, so use that as a
        # heuristic ;).
        add = True
        if kind == "class":
            n = self.getScope(scope+"::"+name if scope else name)
            if n.lineNumber == None:
                n.setValues(name, file_, lineNumber, scope, Struct.CLASS)
            else:
                add = False
        elif kind == "struct":
            n = self.getScope(scope+"::"+name if scope else name)
            if n.lineNumber == None:
                n.setValues(name, file_, lineNumber, scope, Struct.STRUCT)
            else:
                add = False
        elif kind == "function":
            n = Function(self)
            n.setValues(name, file_, lineNumber, scope, e['signature'])
        elif kind == "member" and scope: # namespace members are declared as member, but don't have a scope
            n = Member(self)
            n.setValues(name, file_, lineNumber, scope, {"private": Member.PRIVATE, "protected": Member.PROTECTED, "public": Member.PUBLIC, None: None}[e['access']])
        else:
            n = Entry(self)
            n.setValues(name, file_, lineNumber, scope)
        if add:
            n.addToParent()
Exemple #49
0
class dlg_main(QDialog):

    def __init__(self):
        QDialog.__init__(self)
        self.dlg_csv_error = dlg_csv_error(self)
        self.dlg_csv_error.initGui()

        self.kukaku_dict = OrderedDict()
        self.kukaku_dict[u"第1次地域区画"] = 4
        self.kukaku_dict[u"第2次地域区画"] = 6
        self.kukaku_dict[u"第3次地域区画"] = 8
        self.kukaku_dict[u"5倍地域メッシュ"] = 7
        self.kukaku_dict[u"2倍地域メッシュ"] = 9
        self.kukaku_dict[u"2分の1地域メッシュ"] = 9
        self.kukaku_dict[u"4分の1地域メッシュ"] = 10
        self.kukaku_dict[u"8分の1地域メッシュ"] = 11
        self.kukaku_dict[u"10分の1細分地域メッシュ"] = 10

        self.dat_str = np.array([])
        self.table_header = np.array([])
        self.csv_dat_str = np.array([])

        self.EF_dia = myFileDialog(self)

        self.f_len = np.vectorize(lambda x: len(x))

    def initGui(self):
        self.VBL_main = QVBoxLayout()

        self.HBL_selectfile = QHBoxLayout()
        self.Lab_selectfile = QLabel(u"ファイル名")
        self.LiE_selectfile = QLineEdit()
        self.PuB_selectfile = QPushButton()
        self.PuB_selectfile.setText(u"参照")

        self.HBL_selectfile.addWidget(self.Lab_selectfile)
        self.HBL_selectfile.addWidget(self.LiE_selectfile)
        self.HBL_selectfile.addWidget(self.PuB_selectfile)
        self.VBL_main.addLayout(self.HBL_selectfile)

        self.HBL_layername = QHBoxLayout()
        self.Lab_layername = QLabel(u"レイヤ名")
        self.LiE_layername = QLineEdit()

        self.HBL_layername.addWidget(self.Lab_layername)
        self.HBL_layername.addWidget(self.LiE_layername)
        self.VBL_main.addLayout(self.HBL_layername)

        self.GrL_config = QGridLayout()

        self.Lab_recopt_title = QLabel(u"レコードオプション")
        self.GrL_config.addWidget(self.Lab_recopt_title, 0, 0)

        self.HBL_recopt = QHBoxLayout()
        self.Lab_recopt = QLabel(u"無視するヘッダー行")
        self.SpB_recopt = QSpinBox()
        self.ChB_recopt = QCheckBox()
        self.ChB_recopt.setText(u"最初のレコードはフィールド名を保持している")

        self.HBL_recopt.addWidget(self.Lab_recopt)
        self.HBL_recopt.addWidget(self.SpB_recopt)
        self.HBL_recopt.addWidget(self.ChB_recopt)
        self.GrL_config.addLayout(self.HBL_recopt,0,1)

        self.Lab_mesh_titile = QLabel(u"メッシュ定義")
        self.GrL_config.addWidget(self.Lab_mesh_titile,1,0)

        self.HBL_mesh = QHBoxLayout()
        self.Lab_mesh_field = QLabel(u"メッシュコード列")
        self.CoB_mesh_field = QComboBox()

        self.Lab_mesh_category = QLabel(u"地域メッシュ区画")
        self.CoB_mesh_category = QComboBox()
        self.CoB_mesh_category.addItems(self.kukaku_dict.keys())

        self.HBL_mesh.addWidget(self.Lab_mesh_field)
        self.HBL_mesh.addWidget(self.CoB_mesh_field)
        self.HBL_mesh.addWidget(self.Lab_mesh_category)
        self.HBL_mesh.addWidget(self.CoB_mesh_category)

        self.GrL_config.addLayout(self.HBL_mesh,1,1)

        self.Lab_crs_title = QLabel(u"測地系")
        self.GrL_config.addWidget(self.Lab_crs_title,2,0)

        self.CoB_crs = QComboBox()
        self.CoB_crs.addItems([u"世界測地系(EPSG:4612)",u"日本測地系(EPSG:4301)"])
        self.GrL_config.addWidget(self.CoB_crs,2,1)

        self.VBL_main.addLayout(self.GrL_config)

        self.model_content = QStandardItemModel()
        self.TB_content = QTableView()
        self.TB_content.setModel(self.model_content)

        self.VBL_main.addWidget(self.TB_content)

        self.DBB_main = QDialogButtonBox()
        self.DBB_main.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok)

        self.VBL_main.addWidget(self.DBB_main)

        self.setLayout(self.VBL_main)

        self.ChB_recopt.setChecked(True)
        self.ChB_recopt.stateChanged.connect(self.update_csv_str)
        self.SpB_recopt.valueChanged.connect(self.update_csv_str)
        self.PuB_selectfile.clicked.connect(self.EF_dia.open)

        self.DBB_main.accepted.connect(self.accept)
        self.DBB_main.rejected.connect(self.reject)


    def update_csv_str(self):
        if len(self.dat_str) == 0:
            pass
        else:
            if  self.ChB_recopt.checkState() == 0:
                csv_header = np.array(["field_{0}".format(x) for x in np.arange(0,self.dat_str.shape[1])])
                self.csv_dat_str = self.dat_str[(self.SpB_recopt.value()):,]
            else:
                csv_header = self.dat_str[self.SpB_recopt.value(),]
                self.csv_dat_str =self.dat_str[(self.SpB_recopt.value()+1):,]

            self.clear()

            self.table_header = np.array([hstr if hstr != u"" else 'field_{0}'.format(i) for i,hstr in enumerate(csv_header)])


            load_table(self.model_content, self.table_header, np.arange(0,11), self.csv_dat_str)
            self.CoB_mesh_field.addItems(self.table_header)

    def clear(self):
        self.model_content.clear()
        self.CoB_mesh_field.clear()

    def accept(self):
        if len(self.LiE_selectfile.text()) == 0:
            QMessageBox.warning(self,u"警告",u"ファイルを選択してください")
        elif len(self.LiE_layername.text()) == 0:
            QMessageBox.warning(self,u"警告",u"レイヤ名を入力してください")
        else:
            self.csv_meshid_str = self.csv_dat_str[:,self.CoB_mesh_field.currentIndex()]

            if self.check_decimal():
                self.dlg_csv_error.LB_caution.setText(u"以下のデータのメッシュコードに空白\nまたは数値以外の文字が含まれています")
                load_table(self.dlg_csv_error.model_content,self.table_header, self.e_ind, self.csv_dat_str)
                self.dlg_csv_error.show()
            elif self.check_digit():
                str = u"以下のデータのメッシュコードの桁数が不正です\n"
                str += u'{0}コードは{1}桁の整数です。'.format(self.CoB_mesh_category.currentText(),
                                                  self.kukaku_dict[self.CoB_mesh_category.currentText()])
                self.dlg_csv_error.LB_caution.setText(str)
                load_table(self.dlg_csv_error.model_content,self.table_header, self.e_ind, self.csv_dat_str)
                self.dlg_csv_error.show()
            elif self.check_unique():
                str = u'以下のデータのメッシュコードが重複しています'
                self.dlg_csv_error.LB_caution.setText(str)
                load_table(self.dlg_csv_error.model_content,self.table_header, self.e_ind, self.csv_dat_str)
                self.dlg_csv_error.show()

            else:
                self.create_tmp_csv()
                self.load_wkt_csv()
                return QDialog.accept(self)

    def check_decimal(self):
        e_ind = np.where(~def_c.isdecimal(self.csv_meshid_str))[0]

        if len(e_ind) > 0:
            self.e_ind = e_ind
            return True
        else:
            return False

    def check_digit(self):
        digit_arr = self.f_len(self.csv_meshid_str)
        e_ind = np.where( digit_arr != self.kukaku_dict[self.CoB_mesh_category.currentText()])[0]

        if len(e_ind) > 0:
            self.e_ind = e_ind
            return True
        else:
            return False

    def check_unique(self):
        u,c = np.unique(self.csv_meshid_str,return_counts=True)
        tf = c > 1

        if tf.any():
            self.e_ind = np.where(np.in1d(self.csv_meshid_str, u[tf]))[0]
            return True
        else:
            return False

    def create_tmp_csv(self):



        m_wkt = mesh_wkt(self.CoB_mesh_category.currentText())

        infile_qstr = self.LiE_selectfile.text()
        self.outfile_qstr = self.LiE_selectfile.text().replace(".","_wkt.")

        in_fp = open(infile_qstr,"r")
        out_fp = open(self.outfile_qstr,"wb")
        reader = csv.reader(in_fp)
        writer = csv.writer(out_fp)

        n_skip = self.SpB_recopt.value()
        if n_skip > 0:
            for i in range(0,n_skip):
                next(reader,None)

        if self.ChB_recopt.checkState() == 2:
            header = next(reader,None)
            header.insert(0,"wkt")
            writer.writerow(header)

        for hrow in csv.reader(in_fp):
            hrow.insert(0,m_wkt.res_wkt(hrow[self.CoB_mesh_field.currentIndex()]))
            writer.writerow(hrow)

        in_fp.close()
        out_fp.close()

    def load_wkt_csv(self):
        uri = QUrl.fromLocalFile(self.outfile_qstr)
        uri.addQueryItem("type","csv")
        uri.addQueryItem("delimiter",",")
        uri.addQueryItem("wktField","1")
        uri.addQueryItem("encoding",self.EF_dia.encoding())

        if self.ChB_recopt.checkState() == 2:
            uri.addQueryItem("useHeader","yes")
        else:
            uri.addQueryItem("useHeader","no")

        if self.CoB_crs.currentIndex() == 0:
            uri.addQueryItem("crs","EPSG:4612")
        elif self.CoB_crs.currentIndex() == 0:
            uri.addQueryItem("crs","EPSG:4301")

        self.vlayer = QgsVectorLayer(uri.toString(),self.LiE_layername.text(),"delimitedtext")

        if self.vlayer.isValid():
            QgsMapLayerRegistry.instance().addMapLayer( self.vlayer )
Exemple #50
0
class ListWidget(EditorWidget):
    widgettype = 'List'

    def __init__(self, *args, **kwargs):
        super(ListWidget, self).__init__(*args)
        self.listmodel = QStandardItemModel()
        self._bindvalue = None

    def createWidget(self, parent):
        return QComboBox(parent)

    def _buildfromlist(self, widget, listconfig):
        items = listconfig['items']
        for item in items:
            parts = item.split(';')
            data = parts[0]
            try:
                desc = parts[1]
            except IndexError:
                desc = data

            try:
                path = parts[2]
                path = path.strip()
                icon = QIcon(path)
            except:
                icon = QIcon()

            item = QStandardItem(desc)
            item.setData(data, Qt.UserRole)
            item.setIcon(icon)
            self.listmodel.appendRow(item)

    def _buildfromlayer(self, widget, layerconfig):
        layername = layerconfig['layer']
        keyfield = layerconfig['key']
        valuefield = layerconfig['value']
        filterexp = layerconfig.get('filter', None)

        try:
            layer = QgsMapLayerRegistry.instance().mapLayersByName(
                layername)[0]
        except IndexError:
            roam.utils.warning(
                "Can't find layer {} in project".format(layername))
            return

        keyfieldindex = layer.fieldNameIndex(keyfield)
        valuefieldindex = layer.fieldNameIndex(valuefield)
        if keyfieldindex == -1 or valuefieldindex == -1:
            roam.utils.warning("Can't find key or value column")
            return

        if self.allownulls:
            item = QStandardItem('(no selection)')
            item.setData(None, Qt.UserRole)
            self.listmodel.appendRow(item)

        attributes = {keyfieldindex, valuefieldindex}
        iconfieldindex = layer.fieldNameIndex('icon')
        if iconfieldindex > -1:
            attributes.add(iconfieldindex)

        if not filterexp and valuefieldindex == keyfieldindex and iconfieldindex == -1:
            values = layer.uniqueValues(keyfieldindex)
            for value in values:
                value = nullconvert(value)
                item = QStandardItem(value)
                item.setData(value, Qt.UserRole)
                self.listmodel.appendRow(item)
            return

        flags = QgsFeatureRequest.NoGeometry

        expression = None
        if filterexp:
            expression = QgsExpression(filterexp)
            expression.prepare(layer.pendingFields())
            if expression.hasParserError():
                roam.utils.warning("Expression has parser error: {}".format(
                    expression.parserErrorString()))
                return

            if expression.needsGeometry():
                flags = QgsFeatureRequest.NoFlags

            for field in expression.referencedColumns():
                index = layer.fieldNameIndex(field)
                attributes.add(index)

        request = QgsFeatureRequest().setFlags(flags).setSubsetOfAttributes(
            list(attributes))
        for feature in layer.getFeatures(request):
            if expression and not expression.evaluate(feature):
                continue

            keyvalue = nullconvert(feature[keyfieldindex])
            valuvalue = nullconvert(feature[valuefield])
            try:
                path = feature[iconfieldindex]
                icon = QIcon(path)
            except KeyError:
                icon = QIcon()

            item = QStandardItem(unicode(keyvalue))
            item.setData(unicode(valuvalue), Qt.UserRole)
            item.setIcon(icon)
            self.listmodel.appendRow(item)

    def initWidget(self, widget, config):
        if widget.isEditable():
            widget.editTextChanged.connect(self.emitvaluechanged)

        widget.currentIndexChanged.connect(self.emitvaluechanged)
        widget.setModel(self.listmodel)
        widget.showPopup = self.showpopup
        widget.setIconSize(QSize(24, 24))
        widget.setStyleSheet(
            "QComboBox::drop-down {border-width: 0px;} QComboBox::down-arrow {image: url(noimg); border-width: 0px;}"
        )
        widget.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLength)

    def showpopup(self):
        if self.listmodel.rowCount() == 0:
            return

        self.largewidgetrequest.emit(
            BigListWidget, self.widget.currentIndex(), self._biglistitem,
            dict(model=self.listmodel, label=self.labeltext))

    def updatefromconfig(self):
        super(ListWidget, self).updatefromconfig()

        self.listmodel.clear()
        if 'list' in self.config:
            listconfig = self.config['list']
            self._buildfromlist(self.widget, listconfig)
        elif 'layer' in self.config:
            layerconfig = self.config['layer']
            self._buildfromlayer(self.widget, layerconfig)

        super(ListWidget, self).endupdatefromconfig()

    @property
    def allownulls(self):
        return self.config.get('allownull', False)

    def validate(self, *args):
        if (not self.widget.currentText() == ''
                and not self.widget.currentText() == "(no selection)"):
            return True
        else:
            return False

    def _biglistitem(self, index):
        self.widget.setCurrentIndex(index.row())

    def setvalue(self, value):
        self._bindvalue = value
        index = self.widget.findData(value)
        self.widget.setCurrentIndex(index)
        if index == -1 and self.widget.isEditable():
            if value is None and not self.config['allownull']:
                return

            self.widget.addItem(str(value))
            index = self.widget.count() - 1
            self.widget.setCurrentIndex(index)

    def value(self):
        index = self.widget.currentIndex()
        value = self.widget.itemData(index)
        text = self.widget.currentText()
        if value is None and self.widget.isEditable(
        ) and not text == '(no selection)':
            return self.widget.currentText()

        return value
class SharedSqlQueries:
    """QGIS Plugin Implementation."""

    def __init__(self, iface):
        """Constructor.

        :param iface: An interface instance that will be passed to this class
            which provides the hook by which you can manipulate the QGIS
            application at run time.
        :type iface: QgsInterface
        """
        # Save reference to the QGIS interface
        self.iface = iface

        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)

        # initialize locale UNUSED
        # locale = QSettings().value('locale/userLocale')[0:2]
        # locale_path = os.path.join(
        #     self.plugin_dir,
        #     'i18n',
        #     'SharedSqlQueries_{}.qm'.format(locale))
        #
        # if os.path.exists(locale_path):
        #     self.translator = QTranslator()
        #     self.translator.load(locale_path)
        #
        #     if qVersion() > '4.3.3':
        #         QCoreApplication.installTranslator(self.translator)

        # Declare instance attributes
        self.actions = []
        self.menu = self.tr(u'&Shared SQL Queries')

        self.toolbar = self.iface.addToolBar(u'SharedSqlQueries')
        self.toolbar.setObjectName(u'SharedSqlQueries')

        #print "** INITIALIZING SharedSqlQueries"

        #self.dockwidget = None

        #combo of queries files
        self.comboxQueries = None

        self.config = None
        self.queriesFolder = None
        self.dbrequest = None

        self.selectedQueryPath = None

        self.pluginIsActive = False

    # init related to config file. Return False if no config.json has been found
    def init_config(self):

        #just once
        if self.config is not None:
            return True

        #config file (in plugin directory) :
        configpath = os.path.dirname(__file__) + '/config.json'
        try:
            self.config = JsonFile(configpath)
        except IOError:
            # copy default config json if it does not exist
            self.errorMessage(self.tr(
                u"No config.json file found ! A default one is created but you have to edit it (in your plugin directory)"))
            configpath_default = os.path.dirname(__file__) + '/config_default.json'
            copyfile(configpath_default, configpath)
            return False

        self.queriesFolder = self.config.value("queries_folder")

        #database
        self.dbrequest = Connection(self.config.value("bdpostgis"))

        return True


    # noinspection PyMethodMayBeStatic
    def tr(self, message):
        """Get the translation for a string using Qt translation API.

        We implement this ourselves since we do not inherit QObject.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString
        """
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        #return QCoreApplication.translate('SharedSqlQueries', message)

        return translate.tr(message) #simplier


    def add_action(
        self,
        icon_path,
        text,
        callback,
        enabled_flag=True,
        add_to_menu=True,
        add_to_toolbar=True,
        status_tip=None,
        whats_this=None,
        parent=None):
        """Add a toolbar icon to the toolbar.

        :param icon_path: Path to the icon for this action. Can be a resource
            path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
        :type icon_path: str

        :param text: Text that should be shown in menu items for this action.
        :type text: str

        :param callback: Function to be called when the action is triggered.
        :type callback: function

        :param enabled_flag: A flag indicating if the action should be enabled
            by default. Defaults to True.
        :type enabled_flag: bool

        :param add_to_menu: Flag indicating whether the action should also
            be added to the menu. Defaults to True.
        :type add_to_menu: bool

        :param add_to_toolbar: Flag indicating whether the action should also
            be added to the toolbar. Defaults to True.
        :type add_to_toolbar: bool

        :param status_tip: Optional text to show in a popup when mouse pointer
            hovers over the action.
        :type status_tip: str

        :param parent: Parent widget for the new action. Defaults None.
        :type parent: QWidget

        :param whats_this: Optional text to show in the status bar when the
            mouse pointer hovers over the action.

        :returns: The action that was created. Note that the action is also
            added to self.actions list.
        :rtype: QAction
        """

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            self.toolbar.addAction(action)

        if add_to_menu:
            self.iface.addPluginToMenu(
                self.menu,
                action)

        self.actions.append(action)

        return action


    def initGui(self):
        """Create the menu entries and toolbar icons inside the QGIS GUI."""

        icon_path = ':/plugins/SharedSqlQueries/icon.png'
        self.add_action(
            icon_path,
            text=self.tr(u'Shared SQL Queries'),
            callback=self.run,
            parent=self.iface.mainWindow())

        #combo of queries files
        self.comboxQueries = QComboBox()
        self.comboxQueries.setMinimumHeight(27)
        self.comboxQueries.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        # its model :
        self.queriesModel = QStandardItemModel()
        self.comboxQueries.setModel(self.queriesModel)

        # and its view (treeview) :
        self.queriesView = QTreeView()
        self.queriesView.setHeaderHidden(True)
        self.queriesView.setMinimumHeight(300)
        setWidgetWidth(self.comboxQueries, 0, 0) #no visible
        self.comboxQueries.setView(self.queriesView)

        # capture last clicked query
        self.queriesView.activated.connect(self.querySelected)
        self.queriesView.pressed.connect(self.querySelected)




        self.toolbar.addWidget(self.comboxQueries)

        #Run query button
        self.buttonRunQuery = QPushButton(self.tr("Open"))
        setWidgetWidth(self.buttonRunQuery, 0, 0) #no visible
        self.buttonRunQuery.clicked.connect(self.runQuery)

        self.toolbar.addWidget(self.buttonRunQuery)





    #--------------------------------------------------------------------------

    def onClosePlugin(self):
        """Cleanup necessary items here when plugin dockwidget is closed"""

        #print "** CLOSING SharedSqlQueries"

        # disconnects
        #self.dockwidget.closingPlugin.disconnect(self.onClosePlugin)

        self.pluginIsActive = False


    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""

        #print "** UNLOAD SharedSqlQueries"

        for action in self.actions:
            self.iface.removePluginMenu(
                self.tr(u'Shared SQL Queries'),
                action)
            self.iface.removeToolBarIcon(action)
        # remove the toolbar
        del self.toolbar

    #--------------------------------------------------------------------------

    def run(self):
        """Run method that loads and starts the plugin"""

        # look for config file (required at the first run)
        if not self.init_config():
            # invalid config file
            return

        if not self.pluginIsActive:
            self.pluginIsActive = True

            #first init

            #print "** STARTING SharedSqlQueries"

            # dockwidget may not exist if:
            #    first run of plugin
            #    removed on close (see self.onClosePlugin method)
            #if self.dockwidget == None:
                # Create the dockwidget (after translation) and keep reference
            #    self.dockwidget = SharedSqlQueriesDockWidget()

            # connect to provide cleanup on closing of dockwidget
            #self.dockwidget.closingPlugin.connect(self.onClosePlugin)

            # show the dockwidget
            #self.iface.addDockWidget(Qt.TopDockWidgetArea, self.dockwidget)
            #self.dockwidget.show()

        #Togle visibility of toolbar options (set width coz visible is not usable in toolbar)
        show_options = (self.comboxQueries.minimumWidth() == 0)
        if show_options:
            self.updateComboQueries()
            setWidgetWidth(self.comboxQueries, 300, 300)
            setWidgetWidth(self.buttonRunQuery, 0, 120)
        else:
            setWidgetWidth(self.comboxQueries, 0, 0)
            setWidgetWidth(self.buttonRunQuery, 0, 0)


    #display an error
    def errorMessage(self, message):
        self.iface.messageBar().pushMessage(self.tr(u"Error"), message, level=QgsMessageBar.CRITICAL)

    #read file in query folder and show them in combo tree view
    def updateComboQueries(self):
        self.queriesModel.clear()
        self.queriesModel.setHorizontalHeaderLabels(['Files'])

        item = QStandardItem(self.tr(u"Query File"))
        item.setSelectable(False)
        self.queriesModel.appendRow(item)

        # read directories with sql files
        for path, dirs, files in os.walk(self.queriesFolder):
            for rep in dirs:
                item = QStandardItem(rep)
                item.setData(rep, Qt.UserRole)
                item.setSelectable(False)
                self.queriesModel.appendRow(item)
                # in each directory, look for sql files
                for nomfich in glob.glob(self.queriesFolder + "/" + rep + "/*.sql"):
                    fileName, fileExtension = os.path.splitext(os.path.basename(nomfich))

                    # one item found
                    subitem = QStandardItem(fileName)
                    subitem.setData(nomfich, Qt.UserRole)

                    item.appendRow(subitem)



    #last selected query
    def querySelected(self, index):
        item = self.queriesModel.itemFromIndex(index)
        self.selectedQueryPath = item.data(Qt.UserRole)

    #run selected query
    def runQuery(self):
        #print self.comboxQueries.currentText()
        #print self.selectedQueryPath

        try:
            query = CustomSqlQuery(self.selectedQueryPath)
        except UnicodeDecodeError:
            self.errorMessage(self.tr(u"Query File is not UTF8 encoded ! Please convert it to UTF8 !"))
            return
        except SyntaxError as e:
            self.errorMessage(e.text)
            return
        except Exception as e:
            self.errorMessage(str(e))
            return

        # open param dialog
        dialog = QueryParamDialog(self.iface, self.dbrequest, query, self.toolbar)
        if dialog.exec_() == QDialog.Accepted:

            if dialog.errorMessage != "":
                self.errorMessage(dialog.errorMessage)
                return

            # format query as a Qgis readable sql source
            sql = query.updateFinalSql()

            QgsMessageLog.logMessage(sql, "SharedSql", QgsMessageLog.INFO)

            # add the corresponding layer
            try:

                # save query in a memory layer
                if query.headerValue("layer storage") == "memory":
                    layer = self.dbrequest.sqlAddMemoryLayer(sql, query.headerValue("layer name"), query.headerValue("gid"), query.headerValue("geom"))

                # save query directly as a sql layer
                elif query.headerValue("layer storage") == "source":
                    layer = self.dbrequest.sqlAddLayer(sql, query.headerValue("layer name"), query.headerValue("gid"), query.headerValue("geom"))

                # save query in a file layer
                else:
                    type = query.headerValue("layer storage").lower()
                    driver = None
                    if type == "geojson":
                        driver = "GeoJSON"
                    if type == "shp":
                        driver = "ESRI Shapefile"

                    if driver is None:
                        self.errorMessage(self.tr(u"Unknown file type : ") + str(type))
                        return

                    directory = query.headerValue("layer directory")
                    if directory is None:
                        self.errorMessage(self.tr(u"No layer directory parameter found in query !"))
                        return
                    name = query.headerValue("layer name")

                    # new layer name and file name if file already exists
                    filepath = directory + "/" + name + "." + type
                    filecount = 1
                    new_name = name
                    while os.path.exists(filepath):
                        # file already exists
                        filecount += 1
                        new_name = name + "_" + str(filecount)
                        filepath = directory + "/" + new_name + "." + type
                    name = new_name


                    #wait cursor
                    QApplication.setOverrideCursor(Qt.WaitCursor)

                    # add new layer
                    layer = self.dbrequest.sqlAddFileLayer(sql, driver, filepath, name,
                                    query.headerValue("gid"), query.headerValue("geom"))

                    QApplication.setOverrideCursor(Qt.ArrowCursor)



            except SyntaxError as e:
                QApplication.setOverrideCursor(Qt.ArrowCursor)
                # sql is correct but does not fit QGIS requirement (like '%' char)
                self.errorMessage(self.tr(e.text))
                return

            if layer is None:
                self.errorMessage(self.tr(u"Unable to add a layer corresponding to this query !") + sql)
                # sql which is used in layer query
                print makeSqlValidForLayer(sql)
                return

            # if there's a qml style file corresponding to the query, apply it to the newly added layer
            if os.path.exists(query.styleFilePath()):
                layer.loadNamedStyle(query.styleFilePath())
class dbmanagerUI(QMainWindow, ui_dbmanager.Ui_dbmanagerUI):
    """Main UI for MASAR database manager."""
    def __init__(self):
        """"""
        super(dbmanagerUI, self).__init__()
        self.setupUi(self)
        self.statusbar.showMessage("Ready")

        exitAction = QtGui.QAction(QtGui.QIcon('exit.png'), '&Exit', self)
        exitAction.setShortcut('Ctrl+Q')
        exitAction.setStatusTip('Exit Masar Configuration Manager.')
        exitAction.triggered.connect(QtGui.qApp.quit)

        self.groupdatabasemenubar()

        #default database source, could be 0: SQLite, 1: MongoDB, and 2: MySQL
        self.dbsource = None

        self.defaultdbinfo = self._loadmasarconfig()
        self.usedefaultdb = True

        self.comboxboxSignalMapper = QtCore.QSignalMapper(self)
        self.comboxboxSignalMapper.mapped[QtGui.QWidget].connect(
            self.comboboxSignalMapperMapped)

        self.pushbuttonSignalMapper = QtCore.QSignalMapper(self)
        self.pushbuttonSignalMapper.mapped[QtGui.QWidget].connect(
            self.pushbuttonSignalMapperMapped)

        self.showpvbuttonSignalMapper = QtCore.QSignalMapper(self)
        self.showpvbuttonSignalMapper.mapped[QtGui.QWidget].connect(
            self.showpvbuttonSignalMapperMapped)

        self.choosepvbuttonSignalMapper = QtCore.QSignalMapper(self)
        self.choosepvbuttonSignalMapper.mapped[QtGui.QWidget].connect(
            self.choosepvbuttonSignalMapperMapped)

        self.currentselectedrow4config = -1

        self.selectedsystem = "Others"

        # self.pvgrouptreeview = QTreeView()
        self.pvGroupTreeView.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.pvgroupmodel = QStandardItemModel()
        # self.pvgroupmodel.setHorizontalHeaderLabels(['id', 'date', 'version', "description"])
        self.pvgroupmodel.setHorizontalHeaderLabels(["PV Groups"])
        self.pvGroupTreeView.setModel(self.pvgroupmodel)
        self.pvGroupTreeView.setUniformRowHeights(True)

    @QtCore.pyqtSlot(QtGui.QWidget)
    def comboboxSignalMapperMapped(self, comboBox):
        if self.masarConfigTableWidget.cellWidget(
                comboBox.row, comboBox.column + 1).isEnabled():
            self.masarConfigTableWidget.cellWidget(
                comboBox.row, comboBox.column + 1).setEnabled(False)
            button = self.masarConfigTableWidget.cellWidget(
                comboBox.row, comboBox.column + 1)
            palette = QtGui.QPalette(
                button.palette())  # make a copy of the palette
            palette.setColor(QtGui.QPalette.ButtonText, QtGui.QColor('grey'))
            button.setPalette(palette)
        else:
            self.masarConfigTableWidget.cellWidget(
                comboBox.row, comboBox.column + 1).setEnabled(True)
            button = self.masarConfigTableWidget.cellWidget(
                comboBox.row, comboBox.column + 1)
            palette = QtGui.QPalette(
                button.palette())  # make a copy of the palette
            palette.setColor(QtGui.QPalette.ButtonText, QtGui.QColor('red'))
            button.setPalette(palette)

    @QtCore.pyqtSlot(QtGui.QWidget)
    def pushbuttonSignalMapperMapped(self, pushbutton):
        configstatus = str(
            self.masarConfigTableWidget.cellWidget(
                pushbutton.row, pushbutton.column - 1).currentText())
        cid = str(self.masarConfigTableWidget.item(pushbutton.row, 0).text())
        cname = str(self.masarConfigTableWidget.item(pushbutton.row, 1).text())

        if self.updatemasarconfigstatus(configstatus, cid, configname=cname):
            palette = QtGui.QPalette(
                pushbutton.palette())  # make a copy of the palette
            palette.setColor(QtGui.QPalette.ButtonText, QtGui.QColor('grey'))
            pushbutton.setPalette(palette)
            pushbutton.setEnabled(False)

    @QtCore.pyqtSlot(QtGui.QWidget)
    def showpvbuttonSignalMapperMapped(self, showpvbutton):
        if self.newConfigTableWidget.item(showpvbutton.row,
                                          showpvbutton.column + 2) is None:
            raise RuntimeError("Unknown pv file")
        pvfilename = self.newConfigTableWidget.item(
            showpvbutton.row, showpvbutton.column + 2).text()
        if not os.path.isfile(pvfilename):
            raise RuntimeError(
                "Invalid pv file name for (row, col): (%s, %s)" %
                (showpvbutton.row, showpvbutton.column + 2))

        text = ", ".join(np.loadtxt(str(pvfilename), dtype=str, comments="#"))
        if self.newConfigTableWidget.item(showpvbutton.row, 0) is not None:
            head = self.newConfigTableWidget.item(showpvbutton.row, 0).text()
        else:
            head = ""
        msg = QMessageBox(self,
                          windowTitle='PVs for group %s' % head,
                          text="The following PVs to be added:")
        msg.setDetailedText(text)
        msg.exec_()

    @QtCore.pyqtSlot(QtGui.QWidget)
    def choosepvbuttonSignalMapperMapped(self, chhosepvbutton):
        self.newConfigTableWidget.setItem(
            chhosepvbutton.row, chhosepvbutton.column + 1,
            QTableWidgetItem(QFileDialog.getOpenFileName(self, "Open File")))

    def _loadmasarconfig(self):
        cf = ConfigParser.SafeConfigParser()
        cf.read([
            os.path.expanduser('~/.masarservice.conf'),
            '/etc/masarservice.conf', 'masarservice.conf',
            "%s/masarservice.conf" % os.path.abspath(os.path.dirname(__file__))
        ])
        return cf

    def groupdatabasemenubar(self):
        """Group 3 Databases menu together to make selection exclusive."""
        group = QtGui.QActionGroup(self)
        self.actionSQLite.setActionGroup(group)
        self.actionMongoDB.setActionGroup(group)
        self.actionMySQL.setActionGroup(group)

    def actionsqlitemenu(self):
        """Answer action when SQLite is selected."""
        if self.actionSQLite.isChecked():
            self.dbsource = 0
            self.defaultsqlitedb()
            self.listPvGroupPushButton.setEnabled(True)

    def actionmongodbmenu(self):
        """Answer action when MongoDB is selected."""
        if self.actionMongoDB.isChecked():
            self.dbsource = 1
            self.defaultmongodb()
            self.listPvGroupPushButton.setEnabled(False)

    def actionmysqlmenu(self):
        """Answer action when MySQL is selected."""
        if self.actionMySQL.isChecked():
            QMessageBox.warning(self, 'Warning',
                                "MySQL support not implemented yet.")
            self.actionMySQL.setChecked(False)
            if self.dbsource == 0:
                self.actionSQLite.setChecked(True)
            elif self.dbsource == 1:
                self.actionMongoDB.setChecked(True)

    def showdefaultdbinfo(self):
        """"""
        if self.dbsource == 0:
            self.defaultsqlitedb()
        elif self.dbsource == 1:
            self.defaultmongodb()
        elif self.dbsource == 2:
            QMessageBox.warning(self, 'Warning',
                                "MySQL support not implemented yet.")

    def defaultsqlitedb(self):
        """"""
        if self.defaultdbinfo.has_section("sqlite"):
            self.databaseDefault.setText(
                self.defaultdbinfo.get("sqlite", "database"))
            self.hostDefault.clear()
            self.portDefault.clear()
            self.userDefault.clear()

    def defaultmongodb(self):
        """"""
        if self.defaultdbinfo.has_section("mongodb"):
            self.databaseDefault.setText(
                self.defaultdbinfo.get("mongodb", "database"))
            self.hostDefault.setText(self.defaultdbinfo.get("mongodb", "host"))
            self.portDefault.setText(self.defaultdbinfo.get("mongodb", "port"))
            self.userDefault.setText(
                self.defaultdbinfo.get("mongodb", "username"))

    def getdatabasename(self):
        """"""
        self.database = self.databaseLineEdit.text()

    def getdatabaseport(self):
        """"""
        self.databaseport = self.databaseportLineEdit.text()

    def getdatabasehost(self):
        """"""
        self.databasehost = self.databaseHostLineEdit.text()

    def getdatabasepw(self):
        """"""
        self.databasepw = self.databasePwLineEdit.text()

    def showmasarconfigs(self):
        """"""
        result = None
        if self.dbsource == 0:
            # get data from sqlite
            if self.usedefaultdb:
                # masardb = str(self.databaseDefault.text())
                masardb = str(self.databaseDefault.toPlainText())
            else:
                masardb = str(self.databaseLineEdit.text())

            if masardb != "":
                os.environ["MASAR_SQLITE_DB"] = masardb
            else:
                raise RuntimeError("Cannot find MASAR SQLite Database")

            import pymasarsqlite
            conn = pymasarsqlite.utils.connect()
            result = pymasarsqlite.service.retrieveServiceConfigs(
                conn, servicename="masar")
            pymasarsqlite.utils.close(conn)

        elif self.dbsource == 1:
            # get data from mongodb
            if self.usedefaultdb:
                database = str(self.databaseDefault.toPlainText())
                host = str(self.hostDefault.toPlainText())
                port = str(self.portDefault.toPlainText())
            else:
                database = str(self.databaseLineEdit.text())
                host = str(self.databaseHostLineEdit.text())
                port = str(self.databasePortLineEdit.text())

            import pymasarmongo
            mongoconn, collection = pymasarmongo.db.utils.conn(host=host,
                                                               port=port,
                                                               db=database)
            resultdict = pymasarmongo.pymasarmongo.pymasar.retrieveconfig(
                mongoconn, collection)
            pymasarmongo.db.utils.close(mongoconn)

            result = [['id', 'name', 'desc', 'date', 'version', 'status']]
            for res in resultdict:
                result.append([
                    res['configidx'], res['name'], res['desc'],
                    res['created_on'], res['version'], res['status']
                ])
                # res['system']

        if result is not None:
            self._setconfigtable(result)

    def choosedbsrc(self, bool):
        """Choose DB source"""
        if bool:
            self.usedefaultdb = True
        else:
            self.usedefaultdb = False

    def _setconfigtable(self, content):
        """"""
        # head = self.masarConfigTableWidget.horizontalHeader()
        self.masarConfigTableWidget.clearContents()
        # self.masarConfigTableWidget.setHorizontalHeaderLabels(head)

        if len(content) > 1:
            self.masarConfigTableWidget.setRowCount(len(content) - 1)

            n = 0
            data = sorted(content[1:], key=itemgetter(0), reverse=True)
            for res in data:
                m = 0
                for item in res:
                    if not isinstance(item, basestring):
                        item = str(item)
                    if item:
                        if m == 5:
                            newitem = QtGui.QComboBox()
                            newitem.addItem("active")
                            newitem.addItem("inactive")
                            if item == "active":
                                newitem.setCurrentIndex(0)
                            else:
                                newitem.setCurrentIndex(1)

                            newitem.row = n
                            newitem.column = m
                            self.masarConfigTableWidget.setCellWidget(
                                n, m, newitem)
                            self.comboxboxSignalMapper.setMapping(
                                newitem, newitem)
                            newitem.currentIndexChanged.connect(
                                self.comboxboxSignalMapper.map)

                            updatebutton = QtGui.QPushButton()
                            updatebutton.setText("Update")
                            updatebutton.setEnabled(False)
                            updatebutton.row = n
                            updatebutton.column = m + 1
                            self.pushbuttonSignalMapper.setMapping(
                                updatebutton, updatebutton)
                            self.masarConfigTableWidget.setCellWidget(
                                n, m + 1, updatebutton)
                            updatebutton.clicked.connect(
                                self.pushbuttonSignalMapper.map)
                        else:
                            newitem = QTableWidgetItem(item)
                            newitem.setFlags(Qt.ItemIsEnabled
                                             | Qt.ItemIsSelectable)
                            self.masarConfigTableWidget.setItem(n, m, newitem)
                    m += 1
                n += 1

            self.masarConfigTableWidget.resizeColumnsToContents()

    def updatemasarconfigstatus(self, configstatus, cid, configname=None):
        if self.dbsource == 0:
            # get data from sqlite
            if self.usedefaultdb:
                masardb = str(self.databaseDefault.toPlainText())
            else:
                masardb = str(self.databaseLineEdit.text())

            if masardb != "":
                os.environ["MASAR_SQLITE_DB"] = masardb
            else:
                raise RuntimeError("Cannot find MASAR SQLite Database")

            import pymasarsqlite

            conn = pymasarsqlite.utils.connect()
            pymasarsqlite.service.updateServiceConfigStatus(
                conn, cid, status=configstatus)
            pymasarsqlite.utils.save(conn)
            pymasarsqlite.utils.close(conn)

        elif self.dbsource == 1:
            # get data from mongodb
            if self.usedefaultdb:
                database = str(self.databaseDefault.toPlainText())
                host = str(self.hostDefault.toPlainText())
                port = str(self.portDefault.toPlainText())
            else:
                database = str(self.databaseLineEdit.text())
                host = str(self.databaseHostLineEdit.text())
                port = str(self.databasePortLineEdit.text())

            import pymasarmongo

            mongoconn, collection = pymasarmongo.db.utils.conn(host=host,
                                                               port=port,
                                                               db=database)
            pymasarmongo.pymasarmongo.pymasar.updateconfig(mongoconn,
                                                           collection,
                                                           configname,
                                                           configidx=int(cid),
                                                           status=configstatus)
            pymasarmongo.db.utils.close(mongoconn)

        return True

    def addnewpvgrouprow(self):
        """Add a new row to add pv group to MASAR configuration"""
        print("""Add a new row to add pv group to MASAR configuration""")
        currowcount = self.newConfigTableWidget.rowCount()
        self.newConfigTableWidget.setRowCount(currowcount + 1)

        showpvbutton = QtGui.QPushButton()
        showpvbutton.setText("Show PVs")
        showpvbutton.setEnabled(True)
        showpvbutton.row = currowcount
        showpvbutton.column = 2
        self.showpvbuttonSignalMapper.setMapping(showpvbutton, showpvbutton)
        self.newConfigTableWidget.setCellWidget(currowcount,
                                                showpvbutton.column,
                                                showpvbutton)
        showpvbutton.clicked.connect(self.showpvbuttonSignalMapper.map)

        choosepvbutton = QtGui.QPushButton()
        choosepvbutton.setText("PV File")
        choosepvbutton.setEnabled(True)
        choosepvbutton.row = currowcount
        choosepvbutton.column = 3
        self.choosepvbuttonSignalMapper.setMapping(choosepvbutton,
                                                   choosepvbutton)
        self.newConfigTableWidget.setCellWidget(currowcount,
                                                choosepvbutton.column,
                                                choosepvbutton)
        choosepvbutton.clicked.connect(self.choosepvbuttonSignalMapper.map)

    def removepvgrouprow(self):
        """Remove selected pv group from the configuration to be added into MASAR database"""
        if self.currentselectedrow4config == -1:
            raise RuntimeError("No pv group selected.")
        rownametobedelete = self.newConfigTableWidget.item(
            self.currentselectedrow4config, 0)
        if rownametobedelete is not None:
            rownametobedelete = rownametobedelete.text()
        self.newConfigTableWidget.removeRow(self.currentselectedrow4config)
        if rownametobedelete is not None:
            print("Successfully delete pv group: ", rownametobedelete)
        else:
            print("Successfully delete row: ", self.currentselectedrow4config)
        self.currentselectedrow4config = -1

    def savemasarsqlite(self):
        """"""
        # get data from sqlite
        if self.usedefaultdb:
            masardb = str(self.databaseDefault.toPlainText())
        else:
            masardb = str(self.databaseLineEdit.text())

        if masardb != "":
            os.environ["MASAR_SQLITE_DB"] = masardb
        else:
            QMessageBox.warning(self, "Warning",
                                "Cannot find MASAR SQLite Database")
            return

        import pymasarsqlite
        conn = pymasarsqlite.utils.connect()
        existedresult = pymasarsqlite.service.retrieveServiceConfigs(
            conn, servicename="masar")

        newcfgdata = self._getnewconfigurationdata(existedresult)
        if newcfgdata is None:
            QMessageBox.warning(
                self, "Warning",
                "Not enough information for a new configuration.")
            return
        newcfgname = newcfgdata[0]
        desc = newcfgdata[1]
        msystem = newcfgdata[2]
        # config data format: [[name], [desc], [pv files]]
        cfgdata = newcfgdata[3]

        if newcfgname is None or msystem is None or newcfgdata is None:
            # Nothing to be added.
            QMessageBox.warning(
                self, "Warning",
                "No name or system is given, or empty configuration data.")
            return

        for i in range(len(cfgdata[0])):
            if cfgdata[0][i] is None:
                QMessageBox.warning(self, "Warning", "Wrong PV group name")
                return

            if cfgdata[2][i] is not None and os.path.isfile(cfgdata[2][i]):
                pvs = list(np.loadtxt(cfgdata[2][i], dtype=str, comments="#"))
                if len(pvs) > 0:
                    for j, pv in enumerate(pvs):
                        pvs[j] = pv.strip()
                    pymasarsqlite.pvgroup.savePvGroup(conn,
                                                      cfgdata[0][i],
                                                      func=cfgdata[1][i])
                    pymasarsqlite.pvgroup.saveGroupPvs(conn, cfgdata[0][i],
                                                       pvs)

        try:
            pymasarsqlite.service.saveServiceConfig(conn,
                                                    "masar",
                                                    newcfgname,
                                                    configdesc=desc,
                                                    system=msystem)
            pymasarsqlite.service.saveServicePvGroup(conn, newcfgname,
                                                     cfgdata[0])
        except Exception as e:
            QMessageBox.warning(self, "Error", e.message)
            return

        pymasarsqlite.utils.save(conn)
        QMessageBox.information(
            self, "Congratulation",
            "A new configuration has been added successfully.")
        pymasarsqlite.utils.close(conn)

    def savemasarmongodb(self):
        """"""
        # get data from mongodb
        if self.usedefaultdb:
            database = str(self.databaseDefault.toPlainText())
            host = str(self.hostDefault.toPlainText())
            port = str(self.portDefault.toPlainText())
        else:
            database = str(self.databaseLineEdit.text())
            host = str(self.databaseHostLineEdit.text())
            port = str(self.databasePortLineEdit.text())

        import pymasarmongo

        mongoconn, collection = pymasarmongo.db.utils.conn(host=host,
                                                           port=port,
                                                           db=database)
        existedresult = pymasarmongo.pymasarmongo.pymasar.retrieveconfig(
            mongoconn, collection)
        existedcfg = []
        for res in existedresult:
            existedcfg.append([
                res['configidx'], res['name'], res['desc'], res['created_on'],
                res['version'], res['status']
            ])
        newcfgdata = self._getnewconfigurationdata(existedcfg)
        if newcfgdata is None:
            # Nothing to be added.
            raise ValueError("Empty configuration.")

        newcfgname = newcfgdata[0]
        desc = newcfgdata[1]
        msystem = newcfgdata[2]
        # config data format: [[name], [desc], [pv files]]
        cfgdata = newcfgdata[3]
        pvs = []
        for pvf in cfgdata[2]:
            if pvf is not None and os.path.isfile(pvf):
                pvs += list(np.loadtxt(pvf, dtype=str, comments="#"))
        if pvs:
            for i, pv in enumerate(pvs):
                pvs[i] = pv.strip()
            pymasarmongo.pymasarmongo.pymasar.saveconfig(mongoconn,
                                                         collection,
                                                         newcfgname,
                                                         desc=desc,
                                                         system=msystem,
                                                         pvlist={"names": pvs})
            QMessageBox.information(
                self, "Congratulation",
                "A new configuration has been added successfully.")
        else:
            QMessageBox.warning(self, "Warning",
                                "No PVs available for the new configuration.")

        pymasarmongo.db.utils.close(mongoconn)

    def submitmasarconfig(self):
        """submit a new configuration to MASAR database"""
        if self.dbsource is None:
            QMessageBox.warning(
                self, "Warning",
                "Unknown database source.\nPlease select which database should be use."
            )
            return
        if self.dbsource == 0:
            self.savemasarsqlite()
        elif self.dbsource == 1:
            self.savemasarmongodb()

    def _getnewconfigurationdata(self, existedresult):
        """"""
        newcfgname = self.newConfigurationLineEdit.text()
        if newcfgname is None or str(newcfgname) == "":
            QMessageBox.warning(self, "Warning",
                                "Name of configuration is empty.")
            return None
        elif str(newcfgname) in np.array(existedresult)[:, 1]:
            QMessageBox.warning(
                self, "Warning",
                "Configuration (%s) exists already." % str(newcfgname))
            return None
        else:
            newcfgname = str(newcfgname)
        desc = self.newConfigurationDescription.text()
        if str(desc) == "":
            desc = None
        else:
            desc = str(desc)

        msystem = str(self.systemComboBox.currentText())
        if msystem == "Others":
            msystem = self.systemLineEdit.text()
            if msystem is None or str(msystem) == "":
                QMessageBox.warning(
                    self, "Warning",
                    "System for configuration (%s) not specified yet." %
                    str(newcfgname))
                return None
            else:
                msystem = str(msystem)

        pvgroups = self.newConfigTableWidget.rowCount()
        if pvgroups == 0:
            QMessageBox.warning(
                self, "Warning",
                "No PV founded for new configuration (%s)." % str(newcfgname))
            return None

        pvgroupnames = []
        pvgroupdescs = []
        pvgroupfiles = []
        for count in range(pvgroups):
            # Collect PV group name information
            if self.newConfigTableWidget.item(count, 0) is not None and str(
                    self.newConfigTableWidget.item(count, 0).text()) != "":
                pvgname = str(self.newConfigTableWidget.item(count, 0).text())
                if pvgname in pvgroupnames:
                    QMessageBox.warning(
                        self, "Warning",
                        "Duplicated pv group name: %s." % str(pvgname))
                    return None
                pvgroupnames.append(pvgname)
            elif self.newConfigTableWidget.item(count, 4) is None or str(
                    self.newConfigTableWidget.item(count, 4)) == "":
                continue
            elif self.dbsource == 1:
                pvgroupnames.append(None)
            else:
                QMessageBox.warning(self, "Warning", "Empty pv group name.")
                return None
                # reply = QMessageBox.question(self, "Message",
                #                              "pv group name not specified for row {}.\nContinue?".format(count),
                #                              QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
                # if reply == QMessageBox.Yes:
                #     pvgroupnames.append(None)
                # else:
                #     return None

            # Collection PV group descriptions
            if self.newConfigTableWidget.item(count, 1) is not None:
                pvgroupdescs.append(
                    str(self.newConfigTableWidget.item(count, 1).text()))
            else:
                pvgroupdescs.append(None)

            # PV files for the pv group.
            if self.newConfigTableWidget.item(count, 4) is not None:
                pvgroupfiles.append(
                    str(self.newConfigTableWidget.item(count, 4).text()))
            else:
                pvgroupfiles.append(None)

        if pvgroupfiles:
            return newcfgname, desc, msystem, [
                pvgroupnames, pvgroupdescs, pvgroupfiles
            ]
        else:
            return None

    def currentselectedrow(self, row, col):
        """Cache current selected row in MASAR configuration Table Widget."""
        self.currentselectedrow4config = row

    def updateselectedsystem(self, system):
        """Update selected system if value changed"""
        self.selectedsystem = str(system)

    def updatesystemcombobox(self):
        """Update selected system if value changed"""
        self.systemComboBox.clear()
        if self.dbsource == 0:
            # get data from sqlite
            if self.usedefaultdb:
                # masardb = str(self.databaseDefault.text())
                masardb = str(self.databaseDefault.toPlainText())
            else:
                masardb = str(self.databaseLineEdit.text())

            if masardb != "":
                os.environ["MASAR_SQLITE_DB"] = masardb
            else:
                raise RuntimeError("Cannot find MASAR SQLite Database")

            import pymasarsqlite

            conn = pymasarsqlite.utils.connect()
            result = pymasarsqlite.service.retrieveServiceConfigProps(
                conn, propname="system", servicename="masar")
            index = 0
            if len(result) > 1:
                res = sorted(set(list(np.array(result[1:])[:, 3])))
                #for res in result[1:]:
                self.systemComboBox.addItems(res)
                index = len(res)
            self.systemComboBox.addItem("Others")
            self.systemComboBox.setCurrentIndex(index)
            pymasarsqlite.utils.close(conn)

        elif self.dbsource == 1:
            # get data from mongodb
            if self.usedefaultdb:
                database = str(self.databaseDefault.toPlainText())
                host = str(self.hostDefault.toPlainText())
                port = str(self.portDefault.toPlainText())
            else:
                database = str(self.databaseLineEdit.text())
                host = str(self.databaseHostLineEdit.text())
                port = str(self.databasePortLineEdit.text())

            import pymasarmongo

            mongoconn, collection = pymasarmongo.db.utils.conn(host=host,
                                                               port=port,
                                                               db=database)

            result = pymasarmongo.pymasarmongo.pymasar.retrieveconfig(
                mongoconn, collection)
            pymasarmongo.db.utils.close(mongoconn)

            results = []
            for res in result:
                if res["system"] not in results:
                    results.append(res["system"])
            res = sorted(set(results))
            self.systemComboBox.addItems(res)
            index = len(res)
            self.systemComboBox.addItem("Others")
            self.systemComboBox.setCurrentIndex(index)

    def listpvgroups(self):
        """"""
        self.pvgroupmodel.clear()
        self.pvgroupmodel.setHorizontalHeaderLabels(["PV Groups"])
        if self.dbsource == 0:
            # get data from sqlite
            if self.usedefaultdb:
                # masardb = str(self.databaseDefault.text())
                masardb = str(self.databaseDefault.toPlainText())
            else:
                masardb = str(self.databaseLineEdit.text())

            if masardb != "":
                os.environ["MASAR_SQLITE_DB"] = masardb
            else:
                raise RuntimeError("Cannot find MASAR SQLite Database")

            import pymasarsqlite

            conn = pymasarsqlite.utils.connect()
            result = pymasarsqlite.pvgroup.retrievePvGroups(conn)
            if len(result) > 0:
                result = sorted(result, key=itemgetter(0), reverse=True)
            for res in result:
                parent1 = QStandardItem(res[1])
                child1 = QStandardItem('id: {}'.format(res[0]))
                child2 = QStandardItem('description: {}'.format(res[2]))
                child3 = QStandardItem('date: {}'.format(res[3]))
                child4 = QStandardItem('version: {}'.format(res[4]))
                parent1.appendColumn([child1, child2, child3, child4])
                self.pvgroupmodel.appendRow(parent1)
            selmod = self.pvGroupTreeView.selectionModel()
            index2 = self.pvgroupmodel.indexFromItem(child3)
            selmod.select(
                index2, QItemSelectionModel.Select | QItemSelectionModel.Rows)
            pymasarsqlite.utils.close(conn)

            self.pvGroupTreeView.setContextMenuPolicy(Qt.CustomContextMenu)
            self.pvGroupTreeView.clicked.connect(self.showpvsinpvgroup)
            # self.connect(self.pvGroupTreeView,
            #              QtCore.SIGNAL("clicked(QModelIndex)"),
            #              #QtCore.SIGNAL("customContextMenuRequested(const QPoint &)"),
            #              self.doMenu)

        elif self.dbsource == 1:
            # get data from mongodb
            if self.usedefaultdb:
                database = str(self.databaseDefault.toPlainText())
                host = str(self.hostDefault.toPlainText())
                port = str(self.portDefault.toPlainText())
            else:
                database = str(self.databaseLineEdit.text())
                host = str(self.databaseHostLineEdit.text())
                port = str(self.databasePortLineEdit.text())

            import pymasarmongo

    def showpvsinpvgroup(self, point):
        if point.model().itemFromIndex(point).child(0) is None:
            return
        reply = QMessageBox.question(
            self, 'Message', "show all pvs belong to group {} ?".format(
                point.model().itemFromIndex(point).text()),
            QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if reply == QMessageBox.Yes:
            pvgroupidx = int(
                str(point.model().itemFromIndex(point).child(0).text().split(
                    ":")[1]).strip())

            # msg = QMessageBox(self)
            msg = ShowPvMessageBox()
            msg.setWindowTitle('PVs for group {}'.format(
                point.model().itemFromIndex(point).text()))
            msg.setText("Click show details to see all pvs.")
            # windowTitle = 'PVs for group {}'.format(point.model().itemFromIndex(point).text())

            if self.dbsource == 0:
                # get data from sqlite
                if self.usedefaultdb:
                    # masardb = str(self.databaseDefault.text())
                    masardb = str(self.databaseDefault.toPlainText())
                else:
                    masardb = str(self.databaseLineEdit.text())

                if masardb != "":
                    os.environ["MASAR_SQLITE_DB"] = masardb
                else:
                    raise RuntimeError("Cannot find MASAR SQLite Database")

                import pymasarsqlite

                conn = pymasarsqlite.utils.connect()
                result = pymasarsqlite.pvgroup.retrieveGroupPvs(
                    conn, pvgroupidx)
                text = "\n".join(list(np.array(result)[:, 0]))
                msg.setDetailedText(text)
                msg.exec_()

            elif self.dbsource == 1:
                # get data from mongodb
                if self.usedefaultdb:
                    database = str(self.databaseDefault.toPlainText())
                    host = str(self.hostDefault.toPlainText())
                    port = str(self.portDefault.toPlainText())
                else:
                    database = str(self.databaseLineEdit.text())
                    host = str(self.databaseHostLineEdit.text())
                    port = str(self.databasePortLineEdit.text())

                import pymasarmongo
Exemple #53
0
class ModelAtrributesView(QListView):
    """
    Custom QListView implementation that displays checkable model attributes.
    """
    def __init__(self, parent=None, dataModel=None):
        QListView.__init__(self, parent)

        self._dataModel = dataModel
        self._selectedDisplayMapping = OrderedDict()
        self._attrModel = QStandardItemModel(self)

    def dataModel(self):
        """
        Returns the data model instance.
        """
        return self._dataModel

    def setDataModel(self, dataModel):
        """
        Sets the data model. Should be a callable class rather than the class
        instance.
        """
        if callable(dataModel):
            self._dataModel = dataModel

        else:
            self._dataModel = dataModel.__class__

    def load(self):
        """
        Load the model's attributes into the list view.
        """
        if self._dataModel == None:
            return

        try:
            self._loadAttrs(self._dataModel.displayMapping())
        except AttributeError:
            #Ignore error if model does not contain the displayMapping static method
            pass

    def _loadAttrs(self, attrMapping):
        """
        Loads display mapping into the list view.
        """
        self._attrModel.clear()
        self._attrModel.setColumnCount(2)

        for attrName, displayName in attrMapping.iteritems():
            #Exclude row ID in the list, other unique identifier attributes in the model can be used
            if attrName != "id":
                displayNameItem = QStandardItem(displayName)
                displayNameItem.setCheckable(True)
                attrNameItem = QStandardItem(attrName)

                self._attrModel.appendRow([displayNameItem, attrNameItem])

        self.setModel(self._attrModel)

    def selectedMappings(self):
        """
        Return a dictionary of field names and their corresponding display values.
        """
        selectedAttrs = {}

        for i in range(self._attrModel.rowCount()):
            displayNameItem = self._attrModel.item(i, 0)

            if displayNameItem.checkState() == Qt.Checked:
                attrNameItem = self._attrModel.item(i, 1)
                selectedAttrs[attrNameItem.text()] = displayNameItem.text()

        return selectedAttrs
class DlgSqlLayerWindow(QWidget, Ui_Dialog):
    nameChanged = pyqtSignal(str)

    def __init__(self, iface, layer, parent=None):
        QWidget.__init__(self, parent)
        self.iface = iface
        self.layer = layer

        uri = QgsDataSourceURI(layer.source())
        dbplugin = None
        db = None
        if layer.dataProvider().name() == 'postgres':
            dbplugin = createDbPlugin('postgis', 'postgres')
        elif layer.dataProvider().name() == 'spatialite':
            dbplugin = createDbPlugin('spatialite', 'spatialite')
        elif layer.dataProvider().name() == 'oracle':
            dbplugin = createDbPlugin('oracle', 'oracle')
        elif layer.dataProvider().name() == 'virtual':
            dbplugin = createDbPlugin('vlayers', 'virtual')
        elif layer.dataProvider().name() == 'ogr':
            dbplugin = createDbPlugin('gpkg', 'gpkg')
        if dbplugin:
            dbplugin.connectToUri(uri)
            db = dbplugin.db

        self.dbplugin = dbplugin
        self.db = db
        self.filter = ""
        self.allowMultiColumnPk = isinstance(db, PGDatabase) # at the moment only PostgreSQL allows a primary key to span multiple columns, spatialite doesn't
        self.aliasSubQuery = isinstance(db, PGDatabase) # only PostgreSQL requires subqueries to be aliases
        self.setupUi(self)
        self.setWindowTitle(
            u"%s - %s [%s]" % (self.windowTitle(), db.connection().connectionName(), db.connection().typeNameString()))

        self.defaultLayerName = 'QueryLayer'

        if self.allowMultiColumnPk:
            self.uniqueColumnCheck.setText(self.trUtf8("Column(s) with unique values"))
        else:
            self.uniqueColumnCheck.setText(self.trUtf8("Column with unique values"))

        self.editSql.setFocus()
        self.editSql.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self.editSql.setMarginVisible(True)
        self.initCompleter()

        # allow copying results
        copyAction = QAction("copy", self)
        self.viewResult.addAction(copyAction)
        copyAction.setShortcuts(QKeySequence.Copy)

        copyAction.triggered.connect(self.copySelectedResults)

        self.btnExecute.clicked.connect(self.executeSql)
        self.btnSetFilter.clicked.connect(self.setFilter)
        self.btnClear.clicked.connect(self.clearSql)

        self.presetStore.clicked.connect(self.storePreset)
        self.presetDelete.clicked.connect(self.deletePreset)
        self.presetCombo.activated[str].connect(self.loadPreset)
        self.presetCombo.activated[str].connect(self.presetName.setText)

        self.updatePresetsCombobox()

        self.geomCombo.setEditable(True)
        self.geomCombo.lineEdit().setReadOnly(True)

        self.uniqueCombo.setEditable(True)
        self.uniqueCombo.lineEdit().setReadOnly(True)
        self.uniqueModel = QStandardItemModel(self.uniqueCombo)
        self.uniqueCombo.setModel(self.uniqueModel)
        if self.allowMultiColumnPk:
            self.uniqueCombo.setItemDelegate(QStyledItemDelegate())
            self.uniqueModel.itemChanged.connect(self.uniqueChanged)                # react to the (un)checking of an item
            self.uniqueCombo.lineEdit().textChanged.connect(self.uniqueTextChanged) # there are other events that change the displayed text and some of them can not be caught directly

        self.layerTypeWidget.hide()  # show if load as raster is supported
        #self.loadLayerBtn.clicked.connect(self.loadSqlLayer)
        self.updateLayerBtn.clicked.connect(self.updateSqlLayer)
        self.getColumnsBtn.clicked.connect(self.fillColumnCombos)

        self.queryBuilderFirst = True
        self.queryBuilderBtn.setIcon(QIcon(":/db_manager/icons/sql.gif"))
        self.queryBuilderBtn.clicked.connect(self.displayQueryBuilder)

        self.presetName.textChanged.connect(self.nameChanged)

        # Update from layer
        # Fisrtly the SQL from QgsDataSourceURI table
        sql = uri.table()
        if uri.keyColumn() == '_uid_':
            match = re.search('^\(SELECT .+ AS _uid_,\* FROM \((.*)\) AS _subq_.+_\s*\)$', sql, re.S)
            if match:
                sql = match.group(1)
        else:
            match = re.search('^\((SELECT .+ FROM .+)\)$', sql, re.S)
            if match:
                sql = match.group(1)
        self.editSql.setText(sql)
        self.executeSql()

        # Then the columns
        self.geomCombo.setCurrentIndex(self.geomCombo.findText(uri.geometryColumn(), Qt.MatchExactly))
        if uri.keyColumn() != '_uid_':
            self.uniqueColumnCheck.setCheckState(Qt.Checked)
            if self.allowMultiColumnPk:
                itemsData = uri.keyColumn().split(',')
                for item in self.uniqueModel.findItems("*", Qt.MatchWildcard):
                    if item.data() in itemsData:
                        item.setCheckState(Qt.Checked)
            else:
                keyColumn = uri.keyColumn()
                if self.uniqueModel.findItems(keyColumn):
                    self.uniqueCombo.setEditText(keyColumn)

        # Finally layer name, filter and selectAtId
        self.layerNameEdit.setText(layer.name())
        self.filter = uri.sql()
        if uri.selectAtIdDisabled():
            self.avoidSelectById.setCheckState(Qt.Checked)

    def updatePresetsCombobox(self):
        self.presetCombo.clear()

        names = []
        entries = QgsProject.instance().subkeyList('DBManager', 'savedQueries')
        for entry in entries:
            name = QgsProject.instance().readEntry('DBManager', 'savedQueries/' + entry + '/name')[0]
            names.append(name)

        for name in sorted(names):
            self.presetCombo.addItem(name)
        self.presetCombo.setCurrentIndex(-1)

    def storePreset(self):
        query = self._getSqlQuery()
        if query == "":
            return
        name = self.presetName.text()
        QgsProject.instance().writeEntry('DBManager', 'savedQueries/q' + unicode(name.__hash__()) + '/name', name)
        QgsProject.instance().writeEntry('DBManager', 'savedQueries/q' + unicode(name.__hash__()) + '/query', query)
        index = self.presetCombo.findText(name)
        if index == -1:
            self.presetCombo.addItem(name)
            self.presetCombo.setCurrentIndex(self.presetCombo.count() - 1)
        else:
            self.presetCombo.setCurrentIndex(index)

    def deletePreset(self):
        name = self.presetCombo.currentText()
        QgsProject.instance().removeEntry('DBManager', 'savedQueries/q' + unicode(name.__hash__()))
        self.presetCombo.removeItem(self.presetCombo.findText(name))
        self.presetCombo.setCurrentIndex(-1)

    def loadPreset(self, name):
        query = QgsProject.instance().readEntry('DBManager', 'savedQueries/q' + unicode(name.__hash__()) + '/query')[0]
        name = QgsProject.instance().readEntry('DBManager', 'savedQueries/q' + unicode(name.__hash__()) + '/name')[0]
        self.editSql.setText(query)

    def clearSql(self):
        self.editSql.clear()
        self.editSql.setFocus()
        self.filter = ""

    def executeSql(self):

        sql = self._getSqlQuery()
        if sql == "":
            return

        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))

        # delete the old model
        old_model = self.viewResult.model()
        self.viewResult.setModel(None)
        if old_model:
            old_model.deleteLater()

        cols = []
        quotedCols = []

        try:
            # set the new model
            model = self.db.sqlResultModel(sql, self)
            self.viewResult.setModel(model)
            self.lblResult.setText(self.tr("%d rows, %.1f seconds") % (model.affectedRows(), model.secs()))
            cols = self.viewResult.model().columnNames()
            for col in cols:
                quotedCols.append(self.db.connector.quoteId(col))

        except BaseError as e:
            QApplication.restoreOverrideCursor()
            DlgDbError.showError(e, self)
            self.uniqueModel.clear()
            self.geomCombo.clear()
            return

        self.setColumnCombos(cols, quotedCols)

        self.update()
        QApplication.restoreOverrideCursor()

    def _getSqlLayer(self, _filter):
        hasUniqueField = self.uniqueColumnCheck.checkState() == Qt.Checked
        if hasUniqueField:
            if self.allowMultiColumnPk:
                checkedCols = []
                for item in self.uniqueModel.findItems("*", Qt.MatchWildcard):
                    if item.checkState() == Qt.Checked:
                        checkedCols.append(item.data())
                uniqueFieldName = ",".join(checkedCols)
            elif self.uniqueCombo.currentIndex() >= 0:
                uniqueFieldName = self.uniqueModel.item(self.uniqueCombo.currentIndex()).data()
            else:
                uniqueFieldName = None
        else:
            uniqueFieldName = None
        hasGeomCol = self.hasGeometryCol.checkState() == Qt.Checked
        if hasGeomCol:
            geomFieldName = self.geomCombo.currentText()
        else:
            geomFieldName = None

        query = self._getSqlQuery()
        if query == "":
            return None

        # remove a trailing ';' from query if present
        if query.strip().endswith(';'):
            query = query.strip()[:-1]

        from qgis.core import QgsMapLayer, QgsMapLayerRegistry

        layerType = QgsMapLayer.VectorLayer if self.vectorRadio.isChecked() else QgsMapLayer.RasterLayer

        # get a new layer name
        names = []
        for layer in QgsMapLayerRegistry.instance().mapLayers().values():
            names.append(layer.name())

        layerName = self.layerNameEdit.text()
        if layerName == "":
            layerName = self.defaultLayerName
        newLayerName = layerName
        index = 1
        while newLayerName in names:
            index += 1
            newLayerName = u"%s_%d" % (layerName, index)

        # create the layer
        layer = self.db.toSqlLayer(query, geomFieldName, uniqueFieldName, newLayerName, layerType,
                                   self.avoidSelectById.isChecked(), _filter)
        if layer.isValid():
            return layer
        else:
            return None

    def loadSqlLayer(self):
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        try:
            layer = self._getSqlLayer(self.filter)
            if layer == None:
                return

            from qgis.core import QgsMapLayerRegistry
            QgsMapLayerRegistry.instance().addMapLayers([layer], True)
        finally:
            QApplication.restoreOverrideCursor()

    def updateSqlLayer(self):
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        try:
            layer = self._getSqlLayer(self.filter)
            if layer == None:
                return

            #self.layer.dataProvider().setDataSourceUri(layer.dataProvider().dataSourceUri())
            #self.layer.dataProvider().reloadData()
            XMLDocument = QDomDocument("style")
            XMLMapLayers = XMLDocument.createElement("maplayers")
            XMLMapLayer = XMLDocument.createElement("maplayer")
            self.layer.writeLayerXML(XMLMapLayer, XMLDocument)
            XMLMapLayer.firstChildElement("datasource").firstChild().setNodeValue(layer.source())
            XMLMapLayers.appendChild(XMLMapLayer)
            XMLDocument.appendChild(XMLMapLayers)
            self.layer.readLayerXML(XMLMapLayer)
            self.layer.reload()
            self.iface.actionDraw().trigger()
            self.iface.mapCanvas().refresh()
            self.iface.legendInterface().refreshLayerSymbology(layer)
        finally:
            QApplication.restoreOverrideCursor()

    def fillColumnCombos(self):
        query = self._getSqlQuery()
        if query == "":
            return

        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))

        # remove a trailing ';' from query if present
        if query.strip().endswith(';'):
            query = query.strip()[:-1]

        # get all the columns
        cols = []
        quotedCols = []
        connector = self.db.connector
        if self.aliasSubQuery:
            # get a new alias
            aliasIndex = 0
            while True:
                alias = "_subQuery__%d" % aliasIndex
                escaped = re.compile('\\b("?)' + re.escape(alias) + '\\1\\b')
                if not escaped.search(query):
                    break
                aliasIndex += 1

            sql = u"SELECT * FROM (%s\n) AS %s LIMIT 0" % (unicode(query), connector.quoteId(alias))
        else:
            sql = u"SELECT * FROM (%s\n) WHERE 1=0" % unicode(query)

        c = None
        try:
            c = connector._execute(None, sql)
            cols = connector._get_cursor_columns(c)
            for col in cols:
                quotedCols.append(connector.quoteId(col))

        except BaseError as e:
            QApplication.restoreOverrideCursor()
            DlgDbError.showError(e, self)
            self.uniqueModel.clear()
            self.geomCombo.clear()
            return

        finally:
            if c:
                c.close()
                del c

        self.setColumnCombos(cols, quotedCols)

        QApplication.restoreOverrideCursor()

    def setColumnCombos(self, cols, quotedCols):
        # get sensible default columns. do this before sorting in case there's hints in the column order (eg, id is more likely to be first)
        try:
            defaultGeomCol = next(col for col in cols if col in ['geom', 'geometry', 'the_geom', 'way'])
        except:
            defaultGeomCol = None
        try:
            defaultUniqueCol = [col for col in cols if 'id' in col][0]
        except:
            defaultUniqueCol = None

        colNames = sorted(zip(cols, quotedCols))
        newItems = []
        uniqueIsFilled = False
        for (col, quotedCol) in colNames:
            item = QStandardItem(col)
            item.setData(quotedCol)
            item.setEnabled(True)
            item.setCheckable(self.allowMultiColumnPk)
            item.setSelectable(not self.allowMultiColumnPk)
            if self.allowMultiColumnPk:
                matchingItems = self.uniqueModel.findItems(col)
                if matchingItems:
                    item.setCheckState(matchingItems[0].checkState())
                    uniqueIsFilled = uniqueIsFilled or matchingItems[0].checkState() == Qt.Checked
                else:
                    item.setCheckState(Qt.Unchecked)
            newItems.append(item)
        if self.allowMultiColumnPk:
            self.uniqueModel.clear()
            self.uniqueModel.appendColumn(newItems)
            self.uniqueChanged()
        else:
            previousUniqueColumn = self.uniqueCombo.currentText()
            self.uniqueModel.clear()
            self.uniqueModel.appendColumn(newItems)
            if self.uniqueModel.findItems(previousUniqueColumn):
                self.uniqueCombo.setEditText(previousUniqueColumn)
                uniqueIsFilled = True

        oldGeometryColumn = self.geomCombo.currentText()
        self.geomCombo.clear()
        self.geomCombo.addItems(cols)
        self.geomCombo.setCurrentIndex(self.geomCombo.findText(oldGeometryColumn, Qt.MatchExactly))

        # set sensible default columns if the columns are not already set
        try:
            if self.geomCombo.currentIndex() == -1:
                self.geomCombo.setCurrentIndex(cols.index(defaultGeomCol))
        except:
            pass
        items = self.uniqueModel.findItems(defaultUniqueCol)
        if items and not uniqueIsFilled:
            if self.allowMultiColumnPk:
                items[0].setCheckState(Qt.Checked)
            else:
                self.uniqueCombo.setEditText(defaultUniqueCol)
        try:
            pass
        except:
            pass

    def copySelectedResults(self):
        if len(self.viewResult.selectedIndexes()) <= 0:
            return
        model = self.viewResult.model()

        # convert to string using tab as separator
        text = model.headerToString("\t")
        for idx in self.viewResult.selectionModel().selectedRows():
            text += "\n" + model.rowToString(idx.row(), "\t")

        QApplication.clipboard().setText(text, QClipboard.Selection)
        QApplication.clipboard().setText(text, QClipboard.Clipboard)

    def initCompleter(self):
        dictionary = None
        if self.db:
            dictionary = self.db.connector.getSqlDictionary()
        if not dictionary:
            # use the generic sql dictionary
            from .sql_dictionary import getSqlDictionary

            dictionary = getSqlDictionary()

        wordlist = []
        for name, value in dictionary.iteritems():
            wordlist += value  # concat lists
        wordlist = list(set(wordlist))  # remove duplicates

        api = QsciAPIs(self.editSql.lexer())
        for word in wordlist:
            api.add(word)

        api.prepare()
        self.editSql.lexer().setAPIs(api)

    def displayQueryBuilder(self):
        dlg = QueryBuilderDlg(self.iface, self.db, self, reset=self.queryBuilderFirst)
        self.queryBuilderFirst = False
        r = dlg.exec_()
        if r == QDialog.Accepted:
            self.editSql.setText(dlg.query)

    def _getSqlQuery(self):
        sql = self.editSql.selectedText()
        if len(sql) == 0:
            sql = self.editSql.text()
        return sql

    def uniqueChanged(self):
        # when an item is (un)checked, simply trigger an update of the combobox text
        self.uniqueTextChanged(None)

    def uniqueTextChanged(self, text):
        # Whenever there is new text displayed in the combobox, check if it is the correct one and if not, display the correct one.
        checkedItems = []
        for item in self.uniqueModel.findItems("*", Qt.MatchWildcard):
            if item.checkState() == Qt.Checked:
                checkedItems.append(item.text())
        label = ", ".join(checkedItems)
        if text != label:
            self.uniqueCombo.setEditText(label)

    def setFilter(self):
        from qgis.gui import QgsQueryBuilder
        layer = self._getSqlLayer("")
        if not layer:
            return

        dlg = QgsQueryBuilder(layer)
        dlg.setSql(self.filter)
        if dlg.exec_():
            self.filter = dlg.sql()
        layer.deleteLater()
class ProgramInfoFiles(QWidget, Ui_ProgramInfoFiles):
    def __init__(self, context, update_main_ui_state, set_widget_enabled, is_alive, show_upload_files_wizard, show_download_wizard):
        QWidget.__init__(self)

        self.setupUi(self)

        self.session                 = context.session
        self.script_manager          = context.script_manager
        self.program                 = context.program
        self.update_main_ui_state    = update_main_ui_state
        self.set_widget_enabled      = set_widget_enabled
        self.is_alive                = is_alive
        self.show_download_wizard    = show_download_wizard
        self.bin_directory           = posixpath.join(self.program.root_directory, 'bin')
        self.refresh_in_progress     = False
        self.any_refresh_in_progress = False # set from ProgramInfoMain.update_ui_state
        self.available_files         = []
        self.available_directories   = []
        self.folder_icon             = QIcon(load_pixmap('folder-icon.png'))
        self.file_icon               = QIcon(load_pixmap('file-icon.png'))
        self.tree_files_model        = QStandardItemModel(self)
        self.tree_files_model_header = ['Name', 'Size', 'Last Modified']
        self.tree_files_proxy_model  = FilesProxyModel(self)
        self.last_download_directory = get_home_path()

        self.tree_files_model.setHorizontalHeaderLabels(self.tree_files_model_header)
        self.tree_files_proxy_model.setSourceModel(self.tree_files_model)
        self.tree_files.setModel(self.tree_files_model)
        self.tree_files.setModel(self.tree_files_proxy_model)
        self.tree_files.setColumnWidth(0, 210)
        self.tree_files.setColumnWidth(1, 85)

        self.tree_files.selectionModel().selectionChanged.connect(self.update_ui_state)
        self.tree_files.activated.connect(self.rename_activated_file)
        self.button_upload_files.clicked.connect(show_upload_files_wizard)
        self.button_download_files.clicked.connect(self.download_selected_files)
        self.button_rename_file.clicked.connect(self.rename_selected_file)
        self.button_change_file_permissions.clicked.connect(self.change_permissions_of_selected_file)
        self.button_delete_files.clicked.connect(self.delete_selected_files)

        self.label_error.setVisible(False)

    def update_ui_state(self):
        selection_count = len(self.tree_files.selectionModel().selectedRows())

        self.set_widget_enabled(self.button_upload_files, not self.any_refresh_in_progress)
        self.set_widget_enabled(self.button_download_files, not self.any_refresh_in_progress and selection_count > 0)
        self.set_widget_enabled(self.button_rename_file, not self.any_refresh_in_progress and selection_count == 1)
        self.set_widget_enabled(self.button_change_file_permissions, not self.any_refresh_in_progress and selection_count == 1)
        self.set_widget_enabled(self.button_delete_files, not self.any_refresh_in_progress and selection_count > 0)

    def close_all_dialogs(self):
        pass

    def refresh_files_done(self):
        self.refresh_in_progress = False
        self.update_main_ui_state()

    def refresh_files(self):
        def cb_walk(result):
            okay, message = check_script_result(result, decode_stderr=True)

            if not okay:
                self.label_error.setText('<b>Error:</b> ' + Qt.escape(message))
                self.label_error.setVisible(True)
                self.refresh_files_done()
                return

            self.label_error.setVisible(False)

            def expand_async(data):
                try:
                    walk = json.loads(zlib.decompress(buffer(data)).decode('utf-8'))
                except:
                    walk = None

                if walk == None or not isinstance(walk, dict):
                    available_files       = []
                    available_directories = []
                    walk                  = None
                else:
                    available_files, available_directories = expand_walk_to_lists(walk)

                return walk, available_files, available_directories

            def cb_expand_success(result):
                walk, available_files, available_directories = result

                self.available_files       = available_files
                self.available_directories = available_directories

                if walk != None:
                    expand_walk_to_model(walk, self.tree_files_model, self.folder_icon, self.file_icon)
                else:
                    self.label_error.setText('<b>Error:</b> Received invalid data')
                    self.label_error.setVisible(True)

                self.tree_files.header().setSortIndicator(0, Qt.AscendingOrder)
                self.refresh_files_done()

            def cb_expand_error():
                self.label_error.setText('<b>Error:</b> Internal async error')
                self.label_error.setVisible(True)
                self.refresh_files_done()

            async_call(expand_async, result.stdout, cb_expand_success, cb_expand_error)

        self.refresh_in_progress = True
        self.update_main_ui_state()

        width1 = self.tree_files.columnWidth(0)
        width2 = self.tree_files.columnWidth(1)

        self.tree_files_model.clear()
        self.tree_files_model.setHorizontalHeaderLabels(self.tree_files_model_header)
        self.tree_files.setColumnWidth(0, width1)
        self.tree_files.setColumnWidth(1, width2)

        self.script_manager.execute_script('walk', cb_walk,
                                           [self.bin_directory], max_length=1024*1024,
                                           decode_output_as_utf8=False)

    def get_directly_selected_name_items(self):
        selected_indexes    = self.tree_files.selectedIndexes()
        selected_name_items = []

        for selected_index in selected_indexes:
            if selected_index.column() == 0:
                mapped_index = self.tree_files_proxy_model.mapToSource(selected_index)
                selected_name_items.append(self.tree_files_model.itemFromIndex(mapped_index))

        return selected_name_items

    def download_selected_files(self):
        selected_name_items = self.get_directly_selected_name_items()

        if len(selected_name_items) == 0:
            return

        downloads = []

        def expand(name_item):
            item_type = name_item.data(USER_ROLE_ITEM_TYPE)

            if item_type == ITEM_TYPE_DIRECTORY:
                for i in range(name_item.rowCount()):
                    expand(name_item.child(i, 0))
            elif item_type == ITEM_TYPE_FILE:
                filename = get_full_item_path(name_item)

                downloads.append(Download(filename, QDir.toNativeSeparators(filename)))

        for selected_name_item in selected_name_items:
            expand(selected_name_item)

        if len(downloads) == 0:
            return

        download_directory = get_existing_directory(get_main_window(), 'Download Files',
                                                    self.last_download_directory)

        if len(download_directory) == 0:
            return

        self.last_download_directory = download_directory

        self.show_download_wizard('files', download_directory, downloads)

    def rename_activated_file(self, index):
        if index.column() == 0 and not self.any_refresh_in_progress:
            mapped_index = self.tree_files_proxy_model.mapToSource(index)
            name_item    = self.tree_files_model.itemFromIndex(mapped_index)
            item_type    = name_item.data(USER_ROLE_ITEM_TYPE)

            # only rename files via activation, because directories are expanded
            if item_type == ITEM_TYPE_FILE:
                self.rename_item(name_item)

    def rename_selected_file(self):
        selection_count = len(self.tree_files.selectionModel().selectedRows())

        if selection_count != 1:
            return

        selected_name_items = self.get_directly_selected_name_items()

        if len(selected_name_items) != 1:
            return

        self.rename_item(selected_name_items[0])

    def rename_item(self, name_item):
        item_type = name_item.data(USER_ROLE_ITEM_TYPE)

        if item_type == ITEM_TYPE_FILE:
            title     = 'Rename File'
            type_name = 'file'
        else:
            title     = 'Rename Directory'
            type_name = 'directory'

        old_name = name_item.text()

        # get new name
        dialog = ExpandingInputDialog(get_main_window())
        dialog.setModal(True)
        dialog.setWindowTitle(title)
        dialog.setLabelText('Enter new {0} name:'.format(type_name))
        dialog.setInputMode(QInputDialog.TextInput)
        dialog.setTextValue(old_name)
        dialog.setOkButtonText('Rename')

        if dialog.exec_() != QDialog.Accepted:
            return

        new_name = dialog.textValue()

        if new_name == old_name:
            return

        # check that new name is valid
        if len(new_name) == 0 or new_name == '.' or new_name == '..' or '/' in new_name:
            QMessageBox.critical(get_main_window(), title + ' Error',
                                 'A {0} name cannot be empty, cannot be one dot [.], cannot be two dots [..] and cannot contain a forward slash [/].'
                                 .format(type_name))
            return

        # check that new name is not already in use
        name_item_parent = name_item.parent()

        if name_item_parent == None:
            name_item_parent = self.tree_files_model.invisibleRootItem()

        for i in range(name_item_parent.rowCount()):
            if new_name == name_item_parent.child(i).text():
                QMessageBox.critical(get_main_window(), title + ' Error',
                                     'The new {0} name is already in use.'.format(type_name))
                return

        absolute_old_name = posixpath.join(self.bin_directory, get_full_item_path(name_item))
        absolute_new_name = posixpath.join(posixpath.split(absolute_old_name)[0], new_name)

        def cb_rename(result):
            if not report_script_result(result, title + ' Error', u'Could not rename {0}'.format(type_name)):
                return

            name_item.setText(new_name)

            if self.tree_files.header().sortIndicatorSection() == 0:
                self.tree_files.header().setSortIndicator(0, self.tree_files.header().sortIndicatorOrder())

        self.script_manager.execute_script('rename', cb_rename,
                                           [absolute_old_name, absolute_new_name])

    def change_permissions_of_selected_file(self):
        selection_count = len(self.tree_files.selectionModel().selectedRows())

        if selection_count != 1:
            return

        selected_name_items = self.get_directly_selected_name_items()

        if len(selected_name_items) != 1:
            return

        name_item = selected_name_items[0]
        item_type = name_item.data(USER_ROLE_ITEM_TYPE)
        old_permissions = name_item.data(USER_ROLE_PERMISSIONS)

        if item_type == ITEM_TYPE_FILE:
            title     = 'Change File Permissions'
            type_name = 'file'
        else:
            title     = 'Change Directory Permissions'
            type_name = 'directory'

        dialog = ProgramInfoFilesPermissions(get_main_window(), title, old_permissions)

        if dialog.exec_() != QDialog.Accepted:
            return

        new_permissions = dialog.get_permissions()

        if new_permissions == (old_permissions & 0o777):
            return

        absolute_name = posixpath.join(self.bin_directory, get_full_item_path(name_item))

        def cb_change_permissions(result):
            if not report_script_result(result, title + ' Error', u'Could change {0} permissions'.format(type_name)):
                return

            name_item.setData(new_permissions, USER_ROLE_PERMISSIONS)

        self.script_manager.execute_script('change_permissions', cb_change_permissions,
                                           [absolute_name, str(new_permissions)])

    def delete_selected_files(self):
        button = QMessageBox.question(get_main_window(), 'Delete Files',
                                      'Irreversibly deleting selected files and directories.',
                                      QMessageBox.Ok, QMessageBox.Cancel)

        if not self.is_alive() or button != QMessageBox.Ok:
            return

        selected_name_items = set(self.get_directly_selected_name_items())

        if len(selected_name_items) == 0:
            return

        script_instance_ref = [None]

        def progress_canceled():
            script_instance = script_instance_ref[0]

            if script_instance == None:
                return

            self.script_manager.abort_script(script_instance)

        progress = ExpandingProgressDialog(self)
        progress.set_progress_text_visible(False)
        progress.setModal(True)
        progress.setWindowTitle('Delete Files')
        progress.setLabelText('Collecting files and directories to delete')
        progress.setRange(0, 0)
        progress.canceled.connect(progress_canceled)
        progress.show()

        files_to_delete = []
        dirs_to_delete  = []
        all_done        = False

        while not all_done:
            all_done = True

            for selected_name_item in list(selected_name_items):
                item_done = False
                parent = selected_name_item.parent()

                while not item_done and parent != None:
                    if parent in selected_name_items:
                        selected_name_items.remove(selected_name_item)
                        item_done = True
                    else:
                        parent = parent.parent()

                if item_done:
                    all_done = False
                    break

        for selected_name_item in selected_name_items:
            path      = get_full_item_path(selected_name_item)
            item_type = selected_name_item.data(USER_ROLE_ITEM_TYPE)

            if item_type == ITEM_TYPE_DIRECTORY:
                dirs_to_delete.append(posixpath.join(self.bin_directory, path))
            else:
                files_to_delete.append(posixpath.join(self.bin_directory, path))

        message = 'Deleting '

        if len(files_to_delete) == 1:
            message += '1 file '
        elif len(files_to_delete) > 1:
            message += '{0} files '.format(len(files_to_delete))

        if len(dirs_to_delete) == 1:
            if len(files_to_delete) > 0:
                message += 'and '

            message += '1 directory'
        elif len(dirs_to_delete) > 1:
            if len(files_to_delete) > 0:
                message += 'and '

            message += '{0} directories'.format(len(dirs_to_delete))

        progress.setLabelText(message)

        def cb_delete(result):
            script_instance = script_instance_ref[0]

            if script_instance != None:
                aborted = script_instance.abort
            else:
                aborted = False

            script_instance_ref[0] = None

            progress.cancel()
            self.refresh_files()

            if aborted:
                QMessageBox.information(get_main_window(), 'Delete Files',
                                        u'Delete operation was aborted.')
                return

            report_script_result(result, 'Delete Files Error', 'Could not delete selected files/directories:')

        script_instance_ref[0] = self.script_manager.execute_script('delete', cb_delete,
                                                                    [json.dumps(files_to_delete), json.dumps(dirs_to_delete)],
                                                                    execute_as_user=True)
Exemple #56
0
class DlgSqlWindow(QWidget, Ui_Dialog):
    nameChanged = pyqtSignal(str)

    def __init__(self, iface, db, parent=None):
        QWidget.__init__(self, parent)
        self.iface = iface
        self.db = db
        self.allowMultiColumnPk = isinstance(db, PGDatabase) # at the moment only PostGIS allows a primary key to span multiple columns, spatialite doesn't
        self.setupUi(self)
        self.setWindowTitle(
            u"%s - %s [%s]" % (self.windowTitle(), db.connection().connectionName(), db.connection().typeNameString()))

        self.defaultLayerName = 'QueryLayer'

        if self.allowMultiColumnPk:
            self.uniqueColumnCheck.setText(self.trUtf8("Column(s) with unique values"))
        else:
            self.uniqueColumnCheck.setText(self.trUtf8("Column with unique values"))

        self.editSql.setFocus()
        self.editSql.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self.initCompleter()

        # allow to copy results
        copyAction = QAction("copy", self)
        self.viewResult.addAction(copyAction)
        copyAction.setShortcuts(QKeySequence.Copy)

        copyAction.triggered.connect(self.copySelectedResults)

        self.btnExecute.clicked.connect(self.executeSql)
        self.btnClear.clicked.connect(self.clearSql)

        self.presetStore.clicked.connect(self.storePreset)
        self.presetDelete.clicked.connect(self.deletePreset)
        self.presetCombo.activated[str].connect(self.loadPreset)
        self.presetCombo.activated[str].connect(self.presetName.setText)

        self.updatePresetsCombobox()

        self.geomCombo.setEditable(True)
        self.geomCombo.lineEdit().setReadOnly(True)

        self.uniqueCombo.setEditable(True)
        self.uniqueCombo.lineEdit().setReadOnly(True)
        self.uniqueModel = QStandardItemModel(self.uniqueCombo)
        self.uniqueCombo.setModel(self.uniqueModel)
        if self.allowMultiColumnPk:
            self.uniqueCombo.setItemDelegate(QStyledItemDelegate())
            self.uniqueModel.itemChanged.connect(self.uniqueChanged)                # react to the (un)checking of an item
            self.uniqueCombo.lineEdit().textChanged.connect(self.uniqueTextChanged) # there are other events that change the displayed text and some of them can not be caught directly

        # hide the load query as layer if feature is not supported
        self._loadAsLayerAvailable = self.db.connector.hasCustomQuerySupport()
        self.loadAsLayerGroup.setVisible(self._loadAsLayerAvailable)
        if self._loadAsLayerAvailable:
            self.layerTypeWidget.hide()  # show if load as raster is supported
            self.loadLayerBtn.clicked.connect(self.loadSqlLayer)
            self.getColumnsBtn.clicked.connect(self.fillColumnCombos)
            self.loadAsLayerGroup.toggled.connect(self.loadAsLayerToggled)
            self.loadAsLayerToggled(False)

        self._createViewAvailable = self.db.connector.hasCreateSpatialViewSupport()
        self.btnCreateView.setVisible(self._createViewAvailable)
        if self._createViewAvailable:
            self.btnCreateView.clicked.connect(self.createView)

        self.queryBuilderFirst = True
        self.queryBuilderBtn.setIcon(QIcon(":/db_manager/icons/sql.gif"))
        self.queryBuilderBtn.clicked.connect(self.displayQueryBuilder)

        self.presetName.textChanged.connect(self.nameChanged)

    def updatePresetsCombobox(self):
        self.presetCombo.clear()

        names = []
        entries = QgsProject.instance().subkeyList('DBManager', 'savedQueries')
        for entry in entries:
            name = QgsProject.instance().readEntry('DBManager', 'savedQueries/' + entry + '/name')[0]
            names.append(name)

        for name in sorted(names):
            self.presetCombo.addItem(name)
        self.presetCombo.setCurrentIndex(-1)

    def storePreset(self):
        query = self._getSqlQuery()
        if query == "":
            return
        name = self.presetName.text()
        QgsProject.instance().writeEntry('DBManager', 'savedQueries/q' + unicode(name.__hash__()) + '/name', name)
        QgsProject.instance().writeEntry('DBManager', 'savedQueries/q' + unicode(name.__hash__()) + '/query', query)
        index = self.presetCombo.findText(name)
        if index == -1:
            self.presetCombo.addItem(name)
            self.presetCombo.setCurrentIndex(self.presetCombo.count() - 1)
        else:
            self.presetCombo.setCurrentIndex(index)

    def deletePreset(self):
        name = self.presetCombo.currentText()
        QgsProject.instance().removeEntry('DBManager', 'savedQueries/q' + unicode(name.__hash__()))
        self.presetCombo.removeItem(self.presetCombo.findText(name))
        self.presetCombo.setCurrentIndex(-1)

    def loadPreset(self, name):
        query = QgsProject.instance().readEntry('DBManager', 'savedQueries/q' + unicode(name.__hash__()) + '/query')[0]
        name = QgsProject.instance().readEntry('DBManager', 'savedQueries/q' + unicode(name.__hash__()) + '/name')[0]
        self.editSql.setText(query)

    def loadAsLayerToggled(self, checked):
        self.loadAsLayerGroup.setChecked(checked)
        self.loadAsLayerWidget.setVisible(checked)
        if checked:
            self.fillColumnCombos()

    def clearSql(self):
        self.editSql.clear()
        self.editSql.setFocus()

    def executeSql(self):

        sql = self._getSqlQuery()
        if sql == "":
            return

        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))

        # delete the old model
        old_model = self.viewResult.model()
        self.viewResult.setModel(None)
        if old_model:
            old_model.deleteLater()

        cols = []
        quotedCols = []

        try:
            # set the new model
            model = self.db.sqlResultModel(sql, self)
            self.viewResult.setModel(model)
            self.lblResult.setText(self.tr("%d rows, %.1f seconds") % (model.affectedRows(), model.secs()))
            cols = self.viewResult.model().columnNames()
            for col in cols:
                quotedCols.append(self.db.connector.quoteId(col))

        except BaseError as e:
            QApplication.restoreOverrideCursor()
            DlgDbError.showError(e, self)
            self.uniqueModel.clear()
            self.geomCombo.clear()
            return

        self.setColumnCombos(cols, quotedCols)

        self.update()
        QApplication.restoreOverrideCursor()

    def loadSqlLayer(self):
        hasUniqueField = self.uniqueColumnCheck.checkState() == Qt.Checked
        if hasUniqueField:
            if self.allowMultiColumnPk:
                checkedCols = []
                for item in self.uniqueModel.findItems("*", Qt.MatchWildcard):
                    if item.checkState() == Qt.Checked:
                        checkedCols.append(item.data())
                uniqueFieldName = ",".join(checkedCols)
            elif self.uniqueCombo.currentIndex() >= 0:
                uniqueFieldName = self.uniqueModel.item(self.uniqueCombo.currentIndex()).data()
            else:
                uniqueFieldName = None
        else:
            uniqueFieldName = None
        hasGeomCol = self.hasGeometryCol.checkState() == Qt.Checked
        if hasGeomCol:
            geomFieldName = self.geomCombo.currentText()
        else:
            geomFieldName = None

        query = self._getSqlQuery()
        if query == "":
            return

        # remove a trailing ';' from query if present
        if query.strip().endswith(';'):
            query = query.strip()[:-1]

        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))

        from qgis.core import QgsMapLayer, QgsMapLayerRegistry

        layerType = QgsMapLayer.VectorLayer if self.vectorRadio.isChecked() else QgsMapLayer.RasterLayer

        # get a new layer name
        names = []
        for layer in QgsMapLayerRegistry.instance().mapLayers().values():
            names.append(layer.name())

        layerName = self.layerNameEdit.text()
        if layerName == "":
            layerName = self.defaultLayerName
        newLayerName = layerName
        index = 1
        while newLayerName in names:
            index += 1
            newLayerName = u"%s_%d" % (layerName, index)

        # create the layer
        layer = self.db.toSqlLayer(query, geomFieldName, uniqueFieldName, newLayerName, layerType,
                                   self.avoidSelectById.isChecked())
        if layer.isValid():
            QgsMapLayerRegistry.instance().addMapLayers([layer], True)

        QApplication.restoreOverrideCursor()

    def fillColumnCombos(self):
        query = self._getSqlQuery()
        if query == "":
            return

        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))

        # get a new alias
        aliasIndex = 0
        while True:
            alias = "_%s__%d" % ("subQuery", aliasIndex)
            escaped = re.compile('\\b("?)' + re.escape(alias) + '\\1\\b')
            if not escaped.search(query):
                break
            aliasIndex += 1

        # remove a trailing ';' from query if present
        if query.strip().endswith(';'):
            query = query.strip()[:-1]

        # get all the columns
        cols = []
        quotedCols = []
        connector = self.db.connector
        sql = u"SELECT * FROM (%s\n) AS %s LIMIT 0" % (unicode(query), connector.quoteId(alias))

        c = None
        try:
            c = connector._execute(None, sql)
            cols = connector._get_cursor_columns(c)
            for col in cols:
                quotedCols.append(connector.quoteId(col))

        except BaseError as e:
            QApplication.restoreOverrideCursor()
            DlgDbError.showError(e, self)
            self.uniqueModel.clear()
            self.geomCombo.clear()
            return

        finally:
            if c:
                c.close()
                del c

        self.setColumnCombos(cols, quotedCols)

        QApplication.restoreOverrideCursor()

    def setColumnCombos(self, cols, quotedCols):
        # get sensible default columns. do this before sorting in case there's hints in the column order (eg, id is more likely to be first)
        try:
            defaultGeomCol = next(col for col in cols if col in ['geom', 'geometry', 'the_geom', 'way'])
        except:
            defaultGeomCol = None
        try:
            defaultUniqueCol = [col for col in cols if 'id' in col][0]
        except:
            defaultUniqueCol = None

        colNames = sorted(zip(cols, quotedCols))
        newItems = []
        uniqueIsFilled = False
        for (col, quotedCol) in colNames:
            item = QStandardItem(col)
            item.setData(quotedCol)
            item.setEnabled(True)
            item.setCheckable(self.allowMultiColumnPk)
            item.setSelectable(not self.allowMultiColumnPk)
            if self.allowMultiColumnPk:
                matchingItems = self.uniqueModel.findItems(col)
                if matchingItems:
                    item.setCheckState(matchingItems[0].checkState())
                    uniqueIsFilled = uniqueIsFilled or matchingItems[0].checkState() == Qt.Checked
                else:
                    item.setCheckState(Qt.Unchecked)
            newItems.append(item)
        if self.allowMultiColumnPk:
            self.uniqueModel.clear()
            self.uniqueModel.appendColumn(newItems)
            self.uniqueChanged()
        else:
            previousUniqueColumn = self.uniqueCombo.currentText()
            self.uniqueModel.clear()
            self.uniqueModel.appendColumn(newItems)
            if self.uniqueModel.findItems(previousUniqueColumn):
                self.uniqueCombo.setEditText(previousUniqueColumn)
                uniqueIsFilled = True

        oldGeometryColumn = self.geomCombo.currentText()
        self.geomCombo.clear()
        self.geomCombo.addItems(cols)
        self.geomCombo.setCurrentIndex(self.geomCombo.findText(oldGeometryColumn, Qt.MatchExactly))

        # set sensible default columns if the columns are not already set
        try:
            if self.geomCombo.currentIndex() == -1:
                self.geomCombo.setCurrentIndex(cols.index(defaultGeomCol))
        except:
            pass
        items = self.uniqueModel.findItems(defaultUniqueCol)
        if items and not uniqueIsFilled:
            if self.allowMultiColumnPk:
                items[0].setCheckState(Qt.Checked)
            else:
                self.uniqueCombo.setEditText(defaultUniqueCol)
        try:
            pass
        except:
            pass

    def copySelectedResults(self):
        if len(self.viewResult.selectedIndexes()) <= 0:
            return
        model = self.viewResult.model()

        # convert to string using tab as separator
        text = model.headerToString("\t")
        for idx in self.viewResult.selectionModel().selectedRows():
            text += "\n" + model.rowToString(idx.row(), "\t")

        QApplication.clipboard().setText(text, QClipboard.Selection)
        QApplication.clipboard().setText(text, QClipboard.Clipboard)

    def initCompleter(self):
        dictionary = None
        if self.db:
            dictionary = self.db.connector.getSqlDictionary()
        if not dictionary:
            # use the generic sql dictionary
            from .sql_dictionary import getSqlDictionary

            dictionary = getSqlDictionary()

        wordlist = []
        for name, value in dictionary.iteritems():
            wordlist += value  # concat lists
        wordlist = list(set(wordlist))  # remove duplicates

        api = QsciAPIs(self.editSql.lexer())
        for word in wordlist:
            api.add(word)

        api.prepare()
        self.editSql.lexer().setAPIs(api)

    def displayQueryBuilder(self):
        dlg = QueryBuilderDlg(self.iface, self.db, self, reset=self.queryBuilderFirst)
        self.queryBuilderFirst = False
        r = dlg.exec_()
        if r == QDialog.Accepted:
            self.editSql.setText(dlg.query)

    def createView(self):
        name, ok = QInputDialog.getText(None, "View name", "View name")
        if ok:
            try:
                self.db.connector.createSpatialView(name, self._getSqlQuery())
            except BaseError as e:
                DlgDbError.showError(e, self)

    def _getSqlQuery(self):
        sql = self.editSql.selectedText()
        if len(sql) == 0:
            sql = self.editSql.text()
        return sql

    def uniqueChanged(self):
        # when an item is (un)checked, simply trigger an update of the combobox text
        self.uniqueTextChanged(None)

    def uniqueTextChanged(self, text):
        # Whenever there is new text displayed in the combobox, check if it is the correct one and if not, display the correct one.
        checkedItems = []
        for item in self.uniqueModel.findItems("*", Qt.MatchWildcard):
            if item.checkState() == Qt.Checked:
                checkedItems.append(item.text())
        label = ", ".join(checkedItems)
        if text != label:
            self.uniqueCombo.setEditText(label)
Exemple #57
0
class LandmarkToolbox(QDockWidget, Ui_DockWidget):
    landmarkMessage = pyqtSignal(unicode, int)

    def __init__(self, iface):
        QDockWidget.__init__(self)
        self.setupUi(self)

        self.iface = iface
        self.canvas = self.iface.mapCanvas()
        self.geoCrs = QgsCoordinateReferenceSystem(4326)

        self.btnAddPhoto.setIcon(QIcon(':/icons/camera.svg'))

        self.txtPhotoComment.setPlaceholderText(self.tr('Comment'))
        self.cmbLayers.setFilters(QgsMapLayerProxyModel.VectorLayer)

        self.db = QSqlDatabase.addDatabase('QPSQL')
        self.landmarkId = None
        self.photoId = None
        self.highlight = None

        self.model = QStandardItemModel()
        self.lstPhotos.setModel(self.model)

        self.btnUpdateLandmark.clicked.connect(self.saveLandmark)
        self.btnDeleteLandmark.clicked.connect(self.deleteLandmark)
        self.btnAddPhoto.clicked.connect(self.addPhoto)
        self.btnUpdatePhoto.clicked.connect(self.savePhoto)
        self.btnDeletePhoto.clicked.connect(self.removePhoto)
        self.lstPhotos.selectionModel().selectionChanged.connect(
            self.photoSelected)
        self.lstPhotos.doubleClicked.connect(self.showPhoto)

        self._enableOrDisableButtons()
        self.ToggleToolbox()

    def ToggleToolbox(self):
        layer_list = self.canvas.layers()

        if not layer_list:
            self.hide()
            return
        elif len(layer_list) == 0:
            self.hide()
            return

        self.setVisible(not self.isVisible())

    def getLandmarkID(self):
        #ランドマークがなかった時の処理
        return self.landmarkId

    def openDatabase(self):
        if self.db.isValid():
            settings = QSettings('MatsueGkukan', 'Gkukandb')
            dbHostName = settings.value('hostname')
            dbDatabaseName = settings.value('databasename')
            dbUserName = settings.value('username')
            dbPassword = settings.value('dbpassword')

            self.db.setHostName(dbHostName)
            self.db.setDatabaseName(dbDatabaseName)
            self.db.setUserName(dbUserName)
            self.db.setPassword(dbPassword)

            if not self.db.open():
                self.GKukanMusiumMessage.emit(
                    self.tr('Can not open GKukanMusium database'),
                    QgsMessageBar.WARNING)
                return False

            self.query = QSqlQuery(self.db)
            return True
        else:
            settings = QSettings('MatsueGkukan', 'Gkukandb')
            dbHostName = settings.value('hostname')
            dbDatabaseName = settings.value('databasename')
            dbUserName = settings.value('username')
            dbPassword = settings.value('dbpassword')

            self.db.removeDatabase(dbDatabaseName)
            del self.db
            self.db = None
            self.db = QSqlDatabase.addDatabase('QPSQL')

            self.db.setHostName(dbHostName)
            self.db.setDatabaseName(dbDatabaseName)
            self.db.setUserName(dbUserName)
            self.db.setPassword(dbPassword)

            if not self.db.open():
                self.GKukanMusiumMessage.emit(
                    self.tr('Can not open GKukanMusium database'),
                    QgsMessageBar.WARNING)
                return False

            self.query = QSqlQuery(self.db)
            return True

        return False

    def GetPhotoFolderPath(self):
        if not self.openDatabase():
            return False

        if self.query.exec_(u'select * from m_folder'):
            self.query.first()
            self.folderpath = self.query.value(2)
            self.thumbpath = os.path.join(self.folderpath, 'thumb')
            ret = self.folderpath
        else:
            ret = ''

        self.db.close()

        return ret

    def landmarkSelected(self, infos):
        self.info = infos[0]
        ft = self.info[1]
        self.landmarkId = ft['id']

        self.leLandmarkTitle.setText(ft['title'] if ft['title'] else '')
        self.spnLandmarkClass.setValue(
            ft['icon_type'] if ft['icon_type'] != None else 0)

        self._highlightLandmark()
        self.populatePhotos()
        self._enableOrDisableButtons()

    def populatePhotos(self, index=0):
        self.model.clear()

        QApplication.setOverrideCursor(Qt.WaitCursor)

        # photos is a list of tuples (id, title, imagepath)
        photos = self._photosOfLandmark()
        for i in photos:

            tp = os.path.join(self.thumbpath, str(i[0])) + '.png'

            img = self.thumbnailPhoto(i[2], tp)

            icon = QIcon(img)

            title = i[1] if i[1] else '<unnamed photo> %s' % i[0]

            item = QStandardItem(title)
            item.setIcon(icon)
            item.setData(i[0], Qt.UserRole)
            item.setToolTip(title)
            self.model.appendRow(item)
            lastIdx = self.model.indexFromItem(item)

        idx = self.model.createIndex(0, 0)
        if self.model.rowCount() > 0:
            if index == -1:
                idx = lastIdx
            elif index > 0:
                idx = self.model.createIndex(index, 0)
            self.lstPhotos.selectionModel().select(idx,
                                                   QItemSelectionModel.Select)
        else:
            self._clearForm()

        QApplication.restoreOverrideCursor()

    def thumbnailPhoto(self, imagePath, tp):

        if os.path.exists(tp):
            return QPixmap(tp)
        else:
            if os.path.exists(os.path.dirname(tp)) == False:
                os.mkdir(os.path.dirname(tp))
            pixmap = QPixmap(imagePath).scaled(800, 600).scaled(
                75, 50, Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
            a = pixmap.save(tp, 'PNG')
            return pixmap

    def showPhoto(self, index):
        if not self.openDatabase():
            return

        item = self.lstPhotos.model().itemFromIndex(index)
        self.query.prepare(
            'SELECT filename ,ST_X(geom),ST_Y(geom),geom FROM t_photo WHERE p_id=?;'
        )
        self.query.addBindValue(item.data(Qt.UserRole))
        if self.query.exec_():
            self.query.first()
            path = os.path.join(self.folderpath, self.query.value(0))
            if self.query.value(
                    3) <> '010100000000000000000000000000000000000000':
                lon = self.query.value(1)
                lat = self.query.value(2)
                point = self._transformPoint(QgsPoint(lon, lat))
                self.canvas.freeze(True)
                self.canvas.setCenter(point)
                self.canvas.freeze(False)
                self.canvas.refresh()

            dlg = ViewPhotoDialog(path)
            dlg.exec_()

        else:
            a = self.query.lastError().text()

        self.db.close()

    def saveLandmark(self):
        layer = self.info[0]
        fid = self.info[1].id()

        idxTitle = layer.fieldNameIndex('title')
        idxClassification = layer.fieldNameIndex('icon_type')

        attrs = {idxTitle: self.leLandmarkTitle.text(),\
                 idxClassification: self.spnLandmarkClass.value()
                }

        layer.dataProvider().changeAttributeValues({fid: attrs})
        layer.reload()
        layer.triggerRepaint()

        self.landmarkMessage.emit(self.tr('Landmark updated.'),
                                  QgsMessageBar.INFO)

    def deleteLandmark(self):
        layer = self.info[0]
        fid = self.info[1].id()

        layer.dataProvider().deleteFeatures([fid])
        layer.reload()
        layer.triggerRepaint()

        self._clearAllFields()

        self.landmarkMessage.emit(self.tr('Landmark deleted.'),
                                  QgsMessageBar.INFO)

    def addPhoto(self):
        if self.landmarkId is not None:
            settings = QSettings('MatsueGkukan', 'Gkukandb')
            lastDir = settings.value('lastPhotoDir', '.')

            fileName = QFileDialog.getOpenFileName(self,
                                                   self.tr('Select photo'),
                                                   lastDir,
                                                   self._createFilter())

            if fileName == '':
                return

            settings.setValue('lastPhotoDir',
                              QFileInfo(fileName).absoluteDir().absolutePath())

            projectPath = self.GetPhotoFolderPath() + os.sep
            photoPath = os.path.basename(fileName)
            photoDate = self._photoDate(fileName).toString('yyyy-MM-dd')

            if not self.openDatabase():
                return

            self.query.prepare(
                'INSERT INTO t_photo("cdate", "filename", "landmark_id",lon,lat,angle,geomtype,geom) VALUES(?, ?, ?,?,?,?,?,?);'
            )
            self.query.addBindValue(photoDate)
            self.query.addBindValue(photoPath)
            self.query.addBindValue(self.landmarkId)
            self.query.addBindValue(0)
            self.query.addBindValue(0)
            self.query.addBindValue(0)
            self.query.addBindValue(0)
            self.query.addBindValue(
                '010100000000000000000000000000000000000000')
            if self.query.exec_():
                self._copyPhotoToFolder(fileName, self.landmarkId)
                self.populatePhotos(-1)
            else:
                a = self.query.lastError().text()

            self.db.close()
        else:
            self.landmarkMessage.emit(
                self.tr('Select landmark before adding a photo.'),
                QgsMessageBar.WARNING)

    def savePhoto(self):
        if not self.openDatabase():
            return

        self.query.prepare(
            'UPDATE t_photo SET film_no=?, keywords=?, keyword1=?, keyword2=?, keyword3=?, notes=?, mdate=?, registrant=?, comment=?, reference=?, angle=? WHERE p_id=?;'
        )
        self.query.addBindValue(self.lePhotoTitle.text())
        self.query.addBindValue(self.leKeywords.text())
        self.query.addBindValue(self.leKeyword1.text())
        self.query.addBindValue(self.leKeyword2.text())
        self.query.addBindValue(self.leKeyword3.text())
        self.query.addBindValue(self.txtPhotoComment.toPlainText())
        self.query.addBindValue(
            self.edPhotoDate.dateTime().toString('yyyy-MM-dd'))
        self.query.addBindValue(self.leRegistrant.text())
        self.query.addBindValue(self.leComment.text())
        self.query.addBindValue(self.lerRference.text())
        self.query.addBindValue(self.spnPhotoAngle.value())
        self.query.addBindValue(self.photoId)

        if self.query.exec_():
            self.landmarkMessage.emit(self.tr('Photo updated.'),
                                      QgsMessageBar.INFO)
            self.populatePhotos(self.lstPhotos.currentIndex().row())
        else:
            a = self.query.lastError().text()

        self.db.close()

    def removePhoto(self):
        if not self.openDatabase():
            return

        self.query.prepare('DELETE FROM t_photo WHERE "p_id"=?;')
        self.query.addBindValue(self.photoId)
        if self.query.exec_():
            self.db.close()
            self._removePhotofromFolder()

            self.populatePhotos()

    def photoSelected(self, current, previous):
        if not self.openDatabase():
            return

        idx = current.indexes()[0]
        item = self.lstPhotos.model().itemFromIndex(idx)
        self.photoId = item.data(Qt.UserRole)

        self.query.prepare(
            'SELECT film_no, filename, keywords, keyword1, keyword2, keyword3, notes, mdate, registrant, comment, reference, angle FROM t_photo WHERE p_id=?;'
        )
        self.query.addBindValue(self.photoId)
        if self.query.exec_():
            self.query.first()
            self.filename = self.query.value(1)
            self.lePhotoTitle.setText(
                self.query.value(0) if self.query.value(0) else '')
            self.txtPhotoComment.setPlainText(
                self.query.value(6) if self.query.value(6) else '')
            self.leKeywords.setText(
                self.query.value(2) if self.query.value(2) else '')
            self.leKeyword1.setText(
                self.query.value(3) if self.query.value(3) else '')
            self.leKeyword2.setText(
                self.query.value(4) if self.query.value(4) else '')
            self.leKeyword3.setText(
                self.query.value(5) if self.query.value(5) else '')
            self.leRegistrant.setText(
                self.query.value(8) if self.query.value(8) else '')
            self.leComment.setText(
                self.query.value(9) if self.query.value(9) else '')
            self.lerRference.setText(
                self.query.value(10) if self.query.value(10) else '')
            self.spnPhotoAngle.setValue(
                int(self.query.value(11)) if self.query.value(11) else 0)
            self.edPhotoDate.setDateTime(
                self.query.value(7) if self.query.value(7) else QDateTime.
                currentDateTime())

        self._enableOrDisableButtons()

        self.db.close()

    def _photosOfLandmark(self):

        projectPath = self.GetPhotoFolderPath()

        if not self.openDatabase():
            return

        photos = []
        self.query.prepare(
            'SELECT "p_id", "keywords", "filename" FROM t_photo WHERE "landmark_id"=? ORDER BY "p_id";'
        )
        self.query.addBindValue(self.landmarkId)
        if self.query.exec_():
            while self.query.next():
                photos.append((self.query.value(0), self.query.value(1),
                               os.path.join(projectPath, self.query.value(2))))

        self.db.close()

        return photos

    def _createFilter(self):
        formats = ''
        for f in QImageReader.supportedImageFormats():
            f = unicode(f)
            if f == 'svg':
                continue
            formats += '*.{} *.{} '.format(f.lower(), f.upper())

        return self.tr('Image files (%s);;All files (*.*)' % formats[:-1])

    def _clearForm(self):
        self.lePhotoTitle.clear()
        self.txtPhotoComment.clear()
        self.leKeyword1.clear()
        self.leKeyword2.clear()
        self.leKeyword3.clear()
        self.leRegistrant.clear()
        self.leComment.clear()
        self.lerRference.clear()

        self.photoId = None
        self.landmarkId = None

    def _enableOrDisableButtons(self):
        if self.landmarkId is None:
            self.btnAddPhoto.setEnabled(False)
        else:
            self.btnAddPhoto.setEnabled(True)

        if self.photoId is None:
            self.btnDeletePhoto.setEnabled(False)
            self.btnUpdatePhoto.setEnabled(False)
        else:
            self.btnDeletePhoto.setEnabled(True)
            self.btnUpdatePhoto.setEnabled(True)

    def _highlightLandmark(self):
        self._clearHighlight()

        self.highlight = QgsHighlight(self.canvas, self.info[1].geometry(),
                                      self.info[0])

        settings = QSettings()
        color = QColor(
            settings.value('/Map/highlight/color',
                           QGis.DEFAULT_HIGHLIGHT_COLOR.name()))
        alpha = settings.value('/Map/highlight/colorAlpha',
                               QGis.DEFAULT_HIGHLIGHT_COLOR.alpha(),
                               type=int)
        buffer = settings.value('/Map/highlight/buffer',
                                QGis.DEFAULT_HIGHLIGHT_BUFFER_MM,
                                type=float)
        minWidth = settings.value('/Map/highlight/minWidth',
                                  QGis.DEFAULT_HIGHLIGHT_MIN_WIDTH_MM,
                                  type=float)

        self.highlight.setColor(color)
        color.setAlpha(alpha)
        self.highlight.setFillColor(color)
        self.highlight.setBuffer(buffer)
        self.highlight.setMinWidth(minWidth)
        self.highlight.show()

    def _photoDate(self, path):
        with open(path, 'rb') as imgFile:
            tags = exifread.process_file(imgFile, details=False)

        if 'EXIF GPS GPSDate' in tags:
            return QDateTime.fromString(tags['EXIF GPS GPSDate'].values,
                                        'yyyy:MM:dd')
        else:
            return QDateTime.currentDateTime()

    def _clearHighlight(self):
        if hasattr(self, 'highlight'):
            del self.highlight
            self.highlight = None

    def _clearAllFields(self):
        self.leLandmarkTitle.clear()
        self.spnLandmarkClass.clear()

        self._clearHighlight()
        self._clearForm()
        self.model.clear()

    def _copyPhotoToFolder(self, path, landmark_id):
        projectPath = self.GetPhotoFolderPath()
        dst = os.path.join(projectPath, os.path.basename(path))

        shutil.copy2(path, dst)

    def _removePhotofromFolder(self, path, landmark_id):
        projectPath = self.GetPhotoFolderPath()
        dst = os.path.join(projectPath, path)

        os.remove(dst)

    def _transformPoint(self, pnt):
        crsDest = self.canvas.mapSettings().destinationCrs()
        xform = QgsCoordinateTransform(self.geoCrs, crsDest)
        p2 = xform.transform(pnt)
        return p2