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()
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()
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)
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)
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)
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
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)
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)
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
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")
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)
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)
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)
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)
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())
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
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")
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)))
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
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 )
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()
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")
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)
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)
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()
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))
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)
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
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)
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)
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'
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()
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 )
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
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)
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)
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