Esempio n. 1
0
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())
Esempio n. 4
0
class ProgramInfoFiles(QWidget, Ui_ProgramInfoFiles):
    def __init__(self, context, update_main_ui_state, set_widget_enabled, is_alive, show_upload_files_wizard, show_download_wizard):
        QWidget.__init__(self)

        self.setupUi(self)

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

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

        self.tree_files.selectionModel().selectionChanged.connect(self.update_ui_state)
        self.tree_files.activated.connect(self.rename_activated_file)
        self.button_upload_files.clicked.connect(show_upload_files_wizard)
        self.button_download_files.clicked.connect(self.download_selected_files)
        self.button_rename_file.clicked.connect(self.rename_selected_file)
        self.button_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)
Esempio n. 5
0
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)
Esempio n. 6
0
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
Esempio n. 7
0
class ProgramInfoFiles(QWidget, Ui_ProgramInfoFiles):
    def __init__(self, context, update_main_ui_state, set_widget_enabled,
                 is_alive, show_upload_files_wizard, show_download_wizard):
        QWidget.__init__(self)

        self.setupUi(self)

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

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

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

        self.label_error.setVisible(False)

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

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

    def close_all_dialogs(self):
        pass

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

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

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

            self.label_error.setVisible(False)

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

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

                return walk, available_files, available_directories

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

                self.available_files = available_files
                self.available_directories = available_directories

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

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

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

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

        self.refresh_in_progress = True
        self.update_main_ui_state()

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

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

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

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

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

        return selected_name_items

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

        if len(selected_name_items) == 0:
            return

        downloads = []

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

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

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

        for selected_name_item in selected_name_items:
            expand(selected_name_item)

        if len(downloads) == 0:
            return

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

        if len(download_directory) == 0:
            return

        self.last_download_directory = download_directory

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

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

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

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

        if selection_count != 1:
            return

        selected_name_items = self.get_directly_selected_name_items()

        if len(selected_name_items) != 1:
            return

        self.rename_item(selected_name_items[0])

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

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

        old_name = name_item.text()

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

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

        new_name = dialog.textValue()

        if new_name == old_name:
            return

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

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

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

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

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

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

            name_item.setText(new_name)

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

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

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

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

        selected_name_items = set(self.get_directly_selected_name_items())

        if len(selected_name_items) == 0:
            return

        script_instance_ref = [None]

        def progress_canceled():
            script_instance = script_instance_ref[0]

            if script_instance == None:
                return

            self.script_manager.abort_script(script_instance)

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

        files_to_delete = []
        dirs_to_delete = []
        all_done = False

        while not all_done:
            all_done = True

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

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

                if item_done:
                    all_done = False
                    break

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

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

        message = 'Deleting '

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

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

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

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

        progress.setLabelText(message)

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

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

            script_instance_ref[0] = None

            progress.cancel()
            self.refresh_files()

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

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

        script_instance_ref[0] = self.script_manager.execute_script(
            'delete',
            cb_delete,
            [json.dumps(files_to_delete),
             json.dumps(dirs_to_delete)],
            execute_as_user=True)
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]
Esempio n. 9
0
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)
Esempio n. 10
0
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
Esempio n. 11
0
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)
Esempio n. 12
0
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
Esempio n. 13
0
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())
Esempio n. 14
0
class TreeView(QTreeView):
    def __init__(self, parent):
        QTreeView.__init__(self, parent)
        while not isinstance(parent, QDialog) and not isinstance(parent, QMainWindow):
            parent = parent.parent()
        self.setObjectName("TreeView" + str(len(parent.findChildren(TreeView))))

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

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

        self.hasObject = False
        self.treeNodeList = []

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

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

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

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

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

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

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

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




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

    def get_Visible(self):
        return self.isVisible()
    def set_Visible(self, bool):
        self.setVisible(bool)
    Visible = property(get_Visible, set_Visible, None, None)
Esempio n. 15
0
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
Esempio n. 16
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):
        """

        """
        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)
Esempio n. 17
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]
Esempio n. 18
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)
Esempio n. 19
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)