class NameList(QDockWidget): def __init__(self, window): super(NameList, self).__init__('Current Plots') self.namelist_model = QStandardItemModel() self.namelist_view = QListView() self.namelist_view.setModel(self.namelist_model) self.setWidget(self.namelist_view) self.window = window self.plot_dict = {} self.namelist_view.doubleClicked.connect(self.activate_item) self.namelist_view.setContextMenuPolicy(QtConst.ActionsContextMenu) delete_action = QAction("Delete Selected", self.namelist_view) delete_action.triggered.connect(self.delete_item) self.namelist_view.addAction(delete_action) def activate_item(self, index): item = self.namelist_model.itemFromIndex(index) plot = self.plot_dict[str(item.text())] if plot.closed: plot.closed = False self.window.add_plot(plot) def delete_item(self): index = self.namelist_view.currentIndex() item = self.namelist_model.itemFromIndex(index) del self[str(item.text())] def __getitem__(self, item): return self.plot_dict[item] def __setitem__(self, name, plot): model = QStandardItem(name) model.setEditable(False) self.namelist_model.appendRow(model) self.plot_dict[name] = plot def __contains__(self, value): return value in self.plot_dict def __delitem__(self, name): self.namelist_model.removeRow( self.namelist_model.findItems(name)[0].index().row()) self.plot_dict[name].close() del self.plot_dict[name] def keys(self): return self.plot_dict.keys()
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 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 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 ProgramInfoLogs(QWidget, Ui_ProgramInfoLogs): def __init__(self, context, update_main_ui_state, set_widget_enabled, is_alive, show_download_wizard, set_program_callbacks_enabled): 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.set_program_callbacks_enabled = set_program_callbacks_enabled self.log_directory = posixpath.join(self.program.root_directory, 'log') self.refresh_in_progress = False self.any_refresh_in_progress = False # set from ProgramInfoMain.update_ui_state self.view_dialog = None self.file_icon = QIcon(load_pixmap('file-icon.png')) self.tree_logs_model = QStandardItemModel(self) self.tree_logs_model_header = ['Date/Time', 'Size'] self.tree_logs_proxy_model = LogsProxyModel(self) self.last_download_directory = get_home_path() self.tree_logs_model.setHorizontalHeaderLabels(self.tree_logs_model_header) self.tree_logs_proxy_model.setSourceModel(self.tree_logs_model) self.tree_logs.setModel(self.tree_logs_proxy_model) self.tree_logs.setColumnWidth(0, 250) self.tree_logs.selectionModel().selectionChanged.connect(self.update_ui_state) self.tree_logs.activated.connect(self.view_activated_log) self.button_download_logs.clicked.connect(self.download_selected_logs) self.button_view_log.clicked.connect(self.view_selected_log) self.button_delete_logs.clicked.connect(self.delete_selected_logs) self.label_error.setVisible(False) def update_ui_state(self): selection_count = len(self.tree_logs.selectionModel().selectedRows()) self.set_widget_enabled(self.button_download_logs, not self.any_refresh_in_progress and selection_count > 0) self.set_widget_enabled(self.button_view_log, not self.any_refresh_in_progress and selection_count == 1 and len(self.get_directly_selected_log_items()) == 1) self.set_widget_enabled(self.button_delete_logs, not self.any_refresh_in_progress and selection_count > 0) def close_all_dialogs(self): if self.view_dialog != None: self.view_dialog.close() def refresh_logs_done(self): self.refresh_in_progress = False self.update_main_ui_state() def refresh_logs(self): def cb_program_logs_list(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_logs_done() return try: # FIXME: do decompress in an async_call program_logs_list = json.loads(zlib.decompress(buffer(result.stdout)).decode('utf-8')) except: program_logs_list = None if program_logs_list == None or not isinstance(program_logs_list, dict): self.label_error.setText('<b>Error:</b> Received invalid data') self.label_error.setVisible(True) self.refresh_logs_done() return self.label_error.setVisible(False) def create_file_size_item(size): item = QStandardItem(get_file_display_size(size)) item.setData(size, USER_ROLE_SIZE) return item def update_file_size_item(item, additional_size): current_size = item.data(USER_ROLE_SIZE) new_size = current_size + additional_size item.setText(get_file_display_size(new_size)) item.setData(new_size, USER_ROLE_SIZE) continuous_row = None date_rows = {} time_rows = {} for file_name, file_size in program_logs_list.iteritems(): QApplication.processEvents() file_name_parts = file_name.split('_') if file_name_parts[0] == "continuous": if len(file_name_parts) != 2: continue if continuous_row == None: continuous_item = QStandardItem("Continuous") continuous_item.setData(ITEM_TYPE_PARENT_CONT, USER_ROLE_ITEM_TYPE) continuous_row = [continuous_item, create_file_size_item(0)] self.tree_logs_model.appendRow(continuous_row) log_item = QStandardItem(file_name_parts[1]) log_item.setData(self.file_icon, Qt.DecorationRole) log_item.setData(file_name, USER_ROLE_FILE_NAME) log_item.setData(ITEM_TYPE_LOG_FILE_CONT, USER_ROLE_ITEM_TYPE) continuous_row[0].appendRow([log_item, create_file_size_item(file_size)]) update_file_size_item(continuous_row[1], file_size) else: if len(file_name_parts) != 3: continue try: timestamp = int(file_name_parts[1].split('+')[0]) / 1000000 except ValueError: continue date = QDateTime.fromTime_t(timestamp).toString('yyyy-MM-dd') time = QDateTime.fromTime_t(timestamp).toString('HH:mm:ss') date_time = date + 'T' + time if date in date_rows: date_row = date_rows[date] else: date_item = QStandardItem(date) date_item.setData(ITEM_TYPE_PARENT_DATE, USER_ROLE_ITEM_TYPE) date_row = [date_item, create_file_size_item(0)] date_rows[date] = date_row self.tree_logs_model.appendRow(date_row) if date_time in time_rows: time_row = time_rows[date_time] else: time_item = QStandardItem(time) time_item.setData(ITEM_TYPE_PARENT_TIME, USER_ROLE_ITEM_TYPE) time_row = [time_item, create_file_size_item(0)] time_rows[date_time] = time_row date_row[0].appendRow(time_row) log_item = QStandardItem(file_name_parts[2]) log_item.setData(self.file_icon, Qt.DecorationRole) log_item.setData(file_name, USER_ROLE_FILE_NAME) log_item.setData(ITEM_TYPE_LOG_FILE, USER_ROLE_ITEM_TYPE) time_row[0].appendRow([log_item, create_file_size_item(file_size)]) update_file_size_item(time_row[1], file_size) update_file_size_item(date_row[1], file_size) self.tree_logs.header().setSortIndicator(0, Qt.DescendingOrder) self.refresh_logs_done() self.refresh_in_progress = True self.update_main_ui_state() width = self.tree_logs.columnWidth(0) self.tree_logs_model.clear() self.tree_logs_model.setHorizontalHeaderLabels(self.tree_logs_model_header) self.tree_logs.setColumnWidth(0, width) self.script_manager.execute_script('program_logs_list', cb_program_logs_list, [self.log_directory], max_length=1024*1024, decode_output_as_utf8=False) def get_directly_selected_log_items(self): selected_indexes = self.tree_logs.selectedIndexes() selected_log_items = [] for selected_index in selected_indexes: if selected_index.column() == 0: mapped_index = self.tree_logs_proxy_model.mapToSource(selected_index) selected_item = self.tree_logs_model.itemFromIndex(mapped_index) item_type = selected_item.data(USER_ROLE_ITEM_TYPE) if item_type in [ITEM_TYPE_LOG_FILE, ITEM_TYPE_LOG_FILE_CONT]: selected_log_items.append(selected_item) return selected_log_items def load_log_files_for_ops(self, index_list): logs_download_dict = {'files': {}, 'total_download_size': 0, 'foobar':[]} def populate_log_download(item_list): item_type = item_list[0].data(USER_ROLE_ITEM_TYPE) if item_type == ITEM_TYPE_PARENT_CONT: for i in range(item_list[0].rowCount()): f_size = item_list[0].child(i, 1).data(USER_ROLE_SIZE) # File size file_name = item_list[0].child(i, 0).data(USER_ROLE_FILE_NAME) f_path = posixpath.join(self.log_directory, file_name) # File path if not f_path in logs_download_dict['files']: logs_download_dict['files'][f_path] = {'size': f_size} logs_download_dict['total_download_size'] += f_size logs_download_dict['foobar'].append(file_name) elif item_type == ITEM_TYPE_PARENT_DATE: for i in range(item_list[0].rowCount()): parent_time = item_list[0].child(i) for j in range(parent_time.rowCount()): f_size = parent_time.child(j, 1).data(USER_ROLE_SIZE) # File size file_name = parent_time.child(j, 0).data(USER_ROLE_FILE_NAME) f_path = posixpath.join(self.log_directory, file_name) # File path if not f_path in logs_download_dict['files']: logs_download_dict['files'][f_path] = {'size': f_size} logs_download_dict['total_download_size'] += f_size logs_download_dict['foobar'].append(file_name) elif item_type == ITEM_TYPE_PARENT_TIME: for i in range(item_list[0].rowCount()): f_size = item_list[0].child(i, 1).data(USER_ROLE_SIZE) # File size file_name = item_list[0].child(i, 0).data(USER_ROLE_FILE_NAME) f_path = posixpath.join(self.log_directory, file_name) # File path if not f_path in logs_download_dict['files']: logs_download_dict['files'][f_path] = {'size': f_size} logs_download_dict['total_download_size'] += f_size logs_download_dict['foobar'].append(file_name) elif item_type in [ITEM_TYPE_LOG_FILE, ITEM_TYPE_LOG_FILE_CONT]: f_size = item_list[1].data(USER_ROLE_SIZE) # File size file_name = item_list[0].data(USER_ROLE_FILE_NAME) f_path = posixpath.join(self.log_directory, file_name) # File path if not f_path in logs_download_dict['files']: logs_download_dict['files'][f_path] = {'size': f_size} logs_download_dict['total_download_size'] += f_size logs_download_dict['foobar'].append(file_name) index_rows = [] for index in index_list: if index.column() == 0: index_rows.append([index, index.sibling(index.row(), 1)]) for index_row in index_rows: item_list = [] for index in index_row: item = self.tree_logs_model.itemFromIndex(self.tree_logs_proxy_model.mapToSource(index)) item_list.append(item) populate_log_download(item_list) return logs_download_dict def download_selected_logs(self): index_list = self.tree_logs.selectedIndexes() if len(index_list) == 0: return log_files_to_download = self.load_log_files_for_ops(index_list) if len(log_files_to_download['foobar']) == 0: return download_directory = get_existing_directory(get_main_window(), 'Download Logs', self.last_download_directory) if len(download_directory) == 0: return self.last_download_directory = download_directory downloads = [] for file_name in log_files_to_download['foobar']: downloads.append(Download(file_name, file_name)) self.show_download_wizard('logs', download_directory, downloads) def view_activated_log(self, index): if index.column() == 0 and not self.any_refresh_in_progress: mapped_index = self.tree_logs_proxy_model.mapToSource(index) item = self.tree_logs_model.itemFromIndex(mapped_index) item_type = item.data(USER_ROLE_ITEM_TYPE) if item_type in [ITEM_TYPE_LOG_FILE, ITEM_TYPE_LOG_FILE_CONT]: self.view_log(item) def view_selected_log(self): selection_count = len(self.tree_logs.selectionModel().selectedRows()) if selection_count != 1: return selected_log_items = self.get_directly_selected_log_items() if len(selected_log_items) != 1: return self.view_log(selected_log_items[0]) def view_log(self, item): file_name = posixpath.join(self.log_directory, item.data(USER_ROLE_FILE_NAME)) self.set_program_callbacks_enabled(False) self.view_dialog = ProgramInfoLogsView(self, self.session, file_name) self.view_dialog.exec_() self.view_dialog = None if self.is_alive(): self.set_program_callbacks_enabled(True) # FIXME: make this work like delete_selected_files def delete_selected_logs(self): button = QMessageBox.question(get_main_window(), 'Delete Logs', 'Irreversibly deleting selected logs.', QMessageBox.Ok, QMessageBox.Cancel) if not self.is_alive() or button != QMessageBox.Ok: return def cb_delete(result): self.refresh_logs() report_script_result(result, 'Delete Logs Error', 'Could not delete selected logs') index_list = self.tree_logs.selectedIndexes() if not index_list: return log_files_to_delete = self.load_log_files_for_ops(index_list) if not log_files_to_delete: return file_list = [] for f_path in log_files_to_delete['files']: file_list.append(f_path) if len(file_list) > 0: self.script_manager.execute_script('delete', cb_delete, [json.dumps(file_list), json.dumps([])], execute_as_user=True)
class QMapManager(QDialog): def __init__(self, config, parent=None): QDialog.__init__(self, parent) curdir = os.path.abspath(os.path.dirname(__file__)) PyQt4.uic.loadUi(os.path.join(curdir, 'manager.ui'), self) self.model = QStandardItemModel() self.projectsmodel = QStandardItemModel() self.projectlist.setModel(self.projectsmodel) self.clientlist.setModel(self.model) self.clientlist.selectionModel().selectionChanged.connect(self.update) self.installbutton.pressed.connect(self.installToClient) self.mapper = QDataWidgetMapper() self.mapper.setModel(self.model) self.mapper.addMapping(self.installpath, 1) self.config = config self.populateProjects() self.populateClients() def installToClient(self): index = self.clientlist.selectionModel().currentIndex() item = self.model.itemFromIndex(index) print "Deploying to " + item.text() build.deployTargetByName(item.text()) def update(self, selected, deselected): index = selected.indexes()[0] self.mapper.setCurrentModelIndex(index) item = self.model.itemFromIndex(index) settings = item.data() for row in xrange(0, self.projectsmodel.rowCount()): index = self.projectsmodel.index(row, 0) item = self.projectsmodel.itemFromIndex(index) item.setCheckState(Qt.Unchecked) projects = settings['projects'] for project in projects: if project == "All": i = 0 while self.projectsmodel.item(i): item = self.projectsmodel.item(i) item.setCheckState(Qt.Checked) i += 1 break projectitem = self.projectsmodel.findItems(project)[0] projectitem.setCheckState(Qt.Checked) def populateClients(self): row = 0 for client, settings in self.config['clients'].iteritems(): name = QStandardItem(client) name.setData(settings) path = QStandardItem(settings['path']) self.model.insertRow(row, [name, path]) row += 1 def populateProjects(self): row = 0 for project in getProjects(): projectitem = QStandardItem(project.name) projectitem.setCheckable(True) self.projectsmodel.insertRow(row, projectitem) row += 1
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 TemplateDocumentSelector(QDialog, Ui_frmDocumentSelector): """ Dialog for selecting a document template from the saved list. """ def __init__(self, parent=None, selectMode=True): QDialog.__init__(self, parent) self.setupUi(self) self.notifBar = NotificationBar(self.vlNotification) if selectMode: self.buttonBox.setVisible(True) self.manageButtonBox.setVisible(False) currHeight = self.size().height() self.resize(200, currHeight) else: self.buttonBox.setVisible(False) self.manageButtonBox.setVisible(True) self.setWindowTitle( QApplication.translate("TemplateDocumentSelector", "Template Manager")) #Configure manage buttons btnEdit = self.manageButtonBox.button(QDialogButtonBox.Ok) btnEdit.setText( QApplication.translate("TemplateDocumentSelector", "Edit...")) btnEdit.setIcon(QIcon(":/plugins/stdm/images/icons/edit.png")) btnDelete = self.manageButtonBox.button(QDialogButtonBox.Save) btnDelete.setText( QApplication.translate("TemplateDocumentSelector", "Delete")) btnDelete.setIcon(QIcon(":/plugins/stdm/images/icons/delete.png")) #Connect signals self.buttonBox.accepted.connect(self.onAccept) btnEdit.clicked.connect(self.onEditTemplate) btnDelete.clicked.connect(self.onDeleteTemplate) #Get saved document templates then add to the model templates = documentTemplates() self._docItemModel = QStandardItemModel(parent) self._docItemModel.setColumnCount(2) for name, path in templates.iteritems(): docNameItem = self._createDocNameItem(name) filePathItem = QStandardItem(path) self._docItemModel.appendRow([docNameItem, filePathItem]) self.lstDocs.setModel(self._docItemModel) def _createDocNameItem(self, docName): """ Create a template document standard item. """ #Set icon icon = QIcon() icon.addPixmap(QPixmap(":/plugins/stdm/images/icons/document.png"), QIcon.Normal, QIcon.Off) dnItem = QStandardItem(icon, docName) return dnItem def onEditTemplate(self): """ Slot raised to edit document template. """ self.notifBar.clear() if self.documentMapping() == None: self.notifBar.insertErrorNotification(QApplication.translate("TemplateDocumentSelector", \ "Please select a document template to edit")) return templateName, filePath = self.documentMapping() docName,ok = QInputDialog.getText(self, \ QApplication.translate("TemplateDocumentSelector","Edit Template"), \ QApplication.translate("TemplateDocumentSelector","Please enter the new template name below"), \ text = templateName) if ok and docName == "": self.notifBar.insertErrorNotification(QApplication.translate("TemplateDocumentSelector", \ "Template name cannot be empty")) return elif docName == templateName: return elif ok and docName != "": result, newTemplatePath = self._editTemplate(filePath, docName) if result: #Update view mIndices = self._selectedMappings() docNameItem = self._docItemModel.itemFromIndex(mIndices[0]) filePathItem = self._docItemModel.itemFromIndex(mIndices[1]) docNameItem.setText(docName) filePathItem.setText(newTemplatePath) self.notifBar.insertInfoNotification(QApplication.translate("TemplateDocumentSelector", \ "'{0}' template has been successfully updated".format(docName))) else: self.notifBar.insertErrorNotification(QApplication.translate("TemplateDocumentSelector", \ "Error: '{0}' template could not be updated".format(templateName))) def onDeleteTemplate(self): """ Slot raised to delete document template. """ self.notifBar.clear() if self.documentMapping() == None: self.notifBar.insertErrorNotification(QApplication.translate("TemplateDocumentSelector", \ "Please select a document template to delete")) return templateName, filePath = self.documentMapping() result = QMessageBox.warning(self, QApplication.translate("TemplateDocumentSelector", \ "Confirm delete"), QApplication.translate("TemplateDocumentSelector", \ "Are you sure you want to delete '{0}' template?" \ "This action cannot be undone.\nClick Yes to proceed " \ "or No to cancel.".format(templateName)), QMessageBox.Yes|QMessageBox.No) if result == QMessageBox.No: return status = self._deleteDocument(filePath) if status: #Remove item from list using model index row number selectedDocNameIndices = self.lstDocs.selectionModel( ).selectedRows(0) row = selectedDocNameIndices[0].row() self._docItemModel.removeRow(row) self.notifBar.insertInfoNotification(QApplication.translate("TemplateDocumentSelector", \ "'{0}' template has been successfully removed".format(templateName))) else: self.notifBar.insertErrorNotification(QApplication.translate("TemplateDocumentSelector", \ "Error: '{0}' template could not be removed".format(templateName))) def onAccept(self): """ Slot raised to close the dialog only when a selection has been made by the user. """ self.notifBar.clear() if self.documentMapping() == None: self.notifBar.insertErrorNotification(QApplication.translate("TemplateDocumentSelector", \ "Please select a document")) return self.accept() def _selectedMappings(self): """ Returns the model indices for the selected row. """ selectedDocNameIndices = self.lstDocs.selectionModel().selectedRows(0) selectedFilePathIndices = self.lstDocs.selectionModel().selectedRows(1) if len(selectedDocNameIndices) == 0: return None docNameIndex = selectedDocNameIndices[0] filePathIndex = selectedFilePathIndices[0] return (docNameIndex, filePathIndex) def documentMapping(self): """ Returns a tuple containing the selected document name and the corresponding file name. """ mIndices = self._selectedMappings() if mIndices == None: return None docNameItem = self._docItemModel.itemFromIndex(mIndices[0]) filePathItem = self._docItemModel.itemFromIndex(mIndices[1]) return (docNameItem.text(), filePathItem.text()) def _editTemplate(self, templatePath, newName): """ Updates the template document to use the new name. """ templateFile = QFile(templatePath) if not templateFile.open(QIODevice.ReadOnly): QMessageBox.critical(self, QApplication.translate("TemplateDocumentSelector","Open Operation Error"), \ "{0}\n{1}".format(QApplication.translate("TemplateDocumentSelector","Cannot read template file."), \ templateFile.errorString() )) return (False, "") templateDoc = QDomDocument() if templateDoc.setContent(templateFile): composerElement = templateDoc.documentElement() titleAttr = composerElement.attributeNode("title") if not titleAttr.isNull(): titleAttr.setValue(newName) #Try remove file status = templateFile.remove() if not status: return (False, "") #Create new file newTemplatePath = self._composerTemplatesPath( ) + "/" + newName + ".sdt" newTemplateFile = QFile(newTemplatePath) if not newTemplateFile.open(QIODevice.WriteOnly): QMessageBox.critical(self, QApplication.translate("TemplateDocumentSelector","Save Operation Error"), \ "{0}\n{1}".format(QApplication.translate("TemplateDocumentSelector","Could not save template file."), \ newTemplateFile.errorString() )) return (False, "") if newTemplateFile.write(templateDoc.toByteArray()) == -1: QMessageBox.critical(self, QApplication.translate("TemplateDocumentSelector","Save Error"), \ QApplication.translate("TemplateDocumentSelector","Could not save template file.")) return (False, "") newTemplateFile.close() return (True, newTemplatePath) def _deleteDocument(self, templatePath): """ Delete the document template from the file system. """ docFile = QFile(templatePath) return docFile.remove() def _composerTemplatesPath(self): """ Reads the path of composer templates in the registry. """ regConfig = RegistryConfig() keyName = "ComposerTemplates" valueCollection = regConfig.read([keyName]) if len(valueCollection) == 0: return None else: return valueCollection[keyName]
class ProgramInfoLogs(QWidget, Ui_ProgramInfoLogs): def __init__(self, context, update_main_ui_state, set_widget_enabled, is_alive, show_download_wizard, set_program_callbacks_enabled): 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.set_program_callbacks_enabled = set_program_callbacks_enabled self.log_directory = posixpath.join(self.program.root_directory, 'log') self.refresh_in_progress = False self.any_refresh_in_progress = False # set from ProgramInfoMain.update_ui_state self.view_dialog = None self.file_icon = QIcon(load_pixmap('file-icon.png')) self.tree_logs_model = QStandardItemModel(self) self.tree_logs_model_header = ['Date/Time', 'Size'] self.tree_logs_proxy_model = LogsProxyModel(self) self.last_download_directory = get_home_path() self.tree_logs_model.setHorizontalHeaderLabels( self.tree_logs_model_header) self.tree_logs_proxy_model.setSourceModel(self.tree_logs_model) self.tree_logs.setModel(self.tree_logs_proxy_model) self.tree_logs.setColumnWidth(0, 250) self.tree_logs.selectionModel().selectionChanged.connect( self.update_ui_state) self.tree_logs.activated.connect(self.view_activated_log) self.button_download_logs.clicked.connect(self.download_selected_logs) self.button_view_log.clicked.connect(self.view_selected_log) self.button_delete_logs.clicked.connect(self.delete_selected_logs) self.label_error.setVisible(False) def update_ui_state(self): selection_count = len(self.tree_logs.selectionModel().selectedRows()) self.set_widget_enabled( self.button_download_logs, not self.any_refresh_in_progress and selection_count > 0) self.set_widget_enabled( self.button_view_log, not self.any_refresh_in_progress and selection_count == 1 and len(self.get_directly_selected_log_items()) == 1) self.set_widget_enabled( self.button_delete_logs, not self.any_refresh_in_progress and selection_count > 0) def close_all_dialogs(self): if self.view_dialog != None: self.view_dialog.close() def refresh_logs_done(self): self.refresh_in_progress = False self.update_main_ui_state() def refresh_logs(self): def cb_program_logs_list(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_logs_done() return try: # FIXME: do decompress in an async_call program_logs_list = json.loads( zlib.decompress(buffer(result.stdout)).decode('utf-8')) except: program_logs_list = None if program_logs_list == None or not isinstance( program_logs_list, dict): self.label_error.setText('<b>Error:</b> Received invalid data') self.label_error.setVisible(True) self.refresh_logs_done() return self.label_error.setVisible(False) def create_file_size_item(size): item = QStandardItem(get_file_display_size(size)) item.setData(size, USER_ROLE_SIZE) return item def update_file_size_item(item, additional_size): current_size = item.data(USER_ROLE_SIZE) new_size = current_size + additional_size item.setText(get_file_display_size(new_size)) item.setData(new_size, USER_ROLE_SIZE) continuous_row = None date_rows = {} time_rows = {} for file_name, file_size in program_logs_list.iteritems(): QApplication.processEvents() file_name_parts = file_name.split('_') if file_name_parts[0] == "continuous": if len(file_name_parts) != 2: continue if continuous_row == None: continuous_item = QStandardItem("Continuous") continuous_item.setData(ITEM_TYPE_PARENT_CONT, USER_ROLE_ITEM_TYPE) continuous_row = [ continuous_item, create_file_size_item(0) ] self.tree_logs_model.appendRow(continuous_row) log_item = QStandardItem(file_name_parts[1]) log_item.setData(self.file_icon, Qt.DecorationRole) log_item.setData(file_name, USER_ROLE_FILE_NAME) log_item.setData(ITEM_TYPE_LOG_FILE_CONT, USER_ROLE_ITEM_TYPE) continuous_row[0].appendRow( [log_item, create_file_size_item(file_size)]) update_file_size_item(continuous_row[1], file_size) else: if len(file_name_parts) != 3: continue try: timestamp = int( file_name_parts[1].split('+')[0]) / 1000000 except ValueError: continue date = QDateTime.fromTime_t(timestamp).toString( 'yyyy-MM-dd') time = QDateTime.fromTime_t(timestamp).toString('HH:mm:ss') date_time = date + 'T' + time if date in date_rows: date_row = date_rows[date] else: date_item = QStandardItem(date) date_item.setData(ITEM_TYPE_PARENT_DATE, USER_ROLE_ITEM_TYPE) date_row = [date_item, create_file_size_item(0)] date_rows[date] = date_row self.tree_logs_model.appendRow(date_row) if date_time in time_rows: time_row = time_rows[date_time] else: time_item = QStandardItem(time) time_item.setData(ITEM_TYPE_PARENT_TIME, USER_ROLE_ITEM_TYPE) time_row = [time_item, create_file_size_item(0)] time_rows[date_time] = time_row date_row[0].appendRow(time_row) log_item = QStandardItem(file_name_parts[2]) log_item.setData(self.file_icon, Qt.DecorationRole) log_item.setData(file_name, USER_ROLE_FILE_NAME) log_item.setData(ITEM_TYPE_LOG_FILE, USER_ROLE_ITEM_TYPE) time_row[0].appendRow( [log_item, create_file_size_item(file_size)]) update_file_size_item(time_row[1], file_size) update_file_size_item(date_row[1], file_size) self.tree_logs.header().setSortIndicator(0, Qt.DescendingOrder) self.refresh_logs_done() self.refresh_in_progress = True self.update_main_ui_state() width = self.tree_logs.columnWidth(0) self.tree_logs_model.clear() self.tree_logs_model.setHorizontalHeaderLabels( self.tree_logs_model_header) self.tree_logs.setColumnWidth(0, width) self.script_manager.execute_script('program_logs_list', cb_program_logs_list, [self.log_directory], max_length=1024 * 1024, decode_output_as_utf8=False) def get_directly_selected_log_items(self): selected_indexes = self.tree_logs.selectedIndexes() selected_log_items = [] for selected_index in selected_indexes: if selected_index.column() == 0: mapped_index = self.tree_logs_proxy_model.mapToSource( selected_index) selected_item = self.tree_logs_model.itemFromIndex( mapped_index) item_type = selected_item.data(USER_ROLE_ITEM_TYPE) if item_type in [ITEM_TYPE_LOG_FILE, ITEM_TYPE_LOG_FILE_CONT]: selected_log_items.append(selected_item) return selected_log_items def load_log_files_for_ops(self, index_list): logs_download_dict = { 'files': {}, 'total_download_size': 0, 'foobar': [] } def populate_log_download(item_list): item_type = item_list[0].data(USER_ROLE_ITEM_TYPE) if item_type == ITEM_TYPE_PARENT_CONT: for i in range(item_list[0].rowCount()): f_size = item_list[0].child(i, 1).data( USER_ROLE_SIZE) # File size file_name = item_list[0].child(i, 0).data(USER_ROLE_FILE_NAME) f_path = posixpath.join(self.log_directory, file_name) # File path if not f_path in logs_download_dict['files']: logs_download_dict['files'][f_path] = {'size': f_size} logs_download_dict['total_download_size'] += f_size logs_download_dict['foobar'].append(file_name) elif item_type == ITEM_TYPE_PARENT_DATE: for i in range(item_list[0].rowCount()): parent_time = item_list[0].child(i) for j in range(parent_time.rowCount()): f_size = parent_time.child(j, 1).data( USER_ROLE_SIZE) # File size file_name = parent_time.child( j, 0).data(USER_ROLE_FILE_NAME) f_path = posixpath.join(self.log_directory, file_name) # File path if not f_path in logs_download_dict['files']: logs_download_dict['files'][f_path] = { 'size': f_size } logs_download_dict['total_download_size'] += f_size logs_download_dict['foobar'].append(file_name) elif item_type == ITEM_TYPE_PARENT_TIME: for i in range(item_list[0].rowCount()): f_size = item_list[0].child(i, 1).data( USER_ROLE_SIZE) # File size file_name = item_list[0].child(i, 0).data(USER_ROLE_FILE_NAME) f_path = posixpath.join(self.log_directory, file_name) # File path if not f_path in logs_download_dict['files']: logs_download_dict['files'][f_path] = {'size': f_size} logs_download_dict['total_download_size'] += f_size logs_download_dict['foobar'].append(file_name) elif item_type in [ITEM_TYPE_LOG_FILE, ITEM_TYPE_LOG_FILE_CONT]: f_size = item_list[1].data(USER_ROLE_SIZE) # File size file_name = item_list[0].data(USER_ROLE_FILE_NAME) f_path = posixpath.join(self.log_directory, file_name) # File path if not f_path in logs_download_dict['files']: logs_download_dict['files'][f_path] = {'size': f_size} logs_download_dict['total_download_size'] += f_size logs_download_dict['foobar'].append(file_name) index_rows = [] for index in index_list: if index.column() == 0: index_rows.append([index, index.sibling(index.row(), 1)]) for index_row in index_rows: item_list = [] for index in index_row: item = self.tree_logs_model.itemFromIndex( self.tree_logs_proxy_model.mapToSource(index)) item_list.append(item) populate_log_download(item_list) return logs_download_dict def download_selected_logs(self): index_list = self.tree_logs.selectedIndexes() if len(index_list) == 0: return log_files_to_download = self.load_log_files_for_ops(index_list) if len(log_files_to_download['foobar']) == 0: return download_directory = get_existing_directory( get_main_window(), 'Download Logs', self.last_download_directory) if len(download_directory) == 0: return self.last_download_directory = download_directory downloads = [] for file_name in log_files_to_download['foobar']: downloads.append(Download(file_name, file_name)) self.show_download_wizard('logs', download_directory, downloads) def view_activated_log(self, index): if index.column() == 0 and not self.any_refresh_in_progress: mapped_index = self.tree_logs_proxy_model.mapToSource(index) item = self.tree_logs_model.itemFromIndex(mapped_index) item_type = item.data(USER_ROLE_ITEM_TYPE) if item_type in [ITEM_TYPE_LOG_FILE, ITEM_TYPE_LOG_FILE_CONT]: self.view_log(item) def view_selected_log(self): selection_count = len(self.tree_logs.selectionModel().selectedRows()) if selection_count != 1: return selected_log_items = self.get_directly_selected_log_items() if len(selected_log_items) != 1: return self.view_log(selected_log_items[0]) def view_log(self, item): file_name = posixpath.join(self.log_directory, item.data(USER_ROLE_FILE_NAME)) self.set_program_callbacks_enabled(False) self.view_dialog = ProgramInfoLogsView(self, self.session, file_name) self.view_dialog.exec_() self.view_dialog = None if self.is_alive(): self.set_program_callbacks_enabled(True) # FIXME: make this work like delete_selected_files def delete_selected_logs(self): button = QMessageBox.question(get_main_window(), 'Delete Logs', 'Irreversibly deleting selected logs.', QMessageBox.Ok, QMessageBox.Cancel) if not self.is_alive() or button != QMessageBox.Ok: return def cb_delete(result): self.refresh_logs() report_script_result(result, 'Delete Logs Error', 'Could not delete selected logs') index_list = self.tree_logs.selectedIndexes() if not index_list: return log_files_to_delete = self.load_log_files_for_ops(index_list) if not log_files_to_delete: return file_list = [] for f_path in log_files_to_delete['files']: file_list.append(f_path) if len(file_list) > 0: self.script_manager.execute_script( 'delete', cb_delete, [json.dumps(file_list), json.dumps([])], execute_as_user=True)
class QMapManager(QDialog): def __init__(self, config, parent=None): QDialog.__init__(self, parent) curdir = os.path.abspath(os.path.dirname(__file__)) PyQt4.uic.loadUi(os.path.join(curdir,'manager.ui'), self) self.model = QStandardItemModel() self.projectsmodel = QStandardItemModel() self.projectlist.setModel(self.projectsmodel) self.clientlist.setModel(self.model) self.clientlist.selectionModel().selectionChanged.connect(self.update) self.installbutton.pressed.connect(self.installToClient) self.mapper = QDataWidgetMapper() self.mapper.setModel(self.model) self.mapper.addMapping(self.installpath, 1) self.config = config self.populateProjects() self.populateClients() def installToClient(self): index = self.clientlist.selectionModel().currentIndex() item = self.model.itemFromIndex(index) print "Deploying to " + item.text() build.deployTargetByName(item.text()) def update(self, selected, deselected ): index = selected.indexes()[0] self.mapper.setCurrentModelIndex(index) item = self.model.itemFromIndex(index) settings = item.data() for row in xrange(0,self.projectsmodel.rowCount()): index = self.projectsmodel.index(row, 0) item = self.projectsmodel.itemFromIndex(index) item.setCheckState(Qt.Unchecked) projects = settings['projects'] for project in projects: if project == "All": i = 0 while self.projectsmodel.item(i): item = self.projectsmodel.item(i) item.setCheckState(Qt.Checked) i += 1 break projectitem = self.projectsmodel.findItems(project)[0] projectitem.setCheckState(Qt.Checked) def populateClients(self): row = 0 for client, settings in self.config['clients'].iteritems(): name = QStandardItem(client) name.setData(settings) path = QStandardItem(settings['path']) self.model.insertRow(row, [name, path]) row += 1 def populateProjects(self): row = 0 for project in getProjects(): projectitem = QStandardItem(project.name) projectitem.setCheckable(True) self.projectsmodel.insertRow(row, projectitem) row += 1
class DetailsTreeView(DetailsDBHandler, DetailsDockWidget): def __init__(self, iface, spatial_unit_dock): """ The method initializes the dockwidget. :param iface: QGIS user interface class :type class qgis.utils.iface :param plugin: The STDM plugin :type class :return: None """ from stdm.ui.entity_browser import _EntityDocumentViewerHandler DetailsDockWidget.__init__(self, iface, spatial_unit_dock) DetailsDBHandler.__init__(self) self.spatial_unit_dock = spatial_unit_dock self.view = QTreeView() self.view.setSelectionBehavior( QAbstractItemView.SelectRows ) #self.feature_ids = [] self.layer_table = None self.entity = None self.feature_models = {} self.party_models = {} self.STR_models = {} self.feature_STR_model = {} self.removed_feature = None self.selected_root = None self.model = QStandardItemModel() self.view.setModel(self.model) self.view.setUniformRowHeights(True) self.view.setRootIsDecorated(True) self.view.setAlternatingRowColors(True) self.view.setWordWrap(True) self.view.setHeaderHidden(True) self.view.setEditTriggers( QAbstractItemView.NoEditTriggers ) self.current_profile = current_profile() self.social_tenure = self.current_profile.social_tenure self.spatial_unit = self.social_tenure.spatial_unit self.party = self.social_tenure.party self.view.setMinimumWidth(250) self.doc_viewer_title = QApplication.translate( 'EntityBrowser', 'Document Viewer' ) self.doc_viewer = _EntityDocumentViewerHandler( self.doc_viewer_title, self.iface.mainWindow() ) def set_layer_entity(self): self.layer_table = self.get_layer_source( self.iface.activeLayer() ) if self.layer_table in spatial_tables(): self.entity = self.current_profile.entity_by_name( self.layer_table ) else: self.treeview_error('The layer is not a spatial entity layer. ') def activate_feature_details(self, button_clicked=True): """ Action for showing feature details. :return: """ # Get the active layer. active_layer = self.iface.activeLayer() # TODO fix feature_details_btn is deleted error. if active_layer is not None and \ self.spatial_unit_dock.feature_details_btn.isChecked(): # if feature detail dock is not defined or hidden, create empty dock. if self is None or self.isHidden(): # if the selected layer is not a feature layer, show not # feature layer. (implicitly included in the if statement). if not self.feature_layer(active_layer): self.spatial_unit_dock.feature_details_btn.setChecked(False) return # If the selected layer is feature layer, get data and # display treeview in a dock widget else: select_feature = QApplication.translate( "STDMQGISLoader", "Please select a feature to view their details." ) self.init_dock() self.add_tree_view() self.model.clear() self.treeview_error(select_feature) # enable the select tool self.activate_select_tool() # set entity from active layer in the child class self.set_layer_entity() # set entity for the super class DetailModel self.set_entity(self.entity) # Registery column widget self.set_formatter() #set formatter for social tenure relationship. self.set_formatter(self.social_tenure) self.set_formatter(self.party) # pull data, show treeview active_layer.selectionChanged.connect( self.show_tree ) self.steam_signals(self.entity) # if feature_detail dock is open, toggle close else: self.close_dock( self.spatial_unit_dock.feature_details_btn ) self.feature_details = None # if no active layer, show error message and uncheck the feature tool else: if button_clicked: self.active_layer_check() self.spatial_unit_dock.feature_details_btn.setChecked(False) def add_tree_view(self): """ Adds tree view to the dock widget and sets style. :return: None """ self.tree_scrollArea.setWidget(self.view) def clear_feature_models(self): self.feature_models.clear() def reset_tree_view(self, no_feature=False): #clear feature_ids list, model and highlight self.model.clear() self.clear_sel_highlight() # remove sel_highlight self.disable_buttons(no_feature) if self.removed_feature is None: self.STR_models.clear() self.feature_models.clear() else: self.removed_feature = None features = self.selected_features() # if the selected feature is over 1, # activate multi_select_highlight if not features is None: self.view.clicked.connect( self.multi_select_highlight ) # if there is at least one selected feature if len(features) > 0: self.add_tree_view() #self.feature_ids = features def disable_buttons(self, bool): self.edit_btn.setDisabled(bool) self.delete_btn.setDisabled(bool) def show_tree(self): selected_features = self.selected_features() if len(selected_features) < 1: self.reset_tree_view(True) return if not self.entity is None: self.reset_tree_view() if len(selected_features) < 1: self.disable_buttons(True) return layer_icon = QIcon(':/plugins/stdm/images/icons/layer.gif') roots = self.add_parent_tree( layer_icon, format_name(self.entity.short_name) ) if roots is None: return for id, root in roots.iteritems(): db_model = entity_id_to_model(self.entity, id) self.add_roots(db_model, root, id) def add_parent_tree(self, icon, title): roots = OrderedDict() for feature_id in self.selected_features(): root = QStandardItem(icon, title) root.setData(feature_id) self.set_bold(root) self.model.appendRow(root) roots[feature_id] = root return roots def add_roots(self, model, parent, feature_id): self.feature_models[feature_id] = model if model is None: return self.column_widget_registry(model, self.entity) for i, (col, row) in enumerate(self.formatted_record.iteritems()): child = QStandardItem('{}: {}'.format(col, row)) child.setSelectable(False) parent.appendRow([child]) # Add Social Tenure Relationship steam as a last child if i == len(self.formatted_record)-1: self.add_STR_child(parent, feature_id) self.expand_node(parent) def add_STR_steam(self, parent, STR_id): str_icon = QIcon( ':/plugins/stdm/images/icons/social_tenure.png' ) title = 'Social Tenure Relationship' str_root = QStandardItem(str_icon, title) str_root.setData(STR_id) self.set_bold(str_root) parent.appendRow([str_root]) return str_root def add_no_STR_steam(self, parent): if self.entity.name == self.spatial_unit.name: no_str_icon = QIcon( ':/plugins/stdm/images/icons/remove.png' ) title = 'No STR Defined' no_str_root = QStandardItem(no_str_icon, title) self.set_bold(no_str_root) parent.appendRow([no_str_root]) def add_STR_child(self, parent, feature_id): if len(self.feature_STR_link(feature_id)) < 1: self.add_no_STR_steam(parent) return for record in self.feature_STR_link(feature_id): self.STR_models[record.id] = record str_root = self.add_STR_steam(parent, record.id) # add STR children self.column_widget_registry(record, self.social_tenure) for i, (col, row) in enumerate( self.formatted_record.iteritems() ): STR_child = QStandardItem( '{}: {}'.format(col, row) ) STR_child.setSelectable(False) str_root.appendRow([STR_child]) if i == len(self.formatted_record)-1: self.add_party_child( str_root, record.party_id ) self.feature_STR_model[feature_id] = self.STR_models.keys() def add_party_steam(self, parent, party_id): party_icon = QIcon( ':/plugins/stdm/images/icons/table.png' ) title = format_name(self.party.short_name) party_root = QStandardItem(party_icon, title) party_root.setData(party_id) self.set_bold(party_root) parent.appendRow([party_root]) party_root.setEditable(False) return party_root def add_party_child(self, parent, party_id): db_model = entity_id_to_model(self.party, party_id) self.party_models[party_id] = db_model party_root = self.add_party_steam(parent, party_id) # add STR children self.column_widget_registry(db_model, self.party) for col, row in self.formatted_record.iteritems(): party_child = QStandardItem('{}: {}'.format(col, row)) party_child.setSelectable(False) party_root.appendRow([party_child]) def set_bold(self, standard_item): """ Make a text of Qstandaritem to bold. :param standard_item: Qstandaritem :type: Qstandaritem :return: None """ font = standard_item.font() font.setBold(True) standard_item.setFont(font) def treeview_error(self, message, icon=None): """ Displays error message in feature details treeview. :param title: the title of the treeview. :type: String :param message: The message to be displayed. :type: String :param icon: The icon of the item. :type: Resource string :return: None """ not_feature_ft_msg = QApplication.translate( 'FeatureDetails', message ) if icon== None: root = QStandardItem(not_feature_ft_msg) else: root = QStandardItem(icon, not_feature_ft_msg) self.view.setRootIsDecorated(False) self.model.appendRow(root) self.view.setRootIsDecorated(True) def expand_node(self, parent): """ Make the last tree node expand. :param parent: The parent to expand :type QStandardItem :return:None """ index = self.model.indexFromItem(parent) self.view.expand(index) def multi_select_highlight(self, index): """ Highlights a feature with rubberBald class when selecting features are more than one. :param index: Selected QTreeView item index :type Integer :return: None """ map = self.iface.mapCanvas() try: # Get the selected item text using the index selected_item = self.model.itemFromIndex(index) # Use mutli-select only when more than 1 items are selected. if self.layer.selectedFeatures() < 2: return self.selected_root = selected_item # Split the text to get the key and value. selected_item_text = selected_item.text() selected_value = selected_item.data() # If the first word is feature, expand & highlight. if selected_item_text == format_name(self.spatial_unit.short_name): self.view.expand(index) # expand the item # Clear any existing highlight self.clear_sel_highlight() # Insert highlight # Create expression to target the selected feature expression = QgsExpression( "\"id\"='" + str(selected_value) + "'" ) # Get feature iteration based on the expression ft_iteration = self.layer.getFeatures( QgsFeatureRequest(expression) ) # Retrieve geometry and attributes for feature in ft_iteration: # Fetch geometry geom = feature.geometry() self.sel_highlight = QgsHighlight(map, geom, self.layer) self.sel_highlight.setFillColor(selection_color()) self.sel_highlight.setWidth(4) self.sel_highlight.setColor(QColor(212,95,0, 255)) self.sel_highlight.show() break except AttributeError: # pass attribute error on child items such as party pass except IndexError: pass def steam_signals(self, entity): self.edit_btn.clicked.connect( lambda : self.edit_selected_steam( entity ) ) self.delete_btn.clicked.connect( self.delete_selected_item ) self.view_document_btn.clicked.connect( lambda : self.view_steam_document( entity ) ) def steam_data(self, mode): item = None # if self.view.currentIndex().text() == format_name(self.party): # return None, None # One item is selected and number of feature is also 1 if len(self.layer.selectedFeatures()) == 1 and \ len(self.view.selectedIndexes()) == 1: index = self.view.selectedIndexes()[0] item = self.model.itemFromIndex(index) result = item.data() # One item is selected on the map but not on the treeview elif len(self.layer.selectedFeatures()) == 1 and \ len(self.view.selectedIndexes()) == 0: item = self.model.item(0, 0) result = item.data() # multiple features are selected but one treeview item is selected elif len(self.layer.selectedFeatures()) > 1 and \ len(self.view.selectedIndexes()) == 1: item = self.selected_root result = self.selected_root.data() # multiple features are selected but no treeview item is selected elif len(self.layer.selectedFeatures()) > 1 and \ len(self.view.selectedIndexes()) == 0: result = 'Please, select an item to {}.'.format(mode) else: result = 'Please, select at least one feature to {}.'.format(mode) if result is None: if item is None: item = self.model.item(0, 0) result = item.data() else: result = item.parent().data() return result, item def edit_selected_steam(self, entity): id, item = self.steam_data('edit') feature_edit = True if id is None: return if isinstance(id, str): data_error = QApplication.translate('DetailsTreeView', id) QMessageBox.warning( self.iface.mainWindow(), "Edit Error", data_error ) return if item.text() == 'Social Tenure Relationship': model = self.STR_models[id] feature_edit = False ##TODO add STR wizard edit mode here. elif item.text() == format_name(self.party.short_name): feature_edit = False model = self.party_models[id] editor = EntityEditorDialog( self.party, model, self.iface.mainWindow() ) editor.exec_() else: model = self.feature_models[id] editor = EntityEditorDialog( entity, model, self.iface.mainWindow() ) editor.exec_() #root = self.find_root(entity, id) self.view.expand(item.index()) if feature_edit: self.update_edited_steam(entity, id) else: self.update_edited_steam(self.social_tenure, id) def delete_selected_item(self): str_edit = False id, item = self.steam_data('delete') if isinstance(id, str): data_error = QApplication.translate( 'DetailsTreeView', id ) QMessageBox.warning( self.iface.mainWindow(), 'Delete Error', data_error ) return if item.text() == 'Social Tenure Relationship': str_edit = True db_model = self.STR_models[id] elif item.text() == format_name(self.spatial_unit.short_name) and \ id not in self.feature_STR_model.keys(): db_model = self.feature_models[id] # if spatial unit is linked to STR, don't allow delete elif item.text() == format_name(self.spatial_unit.short_name) and \ id in self.feature_STR_model.keys(): delete_warning = QApplication.translate( 'DetailsTreeView', 'You have to first delete the social tenure \n' 'relationship to delete the {} record.'.format( item.text() ) ) QMessageBox.warning( self.iface.mainWindow(), 'Delete Error', delete_warning ) return # If it is party node, STR exists and don't allow delete. elif item.text() == format_name(self.party.short_name): delete_warning = QApplication.translate( 'DetailsTreeView', 'You have to first delete the social tenure \n' 'relationship to delete the {} record.'.format( item.text() ) ) QMessageBox.warning( self.iface.mainWindow(), 'Delete Error', delete_warning ) return else: return delete_warning = QApplication.translate( 'DetailsTreeView', 'Are you sure you want to delete ' 'the selected record(s)?\n' 'This action cannot be undone.' ) delete_question = QMessageBox.warning( self.iface.mainWindow(), "Delete Warning", delete_warning, QMessageBox.Yes | QMessageBox.No ) if delete_question == QMessageBox.Yes: db_model.delete() if str_edit: del self.STR_models[id] else: self.removed_feature = id del self.feature_models[id] self.updated_removed_steam(str_edit, item) else: return def update_edited_steam(self, entity, feature_id): # remove rows before adding the updated ones. self.layer.setSelectedFeatures( self.feature_models.keys() ) root = self.find_root(entity, feature_id) if root is None: return self.view.selectionModel().select( root.index(), self.view.selectionModel().Select ) self.expand_node(root) self.multi_select_highlight(root.index()) def find_root(self, entity, feature_id): all_roots = self.model.findItems( format_name(entity.short_name) ) root = None for item in all_roots: if item.data() == feature_id: root = item break return root def updated_removed_steam(self, STR_edit, item): if not STR_edit: if len(self.feature_models) > 1: self.refresh_layers() feature_ids = self.feature_models.keys() self.layer.setSelectedFeatures( feature_ids ) else: item.removeRows(0, 2) item.setText('No STR Definded') no_str_icon = QIcon( ':/plugins/stdm/images/icons/remove.png' ) item.setIcon(no_str_icon) def view_steam_document(self, entity): # Slot raised to show the document viewer for the selected entity id, item = self.steam_data('edit') if id is None: return if isinstance(id, str): data_error = QApplication.translate('DetailsTreeView', id) QMessageBox.warning( self.iface.mainWindow(), "Edit Error", data_error ) return if item.text() == 'Social Tenure Relationship': db_model = self.STR_models[id] else: db_model = self.feature_models[id] if not db_model is None: docs = db_model.documents # Notify there are no documents for the selected doc if len(docs) == 0: msg = QApplication.translate( 'EntityBrowser', 'There are no supporting documents ' 'for the selected record.' ) QMessageBox.warning( self, self.doc_viewer_title, msg ) else: self.doc_viewer.load(docs)
class DlgTextItemEditor(QDialog, Ui_TextItemEditor): def __init__(self, parent): QDialog.__init__(self, parent) Ui_TextItemEditor.__init__(self) self.setupUi(self) self.itemName = "Value" # Create an empty model for the list's data self._model = QStandardItemModel(self.list_view) def setupUi(self, TextItemEditor): super().setupUi(TextItemEditor) TextItemEditor.btn_add.clicked.connect(self.on_add_click) TextItemEditor.btn_edit.clicked.connect(self.on_edit_click) TextItemEditor.btn_delete.clicked.connect(self.on_delete_click) def get_items(self): for i in range(self._model.rowCount()): yield self._model.item(i).text() def set_items(self, items): self._model.clear() for item in items: list_item = QStandardItem(item) self._model.appendRow(list_item) self.list_view.setModel(self._model) def get_selected(self): indexes = self.list_view.selectedIndexes() index = indexes[0] if indexes else None if not index: return None, None return self._model.itemFromIndex(index).text(), index def on_add_click(self): item, ok = utils.input_query(None, "Adding %s" % self.itemName.lower(), self.itemName + ":") if ok: list_item = QStandardItem(item) self._model.appendRow(list_item) self.list_view.setCurrentIndex( self._model.indexFromItem(list_item)) self.event_add(item) def on_edit_click(self): text, index = self.get_selected() if not index: QMessageBox.question(None, 'Information', "Select %s from list" % self.itemName.lower(), QMessageBox.Ok) return item, ok = utils.input_query(None, "Editing %s" % self.itemName.lower(), self.itemName + ":", text) if ok: self._model.itemFromIndex(index).setText(item) self.event_edit(item) def on_delete_click(self): text, index = self.get_selected() if not index: QMessageBox.question(None, 'Information', "Select %s from list" % self.itemName.lower(), QMessageBox.Ok) return should_delete = QMessageBox.question( None, "Deleting %s" % self.itemName.lower(), "Delete '%s'?" % text, QMessageBox.Yes, QMessageBox.No) if should_delete == QMessageBox.Yes: self.event_delete(text) self._model.removeRow(index.row()) @Event def event_add(self, item): """Signals the parent that an item has been added to the list :param item: str """ @Event def event_edit(self, item): """Signals the parent that an item has been added to the list :param item: str """ @Event def event_delete(self, item): """Signals the parent that an item has been removed from the list
class RNCLoader: """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 locale = QSettings().value('locale/userLocale')[0:2] locale_path = os.path.join( self.plugin_dir, 'i18n', 'RNCLoader_{}.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'&RNC Loader') # TODO: We are going to let the user set this up in a future iteration self.toolbar = self.iface.addToolBar(u'RNCLoader') self.toolbar.setObjectName(u'RNCLoader') # 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('RNCLoader', message) 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 """ # Create the dialog (after translation) and keep reference self.dlg = RNCLoaderDialog() self.dlg.pushButton.clicked.connect(self.selectRootDirectory) self.chartModel = QStandardItemModel(self.dlg.chartListView) self.filterModel = QSortFilterProxyModel() self.filterModel.setDynamicSortFilter(True) self.filterModel.setFilterCaseSensitivity(False) self.filterModel.setSourceModel(self.chartModel) self.dlg.chartListView.setModel(self.filterModel) self.dlg.filterLineEdit.textChanged.connect( self.filterModel.setFilterWildcard) 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.addPluginToRasterMenu( 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/RNCLoader/icon.png' self.add_action( icon_path, text=self.tr(u'Load RNC'), callback=self.run, parent=self.iface.mainWindow()) def unload(self): """Removes the plugin menu item and icon from QGIS GUI.""" for action in self.actions: self.iface.removePluginRasterMenu( self.tr(u'&RNC Loader'), action) self.iface.removeToolBarIcon(action) # remove the toolbar del self.toolbar def selectRootDirectory(self): filename = QFileDialog.getExistingDirectory( self.dlg, "Select base directory ", self.dlg.rncRootLabel.text()) self.dlg.rncRootLabel.setText(filename) QSettings().setValue('RNCLoader/rnc_root', filename) self.scanCharts(filename) def scanCharts(self, rootdir): bsbs = glob.glob(os.path.join(rootdir, '*/*.BSB')) self.charts = [] for b in bsbs: for l in open(b).readlines(): if l.startswith('K'): parts = l.strip().split('/')[1].split(',') items = {} for p in parts: k, v = p.split('=', 1) items[k] = v if 'FN' in items: self.charts.append( (items['NA'], os.path.join(os.path.dirname(b), items['FN']), items['FN'])) self.listCharts() def listCharts(self): self.chartModel.clear() for c in self.charts: item = QStandardItem(c[0] + ' (' + c[2] + ')') item.setData(c[1]) self.chartModel.appendRow(item) def run(self): """Run method that performs all the real work""" rnc_root = QSettings().value('RNCLoader/rnc_root', '~/') self.dlg.rncRootLabel.setText(rnc_root) self.scanCharts(rnc_root) # show the dialog self.dlg.show() # Run the dialog event loop result = self.dlg.exec_() # See if OK was pressed if result: selected = self.dlg.chartListView.selectedIndexes() for i in selected: item = self.chartModel.itemFromIndex( self.filterModel.mapToSource(i)) self.iface.addRasterLayer(item.data())
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 FileTypes(base2, form2): def __init__(self, parent=None): super(base2, self).__init__(parent) self.setupUi(self) self.model = QStandardItemModel() self.model.itemChanged.connect(self.on_item_changed) self.model.setHorizontalHeaderItem(0, QStandardItem("")) self.model_init() self.saveButton.clicked.connect(self.save) self.okCancelBox.accepted.connect(self.accept) self.okCancelBox.rejected.connect(self.reject) self.treeView.setContextMenuPolicy(Qt.CustomContextMenu) self.treeView.customContextMenuRequested.connect(self.openMenu) self.treeView.setToolTip( 'Here you can select file types that you want to find. Also you can\nadd new category or file type. Click right button on it.' ) def model_init(self): categories = {} if not categories: with open('categories.json') as f: categories = load(f) for element in categories: catItem = QStandardItem(element.keys()[0]) catItem.setCheckable(True) catItem.setCheckState(element[element.keys()[0]]) for type_name, check_state in element["Types"].items(): typeItem = QStandardItem(type_name) typeItem.setCheckable(True) typeItem.setCheckState(check_state) catItem.appendRow(typeItem) self.model.appendRow(catItem) self.treeView.setModel(self.model) self.submit_file_types() def quit(self): self.close() def on_item_changed(self, item): if item.checkState() == Qt.Checked: if item.hasChildren(): for row in range(item.rowCount()): item.child(row).setCheckState(Qt.Checked) else: item.parent().setCheckState(Qt.PartiallyChecked) elif item.checkState() == Qt.Unchecked: if item.hasChildren(): for row in range(item.rowCount()): item.child(row).setCheckState(Qt.Unchecked) else: children = range(item.parent().rowCount()) children.pop(item.index().row()) for row in children: if item.parent().child(row).checkState(): return item.parent().setCheckState(Qt.Unchecked) def submit_file_types(self): categories = [] root = self.model.invisibleRootItem() for num_cat in range(root.rowCount()): category = root.child(num_cat, 0) categories.append({ str(category.text()): category.checkState(), "Types": {} }) for num_type in range(category.rowCount()): type = category.child(num_type, 0) categories[-1]["Types"].update( {str(type.text()): type.checkState()}) return categories def openMenu(self, position): level = 0 index = self.treeView.selectedIndexes()[0] while index.parent().isValid(): index = index.parent() level += 1 menu = QMenu() if level == 0: menu.addAction(self.tr("Add category"), self.add_category) menu.addAction(self.tr("Add type"), self.add_type) menu.addAction(self.tr("Remove"), self.remove) elif level == 1: menu.addAction(self.tr("Add type"), self.add_type) menu.addAction(self.tr("Remove"), self.remove) menu.exec_(self.treeView.viewport().mapToGlobal(position)) def add_category(self): text, ok = QInputDialog.getText(self, 'Create new category', 'Category name:') if ok: catItem = QStandardItem(text) catItem.setCheckable(True) self.model.appendRow(catItem) #self.label.setText(unicode(text)) def add_type(self): text, ok = QInputDialog.getText(self, 'Add new type', 'Postfix:') if ok: catType = QStandardItem(text) catType.setCheckable(True) index = self.treeView.selectedIndexes()[0] if self.model.itemFromIndex(index).hasChildren(): self.model.itemFromIndex(index).appendRow(catType) else: self.model.itemFromIndex(index).parent().appendRow(catType) def save(self): with open('categories.json', 'w') as f: dump(self.submit_file_types(), f) def remove(self): index = self.treeView.selectedIndexes()[0] parent = self.model.itemFromIndex(index).parent() if parent: self.model.removeRow(index.row(), parent.index()) else: parent = self.model.invisibleRootItem() self.model.removeRow(index.row(), parent.index()) @staticmethod def call(): dialog = FileTypes() result = dialog.exec_() categories = dialog.submit_file_types() return categories, result == QDialog.Accepted
class LookupValueSelector(QDialog, Ui_LookupValueSelector): """ A dialog that enables to select a value and code from a lookup. .. versionadded:: 1.5 """ def __init__(self, parent, lookup_entity_name, profile=None): """ """ QDialog.__init__(self, parent, Qt.WindowTitleHint|Qt.WindowCloseButtonHint) self.setupUi(self) self.value_and_code = None if profile is None: self._profile = current_profile() else: self._profile = profile self.lookup_entity = self._profile.entity_by_name( '{}_{}'.format(self._profile.prefix, lookup_entity_name) ) self.notice = NotificationBar(self.notice_bar) self._view_model = QStandardItemModel() self.value_list_box.setModel(self._view_model) header_item = QStandardItem(lookup_entity_name) self._view_model.setHorizontalHeaderItem(0, header_item) self.populate_value_list_view() self.selected_code = None self.selected_value_code = None self.value_list_box.clicked.connect(self.validate_selected_code) def populate_value_list_view(self): self.value_and_code = self.lookup_entity.values for value, code in self.value_and_code.iteritems(): value_code = QStandardItem('{} ({})'.format(value, code.code)) value_code.setData(code.code) self._view_model.appendRow(value_code) def validate_selected_code(self): self.notice.clear() self.selected_code_value() if self.selected_code == '': notice = QApplication.tr(self, 'The selected value has no code.') self.notice.insertWarningNotification(notice) def selected_code_value(self): index = self.value_list_box.currentIndex() item = self._view_model.itemFromIndex(index) self.selected_code = item.data() self.selected_value_code = item.text() def accept(self): self.selected_code_value() self.done(1) def reject(self): self.selected_code = None self.selected_value_code = None self.done(0)
class TemplateDocumentSelector(QDialog,Ui_frmDocumentSelector): """ Dialog for selecting a document template from the saved list. """ def __init__(self, parent=None,selectMode=True, filter_data_source=''): QDialog.__init__(self,parent) self.setupUi(self) self.notifBar = NotificationBar(self.vlNotification) self._mode = selectMode #Filter templates by the specified table name self._filter_data_source = filter_data_source #Document templates in current profile self._profile_templates = [] self._current_profile = current_profile() #Load current profile templates self._load_current_profile_templates() if selectMode: self.buttonBox.setVisible(True) self.manageButtonBox.setVisible(False) currHeight = self.size().height() self.resize(200,currHeight) else: self.buttonBox.setVisible(False) self.manageButtonBox.setVisible(True) self.setWindowTitle( QApplication.translate( "TemplateDocumentSelector", "Template Manager" ) ) #Configure manage buttons btnEdit = self.manageButtonBox.button(QDialogButtonBox.Ok) btnEdit.setText(QApplication.translate("TemplateDocumentSelector","Edit...")) btnEdit.setIcon(QIcon(":/plugins/stdm/images/icons/edit.png")) btnDelete = self.manageButtonBox.button(QDialogButtonBox.Save) btnDelete.setText(QApplication.translate("TemplateDocumentSelector","Delete")) btnDelete.setIcon(QIcon(":/plugins/stdm/images/icons/delete.png")) #Connect signals self.buttonBox.accepted.connect(self.onAccept) btnEdit.clicked.connect(self.onEditTemplate) btnDelete.clicked.connect(self.onDeleteTemplate) #Get saved document templates then add to the model templates = documentTemplates() self._docItemModel = QStandardItemModel(parent) self._docItemModel.setColumnCount(2) #Append current profile templates to the model. for dt in self._profile_templates: if self._template_contains_filter_table(dt): doc_name_item = self._createDocNameItem(dt.name) file_path_item = QStandardItem(dt.path) self._docItemModel.appendRow([doc_name_item,file_path_item]) self.lstDocs.setModel(self._docItemModel) def _load_current_profile_templates(self): # Loads only those templates that refer to tables in the current # profile. if self._current_profile is None: return #Get saved document templates then add to the model templates = documentTemplates() profile_tables = self._current_profile.table_names() #Get templates for the current profile for name, path in templates.iteritems(): doc_temp = _DocumentTemplate.build_from_path(name, path) if doc_temp.data_source is None: continue #Assert data source is in the current profile if doc_temp.data_source.referenced_table_name in profile_tables: self._add_doc_temp(doc_temp) #self._profile_templates.append(doc_temp) if doc_temp.data_source._dataSourceName in user_non_profile_views(): self._add_doc_temp(doc_temp) #self._profile_templates.append(doc_temp) def _add_doc_temp(self, doc_temp): found = False for template in self._profile_templates: if template.name == doc_temp.name: found = True break if not found: self._profile_templates.append(doc_temp) def _template_contains_filter_table(self, document_template): #Returns true if the template refers to the filter data source #If no filter data source defined then always return True if document_template.data_source._dataSourceName in user_non_profile_views(): return True if not self._filter_data_source: return True referenced_table = document_template.referenced_table_name if referenced_table == self._filter_data_source: return True return False @property def mode(self): return self._mode @property def filter_data_source(self): return self._filter_data_source def _createDocNameItem(self,docName): """ Create a template document standard item. """ #Set icon icon = QIcon() icon.addPixmap( QPixmap( ":/plugins/stdm/images/icons/document.png" ), QIcon.Normal, QIcon.Off ) dnItem = QStandardItem(icon,docName) return dnItem def onEditTemplate(self): """ Slot raised to edit document template. """ self.notifBar.clear() if self.documentMapping() == None: self.notifBar.insertErrorNotification(QApplication.translate("TemplateDocumentSelector", \ "Please select a document template to edit")) return templateName,filePath = self.documentMapping() docName,ok = QInputDialog.getText(self, \ QApplication.translate("TemplateDocumentSelector","Edit Template"), \ QApplication.translate("TemplateDocumentSelector","Please enter the new template name below"), \ text = templateName) if ok and docName == "": self.notifBar.insertErrorNotification(QApplication.translate("TemplateDocumentSelector", \ "Template name cannot be empty")) return elif docName == templateName: return elif ok and docName != "": result,newTemplatePath = self._editTemplate(filePath, docName) if result: #Update view mIndices = self._selectedMappings() docNameItem = self._docItemModel.itemFromIndex(mIndices[0]) filePathItem = self._docItemModel.itemFromIndex(mIndices[1]) docNameItem.setText(docName) filePathItem.setText(newTemplatePath) self.notifBar.insertSuccessNotification(QApplication.translate("TemplateDocumentSelector", \ "'{0}' template has been successfully updated".format(docName))) else: self.notifBar.insertErrorNotification(QApplication.translate("TemplateDocumentSelector", \ "Error: '{0}' template could not be updated".format(templateName))) def onDeleteTemplate(self): """ Slot raised to delete document template. """ self.notifBar.clear() if self.documentMapping() == None: self.notifBar.insertErrorNotification(QApplication.translate("TemplateDocumentSelector", \ "Please select a document template to delete")) return templateName,filePath = self.documentMapping() result = QMessageBox.warning(self, QApplication.translate("TemplateDocumentSelector", \ "Confirm delete"), QApplication.translate("TemplateDocumentSelector", \ "Are you sure you want to delete '{0}' template?" \ "This action cannot be undone.\nClick Yes to proceed " \ "or No to cancel.".format(templateName)), QMessageBox.Yes|QMessageBox.No) if result == QMessageBox.No: return status = self._deleteDocument(filePath) if status: #Remove item from list using model index row number selectedDocNameIndices = self.lstDocs.selectionModel().selectedRows(0) row = selectedDocNameIndices[0].row() self._docItemModel.removeRow(row) self.notifBar.insertSuccessNotification(QApplication.translate("TemplateDocumentSelector", \ "'{0}' template has been successfully removed".format(templateName))) else: self.notifBar.insertErrorNotification(QApplication.translate("TemplateDocumentSelector", \ "Error: '{0}' template could not be removed".format(templateName))) def onAccept(self): """ Slot raised to close the dialog only when a selection has been made by the user. """ self.notifBar.clear() if self.documentMapping() == None: self.notifBar.insertErrorNotification(QApplication.translate("TemplateDocumentSelector", \ "Please select a document")) return self.accept() def _selectedMappings(self): """ Returns the model indices for the selected row. """ selectedDocNameIndices = self.lstDocs.selectionModel().selectedRows(0) selectedFilePathIndices = self.lstDocs.selectionModel().selectedRows(1) if len(selectedDocNameIndices) == 0: return None docNameIndex = selectedDocNameIndices[0] filePathIndex = selectedFilePathIndices[0] return (docNameIndex,filePathIndex) def documentMapping(self): """ Returns a tuple containing the selected document name and the corresponding file name. """ mIndices = self._selectedMappings() if mIndices == None: return None docNameItem = self._docItemModel.itemFromIndex(mIndices[0]) filePathItem = self._docItemModel.itemFromIndex(mIndices[1]) return (docNameItem.text(),filePathItem.text()) def _editTemplate(self,templatePath,newName): """ Updates the template document to use the new name. """ templateFile = QFile(templatePath) if not templateFile.open(QIODevice.ReadOnly): QMessageBox.critical(self, QApplication.translate("TemplateDocumentSelector","Open Operation Error"), \ "{0}\n{1}".format(QApplication.translate("TemplateDocumentSelector","Cannot read template file."), \ templateFile.errorString() )) return (False,"") templateDoc = QDomDocument() if templateDoc.setContent(templateFile): composerElement = templateDoc.documentElement() titleAttr = composerElement.attributeNode("_title") if not titleAttr.isNull(): titleAttr.setValue(newName) #Try remove file status = templateFile.remove() if not status: return (False,"") #Create new file newTemplatePath = self._composerTemplatesPath() + "/" + newName + ".sdt" newTemplateFile = QFile(newTemplatePath) if not newTemplateFile.open(QIODevice.WriteOnly): QMessageBox.critical(self, QApplication.translate("TemplateDocumentSelector","Save Operation Error"), \ "{0}\n{1}".format(QApplication.translate("TemplateDocumentSelector","Could not save template file."), \ newTemplateFile.errorString() )) return (False,"") if newTemplateFile.write(templateDoc.toByteArray()) == -1: QMessageBox.critical(self, QApplication.translate("TemplateDocumentSelector","Save Error"), \ QApplication.translate("TemplateDocumentSelector","Could not save template file.")) return (False,"") newTemplateFile.close() return (True,newTemplatePath) def _deleteDocument(self,templatePath): """ Delete the document template from the file system. """ docFile = QFile(templatePath) return docFile.remove() def _composerTemplatesPath(self): """ Reads the path of composer templates in the registry. """ regConfig = RegistryConfig() keyName = "ComposerTemplates" valueCollection = regConfig.read([keyName]) if len(valueCollection) == 0: return None else: return valueCollection[keyName]
class LookupValueSelector(QDialog, Ui_LookupValueSelector): """ A dialog that enables to select a value and code from a lookup. .. versionadded:: 1.5 """ def __init__(self, parent, lookup_entity_name, profile=None): """ Initializes LookupValueSelector. :param parent: The parent of the dialog. :type parent: QWidget :param lookup_entity_name: The lookup entity name :type lookup_entity_name: String :param profile: The current profile object :type profile: Object """ QDialog.__init__(self, parent, Qt.WindowTitleHint | Qt.WindowCloseButtonHint) self.setupUi(self) self.value_and_code = None if profile is None: self._profile = current_profile() else: self._profile = profile self.lookup_entity = self._profile.entity_by_name( '{}_{}'.format(self._profile.prefix, lookup_entity_name) ) self.notice = NotificationBar(self.notice_bar) self._view_model = QStandardItemModel() self.value_list_box.setModel(self._view_model) header_item = QStandardItem(lookup_entity_name) self._view_model.setHorizontalHeaderItem(0, header_item) self.populate_value_list_view() self.selected_code = None self.selected_value_code = None self.value_list_box.clicked.connect(self.validate_selected_code) def populate_value_list_view(self): """ Populates the lookup values and codes. """ self.value_and_code = self.lookup_entity.values for value, code in self.value_and_code.iteritems(): u_value = unicode(value) code_value = self.lookup_entity.values[u_value] value_code = QStandardItem('{} ({})'.format( code_value.value, code.code ) ) value_code.setData(code.code) self._view_model.appendRow(value_code) def validate_selected_code(self): """ Validate the selected code for the presence of Code or not. """ self.notice.clear() self.selected_code_value() if self.selected_code == '': notice = QApplication.tr(self, 'The selected value has no code.') self.notice.insertWarningNotification(notice) def selected_code_value(self): """ Get the selected lookup value. """ index = self.value_list_box.currentIndex() item = self._view_model.itemFromIndex(index) self.selected_code = item.data() self.selected_value_code = item.text() def accept(self): """ Overridden QDialog accept method. """ self.selected_code_value() self.done(1) def reject(self): """ Overridden QDialog accept method. """ self.selected_code = None self.selected_value_code = None self.done(0)
class LookupValueSelector(QDialog, Ui_LookupValueSelector): """ A dialog that enables to select a value and code from a lookup. .. versionadded:: 1.5 """ def __init__(self, parent, lookup_entity_name, profile=None): """ Initializes LookupValueSelector. :param parent: The parent of the dialog. :type parent: QWidget :param lookup_entity_name: The lookup entity name :type lookup_entity_name: String :param profile: The current profile object :type profile: Object """ QDialog.__init__(self, parent, Qt.WindowTitleHint | Qt.WindowCloseButtonHint) self.setupUi(self) self.value_and_code = None if profile is None: self._profile = current_profile() else: self._profile = profile self.lookup_entity = self._profile.entity_by_name('{}_{}'.format( self._profile.prefix, lookup_entity_name)) self.notice = NotificationBar(self.notice_bar) self._view_model = QStandardItemModel() self.value_list_box.setModel(self._view_model) header_item = QStandardItem(lookup_entity_name) self._view_model.setHorizontalHeaderItem(0, header_item) self.populate_value_list_view() self.selected_code = None self.selected_value_code = None self.value_list_box.clicked.connect(self.validate_selected_code) def populate_value_list_view(self): """ Populates the lookup values and codes. """ self.value_and_code = self.lookup_entity.values for value, code in self.value_and_code.iteritems(): u_value = unicode(value) code_value = self.lookup_entity.values[u_value] value_code = QStandardItem('{} ({})'.format( code_value.value, code.code)) value_code.setData(code.code) self._view_model.appendRow(value_code) def validate_selected_code(self): """ Validate the selected code for the presence of Code or not. """ self.notice.clear() self.selected_code_value() if self.selected_code == '': notice = QApplication.tr(self, 'The selected value has no code.') self.notice.insertWarningNotification(notice) def selected_code_value(self): """ Get the selected lookup value. """ index = self.value_list_box.currentIndex() item = self._view_model.itemFromIndex(index) self.selected_code = item.data() self.selected_value_code = item.text() def accept(self): """ Overridden QDialog accept method. """ self.selected_code_value() self.done(1) def reject(self): """ Overridden QDialog accept method. """ self.selected_code = None self.selected_value_code = None self.done(0)