Exemplo n.º 1
0
    def data(self, index, role=Qt.DisplayRole):
        if index.parent().isValid() and \
                        index.parent().column() == Plot.steps and \
                        index.column() == PlotStep.meta:
            if role == Qt.TextAlignmentRole:
                return Qt.AlignRight | Qt.AlignVCenter
            elif role == Qt.ForegroundRole:
                return QBrush(Qt.gray)
            else:
                return QStandardItemModel.data(self, index, role)

        else:
            return QStandardItemModel.data(self, index, role)
Exemplo n.º 2
0
    def data(self, index, role=Qt.EditRole):
        level = 0
        i = index
        while i.parent() != QModelIndex():
            i = i.parent()
            level += 1

        if role == Qt.BackgroundRole:
            if level == 0:
                return QBrush(lightBlue())

        if role == Qt.TextAlignmentRole:
            if level == 0:
                return Qt.AlignCenter

        if role == Qt.FontRole:
            if level in [0, 1]:
                f = qApp.font()
                f.setBold(True)
                return f

        if role == Qt.ForegroundRole:
            if level == 0:
                return QBrush(Qt.darkBlue)

        if role == Qt.SizeHintRole:
            fm = QFontMetrics(qApp.font())
            h = fm.height()
            if level == 0:
                return QSize(0, h + 12)
            elif level == 1:
                return QSize(0, h + 6)

        return QStandardItemModel.data(self, index, role)
Exemplo n.º 3
0
class ListCategory(QSortFilterProxyModel):

    """Expose a list of items as a category for the CompletionModel."""

    def __init__(self, name, items, delete_func=None, parent=None):
        super().__init__(parent)
        self.name = name
        self.srcmodel = QStandardItemModel(parent=self)
        self._pattern = ''
        # ListCategory filters all columns
        self.columns_to_filter = [0, 1, 2]
        self.setFilterKeyColumn(-1)
        for item in items:
            self.srcmodel.appendRow([QStandardItem(x) for x in item])
        self.setSourceModel(self.srcmodel)
        self.delete_func = delete_func

    def set_pattern(self, val):
        """Setter for pattern.

        Args:
            val: The value to set.
        """
        self._pattern = val
        val = re.sub(r' +', r' ', val)  # See #1919
        val = re.escape(val)
        val = val.replace(r'\ ', '.*')
        rx = QRegExp(val, Qt.CaseInsensitive)
        self.setFilterRegExp(rx)
        self.invalidate()
        sortcol = 0
        self.sort(sortcol)

    def lessThan(self, _lindex, rindex):
        """Custom sorting implementation.

        Prefers all items which start with self._pattern. Other than that, keep
        items in their original order.

        Args:
            _lindex: The QModelIndex of the left item (*left* < right)
            rindex: The QModelIndex of the right item (left < *right*)

        Return:
            True if left < right, else False
        """
        qtutils.ensure_valid(rindex)
        right = self.srcmodel.data(rindex)
        return not right.startswith(self._pattern)
Exemplo n.º 4
0
class comics_project_manager_docker(DockWidget):
    setupDictionary = {}
    stringName = i18n("Comics Manager")
    projecturl = None

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

        # Setup layout:
        base = QHBoxLayout()
        widget = QWidget()
        widget.setLayout(base)
        baseLayout = QSplitter()
        base.addWidget(baseLayout)
        self.setWidget(widget)
        buttonLayout = QVBoxLayout()
        buttonBox = QWidget()
        buttonBox.setLayout(buttonLayout)
        baseLayout.addWidget(buttonBox)

        # Comic page list and pages model
        self.comicPageList = QTableView()
        self.comicPageList.verticalHeader().setSectionsMovable(True)
        self.comicPageList.verticalHeader().setDragEnabled(True)
        self.comicPageList.verticalHeader().setDragDropMode(QAbstractItemView.InternalMove)
        self.comicPageList.setAcceptDrops(True)
        self.pagesModel = QStandardItemModel()
        self.comicPageList.doubleClicked.connect(self.slot_open_page)
        self.comicPageList.setIconSize(QSize(128, 128))
        # self.comicPageList.itemDelegate().closeEditor.connect(self.slot_write_description)
        self.pagesModel.layoutChanged.connect(self.slot_write_config)
        self.pagesModel.rowsInserted.connect(self.slot_write_config)
        self.pagesModel.rowsRemoved.connect(self.slot_write_config)
        self.comicPageList.verticalHeader().sectionMoved.connect(self.slot_write_config)
        self.comicPageList.setModel(self.pagesModel)
        pageBox = QWidget()
        pageBox.setLayout(QVBoxLayout())
        zoomSlider = QSlider(Qt.Horizontal, None)
        zoomSlider.setRange(1, 8)
        zoomSlider.setValue(4)
        zoomSlider.setTickInterval(1)
        zoomSlider.setMinimumWidth(10)
        zoomSlider.valueChanged.connect(self.slot_scale_thumbnails)
        self.projectName = Elided_Text_Label()
        pageBox.layout().addWidget(self.projectName)
        pageBox.layout().addWidget(zoomSlider)
        pageBox.layout().addWidget(self.comicPageList)
        baseLayout.addWidget(pageBox)

        self.btn_project = QToolButton()
        self.btn_project.setPopupMode(QToolButton.MenuButtonPopup)
        self.btn_project.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
        menu_project = QMenu()
        self.action_new_project = QAction(i18n("New Project"), None)
        self.action_new_project.triggered.connect(self.slot_new_project)
        self.action_load_project = QAction(i18n("Open Project"), None)
        self.action_load_project.triggered.connect(self.slot_open_config)
        menu_project.addAction(self.action_new_project)
        menu_project.addAction(self.action_load_project)
        self.btn_project.setMenu(menu_project)
        self.btn_project.setDefaultAction(self.action_load_project)
        buttonLayout.addWidget(self.btn_project)

        # Settings dropdown with actions for the different settings menus.
        self.btn_settings = QToolButton()
        self.btn_settings.setPopupMode(QToolButton.MenuButtonPopup)
        self.btn_settings.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
        self.action_edit_project_settings = QAction(i18n("Project Settings"), None)
        self.action_edit_project_settings.triggered.connect(self.slot_edit_project_settings)
        self.action_edit_meta_data = QAction(i18n("Meta Data"), None)
        self.action_edit_meta_data.triggered.connect(self.slot_edit_meta_data)
        self.action_edit_export_settings = QAction(i18n("Export Settings"), None)
        self.action_edit_export_settings.triggered.connect(self.slot_edit_export_settings)
        menu_settings = QMenu()
        menu_settings.addAction(self.action_edit_project_settings)
        menu_settings.addAction(self.action_edit_meta_data)
        menu_settings.addAction(self.action_edit_export_settings)
        self.btn_settings.setDefaultAction(self.action_edit_project_settings)
        self.btn_settings.setMenu(menu_settings)
        buttonLayout.addWidget(self.btn_settings)
        self.btn_settings.setDisabled(True)

        # Add page drop down with different page actions.
        self.btn_add_page = QToolButton()
        self.btn_add_page.setPopupMode(QToolButton.MenuButtonPopup)
        self.btn_add_page.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)

        self.action_add_page = QAction(i18n("Add Page"), None)
        self.action_add_page.triggered.connect(self.slot_add_new_page_single)
        self.action_add_template = QAction(i18n("Add Page From Template"), None)
        self.action_add_template.triggered.connect(self.slot_add_new_page_from_template)
        self.action_add_existing = QAction(i18n("Add Existing Pages"), None)
        self.action_add_existing.triggered.connect(self.slot_add_page_from_url)
        self.action_remove_selected_page = QAction(i18n("Remove Page"), None)
        self.action_remove_selected_page.triggered.connect(self.slot_remove_selected_page)
        self.action_resize_all_pages = QAction(i18n("Batch Resize"), None)
        self.action_resize_all_pages.triggered.connect(self.slot_batch_resize)
        self.btn_add_page.setDefaultAction(self.action_add_page)
        self.action_show_page_viewer = QAction(i18n("View Page In Window"), None)
        self.action_show_page_viewer.triggered.connect(self.slot_show_page_viewer)
        self.action_scrape_authors = QAction(i18n("Scrape Author Info"), None)
        self.action_scrape_authors.setToolTip(i18n("Search for author information in documents and add it to the author list. This doesn't check for duplicates."))
        self.action_scrape_authors.triggered.connect(self.slot_scrape_author_list)
        actionList = []
        menu_page = QMenu()
        actionList.append(self.action_add_page)
        actionList.append(self.action_add_template)
        actionList.append(self.action_add_existing)
        actionList.append(self.action_remove_selected_page)
        actionList.append(self.action_resize_all_pages)
        actionList.append(self.action_show_page_viewer)
        actionList.append(self.action_scrape_authors)
        menu_page.addActions(actionList)
        self.btn_add_page.setMenu(menu_page)
        buttonLayout.addWidget(self.btn_add_page)
        self.btn_add_page.setDisabled(True)

        self.comicPageList.setContextMenuPolicy(Qt.ActionsContextMenu)
        self.comicPageList.addActions(actionList)

        # Export button that... exports.
        self.btn_export = QPushButton(i18n("Export Comic"))
        self.btn_export.clicked.connect(self.slot_export)
        buttonLayout.addWidget(self.btn_export)
        self.btn_export.setDisabled(True)

        self.btn_project_url = QPushButton(i18n("Copy Location"))
        self.btn_project_url.setToolTip(i18n("Copies the path of the project to the clipboard. Useful for quickly copying to a file manager or the like."))
        self.btn_project_url.clicked.connect(self.slot_copy_project_url)
        self.btn_project_url.setDisabled(True)
        buttonLayout.addWidget(self.btn_project_url)

        self.page_viewer_dialog = comics_project_page_viewer.comics_project_page_viewer()

        Application.notifier().imageSaved.connect(self.slot_check_for_page_update)

        buttonLayout.addItem(QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.MinimumExpanding))

    """
    Open the config file and load the json file into a dictionary.
    """

    def slot_open_config(self):
        self.path_to_config = QFileDialog.getOpenFileName(caption=i18n("Please select the json comic config file."), filter=str(i18n("json files") + "(*.json)"))[0]
        if os.path.exists(self.path_to_config) is True:
            configFile = open(self.path_to_config, "r", newline="", encoding="utf-16")
            self.setupDictionary = json.load(configFile)
            self.projecturl = os.path.dirname(str(self.path_to_config))
            configFile.close()
            self.load_config()
    """
    Further config loading.
    """

    def load_config(self):
        self.projectName.setMainText(text=str(self.setupDictionary["projectName"]))
        self.fill_pages()
        self.btn_settings.setEnabled(True)
        self.btn_add_page.setEnabled(True)
        self.btn_export.setEnabled(True)
        self.btn_project_url.setEnabled(True)

    """
    Fill the pages model with the pages from the pages list.
    """

    def fill_pages(self):
        self.loadingPages = True
        self.pagesModel.clear()
        self.pagesModel.setHorizontalHeaderLabels([i18n("Page"), i18n("Description")])
        pagesList = []
        if "pages" in self.setupDictionary.keys():
            pagesList = self.setupDictionary["pages"]
        progress = QProgressDialog()
        progress.setMinimum(0)
        progress.setMaximum(len(pagesList))
        progress.setWindowTitle(i18n("Loading pages..."))
        for url in pagesList:
            absurl = os.path.join(self.projecturl, url)
            if (os.path.exists(absurl)):
                #page = Application.openDocument(absurl)
                page = zipfile.ZipFile(absurl, "r")
                thumbnail = QImage.fromData(page.read("preview.png"))
                pageItem = QStandardItem()
                dataList = self.get_description_and_title(page.read("documentinfo.xml"))
                if (dataList[0].isspace() or len(dataList[0]) < 1):
                    dataList[0] = os.path.basename(url)
                pageItem.setText(dataList[0])
                pageItem.setDragEnabled(True)
                pageItem.setDropEnabled(False)
                pageItem.setEditable(False)
                pageItem.setIcon(QIcon(QPixmap.fromImage(thumbnail)))
                pageItem.setToolTip(url)
                page.close()
                description = QStandardItem()
                description.setText(dataList[1])
                description.setEditable(False)
                listItem = []
                listItem.append(pageItem)
                listItem.append(description)
                self.pagesModel.appendRow(listItem)
                self.comicPageList.resizeRowsToContents()
                self.comicPageList.resizeColumnToContents(0)
                self.comicPageList.setColumnWidth(1, 256)
                progress.setValue(progress.value() + 1)
        progress.setValue(len(pagesList))
        self.loadingPages = False
    """
    Function that is triggered by the zoomSlider
    Resizes the thumbnails.
    """

    def slot_scale_thumbnails(self, multiplier=4):
        self.comicPageList.setIconSize(QSize(multiplier * 32, multiplier * 32))
        self.comicPageList.resizeRowsToContents()

    """
    Function that takes the documentinfo.xml and parses it for the title, subject and abstract tags,
    to get the title and description.
    
    @returns a stringlist with the name on 0 and the description on 1.
    """

    def get_description_and_title(self, string):
        xmlDoc = ET.fromstring(string)
        calligra = str("{http://www.calligra.org/DTD/document-info}")
        name = ""
        if ET.iselement(xmlDoc[0].find(calligra + 'title')):
            name = xmlDoc[0].find(calligra + 'title').text
            if name is None:
                name = " "
        desc = ""
        if ET.iselement(xmlDoc[0].find(calligra + 'subject')):
            desc = xmlDoc[0].find(calligra + 'subject').text
        if desc is None or desc.isspace() or len(desc) < 1:
            if ET.iselement(xmlDoc[0].find(calligra + 'abstract')):
                desc = str(xmlDoc[0].find(calligra + 'abstract').text)
                if desc.startswith("<![CDATA["):
                    desc = desc[len("<![CDATA["):]
                if desc.startswith("]]>"):
                    desc = desc[:-len("]]>")]
        return [name, desc]

    """
    Scrapes authors from the author data in the document info and puts them into the author list.
    Doesn't check for duplicates.
    """

    def slot_scrape_author_list(self):
        listOfAuthors = []
        if "authorList" in self.setupDictionary.keys():
            listOfAuthors = self.setupDictionary["authorList"]
        if "pages" in self.setupDictionary.keys():
            for relurl in self.setupDictionary["pages"]:
                absurl = os.path.join(self.projecturl, relurl)
                page = zipfile.ZipFile(absurl, "r")
                xmlDoc = ET.fromstring(page.read("documentinfo.xml"))
                calligra = str("{http://www.calligra.org/DTD/document-info}")
                authorelem = xmlDoc.find(calligra + 'author')
                author = {}
                if ET.iselement(authorelem.find(calligra + 'full-name')):
                    author["nickname"] = str(authorelem.find(calligra + 'full-name').text)
                if ET.iselement(authorelem.find(calligra + 'email')):
                    author["email"] = str(authorelem.find(calligra + 'email').text)
                if ET.iselement(authorelem.find(calligra + 'position')):
                    author["role"] = str(authorelem.find(calligra + 'position').text)
                listOfAuthors.append(author)
                page.close()
        self.setupDictionary["authorList"] = listOfAuthors

    """
    Edit the general project settings like the project name, concept, pages location, export location, template location, metadata
    """

    def slot_edit_project_settings(self):
        dialog = comics_project_settings_dialog.comics_project_details_editor(self.projecturl)
        dialog.setConfig(self.setupDictionary, self.projecturl)

        if dialog.exec_() == QDialog.Accepted:
            self.setupDictionary = dialog.getConfig(self.setupDictionary)
            self.slot_write_config()
            self.setWindowTitle(self.stringName + ": " + str(self.setupDictionary["projectName"]))

    """
    This allows users to select existing pages and add them to the pages list. The pages are currently not copied to the pages folder. Useful for existing projects.
    """

    def slot_add_page_from_url(self):
        # get the pages.
        urlList = QFileDialog.getOpenFileNames(caption=i18n("Which existing pages to add?"), directory=self.projecturl, filter=str(i18n("Krita files") + "(*.kra)"))[0]

        # get the existing pages list.
        pagesList = []
        if "pages" in self.setupDictionary.keys():
            pagesList = self.setupDictionary["pages"]

        # And add each url in the url list to the pages list and the model.
        for url in urlList:
            if self.projecturl not in urlList:
                newUrl = os.path.join(self.projecturl, self.setupDictionary["pagesLocation"], os.path.basename(url))
                shutil.move(url, newUrl)
                url = newUrl
            relative = os.path.relpath(url, self.projecturl)
            if url not in pagesList:
                page = zipfile.ZipFile(url, "r")
                thumbnail = QImage.fromData(page.read("preview.png"))
                dataList = self.get_description_and_title(page.read("documentinfo.xml"))
                if (dataList[0].isspace() or len(dataList[0]) < 1):
                    dataList[0] = os.path.basename(url)
                newPageItem = QStandardItem()
                newPageItem.setIcon(QIcon(QPixmap.fromImage(thumbnail)))
                newPageItem.setDragEnabled(True)
                newPageItem.setDropEnabled(False)
                newPageItem.setEditable(False)
                newPageItem.setText(dataList[0])
                newPageItem.setToolTip(relative)
                page.close()
                description = QStandardItem()
                description.setText(dataList[1])
                description.setEditable(False)
                listItem = []
                listItem.append(newPageItem)
                listItem.append(description)
                self.pagesModel.appendRow(listItem)
                self.comicPageList.resizeRowsToContents()
                self.comicPageList.resizeColumnToContents(0)

    """
    Remove the selected page from the list of pages. This does not remove it from disk(far too dangerous).
    """

    def slot_remove_selected_page(self):
        index = self.comicPageList.currentIndex()
        self.pagesModel.removeRow(index.row())

    """
    This function adds a new page from the default template. If there's no default template, or the file does not exist, it will 
    show the create/import template dialog. It will remember the selected item as the default template.
    """

    def slot_add_new_page_single(self):
        templateUrl = "templatepage"
        templateExists = False

        if "singlePageTemplate" in self.setupDictionary.keys():
            templateUrl = self.setupDictionary["singlePageTemplate"]
        if os.path.exists(os.path.join(self.projecturl, templateUrl)):
            templateExists = True

        if templateExists is False:
            if "templateLocation" not in self.setupDictionary.keys():
                self.setupDictionary["templateLocation"] = os.path.relpath(QFileDialog.getExistingDirectory(caption=i18n("Where are the templates located?"), options=QFileDialog.ShowDirsOnly), self.projecturl)

            templateDir = os.path.join(self.projecturl, self.setupDictionary["templateLocation"])
            template = comics_template_dialog.comics_template_dialog(templateDir)

            if template.exec_() == QDialog.Accepted:
                templateUrl = os.path.relpath(template.url(), self.projecturl)
                self.setupDictionary["singlePageTemplate"] = templateUrl
        if os.path.exists(os.path.join(self.projecturl, templateUrl)):
            self.add_new_page(templateUrl)

    """
    This function always asks for a template showing the new template window. This allows users to have multiple different
    templates created for back covers, spreads, other and have them accesible, while still having the convenience of a singular
    "add page" that adds a default.
    """

    def slot_add_new_page_from_template(self):
        if "templateLocation" not in self.setupDictionary.keys():
            self.setupDictionary["templateLocation"] = os.path.relpath(QFileDialog.getExistingDirectory(caption=i18n("Where are the templates located?"), options=QFileDialog.ShowDirsOnly), self.projecturl)

        templateDir = os.path.join(self.projecturl, self.setupDictionary["templateLocation"])
        template = comics_template_dialog.comics_template_dialog(templateDir)

        if template.exec_() == QDialog.Accepted:
            templateUrl = os.path.relpath(template.url(), self.projecturl)
            self.add_new_page(templateUrl)

    """
    This is the actual function that adds the template using the template url.
    It will attempt to name the new page projectName+number, and tries to get the first possible
    number that is not in the pages list. If such a file already exists it will only append the file.
    """

    def add_new_page(self, templateUrl):

        # check for page list and or location.
        pagesList = []
        if "pages" in self.setupDictionary.keys():
            pagesList = self.setupDictionary["pages"]
        if (str(self.setupDictionary["pagesLocation"]).isspace()):
            self.setupDictionary["pagesLocation"] = os.path.relpath(QFileDialog.getExistingDirectory(caption=i18n("Where should the pages go?"), options=QFileDialog.ShowDirsOnly), self.projecturl)

        # Search for the possible name.
        extraUnderscore = str()
        if str(self.setupDictionary["projectName"])[-1].isdigit():
            extraUnderscore = "_"
        pageName = str(self.setupDictionary["projectName"]) + extraUnderscore + str(format(len(pagesList), "03d"))
        url = os.path.join(str(self.setupDictionary["pagesLocation"]), pageName + ".kra")
        pageNumber = 0
        if (url in pagesList):
            while (url in pagesList):
                pageNumber += 1
                pageName = str(self.setupDictionary["projectName"]) + extraUnderscore + str(format(pageNumber, "03d"))
                url = os.path.join(str(self.setupDictionary["pagesLocation"]), pageName + ".kra")

        # open the page by opening the template and resaving it, or just opening it.
        absoluteUrl = os.path.join(self.projecturl, url)
        if (os.path.exists(absoluteUrl)):
            newPage = Application.openDocument(absoluteUrl)
        else:
            booltemplateExists = os.path.exists(os.path.join(self.projecturl, templateUrl))
            if booltemplateExists is False:
                templateUrl = os.path.relpath(QFileDialog.getOpenFileName(caption=i18n("Which image should be the basis the new page?"), directory=self.projecturl, filter=str(i18n("Krita files") + "(*.kra)"))[0], self.projecturl)
            newPage = Application.openDocument(os.path.join(self.projecturl, templateUrl))
            newPage.setFileName(absoluteUrl)
            newPage.setName(pageName)
            newPage.exportImage(absoluteUrl, InfoObject())

        # Get out the extra data for the standard item.
        newPageItem = QStandardItem()
        newPageItem.setIcon(QIcon(QPixmap.fromImage(newPage.thumbnail(100, 100))))
        newPageItem.setDragEnabled(True)
        newPageItem.setDropEnabled(False)
        newPageItem.setEditable(False)
        newPageItem.setText(pageName)
        newPageItem.setToolTip(url)

        # close page document.
        newPage.waitForDone()
        if newPage.isIdle():
            newPage.close()

        # add item to page.
        description = QStandardItem()
        description.setText(str(""))
        description.setEditable(False)
        listItem = []
        listItem.append(newPageItem)
        listItem.append(description)
        self.pagesModel.appendRow(listItem)
        self.comicPageList.resizeRowsToContents()
        self.comicPageList.resizeColumnToContents(0)

    """
    Write to the json configuratin file.
    This also checks the current state of the pages list.
    """

    def slot_write_config(self):

        # Don't load when the pages are still being loaded, otherwise we'll be overwriting our own pages list.
        if (self.loadingPages is False):
            print("CPMT: writing comic configuration...")

            # Generate a pages list from the pagesmodel.
            # Because we made the drag-and-drop use the tableview header, we need to first request the logicalIndex
            # for the visualIndex, and then request the items for the logical index in the pagesmodel.
            # After that, we rename the verticalheader to have the appropriate numbers it will have when reloading.
            pagesList = []
            listOfHeaderLabels = []
            for i in range(self.pagesModel.rowCount()):
                listOfHeaderLabels.append(str(i))
            for i in range(self.pagesModel.rowCount()):
                iLogical = self.comicPageList.verticalHeader().logicalIndex(i)
                index = self.pagesModel.index(iLogical, 0)
                if index.isValid() is False:
                    index = self.pagesModel.index(i, 0)
                url = str(self.pagesModel.data(index, role=Qt.ToolTipRole))
                if url not in pagesList:
                    pagesList.append(url)
                listOfHeaderLabels[iLogical] = str(i + 1)
            self.pagesModel.setVerticalHeaderLabels(listOfHeaderLabels)
            self.comicPageList.verticalHeader().update()
            self.setupDictionary["pages"] = pagesList

            # Save to our json file.
            configFile = open(self.path_to_config, "w", newline="", encoding="utf-16")
            json.dump(self.setupDictionary, configFile, indent=4, sort_keys=True, ensure_ascii=False)
            configFile.close()
            print("CPMT: done")

    """
    Open a page in the pagesmodel in Krita.
    """

    def slot_open_page(self, index):
        if index.column() is 0:
            # Get the absolute url from the relative one in the pages model.
            absoluteUrl = os.path.join(self.projecturl, str(self.pagesModel.data(index, role=Qt.ToolTipRole)))

            # Make sure the page exists.
            if os.path.exists(absoluteUrl):
                page = Application.openDocument(absoluteUrl)

                # Set the title to the filename if it was empty. It looks a bit neater.
                if page.name().isspace or len(page.name()) < 1:
                    page.setName(str(self.pagesModel.data(index, role=Qt.DisplayRole)))

                # Add views for the document so the user can use it.
                Application.activeWindow().addView(page)
                Application.setActiveDocument(page)
            else:
                print("CPMT: The page cannot be opened because the file doesn't exist:", absoluteUrl)

    """
    Call up the metadata editor dialog. Only when the dialog is "Accepted" will the metadata be saved.
    """

    def slot_edit_meta_data(self):
        dialog = comics_metadata_dialog.comic_meta_data_editor()

        dialog.setConfig(self.setupDictionary)
        if (dialog.exec_() == QDialog.Accepted):
            self.setupDictionary = dialog.getConfig(self.setupDictionary)
            self.slot_write_config()

    """
    An attempt at making the description editable from the comic pages list. It is currently not working because ZipFile
    has no overwrite mechanism, and I don't have the energy to write one yet.
    """

    def slot_write_description(self, index):

        for row in range(self.pagesModel.rowCount()):
            index = self.pagesModel.index(row, 1)
            indexUrl = self.pagesModel.index(row, 0)
            absoluteUrl = os.path.join(self.projecturl, str(self.pagesModel.data(indexUrl, role=Qt.ToolTipRole)))
            page = zipfile.ZipFile(absoluteUrl, "a")
            xmlDoc = ET.ElementTree()
            ET.register_namespace("", "http://www.calligra.org/DTD/document-info")
            location = os.path.join(self.projecturl, "documentinfo.xml")
            xmlDoc.parse(location)
            xmlroot = ET.fromstring(page.read("documentinfo.xml"))
            calligra = "{http://www.calligra.org/DTD/document-info}"
            aboutelem = xmlroot.find(calligra + 'about')
            if ET.iselement(aboutelem.find(calligra + 'subject')):
                desc = aboutelem.find(calligra + 'subject')
                desc.text = self.pagesModel.data(index, role=Qt.EditRole)
                xmlstring = ET.tostring(xmlroot, encoding='unicode', method='xml', short_empty_elements=False)
                page.writestr(zinfo_or_arcname="documentinfo.xml", data=xmlstring)
                for document in Application.documents():
                    if str(document.fileName()) == str(absoluteUrl):
                        document.setDocumentInfo(xmlstring)
            page.close()

    """
    Calls up the export settings dialog. Only when accepted will the configuration be written.
    """

    def slot_edit_export_settings(self):
        dialog = comics_export_dialog.comic_export_setting_dialog()
        dialog.setConfig(self.setupDictionary)

        if (dialog.exec_() == QDialog.Accepted):
            self.setupDictionary = dialog.getConfig(self.setupDictionary)
            self.slot_write_config()

    """
    Export the comic. Won't work without export settings set.
    """

    def slot_export(self):
        exporter = comics_exporter.comicsExporter()
        exporter.set_config(self.setupDictionary, self.projecturl)
        exportSuccess = exporter.export()
        if exportSuccess:
            print("CPMT: Export success! The files have been written to the export folder!")

    """
    Calls up the comics project setup wizard so users can create a new json file with the basic information.
    """

    def slot_new_project(self):
        setup = comics_project_setup_wizard.ComicsProjectSetupWizard()
        setup.showDialog()
    """
    This is triggered by any document save.
    It checks if the given url in in the pages list, and if so,
    updates the appropriate page thumbnail.
    This helps with the management of the pages, because the user
    will be able to see the thumbnails as a todo for the whole comic,
    giving a good overview over whether they still need to ink, color or
    the like for a given page, and it thus also rewards the user whenever
    they save.
    """

    def slot_check_for_page_update(self, url):
        if "pages" in self.setupDictionary.keys():
            relUrl = os.path.relpath(url, self.projecturl)
            if relUrl in self.setupDictionary["pages"]:
                index = self.pagesModel.index(self.setupDictionary["pages"].index(relUrl), 0)
                index2 = self.pagesModel.index(index.row(), 1)
                if index.isValid():
                    pageItem = self.pagesModel.itemFromIndex(index)
                    page = zipfile.ZipFile(url, "r")
                    dataList = self.get_description_and_title(page.read("documentinfo.xml"))
                    if (dataList[0].isspace() or len(dataList[0]) < 1):
                        dataList[0] = os.path.basename(url)
                    thumbnail = QImage.fromData(page.read("preview.png"))
                    pageItem.setIcon(QIcon(QPixmap.fromImage(thumbnail)))
                    pageItem.setText(dataList[0])
                    self.pagesModel.setItem(index.row(), index.column(), pageItem)
                    self.pagesModel.setData(index2, str(dataList[1]), Qt.DisplayRole)

    """
    Resize all the pages in the pages list.
    It will show a dialog with the options for resizing. Then, it will try to pop up a progress dialog while resizing.
    The progress dialog shows the remaining time and pages.
    """

    def slot_batch_resize(self):
        dialog = QDialog()
        dialog.setWindowTitle(i18n("Risize all pages."))
        buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        buttons.accepted.connect(dialog.accept)
        buttons.rejected.connect(dialog.reject)
        sizesBox = comics_export_dialog.comic_export_resize_widget("Scale", batch=True, fileType=False)
        exporterSizes = comics_exporter.sizesCalculator()
        dialog.setLayout(QVBoxLayout())
        dialog.layout().addWidget(sizesBox)
        dialog.layout().addWidget(buttons)

        if dialog.exec_() == QDialog.Accepted:
            progress = QProgressDialog(i18n("Resizing pages..."), str(), 0, len(self.setupDictionary["pages"]))
            progress.setWindowTitle(i18n("Resizing pages."))
            progress.setCancelButton(None)
            timer = QElapsedTimer()
            timer.start()
            config = {}
            config = sizesBox.get_config(config)
            for p in range(len(self.setupDictionary["pages"])):
                absoluteUrl = os.path.join(self.projecturl, self.setupDictionary["pages"][p])
                progress.setValue(p)
                timePassed = timer.elapsed()
                if (p > 0):
                    timeEstimated = (len(self.setupDictionary["pages"]) - p) * (timePassed / p)
                    passedString = str(int(timePassed / 60000)) + ":" + format(int(timePassed / 1000), "02d") + ":" + format(timePassed % 1000, "03d")
                    estimatedString = str(int(timeEstimated / 60000)) + ":" + format(int(timeEstimated / 1000), "02d") + ":" + format(int(timeEstimated % 1000), "03d")
                    progress.setLabelText(str(i18n("{pages} of {pagesTotal} done. \nTime passed: {passedString}:\n Estimated:{estimated}")).format(pages=p, pagesTotal=len(self.setupDictionary["pages"]), passedString=passedString, estimated=estimatedString))
                if os.path.exists(absoluteUrl):
                    doc = Application.openDocument(absoluteUrl)
                    listScales = exporterSizes.get_scale_from_resize_config(config["Scale"], [doc.width(), doc.height(), doc.resolution(), doc.resolution()])
                    doc.scaleImage(listScales[0], listScales[1], listScales[2], listScales[3], "bicubic")
                    doc.waitForDone()
                    doc.save()
                    doc.waitForDone()
                    doc.close()

    def slot_show_page_viewer(self):
        index = self.comicPageList.currentIndex()
        if index.column() is not 0:
            index = self.pagesModel.index(index.row(), 0)
        # Get the absolute url from the relative one in the pages model.
        absoluteUrl = os.path.join(self.projecturl, str(self.pagesModel.data(index, role=Qt.ToolTipRole)))

        # Make sure the page exists.
        if os.path.exists(absoluteUrl):
            page = zipfile.ZipFile(absoluteUrl, "r")
            image = QImage.fromData(page.read("mergedimage.png"))
            self.page_viewer_dialog.update_image(image)
            self.page_viewer_dialog.show()
            page.close()
    """
    Function to copy the current project location into the clipboard.
    This is useful for users because they'll be able to use that url to quickly
    move to the project location in outside applications.
    """

    def slot_copy_project_url(self):
        if self.projecturl is not None:
            clipboard = qApp.clipboard()
            clipboard.setText(str(self.projecturl))
    """
    This is required by the dockwidget class, otherwise unused.
    """

    def canvasChanged(self, canvas):
        pass
Exemplo n.º 5
0
class MainWindow(QWidget):
    Id, Password = range(2)
    CONFIG_FILE = 'config'

    def __init__(self):
        super().__init__()
        with open(self.CONFIG_FILE, 'a'):
            pass
        self.init()

    def init(self):
        # ------ initUI
        self.resize(555, 245)
        self.setFixedSize(555, 245)
        self.center()
        self.setWindowTitle('Portal Connector')
        self.setWindowIcon(QIcon('gao.ico'))
        self.backgroundRole()
        palette1 = QPalette()
        palette1.setColor(self.backgroundRole(), QColor(250, 250,
                                                        250))  # 设置背景颜色

        self.setPalette(palette1)

        # ------setLeftWidget

        self.dataGroupBox = QGroupBox("Saved", self)
        self.dataGroupBox.setGeometry(10, 10, 60, 20)
        self.dataGroupBox.setStyleSheet(MyGroupBox)

        self.model = QStandardItemModel(0, 2, self)
        self.model.setHeaderData(self.Id, Qt.Horizontal, "Id")
        self.model.setHeaderData(self.Password, Qt.Horizontal, "Pw")

        self.dataView = QTreeView(self)
        self.dataView.setGeometry(10, 32, 255, 150)
        self.dataView.setRootIsDecorated(False)
        self.dataView.setAlternatingRowColors(True)
        self.dataView.setModel(self.model)
        self.dataView.setStyleSheet(MyTreeView)

        save_btn = QPushButton('Save', self)
        save_btn.setGeometry(15, 195, 100, 35)
        save_btn.setStyleSheet(MyPushButton)

        delete_btn = QPushButton('Delete', self)
        delete_btn.setGeometry(135, 195, 100, 35)
        delete_btn.setStyleSheet(MyPushButton)

        # ------ setRightWidget

        username = QLabel('Id:', self)
        username.setGeometry(300, 45, 50, 30)
        username.setStyleSheet(MyLabel)

        self.username_edit = QLineEdit(self)
        self.username_edit.setGeometry(350, 40, 190, 35)
        self.username_edit.setStyleSheet(MyLineEdit)

        password = QLabel('Pw:', self)
        password.setGeometry(300, 100, 50, 30)
        password.setStyleSheet(MyLabel)

        self.password_edit = QLineEdit(self)
        self.password_edit.setGeometry(350, 95, 190, 35)
        self.password_edit.setStyleSheet(MyLineEdit)

        status_label = QLabel('Result:', self)
        status_label.setGeometry(295, 150, 70, 30)
        status_label.setStyleSheet(UnderLabel)

        self.status = QLabel('Disconnect', self)
        self.status.setGeometry(360, 150, 190, 30)
        self.status.setStyleSheet(UnderLabel)

        connect_btn = QPushButton('Connect', self)
        connect_btn.setGeometry(320, 195, 100, 35)
        connect_btn.setStyleSheet(MyPushButton)

        test_btn = QPushButton('Test', self)
        test_btn.setGeometry(440, 195, 100, 35)
        test_btn.setStyleSheet(MyPushButton)

        # ------setTabOrder

        self.setTabOrder(self.username_edit, self.password_edit)
        self.setTabOrder(self.password_edit, connect_btn)
        self.setTabOrder(connect_btn, test_btn)

        # ------setEvent

        self.dataView.mouseDoubleClickEvent = self.set_text
        self.dataView.mousePressEvent = self.set_focus
        delete_btn.clicked.connect(self.removeItem)
        connect_btn.clicked.connect(self.connect_clicked)
        save_btn.clicked.connect(self.save_infomation)
        test_btn.clicked.connect(self.test_network)

        self.readItem(self.CONFIG_FILE)
        self.connect_clicked()
        self.show()

    def connect_clicked(self):
        result = connect_portal(self.username_edit.text(),
                                self.password_edit.text())
        self.status.setText(result)

    def save_infomation(self):
        if self.username_edit.text() and self.password_edit.text():
            try:
                selected = self.dataView.selectedIndexes()[0].row()
                self.modifyItem(selected)
            except IndexError:
                self.addItem(self.username_edit.text(),
                             self.password_edit.text())

    def test_network(self):
        result = test_public()
        self.status.setText(result)

    def set_text(self, event=None):
        try:
            self.username_edit.setText(
                self.dataView.selectedIndexes()[0].data())
            self.password_edit.setText(
                self.dataView.selectedIndexes()[1].data())
        except IndexError:
            pass

    def set_focus(self, event):
        index = self.dataView.indexAt(event.pos())
        if not index.isValid():
            self.dataView.clearSelection()
        else:
            self.dataView.setCurrentIndex(index)

    def readItem(self, filename):
        with open(filename, 'r') as f:
            for line in f.readlines():
                self.addItem(*(line.split()))

        self.dataView.setCurrentIndex(self.dataView.indexAt(QPoint(1, 1)))
        self.set_text()

    def addItem(self, username, password):
        self.model.insertRow(0)
        self.model.setData(self.model.index(0, self.Id), username)
        self.model.setData(self.model.index(0, self.Password), password)
        self.save_to_file()

    def modifyItem(self, row):
        self.model.setData(self.model.index(row, self.Id),
                           self.username_edit.text())
        self.model.setData(self.model.index(row, self.Password),
                           self.password_edit.text())
        self.save_to_file()

    def removeItem(self):
        try:
            self.model.removeRow(self.dataView.selectedIndexes()[0].row())
            self.save_to_file()
        except IndexError:
            pass

    def save_to_file(self):
        with open(self.CONFIG_FILE, 'w') as f:
            for x in range(self.model.rowCount()):
                for y in range(self.model.columnCount()):
                    f.write(self.model.data(self.model.index(x, y)) + " ")
                f.write("\n")

    def center(self):
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())
Exemplo n.º 6
0
class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()

        fileMenu = QMenu("&File", self)
        openAction = fileMenu.addAction("&Open...")
        openAction.setShortcut("Ctrl+O")
        saveAction = fileMenu.addAction("&Save As...")
        saveAction.setShortcut("Ctrl+S")
        quitAction = fileMenu.addAction("E&xit")
        quitAction.setShortcut("Ctrl+Q")

        self.setupModel()
        self.setupViews()

        openAction.triggered.connect(self.openFile)
        saveAction.triggered.connect(self.saveFile)
        quitAction.triggered.connect(QApplication.instance().quit)

        self.menuBar().addMenu(fileMenu)
        self.statusBar()

        self.openFile(':/Charts/qtdata.cht')

        self.setWindowTitle("Chart")
        self.resize(870, 550)

    def setupModel(self):
        self.model = QStandardItemModel(8, 2, self)
        self.model.setHeaderData(0, Qt.Horizontal, "Label")
        self.model.setHeaderData(1, Qt.Horizontal, "Quantity")

    def setupViews(self):
        splitter = QSplitter()
        table = QTableView()
        self.pieChart = PieView()
        splitter.addWidget(table)
        splitter.addWidget(self.pieChart)
        splitter.setStretchFactor(0, 0)
        splitter.setStretchFactor(1, 1)

        table.setModel(self.model)
        self.pieChart.setModel(self.model)

        self.selectionModel = QItemSelectionModel(self.model)
        table.setSelectionModel(self.selectionModel)
        self.pieChart.setSelectionModel(self.selectionModel)

        table.horizontalHeader().setStretchLastSection(True)

        self.setCentralWidget(splitter)

    def openFile(self, path=None):
        if not path:
            path, _ = QFileDialog.getOpenFileName(self, "Choose a data file",
                                                  '', '*.cht')

        if path:
            f = QFile(path)

            if f.open(QFile.ReadOnly | QFile.Text):
                stream = QTextStream(f)

                self.model.removeRows(0, self.model.rowCount(QModelIndex()),
                                      QModelIndex())

                row = 0
                line = stream.readLine()
                while line:
                    self.model.insertRows(row, 1, QModelIndex())

                    pieces = line.split(',')
                    self.model.setData(self.model.index(row, 0, QModelIndex()),
                                       pieces[0])
                    self.model.setData(self.model.index(row, 1, QModelIndex()),
                                       float(pieces[1]))
                    self.model.setData(self.model.index(row, 0, QModelIndex()),
                                       QColor(pieces[2]), Qt.DecorationRole)

                    row += 1
                    line = stream.readLine()

                f.close()
                self.statusBar().showMessage("Loaded %s" % path, 2000)

    def saveFile(self):
        fileName, _ = QFileDialog.getSaveFileName(self, "Save file as", '',
                                                  '*.cht')

        if fileName:
            f = QFile(fileName)

            if f.open(QFile.WriteOnly | QFile.Text):
                for row in range(self.model.rowCount(QModelIndex())):
                    pieces = []

                    pieces.append(
                        self.model.data(
                            self.model.index(row, 0, QModelIndex()),
                            Qt.DisplayRole))
                    pieces.append('%g' % self.model.data(
                        self.model.index(row, 1, QModelIndex()),
                        Qt.DisplayRole))
                    pieces.append(
                        self.model.data(
                            self.model.index(row, 0, QModelIndex()),
                            Qt.DecorationRole).name())

                    f.write(b','.join([p.encode('utf-8') for p in pieces]))
                    f.write(b'\n')

            f.close()
            self.statusBar().showMessage("Saved %s" % fileName, 2000)
Exemplo n.º 7
0
class gcodeFile(QObject):
    '''
  Gestion d'un fichier GCode dans la QListView de l'interface graphique reservee a cet usage
  Methodes :
  - __init__(QListView) -> Initialise et definit les elements de l'UI qui recevront le contenu du fichier
  - showFileOpen()        -> Affiche la boite de dialogue d'ouverture
  - showFileSave()        -> Affiche la boite de dialogue d'enregistrement
  - readFile(filePath)    -> Charge un fichier dans la QListView
  - saveFile(filePath)    -> Enregistre le contenu de la QListView dans un fichier
  - closeFile()           -> Vide la QListView
  - setGcodeChanged(bool) -> Definit si le contenu de la liste a ete modifie depuis la lecture ou l'enregistrement du fichier
  - bool = gcodeChanged() -> Renvoi vrai si le contenu de la liste a ete modifie depuis la lecture ou l'enregistrement du fichier
  '''

    sig_log = pyqtSignal(int, str)  # Message de fonctionnement du composant

    def __init__(self, gcodeFileUi: QListView):
        super().__init__()
        self.__filePath = ""
        self.__gcodeFileUi = gcodeFileUi
        self.__gcodeFileUiModel = QStandardItemModel(self.__gcodeFileUi)
        self.__gcodeFileUiModel.itemChanged.connect(self.on_gcodeChanged)
        self.__gcodeCharge = False
        self.__gcodeChanged = False

    def showFileOpen(self):
        ''' Affiche la boite de dialogue d'ouverture '''
        '''
    if sys.platform == 'linux':
      # Prépare la boite de dialogue
      dialog = Gtk.FileChooserDialog(
        self.tr("Open a GCode file"),
        self,
        Gtk.FileChooserAction.OPEN,
        (
          Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
          Gtk.STOCK_OPEN, Gtk.ResponseType.OK
        )
      )
      dialog.set_local_only(False) # Permet l'affichage des fichiers réseaux sous Linux GTK+3
      dialog_filter = Gtk.FileFilter()
      dialog_filter.set_name(self.tr("GCode file"))
      dialog_filter.add_pattern("*.gcode")
      dialog_filter.add_pattern("*.ngc")
      dialog_filter.add_pattern("*.nc")
      dialog_filter.add_pattern("*.gc")
      dialog_filter.add_pattern("*.cnc")
      dialog.add_filter(dialog_filter)
      # Affiche la boite de dialogue
      response = dialog.run()
      # Traite la réponse
      if response == Gtk.ResponseType.OK:
          print("Open clicked")
          print("File selected: " + dialog.get_filename())
      elif response == Gtk.ResponseType.CANCEL:
          print("Cancel clicked")
      # Libère les ressources
      dialog.destroy()

    else: # sys.platform == 'linux'
    '''
        opt = QtWidgets.QFileDialog.Options()
        ###opt |= QtWidgets.QFileDialog.DontUseNativeDialog
        fName = QtWidgets.QFileDialog.getOpenFileName(
            None,
            self.tr("Open a GCode file"),
            "",
            self.tr("GCode file (*.gcode *.ngc *.nc *.gc *.cnc)"),
            options=opt)
        return fName

    def readFile(self, filePath: str):
        self.sig_log.emit(logSeverity.info.value,
                          self.tr("Reading file: {}").format(filePath))
        try:
            f = open(filePath, 'r')
            lignes = f.readlines()
            f.close()
            self.sig_log.emit(
                logSeverity.info.value,
                self.tr("{} lines in the file").format(len(lignes)))
            # Envoi du contenu dans la liste
            self.__gcodeFileUiModel.clear()
            for l in lignes:
                item = QStandardItem(l.strip())
                self.__gcodeFileUiModel.appendRow(item)
            self.__gcodeFileUi.setModel(self.__gcodeFileUiModel)
            # Selectionne la premiere ligne du fichier dans la liste
            self.selectGCodeFileLine(0)
            # Selectionne l'onglet du fichier
        except Exception as e:
            self.sig_log.emit(
                logSeverity.error.value,
                self.tr("Reading file error: {}").format(filePath))
            self.sig_log.emit(logSeverity.error.value, str(e))
            self.__gcodeFileUiModel.clear()
            self.__filePath = ""
            self.__gcodeChanged = False
            return False

        # Pas d'erreur
        self.__gcodeCharge = True
        self.__filePath = filePath
        self.__gcodeChanged = False
        return True

    def isFileLoaded(self):
        return self.__gcodeCharge

    def filePath(self):
        return self.__filePath

    def fileName(self):
        return os.path.basename(self.__filePath)

    def selectGCodeFileLine(self, num: int):
        ''' Selectionne un element de la liste du fichier GCode '''
        idx = self.__gcodeFileUiModel.index(num, 0, QModelIndex())
        self.__gcodeFileUi.selectionModel().clearSelection()
        self.__gcodeFileUi.selectionModel().setCurrentIndex(
            idx, QItemSelectionModel.SelectCurrent)

    def getGCodeSelectedLine(self):
        ''' Renvoie le N° (0 base) de la ligne selectionnee dans la liste GCode et les donnees de cette ligne. '''
        idx = self.__gcodeFileUi.selectionModel().selectedIndexes()
        return [idx[0].row(), self.__gcodeFileUiModel.data(idx[0])]

    def saveAs(self):
        fName = self.showFileSave()
        if fName[0] != "":
            self.sig_log.emit(logSeverity.info.value,
                              self.tr("saveAs({})").format(fName[0]))
            self.saveFile(fName[0])
        else:
            self.sig_log.emit(logSeverity.info.value,
                              self.tr("saveAs() canceled!"))

    def showFileSave(self):
        ''' Affiche la boite de dialogue "Save as" '''
        opt = QtWidgets.QFileDialog.Options()
        opt |= QtWidgets.QFileDialog.DontUseNativeDialog
        fName = QtWidgets.QFileDialog.getSaveFileName(
            None,
            self.tr("Save GCode file"),
            "",
            self.tr("GCode file (*.gcode *.ngc *.nc *.gc *.cnc)"),
            options=opt)
        return fName

    def saveFile(self, filePath: str = ""):
        if filePath == "":
            if self.__filePath == "":
                # Le nom du fichier n'est pas definit, il n'y a pas de fichier charge, donc, rien a sauvegarder !
                return
            else:
                filePath = self.__filePath
        self.sig_log.emit(logSeverity.info.value,
                          self.tr("Saving file: {}").format(filePath))
        try:
            f = open(filePath, 'w')
            for I in range(self.__gcodeFileUiModel.rowCount()):
                idx = self.__gcodeFileUiModel.index(I, 0, QModelIndex())
                if self.__gcodeFileUiModel.data(idx) != "":
                    f.write(self.__gcodeFileUiModel.data(idx) + '\n')
            f.close()
            self.__filePath = filePath
        except Exception as e:
            self.sig_log.emit(logSeverity.error.value,
                              self.tr("Save file error: {}").format(filePath))
            self.sig_log.emit(logSeverity.error.value, str(e))
        # Supprime les lignes vides dans la grille d'affichage
        self.delEmptyRow()
        # Reinit du flag fichier change
        self.__gcodeChanged = False

    def enQueue(self, com: grblCom, startLine: int = 0, endLine: int = -1):
        """ Envoi des lignes de startLine a endLine dans la file d'attente du grblCom """
        if endLine == -1:
            endLine = self.__gcodeFileUiModel.rowCount()
        for I in range(startLine, endLine + 1):
            idx = self.__gcodeFileUiModel.index(I, 0, QModelIndex())
            if self.__gcodeFileUiModel.data(idx) != "":
                com.gcodePush(self.__gcodeFileUiModel.data(idx))
                com.gcodePush(CMD_GRBL_GET_GCODE_STATE, COM_FLAG_NO_OK)

    def delEmptyRow(self):
        """ Elimination des lignes GCode vides """
        for I in reversed(range(self.__gcodeFileUiModel.rowCount())):
            # On commence par la fin pour pouvoir supprimer sans tout decaler pour la suite
            idx = self.__gcodeFileUiModel.index(I, 0, QModelIndex())
            if self.__gcodeFileUiModel.data(idx) == "":
                self.__gcodeFileUiModel.removeRow(I)

    def deleteGCodeFileLine(self, num: int):
        self.__gcodeFileUiModel.removeRow(num)
        self.__gcodeChanged = True

    def insertGCodeFileLine(self, num: int):
        item = QStandardItem("")
        self.__gcodeFileUiModel.insertRow(num, item)

    def addGCodeFileLine(self, num: int):
        item = QStandardItem("")
        self.__gcodeFileUiModel.insertRow(num + 1, item)

    def showConfirmChangeLost(self):
        m = msgBox(
            title=self.tr("Save Changes"),
            text=self.tr("Do you want to save the changes before closing?"),
            info=self.
            tr("If you do not save, any changes made since opening or the last save will be lost."
               ),
            icon=msgIconList.Question,
            stdButton=msgButtonList.Save | msgButtonList.Cancel
            | msgButtonList.Discard,
            defButton=msgButtonList.Save,
            escButton=msgButtonList.Cancel)
        return (m.afficheMsg())

    def closeFile(self):
        if self.__gcodeChanged:
            # GCode modifie, on demande confirmation
            Ret = self.showConfirmChangeLost()
            if Ret == msgButtonList.Save:
                if self.__filePath == "":
                    filePath = self.showFileSave()
                    if filePath == "":
                        # Annulation de la boite de dialogue
                        return False
                    else:
                        self.__filePath = filePath
                self.saveFile(self.__filePath)
                return True
            elif Ret == msgButtonList.Discard:
                # Fermer le fichier consiste en vider la fenetre GCode
                self.__gcodeFileUiModel.clear()
                self.__gcodeChanged = False
                self.__gcodeCharge = False
                return True
            else:  # Ret == msgButtonList.Cancel:
                return False
        else:
            # GCode non modifie, on ferme sans confirmation
            # Fermer le fichier consiste en vider la fenetre GCode
            # et a supprimer le status GCode charge.
            self.__gcodeFileUiModel.clear()
            self.__gcodeChanged = False
            self.__gcodeCharge = False
            return True

    @pyqtSlot("QStandardItem*")
    def on_gcodeChanged(self, item):
        self.__gcodeChanged = True

    def gcodeChanged(self):
        return self.__gcodeChanged

    def setGcodeChanged(self, value: bool):
        self.__gcodeChanged = value
Exemplo n.º 8
0
class DownloadM3U8QtUI(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super(DownloadM3U8QtUI, self).__init__(parent)
        self.setupUi(self)
        self.ts_sum = 0
        self.success_sum = 0
        self._max_workers = 128
        self._m3u8_ts_list = []
        self._max_retries = 5
        self._m3u8_file_name = str(time.time())
        self.front_url = ''
        self._m3u8_url = ''
        self._chunk_size = 1024
        self.headers = DEFAULT_HEADERS

        self.model_table_list = QStandardItemModel(0, 3)

        self.initial_download_table()

        self.start_download_button.clicked.connect(self.start_download_file)
        self.button_open_folder.clicked.connect(self.open_folder)
        self.download_list_table.doubleClicked.connect(
            self.check_download_list_ts)

        self.download_file_name_input.textChanged.connect(
            self.update_download_m3u8_file_name)

        self.button_merge_ts_to_mp4.clicked.connect(self.merge_m3u8_ts_2_mp4)

    def get_default_model_table_list(self):
        model_table_list = QStandardItemModel(0, 3)
        model_table_list.setHorizontalHeaderLabels(TS_HEADERS)
        return model_table_list

    def update_download_m3u8_file_name(self):
        if self.download_file_name_input.text():
            self._m3u8_file_name = self.download_file_name_input.text()
            self.initial_download_table()

    def update_download_max_workers(self):
        if self.max_async_count_input.text():
            self._max_workers = int(self.max_async_count_input.text())

    def initial_download_table(self):
        self.model_table_list = self.get_default_model_table_list()
        self.download_list_table.horizontalHeader().setStretchLastSection(True)
        self.download_list_table.setEditTriggers(
            QAbstractItemView.NoEditTriggers)
        self.download_list_table.setModel(self.model_table_list)

        self.download_progress.setValue(0)

    # 下载文件的保存路径
    def open_folder(self):
        directory = QFileDialog.getExistingDirectory(self, '选择文件夹')
        self.download_file_path_input.setText(directory)
        self.initial_download_table()

    def check_download_list_ts(self, model_index):
        select_data = self.model_table_list.data(
            self.model_table_list.index(model_index.row(), 1))
        QMessageBox.information(self, '提示', '选中: {}'.format(select_data))

    def parse_download_ts_list(self):
        self.fetch_m3u8_ts_info(self._max_retries)
        self.set_download_table_view()

    async def _start_download_ts_file(self, ts_url, file_name, max_sem):
        async with max_sem:
            await self.m3u8_download_ts(ts_url, file_name, self._max_retries)

    async def _start_download_file(self):
        max_sem = asyncio.Semaphore(self._max_workers)
        tasks = []
        for _index_ts, _ts_url in enumerate(self._m3u8_ts_list):
            tasks.append(
                asyncio.ensure_future(
                    self._start_download_ts_file(_ts_url, _index_ts, max_sem)))

    # 开始下载
    def start_download_file(self):
        self.update_download_m3u8_file_name()
        self.update_download_max_workers()

        self.parse_download_ts_list()

        asyncio.ensure_future(self._start_download_file(), loop=loop)

    # 合并TS 为 MP4, 使用 ffmpeg
    def merge_m3u8_ts_2_mp4(self):
        if self.download_progress.text() != '100%':
            QMessageBox.warning(self, 'Wait a minute...', '稍等片刻,还在下载...')
        else:
            self.merge_ts_to_mp4()

    def merge_ts_to_mp4(self):

        new_m3u8_file = self.get_new_m3u8_file()
        merge_mp4_file = self.get_m3u8_ts_merge_mp4_file()

        file_folder = self.get_save_ts_file_folder()

        merge_cmd = 'ffmpeg -allowed_extensions ALL -i {new_m3u8_file} ' \
              '-acodec copy -vcodec copy -f mp4 {merge_mp4_file}'.format(
            new_m3u8_file=new_m3u8_file,
            merge_mp4_file=merge_mp4_file
        )
        os.system(merge_cmd)
        # os.system(f'rm -rf {file_folder} {new_m3u8_file}')

    def fetch_m3u8_ts_info(self, max_retries):
        if self.download_url_input.text():
            m3u8_url = self.download_url_input.text()
            self.fetch_ts_info_from_url(m3u8_url, max_retries)
        else:
            self.fetch_ts_info_from_file(max_retries)

    def fetch_ts_info_from_url(self, m3u8_url, max_retries):
        try:
            res = requests.get(m3u8_url,
                               timeout=(3, 30),
                               verify=False,
                               headers=self.headers)
            self.front_url = res.url.split(res.request.path_url)[0]
            if "EXT-X-STREAM-INF" in res.text:
                for line in res.text.split('\n'):
                    if "#" in line or not line:
                        continue
                    elif re.search(r'^http', line) is not None:
                        self._m3u8_url = line
                    elif re.search(r'^/', line) is not None:
                        self._m3u8_url = self.front_url + line
                    else:
                        self._m3u8_url = self._m3u8_url.rsplit(
                            "/", 1)[0] + '/' + line
                self.fetch_ts_info_from_url(self._m3u8_url, self._max_retries)
            else:
                m3u8_text_str = res.text
                self.fetch_m3u8_ts_url(m3u8_text_str)
        except Exception as e:
            if max_retries > 0:
                self.fetch_ts_info_from_url(m3u8_url, max_retries - 1)

    def fetch_ts_info_from_file(self, max_retries):
        try:
            res_content = open('/home/1di0t/Videos/合并报表2.m3u8', 'r')
            res_content = res_content.read()
            self.fetch_m3u8_ts_url(res_content)
        except Exception as e:
            if max_retries > 0:
                self.fetch_ts_info_from_file(max_retries - 1)

    def get_save_ts_file_folder(self):
        if not self.download_file_path_input.text():
            file_path_name = './{}'.format(self._m3u8_file_name)
        else:
            file_path_name = '{}/{}'.format(
                self.download_file_path_input.text(), self._m3u8_file_name)
        return file_path_name

    def get_m3u8_ts_merge_mp4_file(self):
        if not self.download_file_path_input.text():
            file_path_name = './{}.mp4'.format(self._m3u8_file_name)
        else:
            file_path_name = '{}/{}.mp4'.format(
                self.download_file_path_input.text(), self._m3u8_file_name)
        return file_path_name

    def get_new_m3u8_file(self):
        if not self.download_file_path_input.text():
            file_path_name = './{}.m3u8'.format(self._m3u8_file_name)
        else:
            file_path_name = '{}/{}.m3u8'.format(
                self.download_file_path_input.text(), self._m3u8_file_name)
        return file_path_name

    def fetch_m3u8_ts_url(self, m3u8_text_str):
        """
        fetch ts url
        """
        file_folder = self.get_save_ts_file_folder()
        new_m3u8_file = self.get_new_m3u8_file()
        if not os.path.exists(file_folder):
            os.mkdir(file_folder)
        new_m3u8_str = ''
        ts = compute_sum()
        for line in m3u8_text_str.split('\n'):
            if "#" in line:
                if "EXT-X-KEY" in line and "URI=" in line:
                    key = self.download_key(line, 5)
                    if key:
                        new_m3u8_str += f'{key}\n'
                        continue
                new_m3u8_str += f'{line}\n'
                if "EXT-X-ENDLIST" in line:
                    break
            elif re.search(r'^http', line) is not None:
                new_m3u8_str += f"{file_folder}/{next(ts)}\n"
                self._m3u8_ts_list.append(line)
            elif re.search(r'^/', line) is not None:
                new_m3u8_str += f"{file_folder}/{next(ts)}\n"
                self._m3u8_ts_list.append(self.front_url + line)
            else:
                new_m3u8_str += f"{file_folder}/{next(ts)}\n"
                self._m3u8_ts_list.append(
                    self._m3u8_url.rsplit("/", 1)[0] + '/' + line)
        self.ts_sum = next(ts)
        with open(f"{new_m3u8_file}", "w") as f:
            f.write(new_m3u8_str)

    def get_ts_key_file_name_with_path(self):
        if not self.download_file_path_input.text():
            file_path_name = './{}/key'.format(self._m3u8_file_name)
        else:
            file_path_name = '{}/{}/key'.format(
                self.download_file_path_input.text(), self._m3u8_file_name)
        return file_path_name

    def download_key(self, key_line, num_retries):
        """
        下载key文件
        """
        key_file_path = self.get_ts_key_file_name_with_path()
        mid_part = re.search(r"URI=[\'|\"].*?[\'|\"]", key_line).group()
        may_key_url = mid_part[5:-1]
        if re.search(r'^http', may_key_url) is not None:
            true_key_url = may_key_url
        elif re.search(r'^/', may_key_url) is not None:
            true_key_url = self.front_url + may_key_url
        else:
            true_key_url = self._m3u8_url.rsplit("/", 1)[0] + '/' + may_key_url
        try:
            res = requests.get(true_key_url,
                               timeout=(5, 60),
                               verify=False,
                               headers=self.headers)
            with open(key_file_path, 'wb') as f:
                f.write(res.content)
            res.close()
            return f'{key_line.split(mid_part)[0]}URI="{key_file_path}"{key_line.split(mid_part)[-1]}'
        except Exception as e:
            print(e)
            if os.path.exists(key_file_path):
                os.remove(key_file_path)
            if num_retries > 0:
                self.download_key(key_line, num_retries - 1)

    def set_download_table_view(self):
        for index_ts, ts_line in enumerate(self._m3u8_ts_list):
            self.model_table_list.appendRow([
                QStandardItem(index_ts),
                QStandardItem(ts_line),
                QStandardItem('未下载')
            ])

    def set_download_list_view(self, list_item):
        list_model = QStringListModel()
        list_model.setStringList(list_item)
        self.download_file_list.setModel(list_model)

    def update_download_process_property(self, value):
        if not self._m3u8_ts_list:
            self.download_progress.setValue(0)
        else:
            process_value = math.ceil((value / len(self._m3u8_ts_list)) * 100)
            self.download_progress.setValue(process_value)

    async def save_resp_content_to_file(self, resp, ts_file_obj):
        while True:
            chunk_file = await resp.content.read(self._chunk_size)
            if not chunk_file:
                break
            ts_file_obj.write(chunk_file)

    async def _m3u8_download_ts(self, ts_url, save_ts_name, max_retry):
        file_path_name = self.get_ts_file_name_with_path(save_ts_name)
        async with aiohttp.ClientSession() as session:
            async with session.get(ts_url) as resp:
                if resp.status == 200:
                    with open(file_path_name, "wb") as ts:
                        await self.save_resp_content_to_file(resp, ts)

                    self.success_sum += 1
                    self.update_download_process_property(self.success_sum)
                else:
                    await self._m3u8_download_ts(ts_url, save_ts_name,
                                                 max_retry - 1)
                resp.close()

    def get_ts_file_name_with_path(self, save_ts_name):
        if not self.download_file_path_input.text():
            file_path_name = './{}/{}'.format(self._m3u8_file_name,
                                              save_ts_name)
        else:
            file_path_name = '{}/{}/{}'.format(
                self.download_file_path_input.text(), self._m3u8_file_name,
                save_ts_name)
        return file_path_name

    async def m3u8_download_ts(self, ts_url, save_ts_name, max_retry):
        try:
            file_path_name = self.get_ts_file_name_with_path(save_ts_name)
            ts_url = ts_url.split('\n')[0]
            if not os.path.exists(file_path_name):
                await self._m3u8_download_ts(ts_url, save_ts_name, max_retry)
            else:
                self.success_sum += 1
        except Exception as e:
            file_path_name = self.get_ts_file_name_with_path(save_ts_name)
            if os.path.exists(file_path_name):
                os.remove(file_path_name)
            if max_retry > 0:
                await self.m3u8_download_ts(ts_url, save_ts_name,
                                            max_retry - 1)
Exemplo n.º 9
0
class MergeDialog(QDialog):
    MAX_FILENUM = 50
    MAX_READLINE = 10000
    REPEAT_SUFFIX = "_suffix_#$:@%|&*(!@#$:@%|"  #设置为现实中很难出现的字符串

    def __init__(self, parent=None):
        super(MergeDialog, self).__init__(parent)
        self.datetime_formats = {
            'yyyy/mm/dd hh:mm:ss': '%Y/%m/%d %H:%M:%S',
            'yyyy/mm/dd h:m:s': '%Y/%m/%d %H:%M:%S',
            'yyyy/mm/dd hh:mm': '%Y/%m/%d %H:%M',
            'yyyy/mm/dd h:m': '%Y/%m/%d %H:%M',
            'yy/mm/dd hh:mm:ss': '%y/%m/%d %H:%M:%S',
            'yy/mm/dd h:m:s': '%y/%m/%d %H:%M:%S',
            'yy/mm/dd hh:mm': '%y/%m/%d %H:%M',
            'yy/mm/dd h:m': '%y/%m/%d %H:%M',
            'yyyy-mm-dd hh:mm:ss': '%Y-%m-%d %H:%M:%S',
            'yyyy-mm-dd h:m:s': '%Y-%m-%d %H:%M:%S',
            'yyyy-mm-dd hh:mm': '%Y-%m-%d %H:%M',
            'yyyy-mm-dd h:m': '%Y-%m-%d %H:%M',
            'yy-mm-dd hh:mm:ss': '%y-%m-%d %H:%M:%S',
            'yy-mm-dd h:m:s': '%y-%m-%d %H:%M:%S',
            'yy-mm-dd hh:mm': '%y-%m-%d %H:%M',
            'yy-mm-dd h:m': '%y-%m-%d %H:%M'
        }
        self.m_selected = False
        self.ui = Ui_MergeDialog()
        self.ui.setupUi(self)
        self.ui.retranslateUi(self)
        self.initUi()
        self.setWindowTitle("Merging Source Files")
        self.m_model = QStandardItemModel(1, 1)
        self.ui.m_fileListView.setModel(self.m_model)
        self.setupConnect()

    def initUi(self):
        coding_list = [
            "utf_8", "utf_16", "latin_1", "cp65001", "ascii", "gb2312",
            "gb18030", "gbk", "big5", "big5hkscs"
        ]
        self.ui.m_srcEncodingCBBox.addItems(coding_list)
        self.ui.m_srcEncodingCBBox.setCurrentIndex(0)
        self.ui.m_destEncodingCBBox.addItems(coding_list)
        self.ui.m_destEncodingCBBox.setCurrentIndex(0)
        self.ui.m_datetimeFormatCBBox.addItems(
            [key for key in self.datetime_formats])
        self.ui.m_datetimeFormatCBBox.setCurrentIndex(0)
        self.ui.m_offsetDataHeadEdit.setToolTip(u"数据相对字头的列偏移量,1为右偏1,-1为左偏1")

    def setupConnect(self):
        self.ui.m_selectButton.clicked.connect(self.onSelectSrcFiles)
        self.ui.m_saveButton.clicked.connect(self.onMerging)
        self.ui.m_quitButton.clicked.connect(self.close)
        self.ui.m_XisDateTimeCheck.stateChanged.connect(
            lambda state: self.ui.m_datetimeFormatCBBox.setEnabled(state))
        self.ui.m_rowSpanCheck.stateChanged.connect(
            lambda state: (self.ui.m_headrowEdit.setEnabled(not state),
                           self.ui.m_dataStartRowEdit.setEnabled(not state),
                           self.ui.m_XColumnEdit.setEnabled(not state),
                           self.ui.m_YStartColumnEdit.setEnabled(not state),
                           self.ui.m_stepEdit.setEnabled(not state),
                           self.ui.m_offsetDataHeadEdit.setEnabled(not state)))

    def onSelectSrcFiles(self):
        (file_list, _) = QFileDialog.getOpenFileNames(
            self, "open data files", "./",
            "Data Files (*.txt *.csv *.dat);;All Files (*.*)")
        if (len(file_list) > 0):
            self.m_model.setRowCount(len(file_list))
            i = 0
            for filename in file_list:
                self.m_model.setData(self.m_model.index(i, 0, QModelIndex()),
                                     filename)
                i = i + 1
            self.m_selected = True

    def onMerging(self):
        if (self.m_selected):
            save_file, _ = QFileDialog.getSaveFileName(
                self, "Open data file", "./",
                "Data Files (*.csv);;All Files (*.*)")
            if (save_file != ""):
                if (self.ui.m_colmunSpanCheck.isChecked()):  #column span
                    try:
                        self.mergingByColumn(save_file)
                    except Exception as e:
                        QMessageBox.warning(self, "Error!", format(e))
                elif (self.ui.m_rowSpanCheck.isChecked()):
                    self.mergingByRow(save_file)
            else:
                QMessageBox.warning(self, "Warning!",
                                    "please select merging file!")
        else:
            QMessageBox.warning(self, "Warning",
                                "source files have not been selected")

    def mergingByColumn(self, save_file):
        head_row = self.ui.m_headrowEdit.value() - 1
        data_startrow = self.ui.m_dataStartRowEdit.value() - 1
        x_column = self.ui.m_XColumnEdit.value() - 1
        ycolumn_step = self.ui.m_stepEdit.value()
        ystart_column = self.ui.m_YStartColumnEdit.value() - 1
        offset_data_head = self.ui.m_offsetDataHeadEdit.value(
        )  #数据相对字头的列偏移量,1为右偏1,-1为左偏1
        src_encoding = self.ui.m_srcEncodingCBBox.currentText()
        dest_encoding = self.ui.m_destEncodingCBBox.currentText()
        delimiter = ","
        if (self.ui.m_semiCheck.isChecked()):
            delimiter = ";"
        elif (self.ui.m_spaceCheck.isChecked()):
            delimiter = " "
        elif (self.ui.m_tabCheck.isChecked()):
            delimiter = "\t"
        elif (self.ui.m_otherCheck.isChecked()):
            ss = self.ui.m_otherEdit.text()
            if (len(ss) >= 1):
                delimiter = ss
        files_nume = self.m_model.rowCount()
        total_data = pandas.DataFrame()
        for i in range(files_nume):
            filename = self.m_model.data(
                self.m_model.index(i, 0, QModelIndex()))
            mycolumns = pandas.read_csv(filename,
                                        sep=delimiter,
                                        encoding=src_encoding,
                                        skipinitialspace=True,
                                        skip_blank_lines=True,
                                        skiprows=head_row,
                                        header=0,
                                        nrows=0).columns
            use_cols = [
                i for i in range(ystart_column, len(mycolumns), ycolumn_step)
            ]
            use_cols.insert(0, x_column)
            myindex_col = None
            if (self.ui.m_XasIndexCheck.isChecked()):
                myindex_col = 0
            data = pandas.read_csv(filename,
                                   sep=delimiter,
                                   encoding=src_encoding,
                                   skipinitialspace=True,
                                   skip_blank_lines=True,
                                   skiprows=data_startrow,
                                   header=None,
                                   index_col=myindex_col,
                                   usecols=use_cols,
                                   nrows=self.MAX_READLINE)
            if (len(use_cols) > 1):  #不大于1列不执行
                j = use_cols[0]
                use_cols = use_cols[1:]
                if (offset_data_head != 0  #以下:只有加上偏移后索引不超出范围才有限
                        and use_cols[0] - offset_data_head >= 0
                        and use_cols[-1] - offset_data_head < len(mycolumns)):
                    use_cols = [i - offset_data_head for i in use_cols]
                if (not self.ui.m_XasIndexCheck.isChecked()):
                    use_cols.insert(0, j)
                data.columns = mycolumns[use_cols]
            data.index.name = "index"
            if (self.ui.m_XisDateTimeCheck.isChecked()):
                if (self.ui.m_XasIndexCheck.isChecked()):
                    data.index.name = "time"
                mydatetime_format = self.datetime_formats[
                    self.ui.m_datetimeFormatCBBox.currentText()]
                data.index = pandas.to_datetime(data.index,
                                                format=mydatetime_format)
            if (i == 0):
                total_data = data
            else:
                total_data = total_data.join(data,
                                             lsuffix=self.REPEAT_SUFFIX,
                                             rsuffix=self.REPEAT_SUFFIX)
        mycolumns = [
            s.replace(self.REPEAT_SUFFIX, "") for s in total_data.columns
        ]
        total_data.columns = mycolumns
        total_data.to_csv(path_or_buf=save_file,
                          sep=delimiter,
                          encoding=dest_encoding)
        #print(total_data)

    def mergingByRow(self, save_file):
        QMessageBox.warning(self, "Warning!", save_file)
Exemplo n.º 10
0
    class ClientList(QWidget):
        def __init__(self, data: ()):
            super().__init__()
            self.tree_view = QTreeView()

            self.model = QStandardItemModel()
            self.rootNode = self.model.invisibleRootItem()
            self.applyModel()

            buttons = QHBoxLayout()
            add = QPushButton("Add")
            add.clicked.connect(self.addClient)
            remove = QPushButton("Remove")
            remove.clicked.connect(self.removeClient)
            buttons.addWidget(add)
            buttons.addWidget(remove)

            layout = QVBoxLayout()
            layout.addLayout(buttons)
            layout.addWidget(self.tree_view)
            self.setLayout(layout)

            if data:
                self.setData(data)

            self.in_progress = False

        def addClient(self):
            branch = [QStandardItem(), QStandardItem()]
            branch[0].setText("/")
            branch[1].setText("client_")
            self.model.appendRow(branch)
            index = self.model.index(self.model.rowCount() - 1, 0)
            self.tree_view.setCurrentIndex(index)
            self.applyModel()

        def removeClient(self):
            index = self.tree_view.currentIndex()
            if index.isValid():
                self.model.removeRow(index.row())
                self.applyModel()

        def applyModel(self):
            self.model.setHeaderData(0, Qt.Horizontal, "Path")
            self.model.setHeaderData(1, Qt.Horizontal, "Client variable")
            self.tree_view.setModel(self.model)

        def getData(self):
            data = {}

            for row in range(self.model.rowCount()):
                path = self.model.data(self.model.index(row, 0))
                client = self.model.data(self.model.index(row, 1))

                if path and client:
                    data[path] = client

            return data

        def setData(self, data):
            for path, client in data.items():
                branch = [QStandardItem(), QStandardItem()]
                branch[0].setText(path)
                branch[1].setText(client)
                self.model.appendRow(branch)
            self.applyModel()
Exemplo n.º 11
0
class comic_export_setting_dialog(QDialog):

    def __init__(self):
        super().__init__()
        self.setLayout(QVBoxLayout())
        self.setWindowTitle(i18n("Export settings"))
        buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)

        buttons.accepted.connect(self.accept)
        buttons.rejected.connect(self.reject)
        mainWidget = QTabWidget()
        self.layout().addWidget(mainWidget)
        self.layout().addWidget(buttons)

        # Set basic crop settings
        # Set which layers to remove before export.
        mainExportSettings = QWidget()
        mainExportSettings.setLayout(QVBoxLayout())
        groupExportCrop = QGroupBox(i18n("Crop settings"))
        formCrop = QFormLayout()
        groupExportCrop.setLayout(formCrop)
        self.chk_toOutmostGuides = QCheckBox(i18n("Crop to outmost guides"))
        self.chk_toOutmostGuides.setChecked(True)
        self.chk_toOutmostGuides.setToolTip(i18n("This will crop to the outmost guides if possible and otherwise use the underlying crop settings."))
        formCrop.addRow("", self.chk_toOutmostGuides)
        btn_fromSelection = QPushButton(i18n("Set margins from active selection"))
        btn_fromSelection.clicked.connect(self.slot_set_margin_from_selection)
        # This doesn't work.
        formCrop.addRow("", btn_fromSelection)
        self.spn_marginLeft = QSpinBox()
        self.spn_marginLeft.setMaximum(99999)
        self.spn_marginLeft.setSuffix(" px")
        formCrop.addRow(i18n("Left:"), self.spn_marginLeft)
        self.spn_marginTop = QSpinBox()
        self.spn_marginTop.setMaximum(99999)
        self.spn_marginTop.setSuffix(" px")
        formCrop.addRow(i18n("Top:"), self.spn_marginTop)
        self.spn_marginRight = QSpinBox()
        self.spn_marginRight.setMaximum(99999)
        self.spn_marginRight.setSuffix(" px")
        formCrop.addRow(i18n("Right:"), self.spn_marginRight)
        self.spn_marginBottom = QSpinBox()
        self.spn_marginBottom.setMaximum(99999)
        self.spn_marginBottom.setSuffix(" px")
        formCrop.addRow(i18n("Bottom:"), self.spn_marginBottom)
        groupExportLayers = QGroupBox(i18n("Layers"))
        formLayers = QFormLayout()
        groupExportLayers.setLayout(formLayers)
        self.cmbLabelsRemove = labelSelector()
        formLayers.addRow(i18n("Label for removal:"), self.cmbLabelsRemove)

        mainExportSettings.layout().addWidget(groupExportCrop)
        mainExportSettings.layout().addWidget(groupExportLayers)
        mainWidget.addTab(mainExportSettings, i18n("General"))

        # CBZ, crop, resize, which metadata to add.
        CBZexportSettings = QWidget()
        CBZexportSettings.setLayout(QVBoxLayout())
        self.CBZactive = QCheckBox(i18n("Export to CBZ"))
        CBZexportSettings.layout().addWidget(self.CBZactive)
        self.CBZgroupResize = comic_export_resize_widget("CBZ")
        CBZexportSettings.layout().addWidget(self.CBZgroupResize)
        self.CBZactive.clicked.connect(self.CBZgroupResize.setEnabled)
        CBZgroupMeta = QGroupBox(i18n("Metadata to add"))
        # CBZexportSettings.layout().addWidget(CBZgroupMeta)
        CBZgroupMeta.setLayout(QFormLayout())

        mainWidget.addTab(CBZexportSettings, "CBZ")

        # ACBF, crop, resize, creator name, version history, panel layer, text layers.
        ACBFExportSettings = QWidget()
        ACBFform = QFormLayout()
        ACBFExportSettings.setLayout(QVBoxLayout())
        ACBFdocInfo = QGroupBox()
        ACBFdocInfo.setTitle(i18n("ACBF Document Info"))
        ACBFdocInfo.setLayout(ACBFform)
        self.lnACBFAuthor = QLineEdit()
        self.lnACBFAuthor.setToolTip(i18n("The person responsible for the generation of the CBZ."))
        self.lnACBFSource = QLineEdit()
        self.lnACBFSource.setToolTip(i18n("Whether the acbf file is an adaption of an existing source, and if so, how to find information about that source. So for example, for an adapted webcomic, the official website url should go here."))
        self.lnACBFID = QLabel()
        self.lnACBFID.setToolTip(i18n("By default this will be filled with a generated universal unique identifier. The ID by itself is merely so that comic book library management programs can figure out if this particular comic is already in their database and whether it has been rated. Of course, the UUID can be changed into something else by manually changing the json, but this is advanced usage."))
        self.spnACBFVersion = QSpinBox()
        self.ACBFhistoryModel = QStandardItemModel()
        acbfHistoryList = QListView()
        acbfHistoryList.setModel(self.ACBFhistoryModel)
        btn_add_history = QPushButton(i18n("Add history entry"))
        btn_add_history.clicked.connect(self.slot_add_history_item)

        ACBFform.addRow(i18n("Author-name:"), self.lnACBFAuthor)
        ACBFform.addRow(i18n("Source:"), self.lnACBFSource)
        ACBFform.addRow(i18n("ACBF UID:"), self.lnACBFID)
        ACBFform.addRow(i18n("Version:"), self.spnACBFVersion)
        ACBFform.addRow(i18n("Version History:"), acbfHistoryList)
        ACBFform.addRow("", btn_add_history)

        ACBFExportSettings.layout().addWidget(ACBFdocInfo)
        mainWidget.addTab(ACBFExportSettings, "ACBF")

        # Epub export, crop, resize, other questions.
        EPUBexportSettings = QWidget()
        EPUBexportSettings.setLayout(QVBoxLayout())
        self.EPUBactive = QCheckBox(i18n("Export to EPUB"))
        EPUBexportSettings.layout().addWidget(self.EPUBactive)
        self.EPUBgroupResize = comic_export_resize_widget("EPUB")
        EPUBexportSettings.layout().addWidget(self.EPUBgroupResize)
        self.EPUBactive.clicked.connect(self.EPUBgroupResize.setEnabled)
        mainWidget.addTab(EPUBexportSettings, "EPUB")

        # For Print. Crop, no resize.
        TIFFExportSettings = QWidget()
        TIFFExportSettings.setLayout(QVBoxLayout())
        self.TIFFactive = QCheckBox(i18n("Export to TIFF"))
        TIFFExportSettings.layout().addWidget(self.TIFFactive)
        self.TIFFgroupResize = comic_export_resize_widget("TIFF")
        TIFFExportSettings.layout().addWidget(self.TIFFgroupResize)
        self.TIFFactive.clicked.connect(self.TIFFgroupResize.setEnabled)
        mainWidget.addTab(TIFFExportSettings, "TIFF")

        # SVG, crop, resize, embed vs link.
        #SVGExportSettings = QWidget()

        #mainWidget.addTab(SVGExportSettings, "SVG")

    """
    Add a history item to the acbf version history list.
    """

    def slot_add_history_item(self):
        newItem = QStandardItem()
        newItem.setText("v" + str(self.spnACBFVersion.value()) + "-" + i18n("in this version..."))
        self.ACBFhistoryModel.appendRow(newItem)

    """
    Get the margins by treating the active selection in a document as the trim area.
    This allows people to snap selections to a vector or something, and then get the margins.
    """

    def slot_set_margin_from_selection(self):
        doc = Application.activeDocument()
        if doc is not None:
            if doc.selection() is not None:
                self.spn_marginLeft.setValue(doc.selection().x())
                self.spn_marginTop.setValue(doc.selection().y())
                self.spn_marginRight.setValue(doc.width() - (doc.selection().x() + doc.selection().width()))
                self.spn_marginBottom.setValue(doc.height() - (doc.selection().y() + doc.selection().height()))

    """
    Load the UI values from the config dictionary given.
    """

    def setConfig(self, config):
        if "cropToGuides" in config.keys():
            self.chk_toOutmostGuides.setChecked(config["cropToGuides"])
        if "cropLeft" in config.keys():
            self.spn_marginLeft.setValue(config["cropLeft"])
        if "cropTop" in config.keys():
            self.spn_marginTop.setValue(config["cropTop"])
        if "cropRight" in config.keys():
            self.spn_marginRight.setValue(config["cropRight"])
        if "cropBottom" in config.keys():
            self.spn_marginBottom.setValue(config["cropBottom"])
        if "labelsToRemove" in config.keys():
            self.cmbLabelsRemove.setLabels(config["labelsToRemove"])
        self.CBZgroupResize.set_config(config)
        if "CBZactive" in config.keys():
            self.CBZactive.setChecked(config["CBZactive"])
        self.EPUBgroupResize.set_config(config)
        if "EPUBactive" in config.keys():
            self.EPUBactive.setChecked(config["EPUBactive"])
        self.TIFFgroupResize.set_config(config)
        if "TIFFactive" in config.keys():
            self.TIFFactive.setChecked(config["TIFFactive"])
        if "acbfAuthor" in config.keys():
            self.lnACBFAuthor.setText(config["acbfAuthor"])
        if "acbfSource" in config.keys():
            self.lnACBFSource.setText(config["acbfSource"])
        if "acbfID" in config.keys():
            self.lnACBFID.setText(config["acbfID"])
        else:
            self.lnACBFID.setText(QUuid.createUuid().toString())
        if "acbfVersion" in config.keys():
            self.spnACBFVersion.setValue(config["acbfVersion"])
        if "acbfHistory" in config.keys():
            for h in config["acbfHistory"]:
                item = QStandardItem()
                item.setText(h)
                self.ACBFhistoryModel.appendRow(item)
        self.CBZgroupResize.setEnabled(self.CBZactive.isChecked())

    """
    Store the GUI values into the config dictionary given.
    
    @return the config diactionary filled with new values.
    """

    def getConfig(self, config):

        config["cropToGuides"] = self.chk_toOutmostGuides.isChecked()
        config["cropLeft"] = self.spn_marginLeft.value()
        config["cropTop"] = self.spn_marginTop.value()
        config["cropBottom"] = self.spn_marginRight.value()
        config["cropRight"] = self.spn_marginBottom.value()
        config["labelsToRemove"] = self.cmbLabelsRemove.getLabels()
        config["CBZactive"] = self.CBZactive.isChecked()
        config = self.CBZgroupResize.get_config(config)
        config["EPUBactive"] = self.EPUBactive.isChecked()
        config = self.EPUBgroupResize.get_config(config)
        config["TIFFactive"] = self.TIFFactive.isChecked()
        config = self.TIFFgroupResize.get_config(config)
        config["acbfAuthor"] = self.lnACBFAuthor.text()
        config["acbfSource"] = self.lnACBFSource.text()
        config["acbfID"] = self.lnACBFID.text()
        config["acbfVersion"] = self.spnACBFVersion.value()
        versionList = []
        for r in range(self.ACBFhistoryModel.rowCount()):
            index = self.ACBFhistoryModel.index(r, 0)
            versionList.append(self.ACBFhistoryModel.data(index, Qt.DisplayRole))
        config["acbfHistory"] = versionList
        return config
class AssetList(MyTreeView):
    class Columns(IntEnum):
        NAME = 0
        BALANCE = 1
        IPFS = 2
        REISSUABLE = 3
        DIVISIONS = 4
        OWNER = 5

    filter_columns = [
        Columns.NAME, Columns.BALANCE, Columns.IPFS, Columns.REISSUABLE,
        Columns.DIVISIONS
    ]

    ROLE_SORT_ORDER = Qt.UserRole + 1000
    ROLE_ASSET_STR = Qt.UserRole + 1001

    def __init__(self, parent):
        super().__init__(parent,
                         self.create_menu,
                         stretch_column=None,
                         editable_columns=[])
        self.wallet = self.parent.wallet
        self.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.setSortingEnabled(True)
        self.std_model = QStandardItemModel(self)
        self.proxy = MySortModel(self, sort_role=self.ROLE_SORT_ORDER)
        self.proxy.setSourceModel(self.std_model)
        self.setModel(self.proxy)
        self.update()
        self.sortByColumn(self.Columns.NAME, Qt.AscendingOrder)
        self.asset_meta = {}

    def webopen_safe(self, url):
        show_warn = self.parent.config.get('show_ipfs_warning', True)
        if show_warn:
            cb = QCheckBox(_("Don't show this message again."))
            cb_checked = False

            def on_cb(x):
                nonlocal cb_checked
                cb_checked = x == Qt.Checked

            cb.stateChanged.connect(on_cb)
            goto = self.parent.question(_(
                'You are about to visit:\n\n'
                '{}\n\n'
                'IPFS hashes can link to anything. Please follow '
                'safe practices and common sense. If you are unsure '
                'about what\'s on the other end of an IPFS, don\'t '
                'visit it!\n\n'
                'Are you sure you want to continue?').format(url),
                                        title=_('Warning: External Data'),
                                        checkbox=cb)

            if cb_checked:
                self.parent.config.set_key('show_ipfs_warning', False)
            if goto:
                webopen(url)
        else:
            webopen(url)

    def mouseDoubleClickEvent(self, event: QMouseEvent):
        idx = self.indexAt(event.pos())
        if not idx.isValid():
            return

        # Get the IPFS from 3rd column
        hm_idx = self.model().mapToSource(self.model().index(idx.row(), 2))
        data = self.std_model.data(hm_idx)
        if data[:2] == 'Qm':  # If it starts with Qm, it's an IPFS
            url = ipfs_explorer_URL(self.parent.config, 'ipfs', data)
            self.webopen_safe(url)

    def refresh_headers(self):
        headers = {
            self.Columns.NAME: _('Name'),
            self.Columns.BALANCE: _('Amount'),
            self.Columns.IPFS: _('Asset Data'),
            self.Columns.REISSUABLE: _('Reissuable'),
            self.Columns.DIVISIONS: _('Divisions'),
            self.Columns.OWNER: _('Owner'),
        }
        self.update_headers(headers)

    @profiler
    def update(self):
        if self.maybe_defer_update():
            return
        current_asset = self.get_role_data_for_current_item(
            col=self.Columns.NAME, role=self.ROLE_ASSET_STR)
        addr_list = self.wallet.get_addresses()
        self.proxy.setDynamicSortFilter(
            False)  # temp. disable re-sorting after every change
        self.std_model.clear()
        self.asset_meta.clear()
        self.refresh_headers()
        set_asset = None

        assets = {}  # type: Dict[str, List[int, Optional[AssetMeta]]]

        for address in addr_list:
            c, u, x = self.wallet.get_addr_balance(address)
            balance = c + u + x

            # Don't display assets we no longer have
            if len(balance.assets) == 0:
                continue

            for asset, balance in balance.assets.items():
                # Don't show hidden assets
                if not self.parent.config.get('show_spam_assets', False):
                    should_continue = False
                    for regex in self.parent.asset_blacklist:
                        if re.search(regex, asset):
                            should_continue = True
                            break
                    for regex in self.parent.asset_whitelist:
                        if re.search(regex, asset):
                            should_continue = False
                            break
                    if should_continue:
                        continue

                if asset not in assets:
                    meta = self.wallet.get_asset_meta(asset)
                    assets[asset] = [balance.value, meta]
                else:
                    assets[asset][0] += balance.value

        for asset, data in assets.items():

            balance = data[0]
            meta = data[1]  # type: AssetMeta

            balance_text = self.parent.format_amount(balance, whitespaces=True)

            if self.config.get('advanced_asset_functions', False):
                if meta and meta.ipfs_str:
                    s = meta.ipfs_str
                    h, a = get_alternate_data(base_decode(s, base=58))
                    ipfs_str = '\nBASE58: {}\nHEX: {}\nLATIN-1: {}\n'.format(
                        s, h, a)
                else:
                    ipfs_str = '\nBASE58: None\nHEX: None\nLATIN-1: None\n'
            else:
                ipfs_str = str(meta.ipfs_str) if meta else ''  # May be none

            is_reis = str(meta.is_reissuable) if meta else ''
            divs = str(meta.divisions) if meta else ''
            ownr = str(meta.is_owner) if meta else ''

            # create item
            labels = [asset, balance_text, ipfs_str, is_reis, divs, ownr]
            asset_item = [QStandardItem(e) for e in labels]
            # align text and set fonts
            for i, item in enumerate(asset_item):
                item.setTextAlignment(Qt.AlignVCenter)
                if i not in (self.Columns.NAME, self.Columns.IPFS):
                    item.setFont(QFont(MONOSPACE_FONT))
            self.set_editability(asset_item)

            # add item
            count = self.std_model.rowCount()
            self.std_model.insertRow(count, asset_item)

        self.asset_meta = assets
        self.set_current_idx(set_asset)
        self.filter()
        self.proxy.setDynamicSortFilter(True)

    def add_copy_menu(self, menu, idx):
        cc = menu.addMenu(_("Copy"))
        for column in self.Columns:
            if self.isColumnHidden(column):
                continue
            column_title = self.model().headerData(column, Qt.Horizontal)
            hm_idx = self.model().mapToSource(self.model().index(
                idx.row(), column))
            column_data = self.std_model.data(hm_idx)
            cc.addAction(column_title,
                         lambda text=column_data, title=column_title: self.
                         place_text_on_clipboard(text, title=title))
        return cc

    def create_menu(self, position):
        org_idx: QModelIndex = self.indexAt(position)

        hm_idx = self.model().mapToSource(self.model().index(org_idx.row(), 0))
        if not hm_idx.isValid():
            return
        asset = self.std_model.data(hm_idx)

        hm_idx = self.model().mapToSource(self.model().index(org_idx.row(), 2))
        if not hm_idx.isValid():
            return
        ipfs = self.std_model.data(hm_idx)

        menu = QMenu()
        self.add_copy_menu(menu, org_idx)

        def send_asset(asset):
            self.parent.show_send_tab()
            self.parent.to_send_combo.setCurrentIndex(
                self.parent.send_options.index(asset))

        menu.addAction(_('Send {}').format(asset), lambda: send_asset(asset))
        if ipfs[:2] == 'Qm':
            url = ipfs_explorer_URL(self.parent.config, 'ipfs', ipfs)
            menu.addAction(_('View IPFS'), lambda: self.webopen_safe(url))
        menu.addAction(_('View History'),
                       lambda: self.parent.show_asset(asset))
        menu.addAction(_('Mark as spam'),
                       lambda: self.parent.hide_asset(asset))

        menu.exec_(self.viewport().mapToGlobal(position))

    def place_text_on_clipboard(self, text: str, *, title: str = None) -> None:
        if is_address(text):
            try:
                self.wallet.check_address_for_corruption(text)
            except InternalAddressCorruption as e:
                self.parent.show_error(str(e))
                raise
        super().place_text_on_clipboard(text, title=title)

    def get_edit_key_from_coordinate(self, row, col):
        return None

    # We don't edit anything here
    def on_edited(self, idx, edit_key, *, text):
        pass
Exemplo n.º 13
0
class comic_export_setting_dialog(QDialog):
    acbfStylesList = [
        "speech", "commentary", "formal", "letter", "code", "heading", "audio",
        "thought", "sign", "sound", "emphasis", "strong"
    ]

    def __init__(self):
        super().__init__()
        self.setLayout(QVBoxLayout())
        self.setWindowTitle(i18n("Export settings"))
        buttons = QDialogButtonBox(QDialogButtonBox.Ok
                                   | QDialogButtonBox.Cancel)

        buttons.accepted.connect(self.accept)
        buttons.rejected.connect(self.reject)
        mainWidget = QTabWidget()
        self.layout().addWidget(mainWidget)
        self.layout().addWidget(buttons)

        # Set basic crop settings
        # Set which layers to remove before export.
        mainExportSettings = QWidget()
        mainExportSettings.setLayout(QVBoxLayout())
        groupExportCrop = QGroupBox(i18n("Crop settings"))
        formCrop = QFormLayout()
        groupExportCrop.setLayout(formCrop)
        self.chk_toOutmostGuides = QCheckBox(i18n("Crop to outmost guides"))
        self.chk_toOutmostGuides.setChecked(True)
        self.chk_toOutmostGuides.setToolTip(
            i18n(
                "This will crop to the outmost guides if possible and otherwise use the underlying crop settings."
            ))
        formCrop.addRow("", self.chk_toOutmostGuides)
        btn_fromSelection = QPushButton(
            i18n("Set margins from active selection"))
        btn_fromSelection.clicked.connect(self.slot_set_margin_from_selection)
        # This doesn't work.
        formCrop.addRow("", btn_fromSelection)
        self.spn_marginLeft = QSpinBox()
        self.spn_marginLeft.setMaximum(99999)
        self.spn_marginLeft.setSuffix(" px")
        formCrop.addRow(i18n("Left:"), self.spn_marginLeft)
        self.spn_marginTop = QSpinBox()
        self.spn_marginTop.setMaximum(99999)
        self.spn_marginTop.setSuffix(" px")
        formCrop.addRow(i18n("Top:"), self.spn_marginTop)
        self.spn_marginRight = QSpinBox()
        self.spn_marginRight.setMaximum(99999)
        self.spn_marginRight.setSuffix(" px")
        formCrop.addRow(i18n("Right:"), self.spn_marginRight)
        self.spn_marginBottom = QSpinBox()
        self.spn_marginBottom.setMaximum(99999)
        self.spn_marginBottom.setSuffix(" px")
        formCrop.addRow(i18n("Bottom:"), self.spn_marginBottom)
        groupExportLayers = QGroupBox(i18n("Layers"))
        formLayers = QFormLayout()
        groupExportLayers.setLayout(formLayers)
        self.cmbLabelsRemove = labelSelector()
        formLayers.addRow(i18n("Label for removal:"), self.cmbLabelsRemove)
        self.ln_text_layer_name = QLineEdit()
        self.ln_text_layer_name.setToolTip(
            i18n(
                "These are keywords that can be used to identify text layers. A layer only needs to contain the keyword to be recognised. Keywords should be comma seperated."
            ))
        self.ln_panel_layer_name = QLineEdit()
        self.ln_panel_layer_name.setToolTip(
            i18n(
                "These are keywords that can be used to identify panel layers. A layer only needs to contain the keyword to be recognised. Keywords should be comma seperated."
            ))
        formLayers.addRow(i18n("Text Layer Key:"), self.ln_text_layer_name)
        formLayers.addRow(i18n("Panel Layer Key:"), self.ln_panel_layer_name)

        mainExportSettings.layout().addWidget(groupExportCrop)
        mainExportSettings.layout().addWidget(groupExportLayers)
        mainWidget.addTab(mainExportSettings, i18n("General"))

        # CBZ, crop, resize, which metadata to add.
        CBZexportSettings = QWidget()
        CBZexportSettings.setLayout(QVBoxLayout())
        self.CBZactive = QCheckBox(i18n("Export to CBZ"))
        CBZexportSettings.layout().addWidget(self.CBZactive)
        self.CBZgroupResize = comic_export_resize_widget("CBZ")
        CBZexportSettings.layout().addWidget(self.CBZgroupResize)
        self.CBZactive.clicked.connect(self.CBZgroupResize.setEnabled)
        CBZgroupMeta = QGroupBox(i18n("Metadata to add"))
        # CBZexportSettings.layout().addWidget(CBZgroupMeta)
        CBZgroupMeta.setLayout(QFormLayout())

        mainWidget.addTab(CBZexportSettings, "CBZ")

        # ACBF, crop, resize, creator name, version history, panel layer, text layers.
        ACBFExportSettings = QWidget()
        ACBFform = QFormLayout()
        ACBFExportSettings.setLayout(QVBoxLayout())
        ACBFdocInfo = QGroupBox()
        ACBFdocInfo.setTitle(i18n("ACBF Document Info"))
        ACBFdocInfo.setLayout(ACBFform)
        self.lnACBFSource = QLineEdit()
        self.lnACBFSource.setToolTip(
            i18n(
                "Whether the acbf file is an adaption of an existing source, and if so, how to find information about that source. So for example, for an adapted webcomic, the official website url should go here."
            ))
        self.lnACBFID = QLabel()
        self.lnACBFID.setToolTip(
            i18n(
                "By default this will be filled with a generated universal unique identifier. The ID by itself is merely so that comic book library management programs can figure out if this particular comic is already in their database and whether it has been rated. Of course, the UUID can be changed into something else by manually changing the json, but this is advanced usage."
            ))
        self.spnACBFVersion = QSpinBox()
        self.ACBFhistoryModel = QStandardItemModel()
        acbfHistoryList = QListView()
        acbfHistoryList.setModel(self.ACBFhistoryModel)
        btn_add_history = QPushButton(i18n("Add history entry"))
        btn_add_history.clicked.connect(self.slot_add_history_item)
        self.chkIncludeTranslatorComments = QCheckBox()
        self.chkIncludeTranslatorComments.setText(
            i18n("Include Translator's Comments"))
        self.chkIncludeTranslatorComments.setToolTip(
            i18n(
                "A PO file can contain translator's comments. If this is checked, the translations comments will be added as references into the ACBF file."
            ))
        self.lnTranslatorHeader = QLineEdit()

        ACBFform.addRow(i18n("Source:"), self.lnACBFSource)
        ACBFform.addRow(i18n("ACBF UID:"), self.lnACBFID)
        ACBFform.addRow(i18n("Version:"), self.spnACBFVersion)
        ACBFform.addRow(i18n("Version History:"), acbfHistoryList)
        ACBFform.addRow("", btn_add_history)
        ACBFform.addRow("", self.chkIncludeTranslatorComments)
        ACBFform.addRow(i18n("Translator Header:"), self.lnTranslatorHeader)

        ACBFAuthorInfo = QWidget()
        acbfAVbox = QVBoxLayout(ACBFAuthorInfo)
        infoLabel = QLabel(
            i18n(
                "The people responsible for the generation of the CBZ/ACBF files."
            ))
        infoLabel.setWordWrap(True)
        ACBFAuthorInfo.layout().addWidget(infoLabel)
        self.ACBFauthorModel = QStandardItemModel(0, 6)
        labels = [
            i18n("Nick Name"),
            i18n("Given Name"),
            i18n("Middle Name"),
            i18n("Family Name"),
            i18n("Email"),
            i18n("Homepage")
        ]
        self.ACBFauthorModel.setHorizontalHeaderLabels(labels)
        self.ACBFauthorTable = QTableView()
        acbfAVbox.addWidget(self.ACBFauthorTable)
        self.ACBFauthorTable.setModel(self.ACBFauthorModel)
        self.ACBFauthorTable.verticalHeader().setDragEnabled(True)
        self.ACBFauthorTable.verticalHeader().setDropIndicatorShown(True)
        self.ACBFauthorTable.verticalHeader().setSectionsMovable(True)
        self.ACBFauthorTable.verticalHeader().sectionMoved.connect(
            self.slot_reset_author_row_visual)
        AuthorButtons = QHBoxLayout()
        btn_add_author = QPushButton(i18n("Add author"))
        btn_add_author.clicked.connect(self.slot_add_author)
        AuthorButtons.addWidget(btn_add_author)
        btn_remove_author = QPushButton(i18n("Remove author"))
        btn_remove_author.clicked.connect(self.slot_remove_author)
        AuthorButtons.addWidget(btn_remove_author)
        acbfAVbox.addLayout(AuthorButtons)

        ACBFStyle = QWidget()
        ACBFStyle.setLayout(QHBoxLayout())
        self.ACBFStylesModel = QStandardItemModel()
        self.ACBFStyleClass = QListView()
        self.ACBFStyleClass.setModel(self.ACBFStylesModel)
        ACBFStyle.layout().addWidget(self.ACBFStyleClass)
        ACBFStyleEdit = QWidget()
        ACBFStyleEditVB = QVBoxLayout(ACBFStyleEdit)
        self.ACBFfontCombo = QFontComboBox()
        self.ACBFdefaultFont = QComboBox()
        self.ACBFdefaultFont.addItems(
            ["sans-serif", "serif", "monospace", "cursive", "fantasy"])
        self.ACBFBold = QCheckBox(i18n("Bold"))
        self.ACBFItal = QCheckBox(i18n("Italic"))
        self.ACBFStyleClass.clicked.connect(self.slot_set_style)
        self.ACBFStyleClass.selectionModel().selectionChanged.connect(
            self.slot_set_style)
        self.ACBFStylesModel.itemChanged.connect(self.slot_set_style)
        self.ACBFfontCombo.currentFontChanged.connect(
            self.slot_font_current_style)
        self.ACBFfontCombo.setEditable(False)
        self.ACBFBold.toggled.connect(self.slot_font_current_style)
        self.ACBFItal.toggled.connect(self.slot_font_current_style)
        colorWidget = QGroupBox(self)
        colorWidget.setTitle(i18n("Text Colors"))
        colorWidget.setLayout(QVBoxLayout())
        self.regularColor = QColorDialog()
        self.invertedColor = QColorDialog()
        self.btn_acbfRegColor = QPushButton(i18n("Regular Text"), self)
        self.btn_acbfRegColor.clicked.connect(self.slot_change_regular_color)
        self.btn_acbfInvColor = QPushButton(i18n("Inverted Text"), self)
        self.btn_acbfInvColor.clicked.connect(self.slot_change_inverted_color)
        colorWidget.layout().addWidget(self.btn_acbfRegColor)
        colorWidget.layout().addWidget(self.btn_acbfInvColor)
        ACBFStyleEditVB.addWidget(colorWidget)
        ACBFStyleEditVB.addWidget(self.ACBFfontCombo)
        ACBFStyleEditVB.addWidget(self.ACBFdefaultFont)
        ACBFStyleEditVB.addWidget(self.ACBFBold)
        ACBFStyleEditVB.addWidget(self.ACBFItal)
        ACBFStyleEditVB.addStretch()
        ACBFStyle.layout().addWidget(ACBFStyleEdit)

        ACBFTabwidget = QTabWidget()
        ACBFTabwidget.addTab(ACBFdocInfo, i18n("Document Info"))
        ACBFTabwidget.addTab(ACBFAuthorInfo, i18n("Author Info"))
        ACBFTabwidget.addTab(ACBFStyle, i18n("Style Sheet"))
        ACBFExportSettings.layout().addWidget(ACBFTabwidget)
        mainWidget.addTab(ACBFExportSettings, i18n("ACBF"))

        # Epub export, crop, resize, other questions.
        EPUBexportSettings = QWidget()
        EPUBexportSettings.setLayout(QVBoxLayout())
        self.EPUBactive = QCheckBox(i18n("Export to EPUB"))
        EPUBexportSettings.layout().addWidget(self.EPUBactive)
        self.EPUBgroupResize = comic_export_resize_widget("EPUB")
        EPUBexportSettings.layout().addWidget(self.EPUBgroupResize)
        self.EPUBactive.clicked.connect(self.EPUBgroupResize.setEnabled)
        mainWidget.addTab(EPUBexportSettings, "EPUB")

        # For Print. Crop, no resize.
        TIFFExportSettings = QWidget()
        TIFFExportSettings.setLayout(QVBoxLayout())
        self.TIFFactive = QCheckBox(i18n("Export to TIFF"))
        TIFFExportSettings.layout().addWidget(self.TIFFactive)
        self.TIFFgroupResize = comic_export_resize_widget("TIFF")
        TIFFExportSettings.layout().addWidget(self.TIFFgroupResize)
        self.TIFFactive.clicked.connect(self.TIFFgroupResize.setEnabled)
        mainWidget.addTab(TIFFExportSettings, "TIFF")

        # SVG, crop, resize, embed vs link.
        #SVGExportSettings = QWidget()

        #mainWidget.addTab(SVGExportSettings, "SVG")

    """
    Add a history item to the acbf version history list.
    """

    def slot_add_history_item(self):
        newItem = QStandardItem()
        newItem.setText("v" + str(self.spnACBFVersion.value()) + "-" +
                        i18n("in this version..."))
        self.ACBFhistoryModel.appendRow(newItem)

    """
    Get the margins by treating the active selection in a document as the trim area.
    This allows people to snap selections to a vector or something, and then get the margins.
    """

    def slot_set_margin_from_selection(self):
        doc = Application.activeDocument()
        if doc is not None:
            if doc.selection() is not None:
                self.spn_marginLeft.setValue(doc.selection().x())
                self.spn_marginTop.setValue(doc.selection().y())
                self.spn_marginRight.setValue(doc.width() -
                                              (doc.selection().x() +
                                               doc.selection().width()))
                self.spn_marginBottom.setValue(doc.height() -
                                               (doc.selection().y() +
                                                doc.selection().height()))

    """
    Add an author with default values initialised.
    """

    def slot_add_author(self):
        listItems = []
        listItems.append(QStandardItem(i18n("Anon")))  # Nick name
        listItems.append(QStandardItem(i18n("John")))  # First name
        listItems.append(QStandardItem())  # Middle name
        listItems.append(QStandardItem(i18n("Doe")))  # Last name
        listItems.append(QStandardItem())  # email
        listItems.append(QStandardItem())  # homepage
        self.ACBFauthorModel.appendRow(listItems)

    """
    Remove the selected author from the author list.
    """

    def slot_remove_author(self):
        self.ACBFauthorModel.removeRow(
            self.ACBFauthorTable.currentIndex().row())

    """
    Ensure that the drag and drop of authors doesn't mess up the labels.
    """

    def slot_reset_author_row_visual(self):
        headerLabelList = []
        for i in range(self.ACBFauthorTable.verticalHeader().count()):
            headerLabelList.append(str(i))
        for i in range(self.ACBFauthorTable.verticalHeader().count()):
            logicalI = self.ACBFauthorTable.verticalHeader().logicalIndex(i)
            headerLabelList[logicalI] = str(i + 1)
        self.ACBFauthorModel.setVerticalHeaderLabels(headerLabelList)

    def slot_set_style(self):
        index = self.ACBFStyleClass.currentIndex()
        if index.isValid():
            item = self.ACBFStylesModel.item(index.row())
            font = QFont()
            font.setFamily(str(item.data(role=Qt.UserRole + 1)))
            self.ACBFfontCombo.setCurrentFont(font)
            self.ACBFdefaultFont.setCurrentText(
                str(item.data(role=Qt.UserRole + 2)))
            bold = item.data(role=Qt.UserRole + 3)
            if bold is not None:
                self.ACBFBold.setChecked(bold)
            else:
                self.ACBFBold.setChecked(False)
            italic = item.data(role=Qt.UserRole + 4)
            if italic is not None:
                self.ACBFItal.setChecked(italic)
            else:
                self.ACBFItal.setChecked(False)

    def slot_font_current_style(self):
        index = self.ACBFStyleClass.currentIndex()
        if index.isValid():
            item = self.ACBFStylesModel.item(index.row())
            font = QFont(self.ACBFfontCombo.currentFont())
            item.setData(font.family(), role=Qt.UserRole + 1)
            item.setData(self.ACBFdefaultFont.currentText(),
                         role=Qt.UserRole + 2)
            item.setData(self.ACBFBold.isChecked(), role=Qt.UserRole + 3)
            item.setData(self.ACBFItal.isChecked(), role=Qt.UserRole + 4)
            self.ACBFStylesModel.setItem(index.row(), item)

    def slot_change_regular_color(self):
        if (self.regularColor.exec_() == QDialog.Accepted):
            square = QPixmap(32, 32)
            square.fill(self.regularColor.currentColor())
            self.btn_acbfRegColor.setIcon(QIcon(square))

    def slot_change_inverted_color(self):
        if (self.invertedColor.exec_() == QDialog.Accepted):
            square = QPixmap(32, 32)
            square.fill(self.invertedColor.currentColor())
            self.btn_acbfInvColor.setIcon(QIcon(square))

    """
    Load the UI values from the config dictionary given.
    """

    def setConfig(self, config):
        if "cropToGuides" in config.keys():
            self.chk_toOutmostGuides.setChecked(config["cropToGuides"])
        if "cropLeft" in config.keys():
            self.spn_marginLeft.setValue(config["cropLeft"])
        if "cropTop" in config.keys():
            self.spn_marginTop.setValue(config["cropTop"])
        if "cropRight" in config.keys():
            self.spn_marginRight.setValue(config["cropRight"])
        if "cropBottom" in config.keys():
            self.spn_marginBottom.setValue(config["cropBottom"])
        if "labelsToRemove" in config.keys():
            self.cmbLabelsRemove.setLabels(config["labelsToRemove"])
        if "textLayerNames" in config.keys():
            self.ln_text_layer_name.setText(", ".join(
                config["textLayerNames"]))
        else:
            self.ln_text_layer_name.setText("text")
        if "panelLayerNames" in config.keys():
            self.ln_panel_layer_name.setText(", ".join(
                config["panelLayerNames"]))
        else:
            self.ln_panel_layer_name.setText("panels")
        self.CBZgroupResize.set_config(config)
        if "CBZactive" in config.keys():
            self.CBZactive.setChecked(config["CBZactive"])
        self.EPUBgroupResize.set_config(config)
        if "EPUBactive" in config.keys():
            self.EPUBactive.setChecked(config["EPUBactive"])
        self.TIFFgroupResize.set_config(config)
        if "TIFFactive" in config.keys():
            self.TIFFactive.setChecked(config["TIFFactive"])

        if "acbfAuthor" in config.keys():
            if isinstance(config["acbfAuthor"], list):
                for author in config["acbfAuthor"]:
                    listItems = []
                    listItems.append(QStandardItem(author.get("nickname", "")))
                    listItems.append(
                        QStandardItem(author.get("first-name", "")))
                    listItems.append(QStandardItem(author.get("initials", "")))
                    listItems.append(QStandardItem(author.get("last-name",
                                                              "")))
                    listItems.append(QStandardItem(author.get("email", "")))
                    listItems.append(QStandardItem(author.get("homepage", "")))
                    self.ACBFauthorModel.appendRow(listItems)
                pass
            else:
                listItems = []
                listItems.append(QStandardItem(
                    config["acbfAuthor"]))  # Nick name
                for i in range(0, 5):
                    listItems.append(QStandardItem())  # First name
                self.ACBFauthorModel.appendRow(listItems)

        if "acbfSource" in config.keys():
            self.lnACBFSource.setText(config["acbfSource"])
        if "acbfID" in config.keys():
            self.lnACBFID.setText(config["acbfID"])
        else:
            self.lnACBFID.setText(QUuid.createUuid().toString())
        if "acbfVersion" in config.keys():
            self.spnACBFVersion.setValue(config["acbfVersion"])
        if "acbfHistory" in config.keys():
            for h in config["acbfHistory"]:
                item = QStandardItem()
                item.setText(h)
                self.ACBFhistoryModel.appendRow(item)
        if "acbfStyles" in config.keys():
            styleDict = config.get("acbfStyles", {})
            for key in self.acbfStylesList:
                keyDict = styleDict.get(key, {})
                style = QStandardItem(key.title())
                style.setCheckable(True)
                if key in styleDict.keys():
                    style.setCheckState(Qt.Checked)
                else:
                    style.setCheckState(Qt.Unchecked)
                style.setData(keyDict.get("font",
                                          QFont().family()),
                              role=Qt.UserRole + 1)
                style.setData(keyDict.get("genericfont", "sans-serif"),
                              role=Qt.UserRole + 2)
                style.setData(keyDict.get("bold", False), role=Qt.UserRole + 3)
                style.setData(keyDict.get("ital", False), role=Qt.UserRole + 4)
                self.ACBFStylesModel.appendRow(style)
            keyDict = styleDict.get("general", {})
            self.regularColor.setCurrentColor(
                QColor(keyDict.get("color", "#000000")))
            square = QPixmap(32, 32)
            square.fill(self.regularColor.currentColor())
            self.btn_acbfRegColor.setIcon(QIcon(square))
            keyDict = styleDict.get("inverted", {})
            self.invertedColor.setCurrentColor(
                QColor(keyDict.get("color", "#FFFFFF")))
            square.fill(self.invertedColor.currentColor())
            self.btn_acbfInvColor.setIcon(QIcon(square))
        else:
            for key in self.acbfStylesList:
                style = QStandardItem(key.title())
                style.setCheckable(True)
                style.setCheckState(Qt.Unchecked)
                style.setData(QFont().family(), role=Qt.UserRole + 1)
                style.setData("sans-serif", role=Qt.UserRole + 2)
                style.setData(False, role=Qt.UserRole + 3)  #Bold
                style.setData(False, role=Qt.UserRole + 4)  #Italic
                self.ACBFStylesModel.appendRow(style)
        self.CBZgroupResize.setEnabled(self.CBZactive.isChecked())
        self.lnTranslatorHeader.setText(
            config.get("translatorHeader", "Translator's Notes"))
        self.chkIncludeTranslatorComments.setChecked(
            config.get("includeTranslComment", False))

    """
    Store the GUI values into the config dictionary given.
    
    @return the config diactionary filled with new values.
    """

    def getConfig(self, config):

        config["cropToGuides"] = self.chk_toOutmostGuides.isChecked()
        config["cropLeft"] = self.spn_marginLeft.value()
        config["cropTop"] = self.spn_marginTop.value()
        config["cropBottom"] = self.spn_marginRight.value()
        config["cropRight"] = self.spn_marginBottom.value()
        config["labelsToRemove"] = self.cmbLabelsRemove.getLabels()
        config["CBZactive"] = self.CBZactive.isChecked()
        config = self.CBZgroupResize.get_config(config)
        config["EPUBactive"] = self.EPUBactive.isChecked()
        config = self.EPUBgroupResize.get_config(config)
        config["TIFFactive"] = self.TIFFactive.isChecked()
        config = self.TIFFgroupResize.get_config(config)
        authorList = []
        for row in range(self.ACBFauthorTable.verticalHeader().count()):
            logicalIndex = self.ACBFauthorTable.verticalHeader().logicalIndex(
                row)
            listEntries = [
                "nickname", "first-name", "initials", "last-name", "email",
                "homepage"
            ]
            author = {}
            for i in range(len(listEntries)):
                entry = self.ACBFauthorModel.data(
                    self.ACBFauthorModel.index(logicalIndex, i))
                if entry is None:
                    entry = " "
                if entry.isspace() is False and len(entry) > 0:
                    author[listEntries[i]] = entry
                elif listEntries[i] in author.keys():
                    author.pop(listEntries[i])
            authorList.append(author)
        config["acbfAuthor"] = authorList
        config["acbfSource"] = self.lnACBFSource.text()
        config["acbfID"] = self.lnACBFID.text()
        config["acbfVersion"] = self.spnACBFVersion.value()
        versionList = []
        for r in range(self.ACBFhistoryModel.rowCount()):
            index = self.ACBFhistoryModel.index(r, 0)
            versionList.append(
                self.ACBFhistoryModel.data(index, Qt.DisplayRole))
        config["acbfHistory"] = versionList

        acbfStylesDict = {}
        for row in range(0, self.ACBFStylesModel.rowCount()):
            entry = self.ACBFStylesModel.item(row)
            if entry.checkState() == Qt.Checked:
                key = entry.text().lower()
                style = {}
                font = entry.data(role=Qt.UserRole + 1)
                if font is not None:
                    style["font"] = font
                genericfont = entry.data(role=Qt.UserRole + 2)
                if font is not None:
                    style["genericfont"] = genericfont
                bold = entry.data(role=Qt.UserRole + 3)
                if bold is not None:
                    style["bold"] = bold
                italic = entry.data(role=Qt.UserRole + 4)
                if italic is not None:
                    style["ital"] = italic
                acbfStylesDict[key] = style
        acbfStylesDict["general"] = {
            "color": self.regularColor.currentColor().name()
        }
        acbfStylesDict["inverted"] = {
            "color": self.invertedColor.currentColor().name()
        }
        config["acbfStyles"] = acbfStylesDict
        config["translatorHeader"] = self.lnTranslatorHeader.text()
        config[
            "includeTranslComment"] = self.chkIncludeTranslatorComments.isChecked(
            )

        # Turn this into something that retreives from a line-edit when string freeze is over.
        config["textLayerNames"] = self.ln_text_layer_name.text().split(",")
        config["panelLayerNames"] = self.ln_panel_layer_name.text().split(",")
        return config
Exemplo n.º 14
0
class UserForm(QtWidgets.QMainWindow, Ui_UserForm):
    def __init__(self, parent):
        # Это здесь нужно для доступа к переменным, методам
        # и т.д. в файле design.py
        super().__init__()
        self.parent_form = parent
        self.setupUi(self)
        self.user_model = None
        self.model = QStandardItemModel()
        self.tvMetadata.setModel(self.model)
        self.model.setHorizontalHeaderLabels(["Name", "Value", "Type", "Row Type"])
        self.tvMetadata.setColumnWidth(0, 200)
        self.tvMetadata.setColumnWidth(1, 290)
        self.tvMetadata.setColumnWidth(2, 290)
        self.tvMetadata.setColumnWidth(3, 100)  # this column should be read-only
        self.btnAddMetodata.clicked.connect(self.add_metadata)
        self.btnRemoveMetodata.clicked.connect(self.remove_metadata)
        self.btnSave.clicked.connect(self.save)
        self.setFixedSize(self.width(), self.height() - 20)

    def apply_model(self):
        model = self.user_model
        self.txbUsername.setText(model.username)
        self.txbComment.setText(model.comment)
        self.txbProviderUserKey.setText(model.provider_user_key)
        self.txbEmail.setText(model.email)
        self.txbIsAdmin.setText(model.is_admin)
        self.txbPassword.setText(model.password)
        self.txbName.setText(model.name)
        self.txbApplicationName.setText(model.application_name)
        self.txbUserState.setText(model.user_state)
        self.txbProviderName.setText(model.provider_name)

        # Clear
        while self.model.rowCount() > 0:
            self.model.removeRow(0)

        for item in model.metadata:
            self.model.appendRow(
                [
                    QStandardItem(item['name']),
                    QStandardItem(item['value']),
                    QStandardItem(item['type']),
                    QStandardItem('MetaData')
                ])

        for item in model.content_data:
            self.model.appendRow(
                [
                    QStandardItem(item['name']),
                    QStandardItem(item['value']),
                    QStandardItem(item['type']),
                    QStandardItem('ContentData')
                ])

    def remove_metadata(self):
        indexes = self.tvMetadata.selectedIndexes()
        for index in indexes:
            self.model.removeRow(index.row())

    def add_metadata(self):
        try:
            dialog = MetadataWidget()
            result = dialog.exec_()
            if result == 1:
                data = dialog.return_data()
                # TODO: Make 'row_type' read only
                self.model.appendRow(
                    [
                        QStandardItem(data['name']),
                        QStandardItem(data['value']),
                        QStandardItem(data['type']),
                        QStandardItem(data['row_type'])
                    ])
        except BaseException as err:
            QMessageBox.about(self, 'PyQt5 message', 'An exception occurred: {}'.format(err))

    def save(self):
        try:
            metadata = []
            content_data = []
            row_count = self.model.rowCount()
            for row in range(row_count):
                name = self.model.data(self.model.index(row, 0))
                value = self.model.data(self.model.index(row, 1))
                value_type = self.model.data(self.model.index(row, 2))
                row_type = self.model.data(self.model.index(row, 3))
                if row_type == "ContentData":
                    content_data.append({'name': name, 'value': value, 'type': value_type})
                if row_type == "MetaData":
                    metadata.append({'name': name, 'value': value, 'type': value_type})

            self.user_model = UserModel(
                username=self.txbUsername.text(),
                comment=self.txbComment.text(),
                provider_user_key=self.txbProviderUserKey.text(),
                email=self.txbEmail.text(),
                is_admin=self.txbIsAdmin.text(),
                password=self.txbPassword.text(),
                name=self.txbName.text(),
                application_name=self.txbApplicationName.text(),
                user_state=self.txbUserState.text(),
                provider_name=self.txbProviderName.text(),
                metadata=metadata,
                content_data=content_data
            )
            self.parent_form.set_user(self.user_model)

        except ValidationError as e:
            QMessageBox.about(self, 'Error message', '{}'.format(e))

        except BaseException as err:
            QMessageBox.about(self, 'Error message', 'An exception occurred: {}'.format(err))

        self.close()
Exemplo n.º 15
0
class IsosViewer(QMainWindow, mageiaSyncUI.Ui_mainWindow):
    #   Display the main window
    def __init__(self, parent=None):
        super(IsosViewer, self).__init__(parent)
        self.setupUi(self)
        self.connectActions()
        self.IprogressBar.setMinimum(0)
        self.IprogressBar.setMaximum(100)
        self.IprogressBar.setValue(0)
        self.IprogressBar.setEnabled(False)
        self.selectAllState=True
        self.stop.setEnabled(False)
        self.destination=''
        self.rsyncThread = mageiaSyncExt.syncThread(self)    # create a thread to launch rsync
        self.rsyncThread.progressSignal.connect(self.setProgress)
        self.rsyncThread.speedSignal.connect(self.setSpeed)
        self.rsyncThread.sizeSignal.connect(self.setSize)
        self.rsyncThread.remainSignal.connect(self.setRemain)
        self.rsyncThread.endSignal.connect(self.syncEnd)
        self.rsyncThread.lvM.connect(self.lvMessage)
        self.rsyncThread.checkSignal.connect(self.checks)
        self.checkThreads=[]    #   A list of thread for each iso

 #      Model for list view in a table
        self.model = QStandardItemModel(0, 6, self)
        headers=[self.tr("Directory"),self.tr("Name"),self.tr("Size"),self.tr("Date"),"SHA1","MD5"]
        i=0
        for label in headers:
            self.model.setHeaderData(i, QtCore.Qt.Horizontal,label )
            i+=1

#       settings for the list view
        self.localList.setModel(self.model)
        self.localList.setColumnWidth(0,220)
        self.localList.setColumnWidth(1,220)
        self.localList.horizontalHeader().setStretchLastSection(True)
        # settings for local iso names management
        self.localListNames=[]

    def multiSelect(self):
    #   allows to select multiple lines in remote ISOs list
        self.listIsos.setSelectionMode(2)

    def add(self, iso):
    #   Add an remote ISO in list
         self.listIsos.addItem(iso)

    def localAdd(self, path,iso,isoSize):
        #   Add an entry in local ISOs list, with indications about checking
        itemPath=QStandardItem(path)
        itemIso=QStandardItem(iso)
        if isoSize==0:
            itemSize=QStandardItem('--')
        else:
            formatedSize='{:n}'.format(isoSize).replace(","," ")
            itemSize=QStandardItem(formatedSize)
        itemSize.setTextAlignment(QtCore.Qt.AlignVCenter|QtCore.Qt.AlignHCenter)
        itemDate=QStandardItem("--/--/--")
        itemDate.setTextAlignment(QtCore.Qt.AlignVCenter|QtCore.Qt.AlignHCenter)
        itemCheck1=QStandardItem("--")
        itemCheck1.setTextAlignment(QtCore.Qt.AlignVCenter|QtCore.Qt.AlignHCenter)
        itemCheck5=QStandardItem("--")
        itemCheck5.setTextAlignment(QtCore.Qt.AlignVCenter|QtCore.Qt.AlignHCenter)
        self.model.appendRow([itemPath,itemIso,itemSize,itemDate, itemCheck1, itemCheck5,])
        self.localListNames.append([path,iso])

    def setProgress(self, value):
        #   Update the progress bar
        self.IprogressBar.setValue(value)

    def setSpeed(self, value):
        #   Update the speed field
        self.speedLCD.display(value)

    def setSize(self, size):
        #   Update the size field
        self.Lsize.setText(size+self.tr(" bytes"))

    def setRemain(self,remainTime):
        content=QtCore.QTime.fromString(remainTime,"h:mm:ss")
        self.timeRemaining.setTime(content)

    def manualChecks(self):
        for iso in self.listIsos.selectedItems():
            path,name=iso.text().split('/')
            try:
                #   Look for ISO in local list
                item=self.model.findItems(name,QtCore.Qt.MatchExactly,1)[0]
            except:
                #   Remote ISO is not yet in local directory. We add it in localList and create the directory
                self.localAdd(path,name,0)
                basedir=QtCore.QDir(self.destination)
                basedir.mkdir(path)
                item=self.model.findItems(name,QtCore.Qt.MatchExactly,1)[0]
            row=self.model.indexFromItem(item).row()
            self.checks(row)

    def checks(self,isoIndex):
        #   processes a checking for each iso
        #   launches a thread for each iso
        newThread=mageiaSyncExt.checkThread(self)
        self.checkThreads.append(newThread)
        self.checkThreads[-1].setup(self.destination,
            self.model.data(self.model.index(isoIndex,0)) ,
            self.model.data(self.model.index(isoIndex,1)),
            isoIndex)
        self.checkThreads[-1].md5Signal.connect(self.md5Check)
        self.checkThreads[-1].sha1Signal.connect(self.sha1Check)
        self.checkThreads[-1].dateSignal.connect(self.dateCheck)
        self.checkThreads[-1].sizeFinalSignal.connect(self.sizeUpdate)
        self.checkThreads[-1].checkStartSignal.connect(self.checkStart)
        self.checkThreads[-1].start()

    def checkStart(self,isoIndex):
        #   the function indicates that checking is in progress
        #   the hundred contains index of the value to check, the minor value contains the row
        col=(int)(isoIndex/100)
        row=isoIndex-col*100
        self.model.setData(self.model.index(row, col, QtCore.QModelIndex()), self.tr("Checking"))

    def md5Check(self,check):
        if check>=128:
            val=self.tr("OK")
            row=check-128
        else:
            val=self.tr("Failed")
            row=check
        self.model.setData(self.model.index(row, 5, QtCore.QModelIndex()), val)

    def sha1Check(self,check):
        if check>=128:
            val=self.tr("OK")
            row=check-128
        else:
            val=self.tr("Failed")
            row=check
        self.model.setData(self.model.index(row, 4, QtCore.QModelIndex()), val)

    def dateCheck(self,check):
        if check>=128:
            val=self.tr("OK")
            row=check-128
        else:
            val=self.tr("Failed")
            row=check
        self.model.setData(self.model.index(row, 3, QtCore.QModelIndex()), val)

    def sizeUpdate(self,signal,isoSize):
        col=(int)(signal/100)
        row=signal-col*100
        self.model.setData(self.model.index(row, col, QtCore.QModelIndex()), isoSize)

    def syncEnd(self, rc):
        if rc==1:
            self.lvMessage(self.tr("Command rsync not found"))
        elif rc==2:
            self.lvMessage(self.tr("Error in rsync parameters"))
        elif rc==3:
            self.lvMessage(self.tr("Unknown error in rsync"))
        self.IprogressBar.setEnabled(False)
        self.syncGo.setEnabled(True)
        self.listIsos.setEnabled(True)
        self.selectAll.setEnabled(True)
        self.stop.setEnabled(False)

    def prefsInit(self):
    #   Load the parameters at first
        params=QtCore.QSettings("Mageia","mageiaSync")
        paramRelease=""
        try:
            paramRelease=params.value("release", type="QString")
            #   the parameters already initialised?
        except:
            pass
        if paramRelease =="":
            # Values are not yet set
            self.pd0=prefsDialog0()
            self.pd0.user.setFocus()
            answer=self.pd0.exec_()
            if answer:
                #   Update params
                self.user=self.pd0.user.text()
                self.password=self.pd0.password.text()
                self.location=self.pd0.location.text()
                params=QtCore.QSettings("Mageia","mageiaSync")
                params.setValue("user",self.user)
                params.setValue("password",self.password)
                params.setValue("location",self.location)
            else:
                pass
#                answer=QDialogButtonBox(QDialogButtonBox.Ok)
                # the user must set values or default values
            self.pd0.close()
            self.pd=prefsDialog()
            if self.password !="":
                code,list=mageiaSyncExt.findRelease('rsync://'+self.user+'@bcd.mageia.org/isos/',self.password)
                if code==0:
                    for item in list:
                        self.pd.release.addItem(item)
            self.pd.password.setText(self.password)
            self.pd.user.setText(self.user)
            self.pd.location.setText(self.location)
            self.pd.selectDest.setText(QtCore.QDir.currentPath())
            self.pd.release.setFocus()
            answer=self.pd.exec_()
            if answer:
                #   Update params
                self.user=self.pd.user.text()
                self.password=self.pd.password.text()
                self.location=self.pd.location.text()
                params=QtCore.QSettings("Mageia","mageiaSync")
                self.release= self.pd.release.currentText()
                self.destination=self.pd.selectDest.text()
                self.bwl=self.pd.bwl.value()
                params.setValue("release", self.release)
                params.setValue("user",self.user)
                params.setValue("password",self.password)
                params.setValue("location",self.location)
                params.setValue("destination",self.destination)
                params.setValue("bwl",str(self.bwl))
            else:
                pass
#                answer=QDialogButtonBox(QDialogButtonBox.Ok)
                print(self.tr("the user must set values or default values"))
            self.pd.close()
        else:
            self.release=params.value("release", type="QString")
            self.user=params.value("user", type="QString")
            self.location=params.value("location", type="QString")
            self.password=params.value("password", type="QString")
            self.destination=params.value("destination", type="QString")
            self.bwl=params.value("bwl",type=int)
        self.localDirLabel.setText(self.tr("Local directory: ")+self.destination)
        if self.location !="":
            self.remoteDirLabel.setText(self.tr("Remote directory: ")+self.location)

    def selectDestination(self):
        #   dialog box to select the destination (local directory)
        directory = QFileDialog.getExistingDirectory(self, self.tr('Select a directory'),'~/')
        isosSync.destination = directory
        self.pd.selectDest.setText(isosSync.destination)


    def selectAllIsos(self):
        #   Select or unselect the ISOs in remote list
        if self.selectAllState :
            for i in range(self.listIsos.count()):
                self.listIsos.item(i).setSelected(True)
            self.selectAll.setText(self.tr("Unselect &All"))
        else:
            for i in range(self.listIsos.count()):
                self.listIsos.item(i).setSelected(False)
            self.selectAll.setText(self.tr("Select &All"))
        self.selectAllState=not self.selectAllState

    def connectActions(self):
        self.actionQuit.triggered.connect(app.quit)
        self.quit.clicked.connect(app.quit)
        self.actionRename.triggered.connect(self.rename)
        self.actionUpdate.triggered.connect(self.updateList)
        self.actionCheck.triggered.connect(self.manualChecks)
        self.actionPreferences.triggered.connect(self.prefs)
        self.syncGo.clicked.connect(self.launchSync)
        self.selectAll.clicked.connect(self.selectAllIsos)

    def updateList(self):
        # From the menu entry
        self.lw = LogWindow()
        self.lw.show()
        self.listIsos.clear()
        self.model.removeRows(0,self.model.rowCount())
        if self.location  == "" :
            self.nameWithPath='rsync://'+self.user+'@bcd.mageia.org/isos/'+self.release+'/'
#            print self.nameWithPath
        else:
            self.nameWithPath=self.location+'/'
        self.lvMessage(self.tr("Source: ")+self.nameWithPath)
        self.fillList = mageiaSyncExt.findIsos()
        self.fillList.setup(self.nameWithPath, self.password,self.destination)
        self.fillList.endSignal.connect(self.closeFill)
        self.fillList.start()
        # Reset the button
        self.selectAll.setText(self.tr("Select &All"))
        self.selectAllState=True

    def lvMessage( self,message):
        #   Add a line in the logview
        self.lvText.append(message)

    def renameDir(self):
        #   Choose the directory where isos are stored
        directory = QFileDialog.getExistingDirectory(self, self.tr('Select a directory'),self.destination)
        self.rd.chooseDir.setText(directory)

    def rename(self):
        #   rename old isos and directories to a new release
        self.rd=renameDialog()
        loc=[]
        loc=self.location.split('/')
        self.rd.oldRelease.setText(loc[-1])
        self.rd.chooseDir.setText(self.destination)
        answer=self.rd.exec_()
        if answer:
            returnMsg=mageiaSyncExt.rename(self.rd.chooseDir.text(),self.rd.oldRelease.text(),str(self.rd.newRelease.text()))
            self.lvMessage(returnMsg)
        self.rd.close()

    def prefs(self):
        # From the menu entry
        self.pd=prefsDialog()
        self.pd.release.addItem(self.release)
        self.pd.password.setText(self.password)
        self.pd.user.setText(self.user)
        self.pd.location.setText(self.location)
        self.pd.selectDest.setText(self.destination)
        self.pd.bwl.setValue(self.bwl)
        params=QtCore.QSettings("Mageia","mageiaSync")
        answer=self.pd.exec_()
        if answer:
            params.setValue("release", self.pd.release.currentText())
            params.setValue("user",self.pd.user.text())
            params.setValue("password",self.pd.password.text())
            params.setValue("location",self.pd.location.text())
            params.setValue("destination",self.pd.selectDest.text())
            params.setValue("bwl",str(self.pd.bwl.value()))
        self.prefsInit()
        self.pd.close()


    def launchSync(self):
        self.IprogressBar.setEnabled(True)
        self.stop.setEnabled(True)
        self.syncGo.setEnabled(False)
        self.listIsos.setEnabled(False)
        self.selectAll.setEnabled(False)
        # Connect the button Stop
        self.stop.clicked.connect(self.stopSync)
        self.rsyncThread.params(self.password, self.bwl)
        for iso in self.listIsos.selectedItems():
            path,name=iso.text().split('/')
            try:
                #   Look for ISO in local list
                item=self.model.findItems(name,QtCore.Qt.MatchExactly,1)[0]
            except:
                #   Remote ISO is not yet in local directory. We add it in localList and create the directory
                self.localAdd(path,name,0)
                basedir=QtCore.QDir(self.destination)
                basedir.mkdir(path)
                item=self.model.findItems(name,QtCore.Qt.MatchExactly,1)[0]
            row=self.model.indexFromItem(item).row()
            if self.location  == "" :
                self.nameWithPath='rsync://'+self.user+'@bcd.mageia.org/isos/'+self.release+'/'+path
            else:
                self.nameWithPath=self.location+path
            if (not str(path).endswith('/')):
                    self.nameWithPath+='/'
            self.rsyncThread.setup(self.nameWithPath, self.destination+'/'+path+'/',row)
        self.rsyncThread.start()             # start the thread
        # Pour les tests uniquement
            #rsync://[email protected]/isos/$release/
        #self.nameWithPath='rsync://ftp5.gwdg.de/pub/linux/mageia/iso/4.1/Mageia-4.1-LiveCD-GNOME-en-i586-CD/'

    def closeFill(self,code):
        if code==0: #   list returned
            list=self.fillList.getList()
            for iso in list:
                self.add(iso)
        elif code==1:
            self.lvMessage(self.tr("Command rsync not found"))
        elif code==2:
            self.lvMessage(self.tr("Error in rsync parameters"))
        elif code==3:
            self.lvMessage(self.tr("Unknown error in rsync"))
        list=self.fillList.getList()

        list=self.fillList.getLocal()
        for path,iso,isoSize in list:
            self.localAdd(path,iso, isoSize)
        self.fillList.quit()
        self.lw.hide()

    def stopSync(self):
        self.rsyncThread.stop()
        self.IprogressBar.setEnabled(False)
        self.stop.setEnabled(False)
        self.syncGo.setEnabled(True)
        self.listIsos.setEnabled(True)
        self.selectAll.setEnabled(True)


    def main(self):
        self.show()
        #   Load or look for intitial parameters
        self.prefsInit()
        # look for Isos list and add it to the isoSync list. Update preferences
        self.updateList()
        self.multiSelect()

    def close(self):
        self.rsyncThread.stop()
        exit(0)
Exemplo n.º 16
0
class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()

        fileMenu = QMenu("&File", self)
        openAction = fileMenu.addAction("&Open...")
        openAction.setShortcut("Ctrl+O")
        saveAction = fileMenu.addAction("&Save As...")
        saveAction.setShortcut("Ctrl+S")
        quitAction = fileMenu.addAction("E&xit")
        quitAction.setShortcut("Ctrl+Q")

        self.setupModel()
        self.setupViews()

        openAction.triggered.connect(self.openFile)
        saveAction.triggered.connect(self.saveFile)
        quitAction.triggered.connect(QApplication.instance().quit)

        self.menuBar().addMenu(fileMenu)
        self.statusBar()

        self.openFile(':/Charts/qtdata.cht')

        self.setWindowTitle("Chart")
        self.resize(870, 550)

    def setupModel(self):
        self.model = QStandardItemModel(8, 2, self)
        self.model.setHeaderData(0, Qt.Horizontal, "Label")
        self.model.setHeaderData(1, Qt.Horizontal, "Quantity")

    def setupViews(self):
        splitter = QSplitter()
        table = QTableView()
        self.pieChart = PieView()
        splitter.addWidget(table)
        splitter.addWidget(self.pieChart)
        splitter.setStretchFactor(0, 0)
        splitter.setStretchFactor(1, 1)

        table.setModel(self.model)
        self.pieChart.setModel(self.model)

        self.selectionModel = QItemSelectionModel(self.model)
        table.setSelectionModel(self.selectionModel)
        self.pieChart.setSelectionModel(self.selectionModel)

        table.horizontalHeader().setStretchLastSection(True)

        self.setCentralWidget(splitter)

    def openFile(self, path=None):
        if not path:
            path, _ = QFileDialog.getOpenFileName(self, "Choose a data file",
                    '', '*.cht')

        if path:
            f = QFile(path)

            if f.open(QFile.ReadOnly | QFile.Text):
                stream = QTextStream(f)

                self.model.removeRows(0, self.model.rowCount(QModelIndex()),
                        QModelIndex())

                row = 0
                line = stream.readLine()
                while line:
                    self.model.insertRows(row, 1, QModelIndex())

                    pieces = line.split(',')
                    self.model.setData(self.model.index(row, 0, QModelIndex()),
                                pieces[0])
                    self.model.setData(self.model.index(row, 1, QModelIndex()),
                                float(pieces[1]))
                    self.model.setData(self.model.index(row, 0, QModelIndex()),
                                QColor(pieces[2]), Qt.DecorationRole)

                    row += 1
                    line = stream.readLine()

                f.close()
                self.statusBar().showMessage("Loaded %s" % path, 2000)

    def saveFile(self):
        fileName, _ = QFileDialog.getSaveFileName(self, "Save file as", '',
                '*.cht')

        if fileName:
            f = QFile(fileName)

            if f.open(QFile.WriteOnly | QFile.Text):
                for row in range(self.model.rowCount(QModelIndex())):
                    pieces = []

                    pieces.append(self.model.data(self.model.index(row, 0, QModelIndex()),
                            Qt.DisplayRole))
                    pieces.append(str(self.model.data(self.model.index(row, 1, QModelIndex()),
                            Qt.DisplayRole)))
                    pieces.append(self.model.data(self.model.index(row, 0, QModelIndex()),
                            Qt.DecorationRole).name())

                    f.write(QByteArray(','.join(pieces)))
                    f.write('\n')

            f.close()
            self.statusBar().showMessage("Saved %s" % fileName, 2000)
class ProcessingView(QWidget):
    def __init__(self, parent=None):
        super(QWidget, self).__init__(parent)
        self.parent = parent
        self.title = "Log Processing"
        self.logFileManager = LogFileManager()
        self.initUI()

    def initUI(self):
        self.setWindowTitle(self.title)
        self.tableView = QTableView()
        self.model = QStandardItemModel()
        self.model.setHorizontalHeaderLabels([
            'File Name', 'Source', 'Validation', 'Cleansing', 'Ingestion',
            'Selection'
        ])  #set headers
        self.tableView.setModel(self.model)
        self.tableView.horizontalHeader().setSectionResizeMode(
            QHeaderView.Stretch)  #resize columns to fit into the widget

        self.vBoxLayout = QVBoxLayout()
        self.vBoxLayout.addWidget(self.tableView)
        self.setLayout(self.vBoxLayout)

    def addToTable(self, logfile):
        name = QStandardItem(logfile.getLogName())
        source = QStandardItem(logfile.getPathToFile())

        # Wrost implementation of all but this will do, probably should
        # be changed
        validation = QStandardItem()
        if logfile.getValidationStatus() == "null":
            validation.setText("IN-PROGRESS")
            validation.setBackground(QBrush(QColor("yellow")))
        elif not logfile.getValidationStatus():
            validation.setText("FAILED")
            validation.setBackground(QBrush(QColor("red")))
        else:
            validation.setText("PASSED")
            validation.setBackground(QBrush(QColor("green")))

        cleansing = QStandardItem()
        if logfile.getLogCleansingStatus() == "null":
            cleansing.setText("IN-PROGRESS")
            cleansing.setBackground(QBrush(QColor("yellow")))
        elif not logfile.getLogCleansingStatus():
            cleansing.setText("FAILED")
            cleansing.setBackground(QBrush(QColor("red")))
        else:
            cleansing.setText("PASSED")
            cleansing.setBackground(QBrush(QColor("green")))

        ingestion = QStandardItem()
        if logfile.getIngestionStatus() == "null":
            ingestion.setText("IN-PROGRESS")
            ingestion.setBackground(QBrush(QColor("yellow")))
        elif not logfile.getIngestionStatus():
            ingestion.setText("FAILED")
            ingestion.setBackground(QBrush(QColor("red")))
        else:
            ingestion.setText("PASSED")
            ingestion.setBackground(QBrush(QColor("green")))

        self.model.appendRow(
            [name, source, validation, cleansing, ingestion,
             QStandardItem()])
        self.tableView.setModel(self.model)

    def deleteFromTable(self):
        pass

    def updateRowStatus(self, logfile, process):
        red = QStandardItem("FAILED")
        red.setBackground(QBrush(QColor("red")))

        green = QStandardItem("PASSED")
        green.setBackground(QBrush(QColor("green")))

        yellow = QStandardItem("IN-PROGRESS")
        yellow.setBackground(QBrush(QColor("yellow")))

        item = None
        row = -1
        col = -1
        for index in range(self.model.rowCount()):
            qindex = self.model.index(index, 0)
            name = self.model.data(qindex)

            if name == logfile.getLogName():
                row = index
                if process == "validation":
                    col = 2
                    if logfile.getValidationStatus() == "null":
                        item = yellow
                    elif not logfile.getValidationStatus():
                        item = red
                    else:
                        item = green
                elif process == "cleansing":
                    col = 3
                    if logfile.getLogCleansingStatus() == "null":
                        item = yellow
                    elif not logfile.getLogCleansingStatus():
                        item = red
                    else:
                        item = green
                else:
                    col = 4
                    if logfile.getIngestionStatus() == "null":
                        item = yellow
                    elif not logfile.getIngestionStatus():
                        item = red
                    else:
                        item = green

                break

        self.model.setItem(row, col, item)
        self.tableView.setModel(self.model)

    def update(self):
        # self.logFileManager.addLogFile("file.py", "root/", "text")
        self.parent.updateView(1)
Exemplo n.º 18
0
class GuiHandViewer(QSplitter):
    def __init__(self, config, querylist, mainwin):
        QSplitter.__init__(self, mainwin)
        self.config = config
        self.main_window = mainwin
        self.sql = querylist
        self.replayer = None

        self.db = Database.Database(self.config, sql=self.sql)

        filters_display = {
            "Heroes": True,
            "Sites": True,
            "Games": True,
            "Currencies": False,
            "Limits": True,
            "LimitSep": True,
            "LimitType": True,
            "Positions": True,
            "Type": True,
            "Seats": False,
            "SeatSep": False,
            "Dates": True,
            "Cards": True,
            "Groups": False,
            "GroupsAll": False,
            "Button1": True,
            "Button2": False
        }

        self.filters = Filters.Filters(self.db, display=filters_display)
        self.filters.registerButton1Name(_("Load Hands"))
        self.filters.registerButton1Callback(self.loadHands)
        self.filters.registerCardsCallback(self.filter_cards_cb)

        scroll = QScrollArea()
        scroll.setWidget(self.filters)

        self.handsFrame = QFrame()
        self.handsVBox = QVBoxLayout()
        self.handsFrame.setLayout(self.handsVBox)

        self.addWidget(scroll)
        self.addWidget(self.handsFrame)
        self.setStretchFactor(0, 0)
        self.setStretchFactor(1, 1)

        self.deck_instance = Deck.Deck(self.config, height=42, width=30)
        self.cardImages = self.init_card_images()

        # Dict of colnames and their column idx in the model/ListStore
        self.colnum = {
            'Stakes': 0,
            'Pos': 1,
            'Street0': 2,
            'Action0': 3,
            'Street1-4': 4,
            'Action1-4': 5,
            'Won': 6,
            'Bet': 7,
            'Net': 8,
            'Game': 9,
            'HandId': 10,
        }
        self.view = QTableView()
        self.view.setSelectionBehavior(QTableView.SelectRows)
        self.handsVBox.addWidget(self.view)
        self.model = QStandardItemModel(0, len(self.colnum), self.view)
        self.filterModel = QSortFilterProxyModel()
        self.filterModel.setSourceModel(self.model)
        self.filterModel.setSortRole(Qt.UserRole)

        self.view.setModel(self.filterModel)
        self.view.verticalHeader().hide()
        self.model.setHorizontalHeaderLabels([
            'Stakes', 'Pos', 'Street0', 'Action0', 'Street1-4', 'Action1-4',
            'Won', 'Bet', 'Net', 'Game', 'HandId'
        ])

        self.view.doubleClicked.connect(self.row_activated)
        self.view.contextMenuEvent = self.contextMenu
        self.filterModel.rowsInserted.connect(
            lambda index, start, end:
            [self.view.resizeRowToContents(r) for r in xrange(start, end + 1)])
        self.filterModel.filterAcceptsRow = lambda row, sourceParent: self.is_row_in_card_filter(
            row)

        self.view.resizeColumnsToContents()
        self.view.setSortingEnabled(True)

    def init_card_images(self):
        suits = ('s', 'h', 'd', 'c')
        ranks = (14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2)

        card_images = [0] * 53
        for j in range(0, 13):
            for i in range(0, 4):
                loc = Card.cardFromValueSuit(ranks[j], suits[i])
                card_image = self.deck_instance.card(suits[i], ranks[j])
                card_images[loc] = card_image
        back_image = self.deck_instance.back()
        card_images[0] = back_image
        return card_images

    def loadHands(self, checkState):
        hand_ids = self.get_hand_ids_from_date_range(
            self.filters.getDates()[0],
            self.filters.getDates()[1])
        self.reload_hands(hand_ids)

    def get_hand_ids_from_date_range(self, start, end):
        q = self.db.sql.query['handsInRange']
        q = q.replace('<datetest>',
                      "between '" + start + "' and '" + end + "'")
        q = self.filters.replace_placeholders_with_filter_values(q)

        c = self.db.get_cursor()

        c.execute(q)
        return [r[0] for r in c.fetchall()]

    def rankedhand(self, hand, game):
        ranks = {
            '0': 0,
            '2': 2,
            '3': 3,
            '4': 4,
            '5': 5,
            '6': 6,
            '7': 7,
            '8': 8,
            '9': 9,
            'T': 10,
            'J': 11,
            'Q': 12,
            'K': 13,
            'A': 14
        }
        suits = {'x': 0, 's': 1, 'c': 2, 'd': 3, 'h': 4}

        if game == 'holdem':
            card1 = ranks[hand[0]]
            card2 = ranks[hand[3]]
            suit1 = suits[hand[1]]
            suit2 = suits[hand[4]]
            if card1 < card2:
                (card1, card2) = (card2, card1)
                (suit1, suit2) = (suit2, suit1)
            if suit1 == suit2:
                suit1 += 4
            return card1 * 14 * 14 + card2 * 14 + suit1
        else:
            return 0

    def reload_hands(self, handids):
        self.hands = {}
        self.model.removeRows(0, self.model.rowCount())
        if len(handids) == 0:
            return
        progress = QProgressDialog("Loading hands", "Abort", 0, len(handids),
                                   self)
        progress.setValue(0)
        progress.show()
        for idx, handid in enumerate(handids):
            if progress.wasCanceled():
                break
            self.hands[handid] = self.importhand(handid)
            self.addHandRow(handid, self.hands[handid])
            progress.setValue(idx + 1)
            if idx % 10 == 0:
                QCoreApplication.processEvents()
                self.view.resizeColumnsToContents()
        self.view.resizeColumnsToContents()

    def addHandRow(self, handid, hand):
        hero = self.filters.getHeroes()[hand.sitename]
        won = 0
        if hero in hand.collectees.keys():
            won = hand.collectees[hero]
        bet = 0
        if hero in hand.pot.committed.keys():
            bet = hand.pot.committed[hero]
        net = won - bet
        pos = hand.get_player_position(hero)
        gt = hand.gametype['category']
        row = []
        if hand.gametype['base'] == 'hold':
            board = []
            board.extend(hand.board['FLOP'])
            board.extend(hand.board['TURN'])
            board.extend(hand.board['RIVER'])

            pre_actions = hand.get_actions_short(hero, 'PREFLOP')
            post_actions = ''
            if 'F' not in pre_actions:  #if player hasen't folded preflop
                post_actions = hand.get_actions_short_streets(
                    hero, 'FLOP', 'TURN', 'RIVER')

            row = [
                hand.getStakesAsString(), pos,
                hand.join_holecards(hero), pre_actions, ' '.join(board),
                post_actions,
                str(won),
                str(bet),
                str(net), gt,
                str(handid)
            ]
        elif hand.gametype['base'] == 'stud':
            third = " ".join(
                hand.holecards['THIRD'][hero][0]) + " " + " ".join(
                    hand.holecards['THIRD'][hero][1])
            # ugh - fix the stud join_holecards function so we can retrieve sanely
            later_streets = []
            later_streets.extend(hand.holecards['FOURTH'][hero][0])
            later_streets.extend(hand.holecards['FIFTH'][hero][0])
            later_streets.extend(hand.holecards['SIXTH'][hero][0])
            later_streets.extend(hand.holecards['SEVENTH'][hero][0])

            pre_actions = hand.get_actions_short(hero, 'THIRD')
            post_actions = ''
            if 'F' not in pre_actions:
                post_actions = hand.get_actions_short_streets(
                    hero, 'FOURTH', 'FIFTH', 'SIXTH', 'SEVENTH')

            row = [
                hand.getStakesAsString(), pos, third, pre_actions,
                ' '.join(later_streets), post_actions,
                str(won),
                str(bet),
                str(net), gt,
                str(handid)
            ]
        elif hand.gametype['base'] == 'draw':
            row = [
                hand.getStakesAsString(), pos,
                hand.join_holecards(hero, street='DEAL'),
                hand.get_actions_short(hero, 'DEAL'), None, None,
                str(won),
                str(bet),
                str(net), gt,
                str(handid)
            ]

        modelrow = [QStandardItem(r) for r in row]
        for index, item in enumerate(modelrow):
            item.setEditable(False)
            if index in (self.colnum['Street0'], self.colnum['Street1-4']):
                cards = item.data(Qt.DisplayRole)
                item.setData(self.render_cards(cards), Qt.DecorationRole)
                item.setData("", Qt.DisplayRole)
                item.setData(cards, Qt.UserRole + 1)
            if index in (self.colnum['Bet'], self.colnum['Net'],
                         self.colnum['Won']):
                item.setData(float(item.data(Qt.DisplayRole)), Qt.UserRole)
        self.model.appendRow(modelrow)

    def copyHandToClipboard(self, checkState, hand):
        handText = StringIO()
        hand.writeHand(handText)
        QApplication.clipboard().setText(handText.getvalue())

    def contextMenu(self, event):
        index = self.view.currentIndex()
        if index.row() < 0:
            return
        hand = self.hands[int(
            index.sibling(index.row(), self.colnum['HandId']).data())]
        m = QMenu()
        copyAction = m.addAction('Copy to clipboard')
        copyAction.triggered.connect(
            partial(self.copyHandToClipboard, hand=hand))
        m.move(event.globalPos())
        m.exec_()

    def filter_cards_cb(self, card):
        if hasattr(self, 'hands'):
            self.filterModel.invalidateFilter()

    def is_row_in_card_filter(self, rownum):
        """ Returns true if the cards of the given row are in the card filter """
        # Does work but all cards that should NOT be displayed have to be clicked.
        card_filter = self.filters.getCards()
        hcs = self.model.data(self.model.index(rownum, self.colnum['Street0']),
                              Qt.UserRole + 1).split(' ')

        if '0x' in hcs:  #if cards are unknown return True
            return True

        gt = self.model.data(self.model.index(rownum, self.colnum['Game']))

        if gt not in ('holdem', 'omahahi', 'omahahilo'): return True
        # Holdem: Compare the real start cards to the selected filter (ie. AhKh = AKs)
        value1 = Card.card_map[hcs[0][0]]
        value2 = Card.card_map[hcs[1][0]]
        idx = Card.twoStartCards(value1, hcs[0][1], value2, hcs[1][1])
        abbr = Card.twoStartCardString(idx)
        return card_filter[abbr]

    def row_activated(self, index):
        handlist = list(sorted(self.hands.keys()))
        self.replayer = GuiReplayer.GuiReplayer(self.config, self.sql,
                                                self.main_window, handlist)

        self.replayer.play_hand(
            handlist.index(
                int(index.sibling(index.row(), self.colnum['HandId']).data())))

    def importhand(self, handid=1):
        # Fetch hand info
        # We need at least sitename, gametype, handid
        # for the Hand.__init__

        h = Hand.hand_factory(handid, self.config, self.db)

        # Set the hero for this hand using the filter for the sitename of this hand
        h.hero = self.filters.getHeroes()[h.sitename]
        return h

    def render_cards(self, cardstring):
        card_width = 30
        card_height = 42
        if cardstring is None or cardstring == '':
            cardstring = "0x"
        cardstring = cardstring.replace("'", "")
        cardstring = cardstring.replace("[", "")
        cardstring = cardstring.replace("]", "")
        cardstring = cardstring.replace("'", "")
        cardstring = cardstring.replace(",", "")
        cards = [Card.encodeCard(c) for c in cardstring.split(' ')]
        n_cards = len(cards)

        pixbuf = QPixmap(card_width * n_cards, card_height)
        painter = QPainter(pixbuf)
        x = 0  # x coord where the next card starts in pixbuf
        for card in cards:
            painter.drawPixmap(x, 0, self.cardImages[card])
            x += card_width
        return pixbuf
Exemplo n.º 19
0
class KeysTab(GalacteekTab):
    def __init__(self, *args, **kw):
        super().__init__(*args, **kw)

        self.ctx.tabIdent = 'ipfs-keys-manager'

        self.resolveTimeout = 60 * 5
        self.keysW = QWidget()
        self.addToLayout(self.keysW)
        self.ui = ui_keys.Ui_KeysForm()
        self.ui.setupUi(self.keysW)

        self.ui.addKeyButton.clicked.connect(self.onAddKeyClicked)
        self.ui.deleteKeyButton.clicked.connect(
            lambda *args: ensure(self.onDelKeyClicked()))

        self.model = QStandardItemModel(parent=self)

        self.ui.treeKeys = KeysView()
        self.ui.treeKeys.doubleClicked.connect(self.onItemDoubleClicked)
        self.ui.treeKeys.setModel(self.model)

        self.ui.verticalLayout.addWidget(self.ui.treeKeys)

        self.setupModel()
        self.app.task(self.listKeys)

    def setupModel(self):
        self.model.clear()
        self.model.setColumnCount(3)
        self.model.setHorizontalHeaderLabels(
            [iKeyName(), iP2PKey(), iKeyResolve()])
        self.ui.treeKeys.header().setSectionResizeMode(
            0, QHeaderView.ResizeToContents)
        self.ui.treeKeys.header().setSectionResizeMode(
            1, QHeaderView.ResizeToContents)

    async def onDelKeyClicked(self):
        idx = self.ui.treeKeys.currentIndex()
        if not idx.isValid():
            return messageBox('Invalid key')

        idxName = self.model.index(idx.row(), 0, idx.parent())
        keyName = self.model.data(idxName)

        if not keyName:
            return

        reply = await questionBoxAsync(
            'Delete key', 'Delete IPNS key <b>{key}</b> ?'.format(key=keyName))

        if reply is True:
            self.app.task(self.delKey, keyName)

    def onAddKeyClicked(self):
        runDialog(AddKeyDialog, self.app, parent=self)

    @ipfsOp
    async def delKey(self, ipfsop, name):
        if await ipfsop.keysRemove(name):
            modelDelete(self.model, name)
        self.updateKeysList()

    @ipfsOp
    async def listKeys(self, ipfsop):
        keys = await ipfsop.keys()
        for key in keys:
            found = modelSearch(self.model, search=key['Name'])
            if len(found) > 0:
                continue

            nameItem = UneditableItem(key['Name'])
            nameItem.setToolTip(key['Name'])

            resolveItem = KeyResolvedItem('')
            self.model.appendRow([nameItem, KeyItem(key['Id']), resolveItem])

            self.app.task(self.keyResolve, key, resolveItem)

    @ipfsOp
    async def keyResolve(self, ipfsop, key, item):
        if not isinstance(item, KeyResolvedItem):
            return

        now = datetime.now()

        update = False
        if item.resolvedLast is None:
            update = True

        if isinstance(item.resolvedLast, datetime):
            delta = now - item.resolvedLast
            if delta.seconds > self.resolveTimeout:
                update = True

        if update is True:
            resolved = await ipfsop.nameResolve(key['Id'])

            if isinstance(resolved, dict):
                rPath = resolved.get('Path')
                if not rPath:
                    item.setBackground(QBrush(QColor('red')))
                elif item.resolvesTo and rPath != item.resolvesTo:
                    color = QColor('#c1f0c1')
                    item.setBackground(QBrush(color))
                else:
                    item.setBackground(QBrush(Qt.NoBrush))

                if rPath and IPFSPath(rPath).valid:
                    item.resolvesTo = rPath
                    item.setText(rPath)
                    item.setToolTip("{path}\n\nResolved date: {date}".format(
                        path=rPath,
                        date=now.isoformat(sep=' ', timespec='seconds')))
            else:
                item.setText(iUnknown())

            item.resolvedLast = now

        # Ensure another one
        self.app.loop.call_later(self.resolveTimeout, self.app.task,
                                 self.keyResolve, key, item)

    def updateKeysList(self):
        self.app.task(self.listKeys)

    def onItemDoubleClicked(self, index):
        # Browse IPNS key associated with current item on double-click
        keyHash = self.model.data(self.model.index(index.row(), 1))
        self.gWindow.addBrowserTab().browseIpnsKey(keyHash)
Exemplo n.º 20
0
class HistoryMatchesWidget(QTreeView):
    historyItemSelected = pyqtSignal(str)
    collapsed = pyqtSignal()

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

        self.setWindowFlag(Qt.Popup, True)
        self.setWindowFlag(Qt.FramelessWindowHint, True)
        self.setWindowFlag(Qt.WindowStaysOnTopHint, True)
        self.setWindowFlag(Qt.Tool, True)
        self.setAttribute(Qt.WA_ShowWithoutActivating)
        self.setWindowModality(Qt.NonModal)
        # self.setWindowModality(Qt.ApplicationModal)

        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        self.app = QApplication.instance()
        self.setObjectName('historySearchResults')
        self.clicked.connect(self.onItemActivated)

        self.hModel = QStandardItemModel()
        self.fontCategory = QFont('Times', 16, QFont.Bold)
        self.fontItems = QFont('Inter UI', 14)
        self.fontItemsTitle = QFont('Inter UI', 14, italic=True)
        self.setModel(self.hModel)
        self.setHeaderHidden(True)

        self.idxSelCount = 0
        self.selectionModel().currentChanged.connect(self.onIndexChanged)

    @property
    def itemRoot(self):
        return self.hModel.invisibleRootItem()

    def onIndexChanged(self, current, previous):
        item = self.hModel.itemFromIndex(current)

        if isinstance(item, ResultCategoryItem) and self.idxSelCount == 0:
            # Automatically jump to first item in the category
            self.selectionModel().clearSelection()

            idx = self.hModel.index(0, 0, current)
            if idx.isValid():
                self.setCurrentIndex(idx)

        self.idxSelCount += 1

    def onItemActivated(self, idx):
        idxUrl = self.hModel.index(idx.row(), 1, idx.parent())
        data = self.hModel.data(idxUrl)

        if isinstance(data, str) and data:
            self.historyItemSelected.emit(data)

    async def showMatches(self, marks, hMatches):
        self.hModel.clear()
        brush = QBrush(QColor('#508cac'))

        mItem = ResultCategoryItem(iHashmarks())
        mItem.setFont(self.fontCategory)
        mItem.setBackground(brush)
        mItemE = UneditableItem('')
        mItemE.setBackground(brush)

        if len(marks) > 0:
            for match in marks:
                title = match.title[0:64] if match.title else iUnknown()

                url = match.preferredUrl()

                itemT = UneditableItem(title)
                itemT.setFont(self.fontItemsTitle)
                item = UneditableItem(url)
                item.setToolTip(url)
                item.setData(url, Qt.EditRole)
                item.setFont(self.fontItems)

                mItem.appendRow([itemT, item])

            self.hModel.invisibleRootItem().appendRow([mItem, mItemE])

        hItem = ResultCategoryItem('History')
        hItemE = UneditableItem('')
        hItem.setFont(self.fontCategory)
        hItem.setBackground(brush)
        hItemE.setBackground(brush)

        if len(hMatches) > 0:
            for match in hMatches:
                title = match['title'][0:64] if match['title'] else iUnknown()
                itemT = UneditableItem(title)
                itemT.setFont(self.fontItemsTitle)

                item = UneditableItem(match['url'])
                item.setToolTip(match['url'])
                item.setData(match['url'], Qt.EditRole)
                item.setFont(self.fontItems)

                hItem.appendRow([itemT, item])

            self.hModel.invisibleRootItem().appendRow([hItem, hItemE])

        self.expandAll()
        self.resizeColumnToContents(0)

    def hideEvent(self, event):
        self.idxSelCount = 0
        super().hideEvent(event)

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Return:
            # The 'activated' signal does not seem to have the same
            # behavior across platforms so we handle Return manually here
            curIdx = self.currentIndex()
            if curIdx.isValid():
                self.onItemActivated(curIdx)
        elif event.key() == Qt.Key_Escape:
            self.hide()
            self.collapsed.emit()
        else:
            super(HistoryMatchesWidget, self).keyPressEvent(event)
Exemplo n.º 21
0
class MainWindow(QtWidgets.QMainWindow, ui):
    def __init__(self, parent=None):
        QtWidgets.QMainWindow.__init__(self, parent)
        self.setupUi(self)
        self.setWindowTitle("{} - {}".format(__title__, __version__))
        self._settingsFile = os.path.join(ROOT, "data", "settings.ini")
        self._threadPool = []
        self.sitesModel = QStandardItemModel()
        self.sitesModel.setHorizontalHeaderLabels(
            ["URL", "Result", "Code", "Status"])
        self.sitesTableView.setModel(self.sitesModel)
        self.importUrlsAction.triggered.connect(self.importUrls)
        self.exportResultsAction.triggered.connect(self.exportResults)
        self.quitAction.triggered.connect(
            lambda: QtWidgets.QApplication.quit())
        self.clearTableAction.triggered.connect(self.clearTable)
        self.aboutAction.triggered.connect(self.about)
        self.startButton.clicked.connect(self.start)
        self.stopButton.clicked.connect(self.stop)
        self.buttonTest.clicked.connect(self.test)
        self.sitesTableView.doubleClicked.connect(
            self.sitesTableView_doubleClicked)
        self.labelActiveThreads = QtWidgets.QLabel("Active threads: 0")
        self.statusbar.addPermanentWidget(self.labelActiveThreads)

        self.actionRemove_selected.triggered.connect(self.removeSelected)
        self.actionInvert_selection.triggered.connect(self.invertSelection)
        self.actionRemove_duplicates.triggered.connect(self.removeDuplicates)
        self.actionSelect_all.triggered.connect(self.sitesTableView.selectAll)

        # Events
        self.resizeEvent = self.onResize
        self.closeEvent = self.onClose
        self.showEvent = self.onShow
        self._tableViewWidth = 0
        self._threads = []
        self._activeThreads = 0
        self._workers = []
        self._progressDone = 0
        self._progressTotal = 0
        self._boldFont = QFont()
        self._boldFont.setBold(True)
        self._recentFIles = []
        self.loadSettings()
        self.centerWindow()
        self.timerPulse = QTimer(self)
        self.timerPulse.timeout.connect(self.pulse)
        self.timerPulse.start(1000)
        # text = readTextFile("data/sites2.txt")
        # for url in text.strip().splitlines():
        #     resultCell = QStandardItem("")
        #     # resultCell.setTextAlignment(Qt.AlignCeter)
        #     codeCell = QStandardItem("")
        #     # codeCell.setTextAlignment(Qt.AlignCenter)
        #     self.sitesModel.appendRow([QStandardItem(url), resultCell, codeCell])
        self.stopButton.setEnabled(False)
        self.buttonTest.setVisible(False)

    def centerWindow(self):
        fg = self.frameGeometry()
        c = QtWidgets.QDesktopWidget().availableGeometry().center()
        fg.moveCenter(c)
        self.move(fg.topLeft())

    def loadSettings(self):
        if os.path.isfile(self._settingsFile):
            settings = QSettings(self._settingsFile, QSettings.IniFormat)
            self.restoreGeometry(settings.value("geometry", ''))
            self.restoreState(settings.value("windowState", ''))
            self._tableViewWidth = int(settings.value("tableViewWidth", ''))
            self.threadsSpin.setValue(
                settings.value("threadsCount", THREADS, type=int))
            self.timeoutSpin.setValue(
                settings.value("timeoutSpin", TIMEOUT, type=int))

    def saveSettings(self):
        settings = QSettings(self._settingsFile, QSettings.IniFormat)
        settings.setValue("geometry", self.saveGeometry())
        settings.setValue("windowState", self.saveState())
        settings.setValue("tableViewWidth",
                          self.sitesTableView.frameGeometry().width())
        settings.setValue("threadsCount", self.threadsSpin.value())
        settings.setValue("timeout", self.timeoutSpin.value())

    def onResize(self, event):
        self.resizeTableColumns()
        QtWidgets.QMainWindow.resizeEvent(self, event)

    def onClose(self, event):
        self.saveSettings()
        QtWidgets.QMainWindow.closeEvent(self, event)

    def onShow(self, event):
        self.resizeTableColumns()
        QtWidgets.QMainWindow.showEvent(self, event)

    def resizeTableColumns(self):
        self.sitesTableView.setColumnWidth(
            0, int(self.sitesTableView.frameGeometry().width() * 0.6))
        self.sitesTableView.setColumnWidth(
            1, int(self.sitesTableView.frameGeometry().width() * 0.1))

    def start(self):
        self.resetTable()
        model = self.sitesModel
        queues = split_list(range(self.sitesModel.rowCount()),
                            self.threadsSpin.value())
        self._progressTotal = self.sitesModel.rowCount()
        self._progressDone = 0
        self._threads = []
        self._workers = []
        for i, rows in enumerate(queues):
            self._threads.append(MyThread())
            queue = Queue()
            for row in rows:
                url = model.data(model.index(row, 0))
                queue.put((row, url))
            self._workers.append(
                CheckAliveWorker(check_alive,
                                 timeout=self.timeoutSpin.value(),
                                 queue=queue))
            self._workers[i].moveToThread(self._threads[i])
            self._threads[i].started.connect(self._workers[i].start)
            self._threads[i].finished.connect(self._threads[i].deleteLater)
            self._workers[i].status.connect(self.onStatus)
            self._workers[i].result.connect(self.onResult)
            self._workers[i].finished.connect(self._threads[i].quit)
            self._workers[i].finished.connect(self._workers[i].deleteLater)
        for i in range(self.threadsSpin.value()):
            self._threads[i].start()
        self.startButton.setEnabled(False)
        self.stopButton.setEnabled(True)

    def setActiveThreadsCount(self, i):
        self._activeThreads = i

    def pulse(self):
        self.labelActiveThreads.setText("Active threads: {}".format(
            MyThread.activeCount))
        if MyThread.activeCount == 0:
            if not self.sitesTableView.isSortingEnabled():
                self.sitesTableView.setSortingEnabled(True)
            if not self.startButton.isEnabled():
                self.startButton.setEnabled(True)
            if self.stopButton.isEnabled():
                self.stopButton.setEnabled(False)
        else:
            if self.sitesTableView.isSortingEnabled():
                self.sitesTableView.setSortingEnabled(False)

    def stop(self):
        for i, _ in enumerate(self._workers):
            self._workers[i]._running = False

    @pyqtSlot(tuple)
    def onStatus(self, tuple_):
        i, status = tuple_
        self.sitesModel.setData(self.sitesModel.index(i, 3), status)

    @pyqtSlot(object)
    def onResult(self, result):
        self.sitesModel.item(result["row"], 1).setFont(self._boldFont)
        # self.sitesModel.item(result["row"], 1).setForeground(Qt.white)
        self.sitesModel.setData(self.sitesModel.index(result["row"], 2),
                                result["status_code"])
        if result["result"]:
            self.sitesModel.setData(self.sitesModel.index(result["row"], 1),
                                    "OK")
            self.sitesModel.item(result["row"], 1).setForeground(Qt.green)
            logger.info("{} {}".format(result["url"], result["status_code"]))
        else:
            self.sitesModel.setData(self.sitesModel.index(result["row"], 1),
                                    "Fail")
            self.sitesModel.item(result["row"], 1).setForeground(Qt.red)
            logger.info("{} {}".format(result["url"], result["msg"]))
        self._progressDone += 1
        self.progressBar.setValue(
            int(float(self._progressDone) / self._progressTotal * 100))

    def importUrls(self):
        filePath, fileType = QtWidgets.QFileDialog.getOpenFileName(
            self, "Import URLs", filter="Text files (*.txt)")
        if filePath:
            text = readTextFile(filePath)
            for url in text.strip().splitlines():
                resultCell = QStandardItem("")
                resultCell.setTextAlignment(Qt.AlignCenter)
                codeCell = QStandardItem("")
                codeCell.setTextAlignment(Qt.AlignCenter)
                self.sitesModel.appendRow(
                    [QStandardItem(url), resultCell, codeCell])

    def sitesTableView_doubleClicked(self, modelIndex):
        model = self.sitesModel
        row = modelIndex.row()
        url = model.data(model.index(row, 0))
        webbrowser.open(url)

    def resetTable(self):
        model = self.sitesModel
        for i in range(model.rowCount()):
            model.setData(model.index(i, 1), "")
            model.setData(model.index(i, 2), "")

    def clearTable(self):
        self.tableRemoveAllRows(self.sitesModel)

    def tableRemoveAllRows(self, model):
        for i in reversed(range(model.rowCount())):
            model.removeRow(i)

    def exportResults(self):
        filePath, fileType = QtWidgets.QFileDialog.getSaveFileName(
            self,
            "Export URLs",
            filter="Text file (*.txt);;CSV file (*.csv);;JSON file (*.json)")
        model = self.sitesModel
        data = []
        for i in range(model.rowCount()):
            data.append({
                "URL": model.data(model.index(i, 0)),
                "Result": model.data(model.index(i, 1)),
                "Code": model.data(model.index(i, 2)),
            })
        if "*.txt" in fileType:
            with open(filePath, "w") as f:
                f.write("\n".join([i["URL"] for i in data]))
        elif "*.csv" in fileType:
            with open(filePath, "w") as f:
                w = csv.DictWriter(f, data[0].keys())
                w.writeheader()
                w.writerows(data)
        elif "*.json" in fileType:
            with open(filePath, "w") as f:
                f.write(json.dumps(data))

    def about(self):
        QtWidgets.QMessageBox.about(
            self, "About {}".format(__title__), """<b>{} v{}</b>
            <p>&copy; 2017.
            <p>{}
            <p>Python {} - Qt {} - PyQt {} on {}""".format(
                __title__, __version__, __description__,
                platform.python_version(), QT_VERSION_STR, PYQT_VERSION_STR,
                platform.system()))

    def test(self):
        pass

    def selectedRows(self):
        rows = set()
        for index in self.sitesTableView.selectionModel().selectedIndexes():
            rows.add(index.row())

        return list(rows)

    def removeSelected(self):
        model = self.sitesModel
        for i in reversed(self.selectedRows()):
            model.removeRow(i)

    def removeDuplicates(self):
        items = []
        foundDuplicates = False
        for i in range(self.sitesModel.rowCount()):
            item = self.sitesModel.data(self.sitesModel.index(i, 0))
            if item not in items:
                items.append(item)
            else:
                foundDuplicates = True
        if foundDuplicates:
            self.clearTable()
            for i, item in enumerate(items):
                self.sitesModel.setData(self.sitesModel.index(i, 0), item)

    def invertSelection(self):
        # self.sitesTableView.selectAll()
        selectedRows = self.selectedRows()
        self.sitesTableView.clearSelection()
        for i in range(self.sitesModel.rowCount()):
            if i not in selectedRows:
                self.sitesTableView.selectRow(i)
Exemplo n.º 22
0
class comic_meta_data_editor(QDialog):
    configGroup = "ComicsProjectManagementTools"

    # Translatable genre dictionary that has it's translated entries added to the genrelist and from which the untranslated items are taken.
    acbfGenreList = {"science_fiction": str(i18n("Science Fiction")), "fantasy": str(i18n("Fantasy")), "adventure": str(i18n("Adventure")), "horror": str(i18n("Horror")), "mystery": str(i18n("Mystery")), "crime": str(i18n("Crime")), "military": str(i18n("Military")), "real_life": str(i18n("Real Life")), "superhero": str(i18n("Superhero")), "humor": str(i18n("Humor")), "western": str(i18n("Western")), "manga": str(i18n("Manga")), "politics": str(i18n("Politics")), "caricature": str(i18n("Caricature")), "sports": str(i18n("Sports")), "history": str(i18n("History")), "biography": str(i18n("Biography")), "education": str(i18n("Education")), "computer": str(i18n("Computer")), "religion": str(i18n("Religion")), "romance": str(i18n("Romance")), "children": str(i18n("Children")), "non-fiction": str(i18n("Non Fiction")), "adult": str(i18n("Adult")), "alternative": str(i18n("Alternative")), "artbook": str(i18n("Artbook")), "other": str(i18n("Other"))}
    acbfAuthorRolesList = {"Writer": str(i18n("Writer")), "Adapter": str(i18n("Adapter")), "Artist": str(i18n("Artist")), "Penciller": str(i18n("Penciller")), "Inker": str(i18n("Inker")), "Colorist": str(i18n("Colorist")), "Letterer": str(i18n("Letterer")), "Cover Artist": str(i18n("Cover Artist")), "Photographer": str(i18n("Photographer")), "Editor": str(i18n("Editor")), "Assistant Editor": str(i18n("Assistant Editor")), "Designer": str(i18n("Designer")), "Translator": str(i18n("Translator")), "Other": str(i18n("Other"))}

    def __init__(self):
        super().__init__()
        # Get the keys for the autocompletion.
        self.genreKeysList = []
        self.characterKeysList = []
        self.ratingKeysList = {}
        self.formatKeysList = []
        self.otherKeysList = []
        self.authorRoleList = []
        for g in self.acbfGenreList.values():
            self.genreKeysList.append(g)
        for r in self.acbfAuthorRolesList.values():
            self.authorRoleList.append(r)
        mainP = Path(os.path.abspath(__file__)).parent
        self.get_auto_completion_keys(mainP)
        extraKeyP = Path(QDir.homePath()) / Application.readSetting(self.configGroup, "extraKeysLocation", str())
        self.get_auto_completion_keys(extraKeyP)

        # Setup the dialog.
        self.setLayout(QVBoxLayout())
        mainWidget = QTabWidget()
        self.layout().addWidget(mainWidget)
        self.setWindowTitle(i18n("Comic Metadata"))
        buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        self.layout().addWidget(buttons)
        buttons.accepted.connect(self.accept)
        buttons.rejected.connect(self.reject)

        # Title, concept, summary, genre, characters, format, rating, language, series, other keywords
        metadataPage = QWidget()
        mformLayout = QFormLayout()
        metadataPage.setLayout(mformLayout)

        self.lnTitle = QLineEdit()
        self.lnTitle.setToolTip(i18n("The proper title of the comic."))

        self.teSummary = QPlainTextEdit()
        self.teSummary.setToolTip(i18n("What will you tell others to entice them to read your comic?"))

        self.lnGenre = QLineEdit()
        genreCompletion = multi_entry_completer()
        genreCompletion.setModel(QStringListModel(self.genreKeysList))
        self.lnGenre.setCompleter(genreCompletion)
        genreCompletion.setCaseSensitivity(False)
        self.lnGenre.setToolTip(i18n("The genre of the work. Prefilled values are from the ACBF, but you can fill in your own. Separate genres with commas. Try to limit the amount to about two or three"))

        self.lnCharacters = QLineEdit()
        characterCompletion = multi_entry_completer()
        characterCompletion.setModel(QStringListModel(self.characterKeysList))
        characterCompletion.setCaseSensitivity(False)
        characterCompletion.setFilterMode(Qt.MatchContains)  # So that if there is a list of names with last names, people can type in a last name.
        self.lnCharacters.setCompleter(characterCompletion)
        self.lnCharacters.setToolTip(i18n("The names of the characters that this comic revolves around. Comma-separated."))

        self.lnFormat = QLineEdit()
        formatCompletion = multi_entry_completer()
        formatCompletion.setModel(QStringListModel(self.formatKeysList))
        formatCompletion.setCaseSensitivity(False)
        self.lnFormat.setCompleter(formatCompletion)

        ratingLayout = QHBoxLayout()
        self.cmbRatingSystem = QComboBox()
        self.cmbRatingSystem.addItems(self.ratingKeysList.keys())
        self.cmbRatingSystem.setEditable(True)
        self.cmbRating = QComboBox()
        self.cmbRating.setEditable(True)
        self.cmbRatingSystem.currentIndexChanged.connect(self.slot_refill_ratings)
        ratingLayout.addWidget(self.cmbRatingSystem)
        ratingLayout.addWidget(self.cmbRating)

        self.lnSeriesName = QLineEdit()
        self.lnSeriesName.setToolTip(i18n("If this is part of a series, enter the name of the series and the number."))
        self.spnSeriesNumber = QSpinBox()
        self.spnSeriesNumber.setPrefix("No. ")
        self.spnSeriesVol = QSpinBox()
        self.spnSeriesVol.setPrefix("Vol. ")
        seriesLayout = QHBoxLayout()
        seriesLayout.addWidget(self.lnSeriesName)
        seriesLayout.addWidget(self.spnSeriesVol)
        seriesLayout.addWidget(self.spnSeriesNumber)

        otherCompletion = multi_entry_completer()
        otherCompletion.setModel(QStringListModel(self.otherKeysList))
        otherCompletion.setCaseSensitivity(False)
        otherCompletion.setFilterMode(Qt.MatchContains)
        self.lnOtherKeywords = QLineEdit()
        self.lnOtherKeywords.setCompleter(otherCompletion)
        self.lnOtherKeywords.setToolTip(i18n("Other keywords that don't fit in the previously mentioned sets. As always, comma-separated"))

        self.cmbLanguage = language_combo_box()
        self.cmbCountry = country_combo_box()
        self.cmbLanguage.currentIndexChanged.connect(self.slot_update_countries)
        self.cmbReadingMode = QComboBox()
        self.cmbReadingMode.addItem(i18n("Left to Right"))
        self.cmbReadingMode.addItem(i18n("Right to Left"))

        self.cmbCoverPage = QComboBox()
        self.cmbCoverPage.setToolTip(i18n("Which page is the cover page? This will be empty if there's no pages."))

        mformLayout.addRow(i18n("Title:"), self.lnTitle)
        mformLayout.addRow(i18n("Cover Page:"), self.cmbCoverPage)
        mformLayout.addRow(i18n("Summary:"), self.teSummary)
        mformLayout.addRow(i18n("Language:"), self.cmbLanguage)
        mformLayout.addRow("", self.cmbCountry)
        mformLayout.addRow(i18n("Reading Direction:"), self.cmbReadingMode)
        mformLayout.addRow(i18n("Genre:"), self.lnGenre)
        mformLayout.addRow(i18n("Characters:"), self.lnCharacters)
        mformLayout.addRow(i18n("Format:"), self.lnFormat)
        mformLayout.addRow(i18n("Rating:"), ratingLayout)
        mformLayout.addRow(i18n("Series:"), seriesLayout)
        mformLayout.addRow(i18n("Other:"), self.lnOtherKeywords)

        mainWidget.addTab(metadataPage, i18n("Work"))

        # The page for the authors.
        authorPage = QWidget()
        authorPage.setLayout(QVBoxLayout())
        explanation = QLabel(i18n("The following is a table of the authors that contributed to this comic. You can set their nickname, proper names (first, middle, last), Role (Penciller, Inker, etc), email and homepage."))
        explanation.setWordWrap(True)
        self.authorModel = QStandardItemModel(0, 8)
        labels = [i18n("Nick Name"), i18n("Given Name"), i18n("Middle Name"), i18n("Family Name"), i18n("Role"), i18n("Email"), i18n("Homepage"), i18n("Language")]
        self.authorModel.setHorizontalHeaderLabels(labels)
        self.authorTable = QTableView()
        self.authorTable.setModel(self.authorModel)
        self.authorTable.verticalHeader().setDragEnabled(True)
        self.authorTable.verticalHeader().setDropIndicatorShown(True)
        self.authorTable.verticalHeader().setSectionsMovable(True)
        self.authorTable.verticalHeader().sectionMoved.connect(self.slot_reset_author_row_visual)
        delegate = author_delegate()
        delegate.setCompleterData(self.authorRoleList, 4)
        delegate.setLanguageData(len(labels) - 1)
        self.authorTable.setItemDelegate(delegate)
        author_button_layout = QWidget()
        author_button_layout.setLayout(QHBoxLayout())
        btn_add_author = QPushButton(i18n("Add Author"))
        btn_add_author.clicked.connect(self.slot_add_author)
        btn_remove_author = QPushButton(i18n("Remove Author"))
        btn_remove_author.clicked.connect(self.slot_remove_author)
        author_button_layout.layout().addWidget(btn_add_author)
        author_button_layout.layout().addWidget(btn_remove_author)
        authorPage.layout().addWidget(explanation)
        authorPage.layout().addWidget(self.authorTable)
        authorPage.layout().addWidget(author_button_layout)
        mainWidget.addTab(authorPage, i18n("Authors"))

        # The page with publisher information.
        publisherPage = QWidget()
        publisherLayout = QFormLayout()
        publisherPage.setLayout(publisherLayout)
        self.publisherName = QLineEdit()
        self.publisherName.setToolTip(i18n("The name of the company, group or person who is responsible for the final version the reader gets."))
        publishDateLayout = QHBoxLayout()
        self.publishDate = QDateEdit()
        self.publishDate.setDisplayFormat(QLocale().system().dateFormat())
        currentDate = QPushButton(i18n("Set Today"))
        currentDate.setToolTip(i18n("Sets the publish date to the current date."))
        currentDate.clicked.connect(self.slot_set_date)
        publishDateLayout.addWidget(self.publishDate)
        publishDateLayout.addWidget(currentDate)
        self.publishCity = QLineEdit()
        self.publishCity.setToolTip(i18n("Traditional publishers are always mentioned in source with the city they are located."))
        self.isbn = QLineEdit()
        self.license = license_combo_box()  # Maybe ought to make this a QLineEdit...
        self.license.setEditable(True)
        self.license.completer().setCompletionMode(QCompleter.PopupCompletion)
        dataBaseReference = QVBoxLayout()
        self.ln_database_name = QLineEdit()
        self.ln_database_name.setToolTip(i18n("If there's an entry in a comics data base, that should be added here. It is unlikely to be a factor for comics from scratch, but useful when doing a conversion."))
        self.cmb_entry_type = QComboBox()
        self.cmb_entry_type.addItems(["IssueID", "SeriesID", "URL"])
        self.cmb_entry_type.setEditable(True)
        self.ln_database_entry = QLineEdit()
        dbHorizontal = QHBoxLayout()
        dbHorizontal.addWidget(self.ln_database_name)
        dbHorizontal.addWidget(self.cmb_entry_type)
        dataBaseReference.addLayout(dbHorizontal)
        dataBaseReference.addWidget(self.ln_database_entry)
        publisherLayout.addRow(i18n("Name:"), self.publisherName)
        publisherLayout.addRow(i18n("City:"), self.publishCity)
        publisherLayout.addRow(i18n("Date:"), publishDateLayout)
        publisherLayout.addRow(i18n("ISBN:"), self.isbn)
        publisherLayout.addRow(i18n("License:"), self.license)
        publisherLayout.addRow(i18n("Database:"), dataBaseReference)

        mainWidget.addTab(publisherPage, i18n("Publisher"))
    """
    Ensure that the drag and drop of authors doesn't mess up the labels.
    """

    def slot_reset_author_row_visual(self):
        headerLabelList = []
        for i in range(self.authorTable.verticalHeader().count()):
            headerLabelList.append(str(i))
        for i in range(self.authorTable.verticalHeader().count()):
            logicalI = self.authorTable.verticalHeader().logicalIndex(i)
            headerLabelList[logicalI] = str(i + 1)
        self.authorModel.setVerticalHeaderLabels(headerLabelList)
    """
    Set the publish date to the current date.
    """

    def slot_set_date(self):
        self.publishDate.setDate(QDate().currentDate())
        
    def slot_update_countries(self):
        code = self.cmbLanguage.codeForCurrentEntry()
        self.cmbCountry.set_country_for_locale(code)

    """
    Append keys to autocompletion lists from the directory mainP.
    """

    def get_auto_completion_keys(self, mainP=Path()):
        genre = Path(mainP / "key_genre")
        characters = Path(mainP / "key_characters")
        rating = Path(mainP / "key_rating")
        format = Path(mainP / "key_format")
        keywords = Path(mainP / "key_other")
        authorRole = Path(mainP / "key_author_roles")
        if genre.exists():
            for t in list(genre.glob('**/*.txt')):
                file = open(str(t), "r", errors="replace")
                for l in file:
                    if str(l).strip("\n") not in self.genreKeysList:
                        self.genreKeysList.append(str(l).strip("\n"))
                file.close()
        if characters.exists():
            for t in list(characters.glob('**/*.txt')):
                file = open(str(t), "r", errors="replace")
                for l in file:
                    if str(l).strip("\n") not in self.characterKeysList:
                        self.characterKeysList.append(str(l).strip("\n"))
                file.close()
        if format.exists():
            for t in list(format.glob('**/*.txt')):
                file = open(str(t), "r", errors="replace")
                for l in file:
                    if str(l).strip("\n") not in self.formatKeysList:
                        self.formatKeysList.append(str(l).strip("\n"))
                file.close()
        if rating.exists():
            for t in list(rating.glob('**/*.csv')):
                file = open(str(t), "r", newline="", encoding="utf-8")
                ratings = csv.reader(file)
                title = os.path.basename(str(t))
                r = 0
                for row in ratings:
                    listItem = []
                    if r is 0:
                        title = row[1]
                    else:
                        listItem = self.ratingKeysList[title]
                        item = []
                        item.append(row[0])
                        item.append(row[1])
                        listItem.append(item)
                    self.ratingKeysList[title] = listItem
                    r += 1
                file.close()
        if keywords.exists():
            for t in list(keywords.glob('**/*.txt')):
                file = open(str(t), "r", errors="replace")
                for l in file:
                    if str(l).strip("\n") not in self.otherKeysList:
                        self.otherKeysList.append(str(l).strip("\n"))
                file.close()
        if authorRole.exists():
            for t in list(authorRole.glob('**/*.txt')):
                file = open(str(t), "r", errors="replace")
                for l in file:
                    if str(l).strip("\n") not in self.authorRoleList:
                        self.authorRoleList.append(str(l).strip("\n"))
                file.close()

    """
    Refill the ratings box.
    This is called whenever the rating system changes.
    """

    def slot_refill_ratings(self):
        if self.cmbRatingSystem.currentText() in self.ratingKeysList.keys():
            self.cmbRating.clear()
            model = QStandardItemModel()
            for i in self.ratingKeysList[self.cmbRatingSystem.currentText()]:
                item = QStandardItem()
                item.setText(i[0])
                item.setToolTip(i[1])
                model.appendRow(item)
            self.cmbRating.setModel(model)

    """
    Add an author with default values initialised.
    """

    def slot_add_author(self):
        listItems = []
        listItems.append(QStandardItem(i18n("Anon")))  # Nick name
        listItems.append(QStandardItem(i18n("John")))  # First name
        listItems.append(QStandardItem())  # Middle name
        listItems.append(QStandardItem(i18n("Doe")))  # Last name
        listItems.append(QStandardItem())  # role
        listItems.append(QStandardItem())  # email
        listItems.append(QStandardItem())  # homepage
        language = QLocale.system().name().split("_")[0]
        if language == "C":
            language = "en"
        listItems.append(QStandardItem(language))  # Language
        self.authorModel.appendRow(listItems)

    """
    Remove the selected author from the author list.
    """

    def slot_remove_author(self):
        self.authorModel.removeRow(self.authorTable.currentIndex().row())

    """
    Load the UI values from the config dictionary given.
    """

    def setConfig(self, config):

        if "title" in config.keys():
            self.lnTitle.setText(config["title"])
        self.teSummary.clear()
        if "pages" in config.keys():
            self.cmbCoverPage.clear()
            for page in config["pages"]:
                self.cmbCoverPage.addItem(page)
            if "cover" in config.keys():
                if config["cover"] in config["pages"]:
                    self.cmbCoverPage.setCurrentText(config["cover"])
        if "summary" in config.keys():
            self.teSummary.appendPlainText(config["summary"])
        if "genre" in config.keys():
            genreList = []
            genreListConf = config["genre"]
            totalMatch = 100
            if isinstance(config["genre"], dict):
                genreListConf = config["genre"].keys()
                totalMatch = 0
            for genre in genreListConf:
                genreKey = genre
                if genre in self.acbfGenreList:
                    genreKey = self.acbfGenreList[genre]
                if isinstance(config["genre"], dict):
                    genreValue = config["genre"][genre]
                    if genreValue > 0:
                        genreKey = str(genreKey + "(" + str(genreValue) + ")")
                genreList.append(genreKey)
            self.lnGenre.setText(", ".join(genreList))
        if "characters" in config.keys():
            self.lnCharacters.setText(", ".join(config["characters"]))
        if "format" in config.keys():
            self.lnFormat.setText(", ".join(config["format"]))
        if "rating" in config.keys():
            self.cmbRating.setCurrentText(config["rating"])
        else:
            self.cmbRating.setCurrentText("")
        if "ratingSystem" in config.keys():
            self.cmbRatingSystem.setCurrentText(config["ratingSystem"])
        else:
            self.cmbRatingSystem.setCurrentText("")
        if "otherKeywords" in config.keys():
            self.lnOtherKeywords.setText(", ".join(config["otherKeywords"]))
        if "seriesName" in config.keys():
            self.lnSeriesName.setText(config["seriesName"])
        if "seriesVolume" in config.keys():
            self.spnSeriesVol.setValue(config["seriesVolume"])
        if "seriesNumber" in config.keys():
            self.spnSeriesNumber.setValue(config["seriesNumber"])
        if "language" in config.keys():
            code = config["language"]
            if "_" in code:
                self.cmbLanguage.setEntryToCode(code.split("_")[0])
                self.cmbCountry.setEntryToCode(code.split("_")[-1])
            else:
                self.cmbLanguage.setEntryToCode(code)
        if "readingDirection" in config.keys():
            if config["readingDirection"] is "leftToRight":
                self.cmbReadingMode.setCurrentIndex(int(Qt.LeftToRight))
            else:
                self.cmbReadingMode.setCurrentIndex(int(Qt.RightToLeft))
        else:
            self.cmbReadingMode.setCurrentIndex(QLocale(self.cmbLanguage.codeForCurrentEntry()).textDirection())
        if "publisherName" in config.keys():
            self.publisherName.setText(config["publisherName"])
        if "publisherCity" in config.keys():
            self.publishCity.setText(config["publisherCity"])
        if "publishingDate" in config.keys():
            self.publishDate.setDate(QDate.fromString(config["publishingDate"], Qt.ISODate))
        if "isbn-number" in config.keys():
            self.isbn.setText(config["isbn-number"])
        if "license" in config.keys():
            self.license.setCurrentText(config["license"])
        else:
            self.license.setCurrentText("")  # I would like to keep it ambiguous whether the artist has thought about the license or not.
        if "authorList" in config.keys():
            authorList = config["authorList"]
            for i in range(len(authorList)):
                author = authorList[i]
                if len(author.keys()) > 0:
                    listItems = []
                    listItems = []
                    listItems.append(QStandardItem(author.get("nickname", "")))
                    listItems.append(QStandardItem(author.get("first-name", "")))
                    listItems.append(QStandardItem(author.get("initials", "")))
                    listItems.append(QStandardItem(author.get("last-name", "")))
                    role = author.get("role", "")
                    if role in self.acbfAuthorRolesList.keys():
                        role = self.acbfAuthorRolesList[role]
                    listItems.append(QStandardItem(role))
                    listItems.append(QStandardItem(author.get("email", "")))
                    listItems.append(QStandardItem(author.get("homepage", "")))
                    listItems.append(QStandardItem(author.get("language", "")))
                    self.authorModel.appendRow(listItems)
        else:
            self.slot_add_author()
        dbRef = config.get("databaseReference", {})
        self.ln_database_name.setText(dbRef.get("name", ""))
        self.ln_database_entry.setText(dbRef.get("entry", ""))
        stringCmbEntryType = self.cmb_entry_type.itemText(0)
        self.cmb_entry_type.setCurrentText(dbRef.get("type", stringCmbEntryType))

    """
    Store the GUI values into the config dictionary given.
    
    @return the config diactionary filled with new values.
    """

    def getConfig(self, config):

        text = self.lnTitle.text()
        if len(text) > 0 and text.isspace() is False:
            config["title"] = text
        elif "title" in config.keys():
            config.pop("title")
        config["cover"] = self.cmbCoverPage.currentText()
        listkeys = self.lnGenre.text()
        if len(listkeys) > 0 and listkeys.isspace() is False:
            preSplit = self.lnGenre.text().split(",")
            genreMatcher = re.compile(r'\((\d+)\)')
            genreList = {}
            totalValue = 0
            for key in preSplit:
                m = genreMatcher.search(key)
                if m:
                    genre = str(genreMatcher.sub("", key)).strip()
                    match = int(m.group()[:-1][1:])
                else:
                    genre = key.strip()
                    match = 0
                if genre in self.acbfGenreList.values():
                    i = list(self.acbfGenreList.values()).index(genre)
                    genreList[list(self.acbfGenreList.keys())[i]] = match
                else:
                    genreList[genre] = match
                totalValue += match
            # Normalize the values:
            for key in genreList.keys():
                if genreList[key] > 0:
                    genreList[key] = round(genreList[key] / totalValue * 100)
            config["genre"] = genreList
        elif "genre" in config.keys():
            config.pop("genre")
        listkeys = self.lnCharacters.text()
        if len(listkeys) > 0 and listkeys.isspace() is False:
            config["characters"] = self.lnCharacters.text().split(", ")
        elif "characters" in config.keys():
            config.pop("characters")
        listkeys = self.lnFormat.text()
        if len(listkeys) > 0 and listkeys.isspace() is False:
            config["format"] = self.lnFormat.text().split(", ")
        elif "format" in config.keys():
            config.pop("format")
        config["ratingSystem"] = self.cmbRatingSystem.currentText()
        config["rating"] = self.cmbRating.currentText()
        listkeys = self.lnOtherKeywords.text()
        if len(listkeys) > 0 and listkeys.isspace() is False:
            config["otherKeywords"] = self.lnOtherKeywords.text().split(", ")
        elif "otherKeywords" in config.keys():
            config.pop("otherKeywords")
        text = self.teSummary.toPlainText()
        if len(text) > 0 and text.isspace() is False:
            config["summary"] = text
        elif "summary" in config.keys():
            config.pop("summary")
        if len(self.lnSeriesName.text()) > 0:
            config["seriesName"] = self.lnSeriesName.text()
            config["seriesNumber"] = self.spnSeriesNumber.value()
            if self.spnSeriesVol.value() > 0:
                config["seriesVolume"] = self.spnSeriesVol.value()
        config["language"] = str(self.cmbLanguage.codeForCurrentEntry()+"_"+self.cmbCountry.codeForCurrentEntry())
        if self.cmbReadingMode.currentIndex() is int(Qt.LeftToRight):
            config["readingDirection"] = "leftToRight"
        else:
            config["readingDirection"] = "rightToLeft"
        authorList = []
        for row in range(self.authorTable.verticalHeader().count()):
            logicalIndex = self.authorTable.verticalHeader().logicalIndex(row)
            listEntries = ["nickname", "first-name", "initials", "last-name", "role", "email", "homepage", "language"]
            author = {}
            for i in range(len(listEntries)):
                entry = self.authorModel.data(self.authorModel.index(logicalIndex, i))
                if entry is None:
                    entry = " "
                if entry.isspace() is False and len(entry) > 0:
                    if listEntries[i] == "role":
                        if entry in self.acbfAuthorRolesList.values():
                            entryI = list(self.acbfAuthorRolesList.values()).index(entry)
                            entry = list(self.acbfAuthorRolesList.keys())[entryI]
                    author[listEntries[i]] = entry
                elif listEntries[i] in author.keys():
                    author.pop(listEntries[i])
            authorList.append(author)
        config["authorList"] = authorList
        config["publisherName"] = self.publisherName.text()
        config["publisherCity"] = self.publishCity.text()
        config["publishingDate"] = self.publishDate.date().toString(Qt.ISODate)
        config["isbn-number"] = self.isbn.text()
        config["license"] = self.license.currentText()
        if self.ln_database_name.text().isalnum() and self.ln_database_entry.text().isalnum():
            dbRef = {}
            dbRef["name"] = self.ln_database_name.text()
            dbRef["entry"] = self.ln_database_entry.text()
            dbRef["type"] = self.cmb_entry_type.currentText()
            config["databaseReference"] = dbRef

        return config
Exemplo n.º 23
0
class MyWindow(QMainWindow):
    def __init__(self):
        super(MyWindow, self).__init__()
        self.lz = []
        self.ts = 0
        self.zahlenListe = []
        dir = os.path.dirname(sys.argv[0])
        self.settingsfile = "%s/%s" % (dir, "Lotto.conf")
        self.zahlen = "%s/%s" % (dir, "zahlen.txt")
        print(self.settingsfile)
        self.settings = QSettings(self.settingsfile, QSettings.IniFormat)
        self.setStyleSheet(stylesheet(self))
        self.lottolink = 'https://www.dielottozahlende.net/lotto-6-aus-49'
        self.mysuper = 5
        self.model = QStandardItemModel(self)
        self.model.setRowCount(7)
        self.tableview = QTableView(self)
        self.tableview.setSortingEnabled(False)
        self.tableview.setGridStyle(1)
        if int(sys.version[0]) > 2:
            self.tableview.setFixedHeight(149)
        else:
            self.tableview.setFixedHeight(171)
        self.tableview.setSizeAdjustPolicy(QAbstractScrollArea.AdjustIgnored)
        self.tableview.horizontalHeader().setStretchLastSection(True)
        self.tableview.verticalHeader().setStretchLastSection(False)
        self.tableview.setCornerButtonEnabled(True)
        self.tableview.setShowGrid(True)
        self.tableview.setModel(self.model)
        self.tableview.hideRow(6)
        self.tableview.hideColumn(13)
        self.pushButtonLoad = QPushButton()
        self.pushButtonLoad.setText("Zahlen holen")
        self.pushButtonLoad.setIcon(QIcon.fromTheme("view-refresh"))
        self.pushButtonLoad.clicked.connect(self.getLotto)
        self.pushButtonLoad.setFixedWidth(110)
        self.pushButtonLoad.setFixedHeight(24)

        self.zahlenAction = QAction(QIcon.fromTheme("edit"),
                                    "Editor",
                                    self,
                                    triggered=self.edit_Tipps,
                                    shortcut="F5")
        self.addAction(self.zahlenAction)

        self.superAction = QAction(QIcon.fromTheme("edit"),
                                   "Superzahl",
                                   self,
                                   triggered=self.setMySuper,
                                   shortcut="F6")
        self.addAction(self.superAction)

        self.infoAction = QAction(QIcon.fromTheme("help-info"),
                                  "Information",
                                  self,
                                  triggered=self.showInfo,
                                  shortcut="F1")
        self.addAction(self.infoAction)

        self.lbl = QLabel()

        self.hbox = QHBoxLayout()
        self.hbox.addWidget(self.pushButtonLoad)

        grid = QVBoxLayout()
        grid.setSpacing(10)
        grid.addLayout(self.hbox)
        grid.addWidget(self.tableview)
        grid.addWidget(self.lbl)

        mywidget = QWidget()
        mywidget.setLayout(grid)
        self.setCentralWidget(mywidget)

        self.readSettings()
        self.tableview.resizeColumnsToContents()
        self.tableview.resizeRowsToContents()
        self.setHeaders()

        print("Wilkommen bei Lotto")
        self.statusBar().showMessage(
            "%s %s" % ("Willkommen bei LottoCheck",
                       " *** F5 Lottozahlen ändern *** F6 Superzahl ändern"),
            0)

    def findTableItems(self):
        model = self.tableview.model()
        self.tableview.selectionModel().clearSelection()
        print(self.zahlenListe)
        for column in range(12):
            start = model.index(0, column)
            for zahl in self.zahlenListe:

                matches = model.match(start, Qt.DisplayRole, str(zahl), 1,
                                      Qt.MatchExactly)
                if matches:
                    index = matches[0]
                    self.tableview.selectionModel().select(
                        index, QItemSelectionModel.Select)

    def saveNumbers(self):
        print(self.model.columnCount(), "Columns")
        textData = ""
        fileName, _ = QFileDialog.getSaveFileName(self, "Open File",
                                                  "~/lottozahlen.csv",
                                                  "CSV Files (*.csv)")
        if fileName:
            print("%s %s" % (fileName, "saved"))
            f = open(fileName, 'w')
            for col in range(self.model.columnCount()):
                textData += str(self.model.horizontalHeaderItem(col).text())
                textData += "\t"
            textData += "\n"
            for row in range(self.model.rowCount() - 1):
                for col in range(self.model.columnCount()):
                    textData += str(self.model.data(self.model.index(row,
                                                                     col)))
                    textData += "\t"
                textData += "\n"
            f.write(textData)
            f.close()

    def edit_Tipps(self):
        self.edWin = Editor()
        with open(self.zahlen, 'r') as f:
            text = f.read()
            self.edWin.tipp_editor.setPlainText(text)
            f.close()
        self.edWin.isModified = False

    def showInfo(self):
        link = "<p><a title='Axel Schneider' href='http://goodoldsongs.jimdo.com' target='_blank'> \
                Axel Schneider</a></p>"
        message = "<h2>LottoCheck 1.0</h2><h4>6 aus 49</h4>created by " + link + " with PyQt5<br>©September 2019<br>" \
                            + "<br>Copyright © 2017 The Qt Company Ltd and other contributors." \
                            + "<br>Qt and the Qt logo are trademarks of The Qt Company Ltd."  \
                            + "<br><br>F5 = Tipps ändern" \
                            + "<br>F6 = Superzahl ändern"
        self.msgbox(message)

    def msgbox(self, message):
        msg = QMessageBox(1, "Information", message, QMessageBox.Ok)
        msg.exec_()

    def getLotto(self):
        if not self.lz == []:
            print("values already here", self.lz)
            self.compare()
        else:
            self.lottolink = "https://www.lotto.de/lotto-6aus49/lottozahlen"
            print("getting Values")
            source = requests.get(self.lottolink).text
            soup = bsoup(source, 'lxml')
            lottoliste = []

            for td in soup.find_all(class_='LottoBall__circle'):
                lottoliste.append(td.text)

            result = lottoliste
            self.zahlenListe = result[:6]
            self.lz = result
            theSuper = lottoliste[6]
            print("Gewinnzahlen:\n", self.zahlenListe)
            self.ts = theSuper
            print("theSuper", self.ts)
            for i in range(6):
                self.model.setData(self.model.index(i, 12), result[i])
                self.model.item(i, 12).setTextAlignment(Qt.AlignCenter)
            self.compare()

    def getSpiel77(self):
        self.lottolink = "https://www.lotto.de/lotto-6aus49/lottozahlen"
        source = requests.get(self.lottolink).text
        soup = bsoup(source, 'lxml')
        result = soup.find(class_='WinningNumbersAdditionalGame__text').text

        print("Spiel 77: ", result)

        super6 = soup.find(class_='WinningNumbersAdditionalGame__text'
                           ).parent.find_next_siblings()[0].text
        print("Super 6: ", super6)

        date = soup.find(class_="WinningNumbers__date").text

        self.lbl.setText(self.lbl.text() + "\n" + date + "\n\n" +
                         result.replace("Spiel 77", "Spiel 77: ") + "\n" +
                         super6.replace("Super 6", "Super 6: "))

    def compare(self):
        ### compare all tipps
        print("self.lz: ", self.lz)
        self.lz = [int(x) for x in self.lz[:6]]
        print(self.mysuper, self.lz)
        self.lbl.clear()
        tipp = []
        for x in range(len(self.tliste)):
            t = []
            tipp = [int(x) for x in self.tliste[x]]
            #            print(tipp)
            for a in self.lz:
                if int(a) in tipp:
                    print(a, "in tipp", str(x + 1))
                    t.append(a)

            rtext = ""
            print("len(t) ", len(t))
            if len(t) == 2 and self.mysuper == self.ts:
                rtext += self.lbl.text()
                rtext += '\ngewonnen in Tipp '
                rtext += str(int(x) + 1)
                rtext += " : "
                rtext += str(t)
                rtext += " *** "
                rtext += str(len(t))
                rtext += "er ***"
                rtext += ' + Superzahl'
                self.lbl.setText(rtext)
            elif len(t) > 2:
                if self.mysuper == self.ts:
                    rtext += self.lbl.text()
                    rtext += '\ngewonnen in Tipp '
                    rtext += str(int(x) + 1)
                    rtext += " : "
                    rtext += str(t)
                    rtext += " *** "
                    rtext += str(len(t))
                    rtext += "er ***"
                    rtext += ' + Superzahl'
                    self.lbl.setText(rtext)
                else:
                    rtext += self.lbl.text()
                    rtext += '\ngewonnen in Tipp '
                    rtext += str(int(x) + 1)
                    rtext += " : "
                    rtext += str(t)
                    rtext += " *** "
                    rtext += str(len(t))
                    rtext += "er ***"
                    self.lbl.setText(rtext)

        if self.lbl.text() == "":
            self.lbl.setText("leider nichts gewonnen ...\n")
        self.statusBar().showMessage(
            "%s %s %s %s" %
            ("Gewinnzahlen: ",
             (', '.join(str(x)
                        for x in self.lz)), " *** Superzahl: ", str(self.ts)),
            0)
        self.getSpiel77()
        self.findTableItems()

    def setHeaders(self):
        self.tableview.horizontalHeader().setVisible(True)
        self.tableview.verticalHeader().setVisible(False)
        for x in range(self.model.columnCount() - 1):
            self.model.setHeaderData(x, Qt.Horizontal,
                                     "%s %s" % ("Tipp", (x + 1)))
        self.model.setHeaderData(self.model.columnCount() - 1, Qt.Horizontal,
                                 "Gewinnzahlen")
        self.tableview.setAlternatingRowColors(True)
        self.tableview.resizeColumnsToContents()
        self.tableview.resizeRowsToContents()

    def closeEvent(self, event):
        print("Goodbye ...")
        self.writeSettings()

    def setMySuper(self):
        s = int(self.mysuper)
        dlg = QInputDialog()
        ms, ok = dlg.getInt(self, 'Superzahl:', "", s, 0, 9, 1, Qt.Dialog)
        if ok:
            self.mysuper = ms
            print("Superzahl =", self.mysuper)

    def readSettings(self):
        if self.settings.contains("mysuper"):
            self.mysuper = self.settings.value("mysuper")
        else:
            self.setMySuper()
        print("Superzahl:", self.mysuper)
        self.tliste = []
        with open(self.zahlen, 'r') as f:
            text = f.read()
            f.close()
            for line in text.splitlines():
                self.tliste.append(line.split(","))
            self.model.setColumnCount(len(self.tliste) + 1)
            for x in range(0, len(self.tliste)):
                tipp = self.tliste[x]
                for i in range(len(tipp)):
                    self.model.setData(self.model.index(i, x), tipp[i])
                    self.model.item(i, x).setTextAlignment(Qt.AlignCenter)

    def writeSettings(self):
        self.settings.setValue("mysuper", self.mysuper)
Exemplo n.º 24
0
class Emotion(QWidget):
    EMOTION_DIR = "./resource/expression"
    WIDTH = 460
    HEIGHT = 300
    selectChanged = pyqtSignal(str)

    def __init__(self, parent=None):  #
        super(Emotion, self).__init__(parent)
        super(Emotion, self).setWindowFlags(QtCore.Qt.Popup)
        self.resize(QSize(Emotion.WIDTH, Emotion.HEIGHT))
        self.setWindowTitle("表情選擇")
        #self.setModal(True)
        #self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint)
        self.emotion_initial()

    def emotion_initial(self):
        self.emotion_table = QTableView()
        self.emotion_table.horizontalHeader().setVisible(False)
        self.emotion_table.verticalHeader().setVisible(False)
        self.emotion_table.setMouseTracking(True)
        self.emotion_table.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.emotion_table.verticalHeader().setDefaultSectionSize(30)
        self.emotion_table.horizontalHeader().setDefaultSectionSize(30)
        self.emotion_table.setIconSize(QSize(30, 30))
        self.emotion_table.entered.connect(self.showEmotionTips)
        self.emotion_model = QStandardItemModel()

        emotions = os.listdir(Emotion.EMOTION_DIR)
        emotions_size = len(emotions)
        #TODO FIX SORTED MISSING CMP BUG
        #emotions = sorted(emotions,key=lambda e:(int(e.split("."))))
        #emotions = sorted(emotions,key=functools.cmp_to_key(emotionCmp))
        i = 0
        while i < emotions_size:
            self.add_emotion(emotions[i:i + 14])
            i = i + 14
        self.emotion_table.setModel(self.emotion_model)
        self.emotion_table.clicked.connect(self.emotion_click)
        #
        mainLayout = QVBoxLayout()
        mainLayout.addWidget(self.emotion_table)
        self.setLayout(mainLayout)
        #self.
    def showEmotionTips(self, index):
        if index.isValid():
            QToolTip.showText(QCursor.pos(), "Hello", None)

    def add_emotion(self, emotions):
        '''
        :param emotions a list of emotion will be adding to emotion table
        '''
        cells = []
        for emotion in emotions:
            item = QtGui.QStandardItem(
                QIcon(os.path.join(Emotion.EMOTION_DIR, emotion)), emotion)
            cells.append(item)
        self.emotion_model.appendRow(cells)

    #def eventFilter(self, event):
    #p = QCursor.pos() - self.pos()
    #item = self.emotion_table

    def emotion_click(self):
        row = self.emotion_table.currentIndex().row()
        column = self.emotion_table.currentIndex().column()
        #self.accept()
        self.close()
        self.selectChanged.emit(self.get_selected_emotion(row, column))

    def get_selected_emotion(self, row, column):
        emotion = self.emotion_model.data(self.emotion_model.index(
            row, column))
        if emotion:
            return (emotion)
        else:
            return "N/A"
Exemplo n.º 25
0
class DataBlock(QWidget):
    def __init__(self, parent=None):
        super(DataBlock, self).__init__(parent)
        self.parent = parent

        loadButton = QPushButton("&Load new data")
        #self.connect(loadButton, SIGNAL('clicked()'), self.on_load) #pyqt4
        loadButton.clicked.connect(self.on_load)
        dataListView = QListView()
        self.dataListModel = QStandardItemModel()
        dataListView.setModel(self.dataListModel)
        plotButton = QPushButton("&Plot data")
        #self.connect(plotButton, SIGNAL('clicked()'), self.on_plot)
        plotButton.clicked.connect(self.on_plot)
        layout = QVBoxLayout(self)
        layout.addWidget(loadButton)
        layout.addWidget(dataListView)
        layout.addWidget(plotButton)
        layout.addStretch(1)

    def update_data(self):
        self.parent.data = []
        for row in range(self.dataListModel.rowCount()):
            i = self.dataListModel.index(row, 0)
            checked = (self.dataListModel.data(
                i, Qt.CheckStateRole) == Qt.Checked)
            if checked:
                self.parent.data.append(self.allsets[row])

    def on_plot(self):
        self.update_data()
        #self.parent.plotblk.on_show()
        #        plots.plot(self.parent.data, axes=self.parent.canvas.axes,
        #            legend=self.parent.plotblk.legendChB.isChecked())
        plots.cvfit_plot(self.parent.data,
                         fig=self.parent.figure,
                         logX=self.parent.plotblk.logXChB.isChecked(),
                         logY=self.parent.plotblk.logYChB.isChecked(),
                         legend=self.parent.plotblk.legendChB.isChecked())
        self.parent.canvas.draw()

    def on_load(self):
        try:
            self.__on_load()
        except:
            pass

    def __on_load(self):
        filename, filt = QFileDialog.getOpenFileName(
            self.parent, "Open a data file...", self.parent.workdir,
            "Excel files (*.xls *.xlsx);;CSV files (*.csv);;TXT files (*.txt);;All files (*.*)"
        )
        self.parent.log.write('\nLoading file: ' + filename)
        type = filename.split('.')[-1]
        self.parent.workdir = os.path.dirname(filename)

        xlssheet = None
        if type == 'xls' or type == 'xlsx':
            book = xlrd.open_workbook(filename)
            sheets = book.sheet_names()
            dialog = dialogs.ExcelSheetDlg(sheets, self)
            if dialog.exec_():
                xlssheet = dialog.returnSheet()

        dialog = dialogs.LoadDataDlg(filename, sheet=xlssheet)
        if dialog.exec_():
            self.allsets = dialog.return_data()

        #self.allsets = cfio.read_sets_from_csv(filename, col=2)
        self.parent.log.write("Loaded: " + os.path.split(str(filename))[1])
        self.dataListModel.clear()
        for set in self.allsets:
            item = QStandardItem(set.title)
            item.setCheckState(Qt.Checked)
            item.setCheckable(True)
            self.dataListModel.appendRow(item)
            self.parent.log.write('\n' + set.title)
            self.parent.log.write(str(set))

        self.parent.data = self.allsets
        self.parent.fname = filename
Exemplo n.º 26
0
class ListCategory(QSortFilterProxyModel):
    """Expose a list of items as a category for the CompletionModel."""
    def __init__(self,
                 name: str,
                 items: Iterable[Tuple[str, ...]],
                 sort: bool = True,
                 delete_func: util.DeleteFuncType = None,
                 parent: QWidget = None):
        super().__init__(parent)
        self.name = name
        self.srcmodel = QStandardItemModel(parent=self)
        self._pattern = ''
        # ListCategory filters all columns
        self.columns_to_filter = [0, 1, 2]
        self.setFilterKeyColumn(-1)
        for item in items:
            self.srcmodel.appendRow([QStandardItem(x) for x in item])
        self.setSourceModel(self.srcmodel)
        self.delete_func = delete_func
        self._sort = sort

    def set_pattern(self, val):
        """Setter for pattern.

        Args:
            val: The value to set.
        """
        if len(val) > 5000:  # avoid crash on huge search terms (#5973)
            log.completion.warning(f"Trimming {len(val)}-char pattern to 5000")
            val = val[:5000]
        self._pattern = val
        val = re.sub(r' +', r' ', val)  # See #1919
        val = re.escape(val)
        val = val.replace(r'\ ', '.*')
        rx = QRegularExpression(val, QRegularExpression.CaseInsensitiveOption)
        qtutils.ensure_valid(rx)
        self.setFilterRegularExpression(rx)
        self.invalidate()
        sortcol = 0
        self.sort(sortcol)

    def lessThan(self, lindex, rindex):
        """Custom sorting implementation.

        Prefers all items which start with self._pattern. Other than that, uses
        normal Python string sorting.

        Args:
            lindex: The QModelIndex of the left item (*left* < right)
            rindex: The QModelIndex of the right item (left < *right*)

        Return:
            True if left < right, else False
        """
        qtutils.ensure_valid(lindex)
        qtutils.ensure_valid(rindex)

        left = self.srcmodel.data(lindex)
        right = self.srcmodel.data(rindex)

        if left is None or right is None:  # pragma: no cover
            log.completion.warning("Got unexpected None value, "
                                   "left={!r} right={!r} "
                                   "lindex={!r} rindex={!r}".format(
                                       left, right, lindex, rindex))
            return False

        leftstart = left.startswith(self._pattern)
        rightstart = right.startswith(self._pattern)

        if leftstart and not rightstart:
            return True
        elif rightstart and not leftstart:
            return False
        elif self._sort:
            return left < right
        else:
            return False
class MainWindow(QtWidgets.QMainWindow):
    connection_status = 0
    file_path = ''
    ftp = ftplib.FTP('')

    def __init__(self):
        super(MainWindow, self).__init__()
        self.ui = uic.loadUi('ftp.ui', self)
        self.model = QFileSystemModel()
        self.setFixedSize(810, 563)
        self.init_ui()
        self.bind_event()
        self.set_validation_type()

    def init_ui(self):
        self.populate_local_tree_view()
        self.show()

    def populate_local_tree_view(self):
        self.fsm = QFileSystemModel(self)
        self.fsm.setRootPath('')
        self.fsm.setReadOnly(True)
        # self.fsm.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot)
        self.ui.local_tree_view.setModel(self.fsm)
        self.local_tree_view.setRootIndex(self.fsm.index(self.fsm.rootPath()))
        self.ui.local_tree_view.setAnimated(False)
        self.ui.local_tree_view.setIndentation(20)
        self.ui.local_tree_view.setSortingEnabled(True)

    def bind_event(self):
        self.ui.btn_login.clicked.connect(self.on_login_click)
        self.ui.local_tree_view.clicked.connect(self.on_tree_view_clicked)
        # self.ui.tableView.doubleClicked.connect(self.on_click)

    def on_tree_view_clicked(self, index):
        index_item = self.fsm.index(index.row(), 0, index.parent())
        path = str(self.fsm.filePath(index_item))
        self.selected_file_path = path

    def set_validation_type(self):
        only_int = QIntValidator()
        self.ui.input_port_number.setValidator(
            only_int)  # allowing only integer input
        self.ui.input_password.setEchoMode(QLineEdit.Password)

    @pyqtSlot()
    def on_login_click(self):
        if self.connection_status == 0:
            host_name = self.ui.input_host_name.text().strip()
            usr = self.ui.input_username.text().strip()
            password = self.ui.input_password.text().strip()
            port_number = self.ui.input_port_number.text().strip()

            if is_not_blank(host_name) and is_not_blank(usr) and is_not_blank(
                    password) and is_not_blank(port_number):
                port_number = int(port_number)
                try:
                    self.ftp.connect(host_name, port_number)
                    self.ftp.login(usr, password)

                    self.add_item_to_log_list_widget("login successful")
                    self.ui.btn_login.setText("Disconnect")
                    self.connection_status = 1
                    self.get_remote_file()
                except ftplib.all_errors:
                    self.add_item_to_log_list_widget(
                        "logged in incorrect credentials")
                    show_error_dialog("Please check your credentials")
            else:
                show_error_dialog("Please enter all credentials")
        else:
            self.logout()

    def get_remote_file(self):
        self.model = QStandardItemModel()
        self.model.setColumnCount(3)
        self.model.setHorizontalHeaderLabels(['Name', 'Size', 'Last Modified'])

        files = self.ftp.nlst()
        i = 0
        for filename in files:
            self.add_row_item(i, 0, self.model, filename)
            try:
                self.ftp.voidcmd('TYPE I')
                size = str(self.ftp.size(filename))
                size = size + " KB"
                self.add_row_item(i, 1, self.model, size)
                modified_time = self.ftp.sendcmd('MDTM ' + filename)
                formatted_time = datetime.strptime(
                    modified_time[4:],
                    "%Y%m%d%H%M%S").strftime("%d %B %Y %H:%M:%S")
                self.add_row_item(i, 2, self.model, formatted_time)
                self.add_item_to_log_list_widget(
                    "remote directory listing successful")
            except:
                item = QStandardItem(filename)
                item.setTextAlignment(Qt.AlignVCenter)
                item.setEditable(False)
                self.model.setItem(i, 0, item)
                item.setIcon(QIcon("static/ic_folder.png"))
            i += 1
        self.ui.tableView.setModel(self.model)
        self.ui.tableView.setSelectionBehavior(QTableView.SelectRows)
        self.set_property()

    @staticmethod
    def add_row_item(row, column, model, text):
        item = QStandardItem(text)
        item.setTextAlignment(Qt.AlignVCenter)
        item.setEditable(False)
        model.setItem(row, column, item)

    def set_property(self):
        self.ui.tableView.setColumnWidth(0, 120)
        self.ui.tableView.setColumnWidth(1, 120)
        self.ui.tableView.setColumnWidth(2, 160)
        self.ui.tableView.setShowGrid(False)
        header = self.ui.tableView.horizontalHeader()
        header.setDefaultAlignment(Qt.AlignVCenter)

    def contextMenuEvent(self, event):
        self.menu = QMenu(self)
        rename_action = QAction('Rename', self)
        rename_action.triggered.connect(lambda: self.rename_slot(event))
        self.menu.addAction(rename_action)

        delete_action = QAction('Delete', self)
        delete_action.triggered.connect(lambda: self.delete_slot(event))
        self.menu.addAction(delete_action)

        download_action = QAction('Download', self)
        download_action.triggered.connect(lambda: self.download_slot(event))
        self.menu.addAction(download_action)

        create_dir_action = QAction('New Dir', self)
        create_dir_action.triggered.connect(
            lambda: self.create_dir_slot(event))
        self.menu.addAction(create_dir_action)

        upload_file_action = QAction('Upload', self)
        upload_file_action.triggered.connect(
            lambda: self.upload_file_slot(event))
        self.menu.addAction(upload_file_action)

        self.menu.popup(QCursor.pos())

    @pyqtSlot()
    def rename_slot(self, event):
        # indexes = self.ui.tableView.selectionModel().selectedRows()
        # for index in sorted(indexes):
        # print('Row %d is selected' % index.row())
        for index in sorted(self.ui.tableView.selectionModel().selectedRows()):
            selected_filename = self.model.data(
                self.model.index(index.row(), 0))
            new_name = show_input_dialog(self, "Rename", "New Name")
            if new_name != '' and len(new_name) != 0:
                try:
                    self.ftp.rename(selected_filename, new_name)
                    self.get_remote_file()
                    self.add_item_to_log_list_widget(selected_filename +
                                                     " change with " +
                                                     new_name +
                                                     " successfully on remote")
                except:
                    show_error_dialog("Unexpected error occurred")
            else:
                show_error_dialog("Invalid file name")
            break  # just rename one file

    @pyqtSlot()
    def delete_slot(self, event):
        for index in sorted(self.ui.tableView.selectionModel().selectedRows()):
            try:
                selected_filename = self.model.data(
                    self.model.index(index.row(), 0))
                self.ftp.rmd(selected_filename)
                self.add_item_to_log_list_widget(selected_filename +
                                                 " deleted on remote")
            except:
                print("")

    @pyqtSlot()
    def download_slot(self, event):
        for index in sorted(self.ui.tableView.selectionModel().selectedRows()):
            selected_filename = str(
                self.model.data(self.model.index(index.row(), 0)))
            try:
                local_file = open(selected_filename, 'wb')
                self.ftp.retrbinary('RETR ' + selected_filename,
                                    local_file.write, 1024)
                local_file.close()
                self.add_item_to_log_list_widget(
                    selected_filename + "file is downloaded successfully")
            except Exception as e:
                print(e)  # show_error_dialog("Unexpected error occurred")

    @pyqtSlot()
    def create_dir_slot(self, event):
        self.add_item_to_log_list_widget(
            "test file is downloaded successfully")
        file_name = show_input_dialog(self, "New Directory", "Directory Name")
        try:
            if file_name != '' and len(file_name) != 0:
                self.ftp.mkd(file_name)
                self.get_remote_file()
                self.add_item_to_log_list_widget(file_name +
                                                 " created successfully")
            else:
                show_error_dialog("Invalid directory name")
        except:
            print("")

    @pyqtSlot()
    def upload_file_slot(self, event):
        try:
            if os.path.isfile(self.file_path):
                file_name = os.path.basename(self.file_path)
                self.ftp.storbinary('STOR ' + file_name,
                                    open(self.file_path, 'rb'))
                self.get_remote_file()
                self.add_item_to_log_list_widget(file_name +
                                                 " uploaded successfully")
        except:
            show_error_dialog("Please choose only one file")

    @pyqtSlot()
    def on_delete_file_click(self):
        files = list(self.ftp.nlst())
        for f in files:
            self.ftp.delete(f)

    def get_size(self, directory):
        size = 0
        for file in self.ftp.nlst(directory):
            size += self.ftp.size(file)
        return size

    def add_item_to_log_list_widget(self, log):
        self.ui.log_list_widget.addItem("status\t" + log)

    def logout(self):
        self.ftp.close()
        self.add_item_to_log_list_widget("logout successfully")
        self.connection_status = 0
        self.ui.btn_login.setText("Connect")
class MessageList(MyTreeView):
    class Columns(IntEnum):
        HEIGHT = 0
        FROM = 1
        DATA = 2

    filter_columns = [Columns.HEIGHT, Columns.FROM, Columns.DATA]

    ROLE_SORT_ORDER = Qt.UserRole + 1000
    ROLE_ASSET_STR = Qt.UserRole + 1001

    def __init__(self, parent):
        super().__init__(parent,
                         self.create_menu,
                         stretch_column=self.Columns.DATA,
                         editable_columns=[])
        self.wallet = self.parent.wallet
        self.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.setSortingEnabled(True)
        self.std_model = QStandardItemModel(self)
        self.proxy = MySortModel(self, sort_role=self.ROLE_SORT_ORDER)
        self.proxy.setSourceModel(self.std_model)
        self.setModel(self.proxy)
        self.update()
        self.sortByColumn(self.Columns.HEIGHT, Qt.AscendingOrder)
        self.messages = {}

    def webopen_safe(self, url):
        show_warn = self.parent.config.get('show_ipfs_warning', True)
        if show_warn:
            cb = QCheckBox(_("Don't show this message again."))
            cb_checked = False

            def on_cb(x):
                nonlocal cb_checked
                cb_checked = x == Qt.Checked

            cb.stateChanged.connect(on_cb)
            goto = self.parent.question(_(
                'You are about to visit:\n\n'
                '{}\n\n'
                'IPFS hashes can link to anything. Please follow '
                'safe practices and common sense. If you are unsure '
                'about what\'s on the other end of an IPFS, don\'t '
                'visit it!\n\n'
                'Are you sure you want to continue?').format(url),
                                        title=_('Warning: External Data'),
                                        checkbox=cb)

            if cb_checked:
                self.parent.config.set_key('show_ipfs_warning', False)
            if goto:
                webopen(url)
        else:
            webopen(url)

    def mouseDoubleClickEvent(self, event: QMouseEvent):
        idx = self.indexAt(event.pos())
        if not idx.isValid():
            return

        # Get the IPFS from 3rd column
        hm_idx = self.model().mapToSource(self.model().index(idx.row(), 2))
        data = self.std_model.data(hm_idx)
        if data[:2] == 'Qm':  # If it starts with Qm, it's an IPFS
            url = ipfs_explorer_URL(self.parent.config, 'ipfs', data)
            self.webopen_safe(url)

    def refresh_headers(self):
        headers = {
            self.Columns.HEIGHT: _('Height'),
            self.Columns.FROM: _('From'),
            self.Columns.DATA: _('Data'),
        }
        self.update_headers(headers)

    @profiler
    def update(self):
        if self.maybe_defer_update():
            return
        self.messages = self.wallet.get_messages()
        self.proxy.setDynamicSortFilter(
            False)  # temp. disable re-sorting after every change
        self.std_model.clear()
        self.refresh_headers()
        set_message = None

        for height, data in self.messages.items():
            for d in data:
                is_from = d[0]
                message_txt = d[1]

                if not self.parent.config.get('get_dev_notifications',
                                              True) and d[2] is None:
                    # The source will only be null if this is a dev notification
                    continue

                # create item
                labels = [height, is_from, message_txt]
                asset_item = [QStandardItem(e) for e in labels]
                # align text and set fonts
                for i, item in enumerate(asset_item):
                    item.setTextAlignment(Qt.AlignVCenter)

                # add item
                count = self.std_model.rowCount()
                self.std_model.insertRow(count, asset_item)

        self.set_current_idx(set_message)
        self.filter()
        self.proxy.setDynamicSortFilter(True)

    def add_copy_menu(self, menu, idx):
        cc = menu.addMenu(_("Copy"))
        for column in self.Columns:
            if self.isColumnHidden(column):
                continue
            column_title = self.model().headerData(column, Qt.Horizontal)
            hm_idx = self.model().mapToSource(self.model().index(
                idx.row(), column))
            column_data = self.std_model.data(hm_idx)
            cc.addAction(column_title,
                         lambda text=column_data, title=column_title: self.
                         place_text_on_clipboard(text, title=title))
        return cc

    def create_menu(self, position):
        org_idx: QModelIndex = self.indexAt(position)

        menu = QMenu()
        self.add_copy_menu(menu, org_idx)

        menu.exec_(self.viewport().mapToGlobal(position))

    def place_text_on_clipboard(self, text: str, *, title: str = None) -> None:
        if is_address(text):
            try:
                self.wallet.check_address_for_corruption(text)
            except InternalAddressCorruption as e:
                self.parent.show_error(str(e))
                raise
        super().place_text_on_clipboard(text, title=title)

    def get_edit_key_from_coordinate(self, row, col):
        return None

    # We don't edit anything here
    def on_edited(self, idx, edit_key, *, text):
        pass
Exemplo n.º 29
0
class ListCategory(QSortFilterProxyModel):
    """Expose a list of items as a category for the CompletionModel."""
    def __init__(self, name, items, sort=True, delete_func=None, parent=None):
        super().__init__(parent)
        self.name = name
        self.srcmodel = QStandardItemModel(parent=self)
        self._pattern = ''
        # ListCategory filters all columns
        self.columns_to_filter = [0, 1, 2]
        self.setFilterKeyColumn(-1)
        for item in items:
            self.srcmodel.appendRow([QStandardItem(x) for x in item])
        self.setSourceModel(self.srcmodel)
        self.delete_func = delete_func
        self._sort = sort

    def set_pattern(self, val):
        """Setter for pattern.

        Args:
            val: The value to set.
        """
        self._pattern = val
        val = re.sub(r' +', r' ', val)  # See #1919
        val = re.escape(val)
        val = val.replace(r'\ ', '.*')
        rx = QRegExp(val, Qt.CaseInsensitive)
        self.setFilterRegExp(rx)
        self.invalidate()
        sortcol = 0
        self.sort(sortcol)

    def lessThan(self, lindex, rindex):
        """Custom sorting implementation.

        Prefers all items which start with self._pattern. Other than that, uses
        normal Python string sorting.

        Args:
            lindex: The QModelIndex of the left item (*left* < right)
            rindex: The QModelIndex of the right item (left < *right*)

        Return:
            True if left < right, else False
        """
        qtutils.ensure_valid(lindex)
        qtutils.ensure_valid(rindex)

        left = self.srcmodel.data(lindex)
        right = self.srcmodel.data(rindex)

        leftstart = left.startswith(self._pattern)
        rightstart = right.startswith(self._pattern)

        if leftstart and not rightstart:
            return True
        elif rightstart and not leftstart:
            return False
        elif self._sort:
            return left < right
        else:
            return False
Exemplo n.º 30
0
class MassTabSelectorGUI(QDockWidget):

    """
    classdocs
    """
    masstabViewRaisedSignal = pyqtSignal(object)

    """ constructor """

    def __init__(self, parent=None):
        super(MassTabSelectorGUI, self).__init__(parent)
        self.ui = Ui_DockWidget_MassTabSelector()
        self.ui.setupUi(self)

    def setup(self, analysis):
        self.ana = analysis
        self.__connect_events()

    def __connect_events(self):
        self.model = QStandardItemModel()
        self.mass_list = []
        for i in range(10):
            mass = 184 + i
            self.mass_list.append(str(mass))
        for i in range(10):
            mass = 209 + i
            self.mass_list.append(str(mass))
        for i in range(10):
            mass = 273.3 + i
            self.mass_list.append(str(mass))
        for i in range(10):
            mass = 294 + i
            self.mass_list.append(str(mass))
        for mass in self.mass_list:
            item = QStandardItem(mass)
            item.setCheckable(True)
            item.setEditable(True)
            self.model.appendRow(item)
        self.view = self.ui.listView_Mass
        self.view.setModel(self.model)
        # changes in one item, don't know which one
        self.model.itemChanged.connect(self.change_list)
        # changes in button
        self.ui.pushButton_ChangeList.clicked.connect(self.emit_list_signal)
        # get peaks found and update automatically the mass list
        self.ana.masstabSelectorRaisedSignal.connect(self.update_list_view)

    def update_list_view(self, xind):
        self.mass_list = []
        for i in range(len(xind)):
            m = "{:.1f}".format(float(xind[i]))
            self.mass_list.append(str(m))
        self.model.clear()
        self.model = QStandardItemModel()

        for mass in self.mass_list:
            item = QStandardItem(mass)
            item.setCheckable(True)
            item.setEditable(True)
            item.setCheckState(Qt.Checked)
            self.model.appendRow(item)
        self.view = self.ui.listView_Mass
        self.view.setModel(self.model)
        # changes in one item, don't know which one
        self.model.itemChanged.connect(self.change_list)

    def change_list(self):
        log.debug("event from %s", self.sender())
        self.oneIsChecked = False
        self.mass_list = []
        count = self.model.rowCount()
        for i in range(count):
            checked = self.model.item(i).checkState()
            if checked:
                mass_name = self.model.data(self.model.index(i, 0))
                self.mass_list.append(mass_name)
                self.oneIsChecked = True

    def emit_list_signal(self):
        log.debug("event from %s", self.sender())
        self.change_list()
        if self.oneIsChecked:
            self.masstabViewRaisedSignal.emit(self.mass_list)
Exemplo n.º 31
0
 def data(self, index, role=None):
     if role == Qt.TextAlignmentRole:
         return Qt.AlignCenter
     return QStandardItemModel.data(self, index, role)
Exemplo n.º 32
0
class ListCategory(QSortFilterProxyModel):

    """Expose a list of items as a category for the CompletionModel."""

    def __init__(self, name, items, sort=True, delete_func=None, parent=None):
        super().__init__(parent)
        self.name = name
        self.srcmodel = QStandardItemModel(parent=self)
        self._pattern = ''
        # ListCategory filters all columns
        self.columns_to_filter = [0, 1, 2]
        self.setFilterKeyColumn(-1)
        for item in items:
            self.srcmodel.appendRow([QStandardItem(x) for x in item])
        self.setSourceModel(self.srcmodel)
        self.delete_func = delete_func
        self._sort = sort

    def set_pattern(self, val):
        """Setter for pattern.

        Args:
            val: The value to set.
        """
        self._pattern = val
        val = re.sub(r' +', r' ', val)  # See #1919
        val = re.escape(val)
        val = val.replace(r'\ ', '.*')
        rx = QRegExp(val, Qt.CaseInsensitive)
        self.setFilterRegExp(rx)
        self.invalidate()
        sortcol = 0
        self.sort(sortcol)

    def lessThan(self, lindex, rindex):
        """Custom sorting implementation.

        Prefers all items which start with self._pattern. Other than that, uses
        normal Python string sorting.

        Args:
            lindex: The QModelIndex of the left item (*left* < right)
            rindex: The QModelIndex of the right item (left < *right*)

        Return:
            True if left < right, else False
        """
        qtutils.ensure_valid(lindex)
        qtutils.ensure_valid(rindex)

        left = self.srcmodel.data(lindex)
        right = self.srcmodel.data(rindex)

        if left is None or right is None:  # pragma: no cover
            log.completion.warning("Got unexpected None value, "
                                   "left={!r} right={!r} "
                                   "lindex={!r} rindex={!r}"
                                   .format(left, right, lindex, rindex))
            return False

        leftstart = left.startswith(self._pattern)
        rightstart = right.startswith(self._pattern)

        if leftstart and not rightstart:
            return True
        elif rightstart and not leftstart:
            return False
        elif self._sort:
            return left < right
        else:
            return False
Exemplo n.º 33
0
class MyPartitionWidget(QWidget):
    unassignedStr="NaN / Outside Range"
    def __init__(self,model,setFunc,comboInit=None,checkable=True,colors=False,parent=None):
        QWidget.__init__(self,parent)
        self.ui=Ui_PartitionWidget()
        self.ui.setupUi(self)
        self.model = model
        self.checkable = checkable
        self.colors = colors
        self.setFunction = setFunc

        initial=None
        search=model.datafield(comboInit)
        i=0
        for col in sorted(set(self.model.cols) - self.model.cfg.internalCols,key=self.model.prettyname):
            if (self.model.fieldmax(col) - self.model.trueFieldMin(col)) > 0:
                self.ui.comboBox.addItem(self.model.prettyname(col),col)
                if col == search:
                    initial=i
                i += 1


        self.parts=None
        self.ui.groupBox.setChecked(initial is not None)
        self.listModel=QStandardItemModel()
        self.ui.listView.setModel(self.listModel)
        if self.colors:
            self.ui.listView.doubleClicked.connect(self.selectColor)

        if initial is not None:
            self.ui.comboBox.setCurrentIndex(initial)
        self.ui.comboBox.currentIndexChanged.connect(self.onComboChange)
        self.onComboChange()

        self.ui.groupBox.toggled.connect(self.rebuild)
        self.ui.binSpinBox.editingFinished.connect(self.rebuild)
        self.ui.maxSpinBox.editingFinished.connect(self.rebuild)
        self.ui.minSpinBox.editingFinished.connect(self.rebuild)
        self.listModel.dataChanged.connect(self.update)

    def onComboChange(self):
        field=self.ui.comboBox.itemData(self.ui.comboBox.currentIndex())

        self.ui.minSpinBox.setMinimum(self.model.fieldmin(field))
        self.ui.maxSpinBox.setMinimum(self.model.fieldmin(field))
        self.ui.minSpinBox.setMaximum(self.model.fieldmax(field))
        self.ui.maxSpinBox.setMaximum(self.model.fieldmax(field))

        self.ui.minSpinBox.setValue(self.model.fieldmin(field))
        self.ui.maxSpinBox.setValue(self.model.fieldmax(field))
        self.rebuild()

    def rebuild(self):
        self.listModel.clear()
        if not self.ui.groupBox.isChecked() and self.ui.groupBox.isCheckable():
            self.parts=None
            self.listModel.clear()
            self.update()
            return
        field=self.ui.comboBox.itemData(self.ui.comboBox.currentIndex())

        minimum=None
        if self.ui.minCheckBox.isChecked():
            minimum=self.ui.minSpinBox.value()
        maximum=None
        if self.ui.maxCheckBox.isChecked():
            maximum=self.ui.maxSpinBox.value()
        bins=None
        if self.ui.binCheckBox.isChecked():
            bins=self.ui.binSpinBox.value()

        self.parts=self.model.partition(field,minimum,maximum,bins)

        lst=list(sorted(self.parts.keys()))
        keep=np.zeros(self.model.data.shape,dtype=bool)
        for i,s in enumerate(lst):
            c=self.model.cfg.color(field,s,i)
            item=QStandardItem(s)
            if self.colors:
                item.setData(QColor(c),Qt.DecorationRole)
            if self.checkable:
                item.setCheckable(True)
                item.setCheckState(Qt.Checked)
            item.setData(s)
            self.listModel.appendRow(item)
            keep |= self.parts[s]
        keep = np.logical_not(keep)
        if np.count_nonzero(keep):
            self.parts[MyPartitionWidget.unassignedStr] = keep
            item=QStandardItem(MyPartitionWidget.unassignedStr)
            if self.colors:
                item.setData(QColor("white"),Qt.DecorationRole)
            if self.checkable:
                item.setCheckable(True)
                item.setCheckState(Qt.Unchecked)
            item.setData("NaN / Outside Range")
            self.listModel.appendRow(item)
        self.update()


    def selectColor(self,index):
        c=QColorDialog.getColor(self.listModel.data(index,Qt.DecorationRole),self)
        if c.isValid():
            self.listModel.setData(index,c,Qt.DecorationRole)

    def update(self):
        if self.setFunction is not None:
            if self.colors:
                self.setFunction(self.currentStacks())
            else:
                self.setFunction(self.currentKeep())

    def current(self):
        parts=None
        if self.parts is not None:
            parts={}
            for i in range(self.listModel.rowCount()):
                it=self.listModel.item(i)
                if not self.checkable or it.checkState() == Qt.Checked:
                    parts[it.text()]=self.parts[it.data()]
        return parts

    def currentStacks(self):
        stack=None
        if self.parts is not None:
            stack=[[],[],[]]
            for i in range(self.listModel.rowCount()):
                it=self.listModel.item(i)
                if not self.checkable or it.checkState() == Qt.Checked:
                    if self.colors:
                        stack[0].append(self.parts[it.data()])
                        stack[1].append(it.data(Qt.DecorationRole).name())
                        stack[2].append(it.text())
        return stack

    def currentKeep(self):
        keep=np.ones(self.model.data.shape,dtype=bool)
        if self.parts is not None:
            keep=np.zeros(self.model.data.shape,dtype=bool)
            for i in range(self.listModel.rowCount()):
                it=self.listModel.item(i)
                if not self.checkable or it.checkState() == Qt.Checked:
                    keep |= self.parts[it.data()]
        return keep
Exemplo n.º 34
0
class MyControlPanel(QWidget):
    flagselected = pyqtSignal(int)

    def __init__(self, model, parent=None):
        QWidget.__init__(self, parent)
        self.ui = Ui_ControlPanel()
        self.ui.setupUi(self)
        self.model = model

        sortmenu = QMenu()
        lIinitial = None
        searchLegend = model.datafield(model.cfg.legendInitial)
        for i, col in enumerate(
                sorted(set(self.model.cols) - self.model.cfg.internalCols,
                       key=self.model.prettyname)):
            # Sort Menu
            a = sortmenu.addAction(self.model.prettyname(col))
            a.setData(col)
            a.triggered.connect(self.onAddSortField)

            # Combo boxes
            self.ui.partComboBox.addItem(self.model.prettyname(col), col)
            self.ui.legendComboBox.addItem(self.model.prettyname(col), col)
            if col == searchLegend:
                lIinitial = i

        # Sort
        self.ui.addSortField.setMenu(sortmenu)
        self.ui.sortByListWidget.itemSelectionChanged.connect(
            self.onSelectionChange)
        self.onSelectionChange()
        self.ui.removeSortField.clicked.connect(self.onRemoveSortField)
        self.ui.moveSortField.clicked.connect(self.onMoveSortFieldUp)
        self.ui.sortAscendingCheckBox.clicked.connect(
            self.onSortAscendingChange)

        # Legend
        self.legend = None
        self.ui.legendGroupBox.setChecked(lIinitial is not None)
        self.legendModel = QStandardItemModel()
        self.ui.legendListView.setModel(self.legendModel)
        self.ui.legendListView.doubleClicked.connect(self.selectLegendColor)

        if lIinitial is not None:
            self.ui.legendComboBox.setCurrentIndex(lIinitial)
        self.ui.legendComboBox.currentIndexChanged.connect(
            self.onLegendComboChange)
        self.onLegendComboChange()

        self.ui.legendGroupBox.toggled.connect(self.calcLegend)
        self.ui.legendBinSpinBox.editingFinished.connect(self.calcLegend)
        self.ui.legendMaxSpinBox.editingFinished.connect(self.calcLegend)
        self.ui.legendMinSpinBox.editingFinished.connect(self.calcLegend)
        self.legendModel.dataChanged.connect(self.updateModelLegend)

        # Partition
        self.partitions = None
        self.partitionModel = QStringListModel()
        self.ui.partitionList.setModel(self.partitionModel)
        self.ui.partitionList.selectionModel().currentChanged.connect(
            self.onSelectPartition)

        self.ui.partComboBox.currentIndexChanged.connect(
            self.onPartitionComboChange)
        self.onPartitionComboChange()

        self.ui.partitionBox.toggled.connect(self.calcPartitions)
        self.ui.partBinSpinBox.valueChanged.connect(self.calcPartitions)
        self.ui.partMaxSpinBox.valueChanged.connect(self.calcPartitions)
        self.ui.partMinSpinBox.valueChanged.connect(self.calcPartitions)

        # Flags
        if self.model.flagFilter.isActive():
            self.ui.flagIgnore.setChecked(True)
            if self.model.flagFilter.invert:
                self.ui.flagExclude.setChecked(True)
            else:
                self.ui.flagKeep.setChecked(True)
        else:
            self.ui.flagIgnore.setChecked(True)
        self.model.flagFilter.filterchange.connect(self.onFlagChange)
        self.flagModel = QStringListModel()
        self.ui.flaggedList.setModel(self.flagModel)
        self.ui.flagIgnore.clicked.connect(self.onIgnoreFlags)
        self.ui.flagExclude.clicked.connect(self.onExcludeFlags)
        self.ui.flagKeep.clicked.connect(self.onOnlyFlags)
        self.ui.flaggedList.selectionModel().currentChanged.connect(
            self.onSelectFlag)

        # Limits
        self.ui.limitCheckBox.clicked.connect(self.onLimitChange)
        self.ui.limTopButton.clicked.connect(self.onLimitChange)
        self.ui.limRandomButton.clicked.connect(self.onLimitChange)
        self.ui.limitSpinBox.editingFinished.connect(self.onLimitChange)
        self.onLimitChange()

        # Filters
        self.filtermodel = FilterModel(model.topfilter, model)
        self.ui.filterTreeView.setEditTriggers(
            QAbstractItemView.AllEditTriggers)
        self.ui.filterTreeView.setModel(self.filtermodel)
        if qt5:
            self.ui.filterTreeView.header().setSectionResizeMode(
                0, QHeaderView.ResizeToContents)
            self.ui.filterTreeView.header().setSectionResizeMode(
                1, QHeaderView.ResizeToContents)
        else:
            self.ui.filterTreeView.header().setResizeMode(
                0, QHeaderView.ResizeToContents)
            self.ui.filterTreeView.header().setResizeMode(
                1, QHeaderView.ResizeToContents)
        self.ui.filterTreeView.setItemDelegate(
            filterEditDelegate.FilterItemDelegate())
        self.filtermodel.rowsInserted.connect(self.ui.filterTreeView.expandAll)
        self.ui.filterTreeView.expandAll()

        # Filters Save/Load
        self.ui.saveFiltersButton.clicked.connect(self.onSaveFilters)
        self.ui.loadFiltersButton.clicked.connect(self.onLoadFilters)

    # Sorting
    def onAddSortField(self):
        field = self.sender().data()
        self.ui.sortByListWidget.addItem(self.model.prettyname(field))
        self.model.sortlst.append(field)
        self.model.onSortChange()

    def onSelectionChange(self):
        hasSelection = bool(len(self.ui.sortByListWidget.selectedItems()))
        self.ui.removeSortField.setEnabled(hasSelection)
        canMove = hasSelection
        for item in self.ui.sortByListWidget.selectedItems():
            canMove &= self.ui.sortByListWidget.row(item) != 0
        self.ui.moveSortField.setEnabled(canMove)

    def onRemoveSortField(self):
        rows = []
        for item in self.ui.sortByListWidget.selectedItems():
            rows.append(self.ui.sortByListWidget.row(item))
        for r in sorted(rows, reverse=True):
            del self.model.sortlst[r]
            self.ui.sortByListWidget.takeItem(r)
        self.model.onSortChange()
        self.onSelectionChange()

    def onMoveSortFieldUp(self):
        rows = []
        for item in self.ui.sortByListWidget.selectedItems():
            rows.append(self.ui.sortByListWidget.row(item))
        for r in sorted(rows):
            self.model.sortlst.insert(r - 1, self.model.sortlst.pop(r))
            self.ui.sortByListWidget.insertItem(
                r - 1, self.ui.sortByListWidget.takeItem(r))
            self.ui.sortByListWidget.setCurrentItem(
                self.ui.sortByListWidget.item(r - 1))
        self.model.onSortChange()

    def setSort(self, lst):
        for field in lst:
            self.ui.sortByListWidget.addItem(self.model.prettyname(field))
            self.model.sortlst.append(field)
        self.model.onSortChange()

    def onSortAscendingChange(self):
        self.model.reverseSort = not self.ui.sortAscendingCheckBox.isChecked()
        self.model.onSortChange()

    # Legends
    def onLegendComboChange(self):
        field = self.ui.legendComboBox.itemData(
            self.ui.legendComboBox.currentIndex())
        enable = not self.model.isCategorical(field)

        if self.ui.legendGroupBox.isChecked() or not enable:
            self.ui.legendBinSpinBox.setEnabled(enable)
            self.ui.legendMinSpinBox.setEnabled(enable)
            self.ui.legendMaxSpinBox.setEnabled(enable)

        self.ui.legendMinSpinBox.setMinimum(self.model.fieldmin(field))
        self.ui.legendMaxSpinBox.setMinimum(self.model.fieldmin(field))
        self.ui.legendMinSpinBox.setMaximum(self.model.fieldmax(field))
        self.ui.legendMaxSpinBox.setMaximum(self.model.fieldmax(field))

        self.ui.legendMinSpinBox.setValue(self.model.fieldmin(field))
        self.ui.legendMaxSpinBox.setValue(self.model.fieldmax(field))
        self.calcLegend()

    def calcLegend(self):
        self.legendModel.clear()
        if not self.ui.legendGroupBox.isChecked():
            self.legend = None
            self.legendModel.clear()
            self.updateModelLegend()
            return
        field = self.ui.legendComboBox.itemData(
            self.ui.legendComboBox.currentIndex())
        num = None
        minimum = None
        maximum = None
        if self.ui.legendBinSpinBox.isEnabled():
            num = self.ui.legendBinSpinBox.value()
            minimum = self.ui.legendMinSpinBox.value()
            maximum = self.ui.legendMaxSpinBox.value()
        self.legend = self.model.partition(field, minimum, maximum, num)
        lst = list(sorted(self.legend.keys()))
        for i, s in enumerate(lst):
            c = self.model.cfg.color(field, s, i)
            item = QStandardItem(s)
            item.setData(QColor(c), Qt.DecorationRole)
            item.setData(s)
            self.legendModel.appendRow(item)
        self.updateModelLegend()

    def selectLegendColor(self, index):
        c = QColorDialog.getColor(
            self.legendModel.data(index, Qt.DecorationRole), self)
        if c.isValid():
            self.legendModel.setData(index, c, Qt.DecorationRole)

    def updateModelLegend(self):
        stack = None
        if self.legend is not None:
            stack = [[], [], []]
            for i in range(self.legendModel.rowCount()):
                it = self.legendModel.item(i)
                stack[0].append(self.legend[it.data()])
                stack[1].append(it.data(Qt.DecorationRole).name())
                stack[2].append(it.text())
        self.model.setStacks(stack)

    # Partitions
    def onPartitionComboChange(self):
        field = self.ui.partComboBox.itemData(
            self.ui.partComboBox.currentIndex())
        enable = not self.model.isCategorical(field)

        if self.ui.partitionBox.isChecked() or not enable:
            self.ui.partBinSpinBox.setEnabled(enable)
            self.ui.partMinSpinBox.setEnabled(enable)
            self.ui.partMaxSpinBox.setEnabled(enable)

        self.ui.partMinSpinBox.setMinimum(self.model.fieldmin(field))
        self.ui.partMaxSpinBox.setMinimum(self.model.fieldmin(field))
        self.ui.partMinSpinBox.setMaximum(self.model.fieldmax(field))
        self.ui.partMaxSpinBox.setMaximum(self.model.fieldmax(field))

        self.ui.partMinSpinBox.setValue(self.model.fieldmin(field))
        self.ui.partMaxSpinBox.setValue(self.model.fieldmax(field))
        self.calcPartitions()

    def calcPartitions(self):
        self.model.clearPartition()
        if not self.ui.partitionBox.isChecked():
            self.partitions = None
            self.partitionModel.setStringList([])
            return
        field = self.ui.partComboBox.itemData(
            self.ui.partComboBox.currentIndex())
        num = None
        minimum = None
        maximum = None
        if self.ui.partBinSpinBox.isEnabled():
            num = self.ui.partBinSpinBox.value()
            minimum = self.ui.partMinSpinBox.value()
            maximum = self.ui.partMaxSpinBox.value()
        self.partitions = self.model.partition(field, minimum, maximum, num)
        self.partitionModel.setStringList(sorted(self.partitions.keys()))

    def onSelectPartition(self):
        part = self.partitionModel.data(
            self.ui.partitionList.selectionModel().currentIndex(),
            Qt.DisplayRole)
        self.model.setPartition(self.partitions[part])

    # Flags
    def onFlagChange(self):
        slist = []
        for i in np.where(self.model.flagFilter.keep)[0]:
            slist.append(str(i))
        self.flagModel.setStringList(slist)

    def onIgnoreFlags(self, checked):
        if checked:
            self.model.flagFilter.setActive(False)

    def onExcludeFlags(self, checked):
        if checked:
            self.model.flagFilter.setInvert(True)
            self.model.flagFilter.setActive(True)

    def onOnlyFlags(self, checked):
        if checked:
            self.model.flagFilter.setInvert(False)
            self.model.flagFilter.setActive(True)

    def onSelectFlag(self):
        row = int(
            self.flagModel.data(
                self.ui.flaggedList.selectionModel().currentIndex(),
                Qt.DisplayRole))
        self.flagselected.emit(row)

    # Limits
    def onLimitChange(self):
        if self.ui.limitCheckBox.isChecked():
            self.model.limit = self.ui.limitSpinBox.value()
        else:
            self.model.limit = None
        self.model.limitModeRandom = self.ui.limRandomButton.isChecked()

    def setLimit(self, l):
        self.ui.limitSpinBox.setValue(l)
        self.ui.limitCheckBox.setChecked(True)
        self.onLimitChange()

    # Filters
    def onSaveFilters(self):
        name = QFileDialog.getSaveFileName(self,
                                           'Save Filters As',
                                           filter='*.xml')
        if qt5:
            if name:
                self.model.saveFilters(name[0])
        elif name is not None and len(name):
            self.model.saveFilters(name)

    def onLoadFilters(self):
        name = QFileDialog.getOpenFileName(self,
                                           'Load Filter File',
                                           filter='*.xml')
        if qt5:
            if name:
                self.model.loadFilters(name[0])
        elif name is not None and len(name):
            self.model.loadFilters(name)
Exemplo n.º 35
0
class logicGoods(Ui_sysGoods, QDialog):
    def __init__(self, MySQL):
        super().__init__()
        self.setupUi(self)
        self.MySQL = MySQL
        self.listFunc.currentRowChanged.connect(
            self.stackedWidget.setCurrentIndex)  # list和右边窗口index绑定
        self.listFunc.currentRowChanged.connect(self.left_row_changed)

        # 货物增删查
        self.headers = ['货物名', '货物简介', '数目']
        self.data_model = QStandardItemModel()
        self.data_model_2 = QStandardItemModel()
        self.data = []
        self.tv_goods.setModel(self.data_model)
        self.tv_goods_2.setModel(self.data_model_2)

        self.tv_goods.horizontalHeader().setSectionResizeMode(
            QHeaderView.Stretch)
        self.tv_goods.setEditTriggers(QTableView.NoEditTriggers)  # 不可编辑
        self.tv_goods.setSelectionBehavior(QTableView.SelectRows)  # 选中行

        self.tv_goods_2.horizontalHeader().setSectionResizeMode(
            QHeaderView.Stretch)
        self.tv_goods_2.setEditTriggers(QTableView.NoEditTriggers)  # 不可编辑
        self.tv_goods_2.setSelectionBehavior(QTableView.SelectRows)  # 选中行

        self.onlyInt = QIntValidator()
        self.le_inout_price.setValidator(self.onlyInt)  # 限制只能为int
        self.le_inout_num.setValidator(self.onlyInt)

        self.listFunc.setCurrentRow(0)
        #self.myclear()

        self.le_filter.textChanged.connect(self.on_le_filter_textChange)
        self.le_inout_name.textChanged.connect(self.on_le_filter_textChange_2)
        self.cb_inout_year.currentIndexChanged.connect(self.inout_day_parser)
        self.cb_inout_month.currentIndexChanged.connect(self.inout_day_parser)
        ################################# 按钮事件 #############################################

        self.btn_confirm_add.clicked.connect(self.on_btn_add_goods)
        self.btn_remove_select.clicked.connect(self.on_btn_remove_goods)
        self.btn_inout_confirm.clicked.connect(self.on_btn_inout_confirm)

    def row_sel_change(self):
        pass

    def inout_day_parser(self):
        self.cb_inout_day.clear()
        year = self.cb_inout_year.currentText()
        month = self.cb_inout_month.currentText()
        if year != '' and month != '':
            year = int(year)
            month = int(month)
            days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
            # 闰年逻辑
            if year % 400 == 0 or year % 100 != 0 and year % 4 == 0:
                days[1] = 29
            self.cb_inout_day.addItems(
                [str(i) for i in range(1, days[month - 1] + 1)])

    def inout_date_clear(self):
        self.cb_inout_year.clear()
        self.cb_inout_month.clear()
        self.cb_inout_day.clear()
        year = dt.now().year
        month = dt.now().month
        day = dt.now().day
        self.cb_inout_year.addItems([str(year - 1), str(year), str(year + 1)])
        self.cb_inout_year.setCurrentIndex(1)

        self.cb_inout_month.addItems([str(i) for i in range(1, 13)])
        self.cb_inout_month.setCurrentIndex(month - 1)

        self.inout_day_parser()
        self.cb_inout_day.setCurrentIndex(day - 1)

    def myclear(self):
        self.btn_remove_select.setEnabled(False)
        self.le_filter.setText('')

        # 表刷新/后台缓存数据刷新
        self.data_model.clear()
        self.data_model.setHorizontalHeaderLabels(self.headers)
        self.data = []

        # 添加货物刷新
        self.le_goods_name.setText('')
        self.te_goods_info.setText('')
        self.btn_confirm_add.setEnabled(False)

        # 进/出库登记刷新
        self.le_inout_name.clear()
        self.le_inout_num.clear()
        self.le_inout_price.clear()
        self.inout_date_clear()

        # 后台数据刷新
        self.btn_remove_select.setEnabled(True)
        self.btn_confirm_add.setEnabled(True)
        self.le_filter.clear()
        self.btn_inout_confirm.setEnabled(True)
        self.connect_db()

        self.listFunc.setCurrentRow(0)
        self.data_model_2.clear()
        self.data_model_2.setHorizontalHeaderLabels(self.headers)
        for i, (goods_name, goods_info, goods_num) in enumerate(self.data):
            self.data_model_2.appendRow([
                QStandardItem(goods_name),
                QStandardItem(goods_info),
                QStandardItem(str(goods_num))
            ])

    #### 按钮/输入事件
    def on_btn_inout_confirm(self):
        # 校验
        if self.le_inout_name.text() == '':
            QMessageBox.critical(self, '错误', '货物名不能为空!', QMessageBox.Ok,
                                 QMessageBox.Ok)
        elif self.le_inout_price.text() == '':
            QMessageBox.critical(self, '错误', '货物价格不能为空!', QMessageBox.Ok,
                                 QMessageBox.Ok)
        elif self.le_inout_num.text() == '':
            QMessageBox.critical(self, '错误', '货物数目不能为空!', QMessageBox.Ok,
                                 QMessageBox.Ok)

        else:
            goods_name = self.le_inout_name.text()
            goods_price = int(self.le_inout_price.text())
            goods_num = int(self.le_inout_num.text())
            year = int(self.cb_inout_year.currentText())
            month = int(self.cb_inout_month.currentText())
            day = int(self.cb_inout_day.currentText())
            inout = 1 - self.cb_inout.currentIndex()
            try:
                sql = '''INSERT INTO goods_inout VALUES(
                    '{}',{},{},'{}-{}-{}',{}
                )'''.format(goods_name, goods_price, inout, year, month, day,
                            goods_num)
                print(sql)

                str_confirm = '''  操作类型:\t{}\t\n  货物名:\t{}\t\n  货物价格:\t{}\t\n  货物数目:\t{}\t\n  操作日期:\t{}-{}-{}\t
                '''.format(self.cb_inout.currentText(), goods_name,
                           goods_price, goods_num, year, month, day)

                reply = QMessageBox.question(self, '操作确认', str_confirm,
                                             QMessageBox.Yes | QMessageBox.No,
                                             QMessageBox.Yes)

                # 插入数据库
                if reply == QMessageBox.Yes:
                    find = False
                    left = 0
                    changed_idx = -1
                    for i, (cache_goods_name, cache_goods_info,
                            cache_goods_num) in enumerate(self.data):
                        if goods_name == cache_goods_name:
                            left = cache_goods_num
                            find = True
                            changed_idx = i
                            break

                    if find == True:

                        # 进出库登记,正确出库或者入库后才执行
                        def inout_mark(MySQL, sql):
                            flag = MySQL.InsertFromDataBse(sql)
                            if (flag == True):
                                QMessageBox.information(
                                    self, '提示',
                                    '{}成功'.format(self.cb_inout.currentText()),
                                    QMessageBox.Ok, QMessageBox.Ok)

                            return flag

                        # 入库
                        if inout == 1:
                            flag = inout_mark(self.MySQL, sql)
                            if flag == True:
                                # 刷新缓存数据
                                self.data[changed_idx][2] += goods_num
                                self.flush_data_model_2()
                            else:
                                QMessageBox.critical(
                                    self, '错误',
                                    '连接数据库失败!\n考虑是否是以下原因引起:\n (1)网络连接出现问题',
                                    QMessageBox.Ok, QMessageBox.Ok)
                        # 出库
                        elif inout == 0:
                            if left < goods_num:
                                QMessageBox.critical(
                                    self, '错误',
                                    '当前仓库的“{}”的库存不足!\n当前库存:{}'.format(
                                        goods_name,
                                        left), QMessageBox.Ok, QMessageBox.Ok)
                            else:
                                flag = inout_mark(self.MySQL, sql)
                                if flag == True:
                                    # 刷新缓存数据
                                    self.data[changed_idx][2] -= goods_num
                                    self.flush_data_model_2()
                                else:
                                    QMessageBox.critical(
                                        self, '错误',
                                        '连接数据库失败!\n考虑是否是以下原因引起:\n (1)网络连接出现问题',
                                        QMessageBox.Ok, QMessageBox.Ok)

                    else:
                        QMessageBox.critical(
                            self, '错误', '仓库中未存在名为“{}”的货物!'.format(goods_name),
                            QMessageBox.Ok, QMessageBox.Ok)

            except Exception as e:
                print(e)

    def on_btn_remove_goods(self):
        row = self.tv_goods.currentIndex().row()

        if row == -1:
            QMessageBox.critical(self, '错误', '未选中任何货物', QMessageBox.Ok,
                                 QMessageBox.Ok)
        else:
            name = self.data_model.index(row, 0)
            info = self.data_model.index(row, 1)
            num = self.data_model.index(row, 2)
            dl_goods_name = str(self.data_model.data(name))
            dl_goods_info = str(self.data_model.data(info))
            dl_goods_num = str(self.data_model.data(num))

            try:
                str_confirm = '''是否删除下列货物:\n  货物名:\t{}\n  货物简介:\t{}\n  货物余量:\t{}'''.format(
                    dl_goods_name, dl_goods_info, dl_goods_num)
                reply = QMessageBox.question(self, '确认信息', str_confirm,
                                             QMessageBox.Yes | QMessageBox.No,
                                             QMessageBox.Yes)

                # 插入数据库
                if reply == QMessageBox.Yes:
                    sql = '''DELETE FROM goods WHERE goods_name='{}' '''.format(
                        dl_goods_name)
                    flag = self.MySQL.DeleteFromDataBse(sql)
                    if (flag == True):
                        # 从缓存数据中删除数据
                        for i, (goods_name, goods_info,
                                goods_num) in enumerate(self.data):
                            if dl_goods_name == goods_name:
                                self.data.pop(i)
                                break
                        # 从数据模型中删除
                        self.data_model.removeRow(row)
                        QMessageBox.information(self, '提示', '删除货物成功',
                                                QMessageBox.Ok, QMessageBox.Ok)

                    else:
                        QMessageBox.critical(
                            self, '错误', '删除货物失败!\n考虑是否是以下原因引起:\n (1)网络连接出现问题',
                            QMessageBox.Ok, QMessageBox.Ok)
            except Exception as e:
                print(e)

    def on_le_filter_textChange(self):
        term = self.le_filter.text().strip()

        # 清空数据模型
        self.data_model.clear()
        self.data_model.setHorizontalHeaderLabels(self.headers)
        # 备份缓存数据
        if term == '':
            for i, (goods_name, goods_info, goods_num) in enumerate(self.data):
                self.data_model.appendRow([
                    QStandardItem(goods_name),
                    QStandardItem(goods_info),
                    QStandardItem(str(goods_num))
                ])
        else:
            for i, (goods_name, goods_info, goods_num) in enumerate(self.data):
                if term in goods_name:
                    self.data_model.appendRow([
                        QStandardItem(goods_name),
                        QStandardItem(goods_info),
                        QStandardItem(str(goods_num))
                    ])

    def on_le_filter_textChange_2(self):
        term = self.le_inout_name.text().strip()

        # 清空数据模型
        self.data_model_2.clear()
        self.data_model_2.setHorizontalHeaderLabels(self.headers)
        # 备份缓存数据
        if term == '':
            for i, (goods_name, goods_info, goods_num) in enumerate(self.data):
                self.data_model_2.appendRow([
                    QStandardItem(goods_name),
                    QStandardItem(goods_info),
                    QStandardItem(str(goods_num))
                ])
        else:
            for i, (goods_name, goods_info, goods_num) in enumerate(self.data):
                if term in goods_name:
                    self.data_model_2.appendRow([
                        QStandardItem(goods_name),
                        QStandardItem(goods_info),
                        QStandardItem(str(goods_num))
                    ])

    def connect_db(self):
        self.data_model.clear()
        self.data_model.setHorizontalHeaderLabels(self.headers)
        try:
            sql = '''SELECT * FROM goods'''
            flag, result = self.MySQL.SelectFromDataBse(sql)
            if (flag == True):
                for d in result:
                    self.data.append(list(d))
            else:
                QMessageBox.critical(self, '错误', '连接货物数据库失败', QMessageBox.Ok,
                                     QMessageBox.Ok)
                self.btn_remove_select.setEnabled(False)
                self.btn_confirm_add.setEnabled(False)
                self.btn_inout_confirm.setEnabled(False)
        except Exception as e:
            print(e)

    def on_btn_add_goods(self):
        # 有效性检查
        if self.le_goods_name.text() == '':
            QMessageBox.critical(self, '错误', '货物名不能为空!', QMessageBox.Ok,
                                 QMessageBox.Ok)

        else:
            goods_name = self.le_goods_name.text()
            goods_info = self.te_goods_info.toPlainText()
            sql = '''
            INSERT INTO goods (goods_name, goods_info) VALUES ('{}','{}')
            '''.format(goods_name, goods_info)

            try:
                str_confirm = '''插入信息如下:\n  货物名:\t{}\n  货物简介:\t{}'''.format(
                    goods_name, goods_info)
                reply = QMessageBox.question(self, '确认信息', str_confirm,
                                             QMessageBox.Yes | QMessageBox.No,
                                             QMessageBox.Yes)

                # 插入数据库
                if reply == QMessageBox.Yes:
                    flag = self.MySQL.InsertFromDataBse(sql)
                    if not flag:
                        QMessageBox.critical(
                            self, '错误',
                            '插入数据库失败!\n考虑是否是以下原因引起:\n (1)数据库存在同名的货物\n (2)网络连接出现问题',
                            QMessageBox.Ok, QMessageBox.Ok)
                    else:
                        QMessageBox.information(self, '提示', '添加货物成功',
                                                QMessageBox.Ok, QMessageBox.Ok)

                        # 更新缓存
                        self.data.append([goods_name, goods_info, 0])

                        self.data_model.appendRow([
                            QStandardItem(goods_name),
                            QStandardItem(goods_info),
                            QStandardItem(str(0))
                        ])

                        # 清空输入
                        self.le_goods_name.clear()
                        self.te_goods_info.clear()
            except Exception as e:
                print(e)

    def left_row_changed(self):
        row = self.listFunc.currentIndex().row()
        print(row)

        # 进出库登记
        if row == 0:
            self.flush_data_model_2()

        # 货物增删查
        elif row == 1:
            self.data_model.clear()
            self.data_model.setHorizontalHeaderLabels(self.headers)
            for i, (goods_name, goods_info, goods_num) in enumerate(self.data):
                self.data_model.appendRow([
                    QStandardItem(goods_name),
                    QStandardItem(goods_info),
                    QStandardItem(str(goods_num))
                ])

    def flush_data_model_2(self):
        self.le_inout_name.clear()
        self.le_inout_num.clear()
        self.le_inout_price.clear()
        self.cb_inout.setCurrentIndex(0)

        self.data_model_2.clear()
        self.data_model_2.setHorizontalHeaderLabels(self.headers)
        for i, (goods_name, goods_info, goods_num) in enumerate(self.data):
            self.data_model_2.appendRow([
                QStandardItem(goods_name),
                QStandardItem(goods_info),
                QStandardItem(str(goods_num))
            ])