Exemple #1
0
def create_QListView(ui, qle=None):
    ql = QListView(ui)
    ql.setEditTriggers(QAbstractItemView.NoEditTriggers)
    ql.setSelectionMode(QAbstractItemView.ExtendedSelection)
    ql.setDefaultDropAction(Qt.TargetMoveAction)
    ql.setDragDropMode(QAbstractItemView.InternalMove);
    ql.setMinimumHeight(80)
    ql.clicked.connect(lambda: slot_qlv_check_list(ui, ql))
    if qle is not None:
        ql.doubleClicked.connect(lambda: slot_qlv_double_click(ui, ql, qle))
    return ql
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 = QListView()
        self.comicPageList.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.comicPageList.setDragEnabled(True)
        self.comicPageList.setDragDropMode(QAbstractItemView.InternalMove)
        self.comicPageList.setDefaultDropAction(Qt.MoveAction)
        self.comicPageList.setAcceptDrops(True)
        self.comicPageList.setItemDelegate(comic_page_delegate())
        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.pagesModel.rowsMoved.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"), self)
        self.action_new_project.triggered.connect(self.slot_new_project)
        self.action_load_project = QAction(i18n("Open Project"), self)
        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"),
                                                    self)
        self.action_edit_project_settings.triggered.connect(
            self.slot_edit_project_settings)
        self.action_edit_meta_data = QAction(i18n("Meta Data"), self)
        self.action_edit_meta_data.triggered.connect(self.slot_edit_meta_data)
        self.action_edit_export_settings = QAction(i18n("Export Settings"),
                                                   self)
        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"), self)
        self.action_add_page.triggered.connect(self.slot_add_new_page_single)
        self.action_add_template = QAction(i18n("Add Page from Template"),
                                           self)
        self.action_add_template.triggered.connect(
            self.slot_add_new_page_from_template)
        self.action_add_existing = QAction(i18n("Add Existing Pages"), self)
        self.action_add_existing.triggered.connect(self.slot_add_page_from_url)
        self.action_remove_selected_page = QAction(i18n("Remove Page"), self)
        self.action_remove_selected_page.triggered.connect(
            self.slot_remove_selected_page)
        self.action_resize_all_pages = QAction(i18n("Batch Resize"), self)
        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"),
                                               self)
        self.action_show_page_viewer.triggered.connect(
            self.slot_show_page_viewer)
        self.action_scrape_authors = QAction(i18n("Scrape Author Info"), self)
        self.action_scrape_authors.setToolTip(
            i18n(
                "Search for author information in documents and add it to the author list. This does not check for duplicates."
            ))
        self.action_scrape_authors.triggered.connect(
            self.slot_scrape_author_list)
        self.action_scrape_translations = QAction(
            i18n("Scrape Text for Translation"), self)
        self.action_scrape_translations.triggered.connect(
            self.slot_scrape_translations)
        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)
        actionList.append(self.action_scrape_translations)
        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()
        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].replace("_", " "))
                pageItem.setDragEnabled(True)
                pageItem.setDropEnabled(False)
                pageItem.setEditable(False)
                pageItem.setIcon(QIcon(QPixmap.fromImage(thumbnail)))
                pageItem.setData(dataList[1], role=CPE.DESCRIPTION)
                pageItem.setData(url, role=CPE.URL)
                pageItem.setData(dataList[2], role=CPE.KEYWORDS)
                pageItem.setData(dataList[3], role=CPE.LASTEDIT)
                pageItem.setData(dataList[4], role=CPE.EDITOR)
                pageItem.setToolTip(url)
                page.close()
                self.pagesModel.appendRow(pageItem)
                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))

    """
    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 = xmlDoc[0].find(calligra + 'abstract').text
                if desc is not None:
                    if desc.startswith("<![CDATA["):
                        desc = desc[len("<![CDATA["):]
                    if desc.startswith("]]>"):
                        desc = desc[:-len("]]>")]
        keywords = ""
        if ET.iselement(xmlDoc[0].find(calligra + 'keyword')):
            keywords = xmlDoc[0].find(calligra + 'keyword').text
        date = ""
        if ET.iselement(xmlDoc[0].find(calligra + 'date')):
            date = xmlDoc[0].find(calligra + 'date').text
        author = []
        if ET.iselement(xmlDoc[1].find(calligra + 'creator-first-name')):
            string = xmlDoc[1].find(calligra + 'creator-first-name').text
            if string is not None:
                author.append(string)
        if ET.iselement(xmlDoc[1].find(calligra + 'creator-last-name')):
            string = xmlDoc[1].find(calligra + 'creator-last-name').text
            if string is not None:
                author.append(string)
        if ET.iselement(xmlDoc[1].find(calligra + 'full-name')):
            string = xmlDoc[1].find(calligra + 'full-name').text
            if string is not None:
                author.append(string)

        return [name, desc, keywords, date, " ".join(author)]

    """
    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 + 'creator-first-name')):
                    author["first-name"] = str(
                        authorelem.find(calligra + 'creator-first-name').text)

                if ET.iselement(authorelem.find(calligra + 'initial')):
                    author["initials"] = str(
                        authorelem.find(calligra + 'initial').text)

                if ET.iselement(authorelem.find(calligra +
                                                'creator-last-name')):
                    author["last-name"] = str(
                        authorelem.find(calligra + 'creator-last-name').text)

                if ET.iselement(authorelem.find(calligra + 'email')):
                    author["email"] = str(
                        authorelem.find(calligra + 'email').text)

                if ET.iselement(authorelem.find(calligra + 'contact')):
                    contact = authorelem.find(calligra + 'contact')
                    contactMode = contact.get("type")
                    if contactMode == "email":
                        author["email"] = str(contact.text)
                    if contactMode == "homepage":
                        author["homepage"] = str(contact.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.projectName.setMainText(
                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].replace("_", " "))
                newPageItem.setData(dataList[1], role=CPE.DESCRIPTION)
                newPageItem.setData(relative, role=CPE.URL)
                newPageItem.setData(dataList[2], role=CPE.KEYWORDS)
                newPageItem.setData(dataList[3], role=CPE.LASTEDIT)
                newPageItem.setData(dataList[4], role=CPE.EDITOR)
                newPageItem.setToolTip(relative)
                page.close()
                self.pagesModel.appendRow(newPageItem)

    """
    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 accessible, 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.
    """

    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 not "pageNumber" in self.setupDictionary.keys():
            self.setupDictionary['pageNumber'] = 0

        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 = "_"
        self.setupDictionary['pageNumber'] += 1
        pageName = str(self.setupDictionary["projectName"]).replace(
            " ", "_") + extraUnderscore + str(
                format(self.setupDictionary['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.waitForDone()
            newPage.setFileName(absoluteUrl)
            newPage.setName(pageName.replace("_", " "))
            newPage.save()
            newPage.waitForDone()

        # Get out the extra data for the standard item.
        newPageItem = QStandardItem()
        newPageItem.setIcon(
            QIcon(QPixmap.fromImage(newPage.thumbnail(256, 256))))
        newPageItem.setDragEnabled(True)
        newPageItem.setDropEnabled(False)
        newPageItem.setEditable(False)
        newPageItem.setText(pageName.replace("_", " "))
        newPageItem.setData("", role=CPE.DESCRIPTION)
        newPageItem.setData(url, role=CPE.URL)
        newPageItem.setData("", role=CPE.KEYWORDS)
        newPageItem.setData("", role=CPE.LASTEDIT)
        newPageItem.setData("", role=CPE.EDITOR)
        newPageItem.setToolTip(url)

        # close page document.
        while os.path.exists(absoluteUrl) is False:
            qApp.processEvents()

        newPage.close()

        # add item to page.
        self.pagesModel.appendRow(newPageItem)

    """
    Write to the json configuration 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.
            pagesList = []
            for i in range(self.pagesModel.rowCount()):
                index = self.pagesModel.index(i, 0)
                url = str(self.pagesModel.data(index, role=CPE.URL))
                if url not in pagesList:
                    pagesList.append(url)
            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=CPE.URL)))

            # 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)).replace(
                                                     "_", " "))

                # 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=CPE.URL)))
            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):

        #ensure there is a unique identifier
        if "uuid" not in self.setupDictionary.keys():
            uuid = str()
            if "acbfID" in self.setupDictionary.keys():
                uuid = str(self.setupDictionary["acbfID"])
            else:
                uuid = QUuid.createUuid().toString()
            self.setupDictionary["uuid"] = uuid

        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!"
            )
            QMessageBox.information(
                self, i18n("Export success"),
                i18n("The files have been written to the export folder."),
                QMessageBox.Ok)

    """
    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)
                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])
                    pageItem.setData(dataList[1], role=CPE.DESCRIPTION)
                    pageItem.setData(url, role=CPE.URL)
                    pageItem.setData(dataList[2], role=CPE.KEYWORDS)
                    pageItem.setData(dataList[3], role=CPE.LASTEDIT)
                    pageItem.setData(dataList[4], role=CPE.EDITOR)
                    self.pagesModel.setItem(index.row(), index.column(),
                                            pageItem)

    """
    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("Resize 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))
                    qApp.processEvents()
                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 = int(self.comicPageList.currentIndex().row())
        self.page_viewer_dialog.load_comic(self.path_to_config)
        self.page_viewer_dialog.go_to_page_index(index)
        self.page_viewer_dialog.show()

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

    """
    Scrape text files with the textlayer keys for text, and put those in a POT
    file. This makes it possible to handle translations.
    """

    def slot_scrape_translations(self):
        translationFolder = self.setupDictionary.get("translationLocation",
                                                     "translations")
        fullTranslationPath = os.path.join(self.projecturl, translationFolder)
        os.makedirs(fullTranslationPath, exist_ok=True)
        textLayersToSearch = self.setupDictionary.get("textLayerNames",
                                                      ["text"])

        scraper = comics_project_translation_scraper.translation_scraper(
            self.projecturl, translationFolder, textLayersToSearch,
            self.setupDictionary["projectName"])
        # Run text scraper.
        language = self.setupDictionary.get("language", "en")
        metadata = {}
        metadata["title"] = self.setupDictionary.get("title", "")
        metadata["summary"] = self.setupDictionary.get("summary", "")
        metadata["keywords"] = ", ".join(
            self.setupDictionary.get("otherKeywords", [""]))
        metadata["transnotes"] = self.setupDictionary.get(
            "translatorHeader", "Translator's Notes")
        scraper.start(self.setupDictionary["pages"], language, metadata)
        QMessageBox.information(
            self, i18n("Scraping success"),
            str(i18n("POT file has been written to: {file}")).format(
                file=fullTranslationPath), QMessageBox.Ok)

    """
    This is required by the dockwidget class, otherwise unused.
    """

    def canvasChanged(self, canvas):
        pass
class PrioritizeDialog(QDialog):
    def __init__(self, parent, app, **kwargs):
        flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint
        super().__init__(parent, flags, **kwargs)
        self._setupUi()
        self.model = PrioritizeDialogModel(app=app.model)
        self.categoryList = ComboboxModel(model=self.model.category_list,
                                          view=self.categoryCombobox)
        self.criteriaList = ListviewModel(model=self.model.criteria_list,
                                          view=self.criteriaListView)
        self.prioritizationList = PrioritizationList(
            model=self.model.prioritization_list,
            view=self.prioritizationListView)
        self.model.view = self

        self.addCriteriaButton.clicked.connect(self.model.add_selected)
        self.criteriaListView.doubleClicked.connect(self.model.add_selected)
        self.removeCriteriaButton.clicked.connect(self.model.remove_selected)
        self.prioritizationListView.doubleClicked.connect(
            self.model.remove_selected)
        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.rejected.connect(self.reject)

    def _setupUi(self):
        self.setWindowTitle(tr("Re-Prioritize duplicates"))
        self.resize(700, 400)

        # widgets
        msg = tr(
            "Add criteria to the right box and click OK to send the dupes that correspond the "
            "best to these criteria to their respective group's "
            "reference position. Read the help file for more information.")
        self.promptLabel = QLabel(msg)
        self.promptLabel.setWordWrap(True)
        self.categoryCombobox = QComboBox()
        self.criteriaListView = QListView()
        self.criteriaListView.setSelectionMode(
            QAbstractItemView.ExtendedSelection)
        self.addCriteriaButton = QPushButton(
            self.style().standardIcon(QStyle.SP_ArrowRight), "")
        self.removeCriteriaButton = QPushButton(
            self.style().standardIcon(QStyle.SP_ArrowLeft), "")
        self.prioritizationListView = QListView()
        self.prioritizationListView.setAcceptDrops(True)
        self.prioritizationListView.setDragEnabled(True)
        self.prioritizationListView.setDragDropMode(
            QAbstractItemView.InternalMove)
        self.prioritizationListView.setSelectionBehavior(
            QAbstractItemView.SelectRows)
        self.prioritizationListView.setSelectionMode(
            QAbstractItemView.ExtendedSelection)
        self.buttonBox = QDialogButtonBox()
        self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel
                                          | QDialogButtonBox.Ok)

        # layout
        self.mainLayout = QVBoxLayout(self)
        self.mainLayout.addWidget(self.promptLabel)
        self.splitter = QSplitter()
        sp = self.splitter.sizePolicy()
        sp.setVerticalPolicy(QSizePolicy.Expanding)
        self.splitter.setSizePolicy(sp)
        self.leftSide = QWidget()
        self.leftWidgetsLayout = QVBoxLayout()
        self.leftWidgetsLayout.addWidget(self.categoryCombobox)
        self.leftWidgetsLayout.addWidget(self.criteriaListView)
        self.leftSide.setLayout(self.leftWidgetsLayout)
        self.splitter.addWidget(self.leftSide)
        self.rightSide = QWidget()
        self.rightWidgetsLayout = QHBoxLayout()
        self.addRemoveButtonsLayout = QVBoxLayout()
        self.addRemoveButtonsLayout.addItem(vertical_spacer())
        self.addRemoveButtonsLayout.addWidget(self.addCriteriaButton)
        self.addRemoveButtonsLayout.addWidget(self.removeCriteriaButton)
        self.addRemoveButtonsLayout.addItem(vertical_spacer())
        self.rightWidgetsLayout.addLayout(self.addRemoveButtonsLayout)
        self.rightWidgetsLayout.addWidget(self.prioritizationListView)
        self.rightSide.setLayout(self.rightWidgetsLayout)
        self.splitter.addWidget(self.rightSide)
        self.mainLayout.addWidget(self.splitter)
        self.mainLayout.addWidget(self.buttonBox)
Exemple #4
0
class MainWindow(QWidget):
    """
    This is the graphical user interface
    """
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        # setup entire layout
        vbox = QVBoxLayout()
        self.setLayout(vbox)

        # default size
        self.resize(640, 480)

        # main window title
        self.setWindowTitle("Vanilla Search Engine")

        # main window icon
        self.setWindowIcon(
            QIcon(
                os.path.dirname(os.path.abspath(__file__)) +
                '/icons/vanilla.png'))

        # add query input field
        searchLayout = QHBoxLayout()
        searchLabel = QLabel("Query: ")
        searchLayout.addWidget(searchLabel)
        self.searchField = QLineEdit()
        # self.searchField.move(20, 20)
        # self.searchField.resize(200, 40)
        searchLayout.addWidget(self.searchField)
        vbox.addLayout(searchLayout)

        # add choice of model, Boolean, or VSM
        modelLabel = QLabel("Choice of model: \t\t")
        modelLayout = QHBoxLayout()
        modelGroup = QButtonGroup(self)
        self.modelChoiceButton1 = QRadioButton("Boolean")
        self.modelChoiceButton1.setChecked(True)
        self.modelChoiceButton2 = QRadioButton("VSM")
        self.modelChoiceButton2.setChecked(False)
        self.modelChoiceButton1.toggled.connect(
            lambda: self.changeChoiceState(self.modelChoiceButton1))
        self.modelChoiceButton2.toggled.connect(
            lambda: self.changeChoiceState(self.modelChoiceButton2))
        modelLayout.addWidget(modelLabel)
        modelLayout.addWidget(self.modelChoiceButton1)
        modelGroup.addButton(self.modelChoiceButton1)
        modelLayout.addWidget(self.modelChoiceButton2)
        modelGroup.addButton(self.modelChoiceButton2)
        modelLayout.addStretch(1)
        vbox.addLayout(modelLayout)

        # add choice of collection: UofO catalog, ..
        collectionLabel = QLabel("Choice of collection: \t")
        collectionLayout = QHBoxLayout()
        collectionButtonGroup = QButtonGroup(self)
        self.collectionChoiceButton1 = QRadioButton("UofO catalog")
        self.collectionChoiceButton1.setChecked(True)
        self.collectionChoiceButton1.toggled.connect(
            lambda: self.changeChoiceState(self.collectionChoiceButton1))
        collectionLayout.addWidget(collectionLabel)
        collectionLayout.addWidget(self.collectionChoiceButton1)
        collectionButtonGroup.addButton(self.collectionChoiceButton1)
        collectionLayout.addStretch(1)
        vbox.addLayout(collectionLayout)

        # TODO: the information needed for the spelling correction

        # add a search button
        searchButtonLayout = QHBoxLayout()
        self.searchButton = QPushButton('Search')
        self.searchButton.setToolTip('This is a search button')
        # self.searchButton.move(20, 400)
        self.searchButton.clicked.connect(self.click_search)
        searchButtonLayout.addStretch(1)
        searchButtonLayout.addWidget(self.searchButton)
        vbox.addLayout(searchButtonLayout)

        # add a qeury result listView
        self.doc_ids = []  # record the doc IDs
        queryResultLayout = QHBoxLayout()
        queryResultLabel = QLabel("Query result: \t")
        self.queryResult = QListView()
        self.queryResult.setAcceptDrops(False)
        self.queryResult.setDragDropMode(
            QtWidgets.QAbstractItemView.NoDragDrop)
        self.queryResult.setSelectionMode(
            QtWidgets.QAbstractItemView.ExtendedSelection)
        self.queryResult.setResizeMode(QListView.Fixed)
        self.queryResult.clicked.connect(self.selectItem)
        queryResultLayout.addWidget(queryResultLabel)
        queryResultLayout.addWidget(self.queryResult)
        # queryResultLayout.addStretch(1)
        vbox.addLayout(queryResultLayout)

        # setup corpus access
        self.courses = CorpusAccess(COURSE_DICT)

        # centerize main window
        self.center()

        self.show()

    # move the window to the center of screen
    def center(self):
        frameGm = self.frameGeometry()
        screen = QApplication.desktop().screenNumber(
            QApplication.desktop().cursor().pos())
        centerPoint = QApplication.desktop().screenGeometry(screen).center()
        frameGm.moveCenter(centerPoint)
        self.move(frameGm.topLeft())

    @pyqtSlot()
    def click_search(self):
        # TODO: debugging
        print("Search button clicked.")

        query_string = self.searchField.text().strip()

        # setup QMessageBox
        if query_string == "":
            buttonReplay = self.__create_message_box(
                "The query string cannot be blank")
        else:
            idxf2 = Index.load(INDEX)
            self.doc_ids = query(idxf2, query_string)
            if len(self.doc_ids) == 0:
                msg = self.__create_message_box("Could not find anything.")
            else:
                model = QStandardItemModel()
                for doc in self.doc_ids:
                    item = QStandardItem(doc)
                    model.appendRow(item)
                self.queryResult.setModel(model)
                self.queryResult.show()

    def changeChoiceState(self, button: QPushButton):
        if button.isChecked():
            if button.text() == 'Boolean':
                # TODO: debugging
                print("Radio button '%s' is clicked." % button.text())
            elif button.text() == 'VSM':
                # TODO: debugging
                print("Radio button '%s' is clicked." % button.text())
            elif button.text() == 'UofO catalog':
                # print("Radio button '%s' is clicked." % button.text())
                html_file = "UofO_Courses.html"
                if os.path.exists(CURRENT_DIR + "/../%s" % html_file):
                    setup(html_file=CURRENT_DIR + "/../%s" % html_file)
                else:
                    raise Exception("Could not find '%s'" % html_file)

    def selectItem(self):
        selectedId = self.queryResult.selectedIndexes()[0].row()
        print("id selected: %d" % selectedId)
        print("course: %s" % self.doc_ids[selectedId])
        print("course content: %s" %
              self.courses.get(self.doc_ids[selectedId]))
        self.__display_course_details(
            self.doc_ids[selectedId],
            self.courses.get(self.doc_ids[selectedId]))

    def __display_course_details(self, course_id, course_details):
        dialog = QDialog(parent=self)
        dialog.setWindowTitle("%s" % course_id)
        dialog.resize(320, 240)
        vbox = QVBoxLayout()

        hbox1 = QHBoxLayout()
        titelLabel = QLabel("Title: \t")
        title = QLineEdit()
        title.setText(course_details.get('title'))
        title.setReadOnly(True)
        hbox1.addWidget(titelLabel)
        hbox1.addWidget(title)

        hbox2 = QHBoxLayout()
        contentLabel = QLabel("Content: ")
        content = QTextEdit()
        content.setReadOnly(True)
        content.setText(course_details.get('content'))
        hbox2.addWidget(contentLabel)
        hbox2.addWidget(content)

        vbox.addLayout(hbox1)
        vbox.addLayout(hbox2)

        dialog.setLayout(vbox)
        dialog.exec_()

    def __create_message_box(self, message):
        message_box = QMessageBox()
        message_box.setIcon(QMessageBox.Information)
        message_box.setText(message)
        message_box.setWindowTitle("Search result message")
        message_box.setStandardButtons(QMessageBox.Ok)
        message_box.exec_()
class MainWindow(QWidget):

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

    def initUI(self):
        self.methods = ['option 1', 'option 2', 'option 3']
        self.central_widget = QWidget(self)
        self.layout = QVBoxLayout(self.central_widget)
        self.layout.addStretch()
        self.create_methods_list()
        self.layout.addWidget(self.methods_list)
        self.layout.addStretch()

        wordLabel = DragLabel('draggami', self)

        #self.setCentralWidget(self.central_widget)
        self.setAcceptDrops(True)
        self.show()

    def create_methods_list(self):
        self.methods_list = QListView()
        self.methods_list.setDragDropMode(QListView.InternalMove)
        self.methods_list.setDefaultDropAction(Qt.MoveAction)
        self.methods_list.setDragDropMode(False)
        self.methods_list.setAcceptDrops(True)
        self.methods_list.setDropIndicatorShown(True)
        self.methods_list.setDragEnabled(True)
        self.methods_list.setWindowTitle('Method Order')

        self.methods_model = QStandardItemModel(self.methods_list)
        for method in self.methods:
            item = QStandardItem(method)
            item.setData(method)
            item.setCheckable(True)
            item.setDragEnabled(True)
            item.setDropEnabled(False)
            item.setCheckState(True)

            self.methods_model.appendRow(item)

        self.methods_model.itemChanged.connect(self.method_item_changed)
        self.methods_list.setModel(self.methods_model)
        self.methods_list.setMinimumHeight(
            self.methods_list.sizeHintForRow(0) *
            (self.methods_model.rowCount() + 2))

    def method_item_changed(self):
        print(self.methods_model.rowCount())
        i = 0
        new_methods = []
        while self.methods_model.item(i):
            if self.methods_model.item(i).checkState():
                new_methods.append(self.methods_model.item(i).data())
            i += 1
        self.methods = new_methods
        print(self.methods)

    def dragEnterEvent(self, event):
        if event.mimeData().hasText():
            if event.source() in self.children():
                event.setDropAction(Qt.MoveAction)
                event.accept()
            else:
                event.acceptProposedAction()
        else:
            event.ignore()

    def dropEvent(self, event):
        print('evento di drop')
        if event.mimeData().hasText():
            mime = event.mimeData()
            pieces = mime.text().split()
            position = event.pos()
            hotSpot = QPoint()

            hotSpotPos = mime.data('application/x-hotspot').split(' ')
            pippo = QListView()
            pippo.append
            self.lista
            """
            if len(hotSpotPos) == 2:
               hotSpot.setX(hotSpotPos[0].toInt()[0])
               hotSpot.setY(hotSpotPos[1].toInt()[0])

            for piece in pieces:
                newLabel = DragLabel(piece, self)
                newLabel.move(position - hotSpot)
                newLabel.show()

                position += QPoint(newLabel.width(), 0)

            if event.source() in self.children():
                event.setDropAction(Qt.MoveAction)
                event.accept()
            else:
                event.acceptProposedAction()
            """
        else:
            event.ignore()
        self.setCheckable(True)

    def set(self, xml: str):
        self.property = xml

    def get(self):
        print(self.property)


app = QApplication(sys.argv)

# Our main window will be a QListView
list = QListView()
# Set flags so that drag and drop is enabled (should all be set from the .ui file
list.setMovement(QListView.Snap)
list.setDragDropMode(QListView.InternalMove)
list.setDragDropOverwriteMode(False)
list.setDefaultDropAction(Qt.MoveAction)
list.setSelectionMode(QListView.ExtendedSelection)
list.setWindowTitle('Honey-Do List')
list.setMinimumSize(600, 400)

# Create an empty model for the list's data
model = QStandardItemModel(list)

# Add some textual items
foods = [
    'Cookie dough',  # Must be store-bought
    'Hummus',  # Must be homemade
    'Spaghetti',  # Must be saucy
    'Dal makhani',  # Must be spicy
Exemple #7
0
class Editor(QWidget):
    """
    This class is the central widget of the MainWindow.
    It contains the items library, diagram graphics scene and graphics view, and the inspector widget

    Function of Connections:
    Logically:
    A Connection is composed of a fromPort and a toPort, which gives the direction of the pipe.
    Ports are attached to Blocks.
    Visually:
    A diagram editor has a QGraphicsLineItem (connLineItem) which is set Visible only when a connection is being created

    Function of BlockItems:
    Items can be added to the library by adding them to the model of the library broswer view.
    Then they can be dragged and dropped into the diagram view.

    Function of trnsysExport:
    When exporting the trnsys file, exportData() is called.

    Function of save and load:
    A diagram can be saved to a json file by calling encodeDiagram and can then be loaded by calling decodeDiagram wiht
    appropiate filenames.

    Attributes
    ----------
    projectFolder : str
        Path to the folder of the project
    diagramName : str
        Name used for saving the diagram
    saveAsPath : :obj:`Path`
        Default saving location is trnsysGUI/diagrams, path only set if "save as" used
    idGen : :obj:`IdGenerator`
        Is used to distribute ids (id, trnsysId(for trnsysExport), etc)
    alignMode : bool
        Enables mode in which a dragged block is aligned to y or x value of another one
        Toggled in the MainWindow class in toggleAlignMode()

    editorMode : int
        Mode 0: Pipes are PolySun-like
        Mode 1: Pipes have only 90deg angles, visio-like
    snapGrid : bool
        Enable/Disable align grid
    snapSize : int
        Size of align grid

    horizontalLayout : :obj:`QHBoxLayout`
    Contains the diagram editor and the layout containing the library browser view and the listview
    vertL : :obj:`QVBoxLayout`
    Cointains the library browser view and the listWidget

    moveDirectPorts: bool
        Enables/Disables moving direct ports of storagetank (doesn't work with HxPorts yet)
    diagramScene : :obj:`QGraphicsScene`
        Contains the "logical" part of the diagram
    diagramView : :obj:`QGraphicsView`
        Contains the visualization of the diagramScene
    _currentlyDraggedConnectionFromPort : :obj:`PortItem`
    connectionList : :obj:`List` of :obj:`Connection`
    trnsysObj : :obj:`List` of :obj:`BlockItem` and :obj:`Connection`
    graphicalObj : :obj:`List` of :obj:`GraphicalItem`
    connLine : :obj:`QLineF`
    connLineItem = :obj:`QGraphicsLineItem`

    """
    def __init__(self, parent, projectFolder, jsonPath, loadValue, logger):
        super().__init__(parent)

        self.logger = logger

        self.logger.info("Initializing the diagram editor")

        self.projectFolder = projectFolder

        self.diagramName = os.path.split(self.projectFolder)[-1] + ".json"
        self.saveAsPath = _pl.Path()
        self.idGen = IdGenerator()

        self.testEnabled = False
        self.existReference = True

        self.controlExists = 0
        self.controlDirectory = ""

        self.alignMode = False

        self.moveDirectPorts = False

        self.editorMode = 1

        # Related to the grid blocks can snap to
        self.snapGrid = False
        self.snapSize = 20

        self.trnsysPath = _pl.Path(r"C:\Trnsys17\Exe\TRNExe.exe")

        self.horizontalLayout = QHBoxLayout(self)
        self.libraryBrowserView = QListView(self)
        self.libraryModel = LibraryModel(self)

        self.libraryBrowserView.setGridSize(QSize(65, 65))
        self.libraryBrowserView.setResizeMode(QListView.Adjust)
        self.libraryModel.setColumnCount(0)

        componentNamesWithIcon = [
            ("Connector", _img.CONNECTOR_SVG.icon()),
            ("TeePiece", _img.TEE_PIECE_SVG.icon()),
            ("DPTee", _img.DP_TEE_PIECE_SVG.icon()),
            ("SPCnr", _img.SINGLE_DOUBLE_PIPE_CONNECTOR_SVG.icon()),
            ("DPCnr", _img.DOUBLE_DOUBLE_PIPE_CONNECTOR_SVG.icon()),
            ("TVentil", _img.T_VENTIL_SVG.icon()),
            ("WTap_main", _img.W_TAP_MAIN_SVG.icon()),
            ("WTap", _img.W_TAP_SVG.icon()),
            ("Pump", _img.PUMP_SVG.icon()),
            ("Collector", _img.COLLECTOR_SVG.icon()),
            ("GroundSourceHx", _img.GROUND_SOURCE_HX_SVG.icon()),
            ("PV", _img.PV_SVG.icon()),
            ("HP", _img.HP_SVG.icon()),
            ("HPTwoHx", _img.HP_TWO_HX_SVG.icon()),
            ("HPDoubleDual", _img.HP_DOUBLE_DUAL_SVG.icon()),
            ("HPDual", _img.HP_DUAL_SVG.icon()),
            ("AirSourceHP", _img.AIR_SOURCE_HP_SVG.icon()),
            ("StorageTank", _img.STORAGE_TANK_SVG.icon()),
            ("IceStorage", _img.ICE_STORAGE_SVG.icon()),
            ("PitStorage", _img.PIT_STORAGE_SVG.icon()),
            ("IceStorageTwoHx", _img.ICE_STORAGE_TWO_HX_SVG.icon()),
            ("ExternalHx", _img.EXTERNAL_HX_SVG.icon()),
            ("Radiator", _img.RADIATOR_SVG.icon()),
            ("Boiler", _img.BOILER_SVG.icon()),
            ("Sink", _img.SINK_SVG.icon()),
            ("Source", _img.SOURCE_SVG.icon()),
            ("SourceSink", _img.SOURCE_SINK_SVG.icon()),
            ("Geotherm", _img.GEOTHERM_SVG.icon()),
            ("Water", _img.WATER_SVG.icon()),
            ("Crystalizer", _img.CRYSTALIZER_SVG.icon()),
            ("GenericBlock", _img.GENERIC_BLOCK_PNG.icon()),
            ("GraphicalItem", _img.GENERIC_ITEM_PNG.icon()),
        ]

        libItems = [
            QtGui.QStandardItem(icon, name)
            for name, icon in componentNamesWithIcon
        ]

        for i in libItems:
            self.libraryModel.appendRow(i)

        self.libraryBrowserView.setModel(self.libraryModel)
        self.libraryBrowserView.setViewMode(self.libraryBrowserView.IconMode)
        self.libraryBrowserView.setDragDropMode(
            self.libraryBrowserView.DragOnly)

        self.diagramScene = Scene(self)
        self.diagramView = View(self.diagramScene, self)

        # For list view
        self.vertL = QVBoxLayout()
        self.vertL.addWidget(self.libraryBrowserView)
        self.vertL.setStretchFactor(self.libraryBrowserView, 2)
        self.listV = QListWidget()
        self.vertL.addWidget(self.listV)
        self.vertL.setStretchFactor(self.listV, 1)

        # for file browser
        self.projectPath = ""
        self.fileList = []

        if loadValue == "new" or loadValue == "json":
            self.createProjectFolder()

        self.fileBrowserLayout = QVBoxLayout()
        self.pathLayout = QHBoxLayout()
        self.projectPathLabel = QLabel("Project Path:")
        self.PPL = QLineEdit(self.projectFolder)
        self.PPL.setDisabled(True)

        self.pathLayout.addWidget(self.projectPathLabel)
        self.pathLayout.addWidget(self.PPL)
        self.scroll = QScrollArea()
        self.scroll.setWidgetResizable(True)
        self.splitter = QSplitter(Qt.Vertical, )
        self.splitter.setChildrenCollapsible(False)
        self.scroll.setWidget(self.splitter)
        self.scroll.setFixedWidth(350)
        self.fileBrowserLayout.addLayout(self.pathLayout)
        self.fileBrowserLayout.addWidget(self.scroll)
        self.createDdckTree(self.projectFolder)

        if loadValue == "new" or loadValue == "json":
            self.createConfigBrowser(self.projectFolder)
            self.copyGenericFolder(self.projectFolder)
            self.createHydraulicDir(self.projectFolder)
            self.createWeatherAndControlDirs(self.projectFolder)

        self.horizontalLayout.addLayout(self.vertL)
        self.horizontalLayout.addWidget(self.diagramView)
        self.horizontalLayout.addLayout(self.fileBrowserLayout)
        self.horizontalLayout.setStretchFactor(self.diagramView, 5)
        self.horizontalLayout.setStretchFactor(self.libraryBrowserView, 1)

        self._currentlyDraggedConnectionFromPort = None
        self.connectionList = []
        self.trnsysObj = []
        self.graphicalObj = []
        self.fluids = _hlm.Fluids([])
        self.hydraulicLoops = _hlm.HydraulicLoops([])

        self.copyGroupList = QGraphicsItemGroup()
        self.selectionGroupList = QGraphicsItemGroup()

        self.printerUnitnr = 0

        # Different colors for connLineColor
        colorsc = "red"
        linePx = 4
        if colorsc == "red":
            connLinecolor = QColor(Qt.red)
        elif colorsc == "blueish":
            connLinecolor = QColor(3, 124, 193)  # Blue
        elif colorsc == "darkgray":
            connLinecolor = QColor(140, 140, 140)  # Gray
        else:
            connLinecolor = QColor(196, 196, 196)  # Gray

        # Only for displaying on-going creation of connection
        self.connLine = QLineF()
        self.connLineItem = QGraphicsLineItem(self.connLine)
        self.connLineItem.setPen(QtGui.QPen(connLinecolor, linePx))
        self.connLineItem.setVisible(False)
        self.diagramScene.addItem(self.connLineItem)

        # For line that shows quickly up when using the align mode
        self.alignYLine = QLineF()
        self.alignYLineItem = QGraphicsLineItem(self.alignYLine)
        self.alignYLineItem.setPen(QtGui.QPen(QColor(196, 249, 252), 2))
        self.alignYLineItem.setVisible(False)
        self.diagramScene.addItem(self.alignYLineItem)

        # For line that shows quickly up when using align mode
        self.alignXLine = QLineF()
        self.alignXLineItem = QGraphicsLineItem(self.alignXLine)
        self.alignXLineItem.setPen(QtGui.QPen(QColor(196, 249, 252), 2))
        self.alignXLineItem.setVisible(False)
        self.diagramScene.addItem(self.alignXLineItem)

        if loadValue == "load" or loadValue == "copy":
            self._decodeDiagram(os.path.join(self.projectFolder,
                                             self.diagramName),
                                loadValue=loadValue)
        elif loadValue == "json":
            self._decodeDiagram(jsonPath, loadValue=loadValue)

    # Debug function
    def dumpInformation(self):
        self.logger.debug("Diagram information:")
        self.logger.debug("Mode is " + str(self.editorMode))

        self.logger.debug("Next ID is " + str(self.idGen.getID()))
        self.logger.debug("Next cID is " + str(self.idGen.getConnID()))

        self.logger.debug("TrnsysObjects are:")
        for t in self.trnsysObj:
            self.logger.debug(str(t))
        self.logger.debug("")

        self.logger.debug("Scene items are:")
        sItems = self.diagramScene.items()
        for it in sItems:
            self.logger.info(str(it))
        self.logger.debug("")

        for c in self.connectionList:
            c.printConn()
        self.logger.debug("")

    # Connections related methods
    def startConnection(self, port):
        self._currentlyDraggedConnectionFromPort = port

    def _createConnection(self, startPort, endPort) -> None:
        if startPort is not endPort:
            if (isinstance(startPort.parent, StorageTank)
                    and isinstance(endPort.parent, StorageTank)
                    and startPort.parent != endPort.parent):
                msgSTank = QMessageBox(self)
                msgSTank.setText(
                    "Storage Tank to Storage Tank connection is not working atm!"
                )
                msgSTank.exec_()

            isValidSinglePipeConnection = isinstance(
                startPort, SinglePipePortItem) and isinstance(
                    endPort, SinglePipePortItem)
            if isValidSinglePipeConnection:
                command = CreateSinglePipeConnectionCommand(
                    startPort, endPort, self)
            elif isinstance(startPort, DoublePipePortItem) and isinstance(
                    endPort, DoublePipePortItem):
                command = CreateDoublePipeConnectionCommand(
                    startPort, endPort, self)
            else:
                raise AssertionError(
                    "Can only connect port items. Also, they have to be of the same type."
                )

            self.parent().undoStack.push(command)

    def sceneMouseMoveEvent(self, event):
        """
        This function is for dragging and connecting one port to another.
        When dragging, the fromPort will remain enlarged and black in color and when the toPort is hovered over, it will be
        enlarged and turn red.
        A port's details will also be displayed at the widget when they are hovered over.
        """
        fromPort = self._currentlyDraggedConnectionFromPort
        if not fromPort:
            return

        fromX = fromPort.scenePos().x()
        fromY = fromPort.scenePos().y()

        toX = event.scenePos().x()
        toY = event.scenePos().y()

        self.connLine.setLine(fromX, fromY, toX, toY)
        self.connLineItem.setLine(self.connLine)
        self.connLineItem.setVisible(True)

        hitPortItem = self._getHitPortItemOrNone(event)

        if not hitPortItem:
            return

        mousePosition = event.scenePos()

        portItemX = hitPortItem.scenePos().x()
        portItemY = hitPortItem.scenePos().y()

        distance = _math.sqrt((mousePosition.x() - portItemX)**2 +
                              (mousePosition.y() - portItemY)**2)
        if distance <= 3.5:
            hitPortItem.enlargePortSize()
            hitPortItem.innerCircle.setBrush(hitPortItem.ashColorR)
            self.listV.clear()
            hitPortItem.debugprint()
        else:
            hitPortItem.resetPortSize()
            hitPortItem.innerCircle.setBrush(hitPortItem.visibleColor)
            self.listV.clear()
            fromPort.debugprint()

        fromPort.enlargePortSize()
        fromPort.innerCircle.setBrush(hitPortItem.visibleColor)

    def _getHitPortItemOrNone(self,
                              event: QEvent) -> _tp.Optional[PortItemBase]:
        fromPort = self._currentlyDraggedConnectionFromPort
        mousePosition = event.scenePos()

        relevantPortItems = self._getRelevantHitPortItems(
            mousePosition, fromPort)
        if not relevantPortItems:
            return None

        numberOfHitPortsItems = len(relevantPortItems)
        if numberOfHitPortsItems > 1:
            raise NotImplementedError(
                "Can't deal with overlapping port items.")

        hitPortItem = relevantPortItems[0]

        return hitPortItem

    def sceneMouseReleaseEvent(self, event):
        if not self._currentlyDraggedConnectionFromPort:
            return
        fromPort = self._currentlyDraggedConnectionFromPort

        self._currentlyDraggedConnectionFromPort = None
        self.connLineItem.setVisible(False)

        mousePosition = event.scenePos()
        relevantPortItems = self._getRelevantHitPortItems(
            mousePosition, fromPort)

        numberOfHitPortsItems = len(relevantPortItems)

        if numberOfHitPortsItems > 1:
            raise NotImplementedError(
                "Can't deal with overlapping port items.")

        if numberOfHitPortsItems == 1:
            toPort = relevantPortItems[0]

            if toPort != fromPort:
                self._createConnection(fromPort, toPort)

    def _getRelevantHitPortItems(
            self, mousePosition: QPointF,
            fromPort: PortItemBase) -> _tp.Sequence[PortItemBase]:
        hitItems = self.diagramScene.items(mousePosition)
        relevantPortItems = [
            i for i in hitItems if isinstance(i, PortItemBase)
            and type(i) == type(fromPort) and not i.connectionList
        ]
        return relevantPortItems

    def cleanUpConnections(self):
        for c in self.connectionList:
            c.niceConn()

    def exportHydraulics(self, exportTo=_tp.Literal["ddck", "mfs"]):
        assert exportTo in ["ddck", "mfs"]

        if not self._isHydraulicConnected():
            messageBox = QMessageBox()
            messageBox.setWindowTitle("Hydraulic not connected")
            messageBox.setText(
                "You need to connect all port items before you can export the hydraulics."
            )
            messageBox.setStandardButtons(QMessageBox.Ok)
            messageBox.exec()
            return

        self.logger.info(
            "------------------------> START OF EXPORT <------------------------"
        )

        self.sortTrnsysObj()

        fullExportText = ""

        ddckFolder = os.path.join(self.projectFolder, "ddck")

        if exportTo == "mfs":
            mfsFileName = self.diagramName.split(".")[0] + "_mfs.dck"
            exportPath = os.path.join(self.projectFolder, mfsFileName)
        elif exportTo == "ddck":
            exportPath = os.path.join(ddckFolder, "hydraulic\\hydraulic.ddck")

        if self._doesFileExistAndDontOverwrite(exportPath):
            return None

        self.logger.info("Printing the TRNSYS file...")

        if exportTo == "mfs":
            header = open(os.path.join(ddckFolder, "generic\\head.ddck"), "r")
            headerLines = header.readlines()
            for line in headerLines:
                if line[:4] == "STOP":
                    fullExportText += "STOP = 1 \n"
                else:
                    fullExportText += line
            header.close()
        elif exportTo == "ddck":
            fullExportText += "*************************************\n"
            fullExportText += "** BEGIN hydraulic.ddck\n"
            fullExportText += "*************************************\n\n"
            fullExportText += "*************************************\n"
            fullExportText += "** Outputs to energy balance in kWh\n"
            fullExportText += (
                "** Following this naming standard : qSysIn_name, qSysOut_name, elSysIn_name, elSysOut_name\n"
            )
            fullExportText += "*************************************\n"
            fullExportText += "EQUATIONS 1\n"
            fullExportText += "qSysOut_PipeLoss = PipeLossTot\n"

        simulationUnit = 450
        simulationType = 935
        descConnLength = 20

        exporter = self._createExporter()

        blackBoxProblem, blackBoxText = exporter.exportBlackBox(
            exportTo=exportTo)
        if blackBoxProblem:
            return None

        fullExportText += blackBoxText
        if exportTo == "mfs":
            fullExportText += exporter.exportMassFlows()
            fullExportText += exporter.exportPumpOutlets()
            fullExportText += exporter.exportDivSetting(simulationUnit - 10)

        fullExportText += exporter.exportDoublePipeParameters(
            exportTo=exportTo)

        fullExportText += exporter.exportParametersFlowSolver(
            simulationUnit, simulationType, descConnLength)

        fullExportText += exporter.exportInputsFlowSolver()
        fullExportText += exporter.exportOutputsFlowSolver(simulationUnit)
        fullExportText += exporter.exportFluids() + "\n"
        fullExportText += exporter.exportHydraulicLoops() + "\n"
        fullExportText += exporter.exportPipeAndTeeTypesForTemp(
            simulationUnit + 1)  # DC-ERROR
        fullExportText += exporter.exportPrintPipeLosses()

        fullExportText += exporter.exportMassFlowPrinter(
            self.printerUnitnr, 15)
        fullExportText += exporter.exportTempPrinter(self.printerUnitnr + 1,
                                                     15)

        if exportTo == "mfs":
            fullExportText += "CONSTANTS 1\nTRoomStore=1\n"
            fullExportText += "ENDS"

        self.logger.info(
            "------------------------> END OF EXPORT <------------------------"
        )

        if exportTo == "mfs":
            f = open(exportPath, "w")
            f.truncate(0)
            f.write(fullExportText)
            f.close()
        elif exportTo == "ddck":
            if fullExportText[:1] == "\n":
                fullExportText = fullExportText[1:]
            hydraulicFolder = os.path.split(exportPath)[0]
            if not (os.path.isdir(hydraulicFolder)):
                os.makedirs(hydraulicFolder)
            f = open(exportPath, "w")
            f.truncate(0)
            f.write(fullExportText)
            f.close()

        try:
            lines = _du.loadDeck(exportPath,
                                 eraseBeginComment=True,
                                 eliminateComments=True)
            _du.checkEquationsAndConstants(lines, exportPath)
        except Exception as error:
            errorMessage = f"An error occurred while exporting the system hydraulics: {error}"
            _errs.showErrorMessageBox(errorMessage)
            return None

        return exportPath

    def _createExporter(self) -> Export:
        massFlowContributors = self._getMassFlowContributors()
        exporter = Export(massFlowContributors, self)
        return exporter

    def _getMassFlowContributors(
            self) -> _tp.Sequence[_mfs.MassFlowNetworkContributorMixin]:
        massFlowContributors = [
            o for o in self.trnsysObj
            if isinstance(o, _mfs.MassFlowNetworkContributorMixin)
        ]
        return massFlowContributors

    def _isHydraulicConnected(self) -> bool:
        for obj in self.trnsysObj:
            if not isinstance(obj, _mfs.MassFlowNetworkContributorMixin):
                continue

            internalPiping = obj.getInternalPiping()

            for portItem in internalPiping.modelPortItemsToGraphicalPortItem.values(
            ):
                if not portItem.connectionList:
                    return False

        return True

    def _doesFileExistAndDontOverwrite(self, folderPath):
        if not _pl.Path(folderPath).exists():
            return False

        qmb = QMessageBox(self)
        qmb.setText(
            f"Warning: {folderPath} already exists. Do you want to overwrite it or cancel?"
        )
        qmb.setStandardButtons(QMessageBox.Save | QMessageBox.Cancel)
        qmb.setDefaultButton(QMessageBox.Cancel)
        ret = qmb.exec()

        if ret == QMessageBox.Cancel:
            self.canceled = True
            self.logger.info("Canceling")
            return True

        self.canceled = False
        self.logger.info("Overwriting")
        return False

    def exportHydraulicControl(self):
        self.logger.info(
            "------------------------> START OF EXPORT <------------------------"
        )

        self.sortTrnsysObj()

        fullExportText = ""

        ddckFolder = os.path.join(self.projectFolder, "ddck")

        hydCtrlPath = os.path.join(ddckFolder,
                                   "control\\hydraulic_control.ddck")
        if _pl.Path(hydCtrlPath).exists():
            qmb = QMessageBox(self)
            qmb.setText(
                "Warning: " +
                "The file hydraulic_control.ddck already exists in the control folder. Do you want to overwrite it or cancel?"
            )
            qmb.setStandardButtons(QMessageBox.Save | QMessageBox.Cancel)
            qmb.setDefaultButton(QMessageBox.Cancel)
            ret = qmb.exec()
            if ret == QMessageBox.Save:
                self.canceled = False
                self.logger.info("Overwriting")
            else:
                self.canceled = True
                self.logger.info("Canceling")
                return

        fullExportText += "*************************************\n"
        fullExportText += "**BEGIN hydraulic_control.ddck\n"
        fullExportText += "*************************************\n"

        simulationUnit = 450

        exporter = self._createExporter()

        fullExportText += exporter.exportPumpOutlets()
        fullExportText += exporter.exportMassFlows()
        fullExportText += exporter.exportDivSetting(simulationUnit - 10)

        self.logger.info(
            "------------------------> END OF EXPORT <------------------------"
        )

        if fullExportText[:1] == "\n":
            fullExportText = fullExportText[1:]
        controlFolder = os.path.split(hydCtrlPath)[0]
        if not (os.path.isdir(controlFolder)):
            os.makedirs(controlFolder)
        f = open(str(hydCtrlPath), "w")
        f.truncate(0)
        f.write(fullExportText)
        f.close()

        return hydCtrlPath

    def sortTrnsysObj(self):
        res = self.trnsysObj.sort(key=self.sortId)
        for s in self.trnsysObj:
            self.logger.debug("s has tr id " + str(s.trnsysId) +
                              " has dname " + s.displayName)

    def sortId(self, l1):
        """
        Sort function returning a sortable key
        Parameters
        ----------
        l1 : Block/Connection

        Returns
        -------

        """
        return l1.trnsysId

    def setName(self, newName):
        self.diagramName = newName

    def delBlocks(self):
        """
        Deletes the whole diagram

        Returns
        -------

        """
        self.hydraulicLoops.clear()

        while len(self.trnsysObj) > 0:
            self.logger.info("In deleting...")
            self.trnsysObj[0].deleteBlock()

        while len(self.graphicalObj) > 0:
            self.graphicalObj[0].deleteBlock()

    # Encoding / decoding
    def encodeDiagram(self, filename):
        """
        Encodes the diagram to a json file.

        Parameters
        ----------
        filename : str

        Returns
        -------

        """
        self.logger.info("filename is at encoder " + str(filename))
        # if filename != "":
        with open(filename, "w") as jsonfile:
            json.dump(self, jsonfile, indent=4, sort_keys=True, cls=Encoder)

    def _decodeDiagram(self, filename, loadValue="load"):
        self.logger.info("Decoding " + filename)
        with open(filename, "r") as jsonfile:
            blocklist = json.load(jsonfile, cls=Decoder, editor=self)

        blockFolderNames = []

        for j in blocklist["Blocks"]:
            for k in j:
                if isinstance(k, BlockItem):
                    k.setParent(self.diagramView)
                    k.changeSize()
                    self.diagramScene.addItem(k)
                    blockFolderNames.append(k.displayName)

                if isinstance(k, StorageTank):
                    k.updateImage()

                if isinstance(k, GraphicalItem):
                    k.setParent(self.diagramView)
                    self.diagramScene.addItem(k)

                if isinstance(k, dict):
                    if "__idDct__" in k:
                        # here we don't set the ids because the copyGroup would need access to idGen
                        self.logger.debug(
                            "Found the id dict while loading, not setting the ids"
                        )

                        self.idGen.setID(k["GlobalId"])
                        self.idGen.setTrnsysID(k["trnsysID"])
                        self.idGen.setConnID(k["globalConnID"])

                    if "__nameDct__" in k:
                        self.logger.debug("Found the name dict while loading")
                        if loadValue == "load":
                            self.diagramName = k["DiagramName"]

        blockFolderNames.append("generic")
        blockFolderNames.append("hydraulic")
        blockFolderNames.append("weather")
        blockFolderNames.append("control")

        ddckFolder = os.path.join(self.projectFolder, "ddck")
        ddckFolders = os.listdir(ddckFolder)
        additionalFolders = []

        for folder in ddckFolders:
            if folder not in blockFolderNames and "StorageTank" not in folder:
                additionalFolders.append(folder)

        if len(additionalFolders) > 0:
            warnBox = QMessageBox()
            warnBox.setWindowTitle("Additional ddck-folders")

            if len(additionalFolders) == 1:
                text = "The following ddck-folder does not have a corresponding component in the diagram:"
            else:
                text = "The following ddck-folders do not have a corresponding component in the diagram:"

            for folder in additionalFolders:
                text += "\n\t" + folder

            warnBox.setText(text)
            warnBox.setStandardButtons(QMessageBox.Ok)
            warnBox.setDefaultButton(QMessageBox.Ok)
            warnBox.exec()

        for t in self.trnsysObj:
            t.assignIDsToUninitializedValuesAfterJsonFormatMigration(
                self.idGen)

            self.logger.debug("Tr obj is" + str(t) + " " + str(t.trnsysId))
            if hasattr(t, "isTempering"):
                self.logger.debug("tv has " + str(t.isTempering))

        self._decodeHydraulicLoops(blocklist)

    def _decodeHydraulicLoops(self, blocklist):
        singlePipeConnections = [
            c for c in self.connectionList
            if isinstance(c, SinglePipeConnection)
        ]
        if "hydraulicLoops" not in blocklist:
            hydraulicLoops = _hlmig.createLoops(singlePipeConnections,
                                                self.fluids.WATER)
        else:
            serializedHydraulicLoops = blocklist["hydraulicLoops"]
            hydraulicLoops = _hlm.HydraulicLoops.createFromJson(
                serializedHydraulicLoops, singlePipeConnections, self.fluids)

        self.hydraulicLoops = hydraulicLoops

    def exportSvg(self):
        """
        For exporting a svg file (text is still too large)
        Returns
        -------

        """
        generator = QSvgGenerator()
        generator.setResolution(300)
        generator.setSize(
            QSize(self.diagramScene.width(), self.diagramScene.height()))
        # generator.setViewBox(QRect(0, 0, 800, 800))
        generator.setViewBox(self.diagramScene.sceneRect())
        generator.setFileName("VectorGraphicsExport.svg")

        painter = QPainter()
        painter.begin(generator)
        painter.setRenderHint(QPainter.Antialiasing)
        self.diagramScene.render(painter)
        painter.end()

    # Saving related
    def save(self, showWarning=True):
        """
        If saveas has not been used, diagram will be saved in "/diagrams"
        If saveas has been used, diagram will be saved in self.saveAsPath
        Returns
        -------

        """
        self.diagramName = os.path.split(self.projectFolder)[-1] + ".json"
        diagramPath = os.path.join(self.projectFolder, self.diagramName)

        if os.path.isfile(diagramPath) and showWarning:
            qmb = QMessageBox(self)
            qmb.setText(
                "Warning: " +
                "This diagram name exists already. Do you want to overwrite or cancel?"
            )
            qmb.setStandardButtons(QMessageBox.Save | QMessageBox.Cancel)
            qmb.setDefaultButton(QMessageBox.Cancel)
            ret = qmb.exec()

            if ret != QMessageBox.Save:
                self.logger.info("Canceling")
                return

            self.logger.info("Overwriting")
            self.encodeDiagram(diagramPath)

        self.encodeDiagram(diagramPath)
        msgb = QMessageBox(self)
        msgb.setText("Saved diagram at " + diagramPath)
        msgb.exec()

    def saveToProject(self):
        projectPath = self.projectPath

    def renameDiagram(self, newName):
        """

        Parameters
        ----------
        newName

        Returns
        -------

        """

        if self.saveAsPath.name != "":
            # print("Path name is " + self.saveAsPath.name)
            if newName + ".json" in self.saveAsPath.glob("*"):
                QMessageBox(
                    self, "Warning",
                    "This diagram name exists already in the directory."
                    " Please rename this diagram")
            else:
                self.saveAsPath = _pl.Path(
                    self.saveAsPath.stem[0:self.saveAsPath.name.
                                         index(self.diagramName)] + newName)

        self.diagramName = newName
        self.parent().currentFile = newName
        # fromPath = self.projectFolder
        # destPath = os.path.dirname(__file__)
        # destPath = os.path.join(destPath, 'default')
        # destPath = os.path.join(destPath, newName)
        # os.rename(fromPath, destPath)

        # print("Path is now: " + str(self.saveAsPath))
        # print("Diagram name is: " + self.diagramName)

    def saveAtClose(self):
        self.logger.info("saveaspath is " + str(self.saveAsPath))

        # closeDialog = closeDlg()
        # if closeDialog.closeBool:
        filepath = _pl.Path(
            _pl.Path(__file__).resolve().parent.joinpath("recent"))
        self.encodeDiagram(str(filepath.joinpath(self.diagramName + ".json")))

    # Mode related
    def setAlignMode(self, b):
        self.alignMode = True

    def setEditorMode(self, b):
        self.editorMode = b

    def setMoveDirectPorts(self, b):
        """
        Sets the bool moveDirectPorts. When mouse released in diagramScene, moveDirectPorts is set to False again
        Parameters
        ----------
        b : bool

        Returns
        -------

        """
        self.moveDirectPorts = b

    def setSnapGrid(self, b):
        self.snapGrid = b

    def setSnapSize(self, s):
        self.snapSize = s

    def setConnLabelVis(self, isVisible: bool) -> None:
        for c in self.trnsysObj:
            if isinstance(c, ConnectionBase):
                c.setLabelVisible(isVisible)
            if isinstance(c, BlockItem):
                c.label.setVisible(isVisible)
            if isinstance(c, TVentil):
                c.posLabel.setVisible(isVisible)

    def updateConnGrads(self):
        for t in self.trnsysObj:
            if isinstance(t, ConnectionBase):
                t.updateSegGrads()

    # Dialog calls
    def showBlockDlg(self, bl):
        c = BlockDlg(bl, self)

    def showDoublePipeBlockDlg(self, bl):
        c = DoublePipeBlockDlg(bl, self)

    def showPumpDlg(self, bl):
        c = PumpDlg(bl, self)

    def showDiagramDlg(self):
        c = diagramDlg(self)

    def showGenericPortPairDlg(self, bl):
        c = GenericPortPairDlg(bl, self)

    def showHxDlg(self, hx):
        c = hxDlg(hx, self)

    def showSegmentDlg(self, seg):
        c = segmentDlg(seg, self)

    def showTVentilDlg(self, bl):
        c = TVentilDlg(bl, self)

    def showConfigStorageDlg(self, bl):
        c = ConfigureStorageDialog(bl, self)

    def getConnection(self, n):
        return self.connectionList[int(n)]

    # Unused
    def create_icon(self, map_icon):
        map_icon.fill()
        painter = QPainter(map_icon)
        painter.fillRect(10, 10, 40, 40, QColor(88, 233, 252))
        # painter.setBrush(Qt.red)
        painter.setBrush(QColor(252, 136, 98))
        painter.drawEllipse(36, 2, 15, 15)
        painter.setBrush(Qt.yellow)
        painter.drawEllipse(20, 20, 20, 20)
        painter.end()

    def setTrnsysIdBack(self):
        self.idGen.trnsysID = max(t.trnsysId for t in self.trnsysObj)

    def findStorageCorrespPorts1(self, portList):
        """
        This function gets the ports on the other side of pipes connected to a port of the StorageTank. Unused

        Parameters
        ----------
        portList : :obj:`List` of :obj:`PortItems`

        Returns
        -------

        """

        res = []
        # print("Finding c ports")
        for p in portList:
            if len(p.connectionList) > 0:  # check if not >1 needed
                # connectionList[0] is the hidden connection created when the portPair is
                i = 0
                # while type(p.connectionList[i].fromPort.parent) is StorageTank and type(p.connectionList[i].toPort.parent) is StorageTank:
                while (p.connectionList[i].fromPort.parent) == (
                        p.connectionList[i].toPort.parent):
                    i += 1
                if len(p.connectionList) >= i + 1:
                    if p.connectionList[i].fromPort is p:
                        res.append(p.connectionList[i].toPort)
                    elif p.connectionList[i].toPort is p:
                        res.append(p.connectionList[i].fromPort)
                    else:
                        self.logger.debug("Port is not fromPort nor toPort")

        # [print(p.parent.displayName) for p in res]
        return res

    def printPDF(self):
        """
        ---------------------------------------------
        Export diagram as pdf onto specified folder
        fn = user input directory
        ---------------------------------------------
        """
        fn, _ = QFileDialog.getSaveFileName(self, "Export PDF", None,
                                            "PDF files (.pdf);;All Files()")
        if fn != "":
            if QFileInfo(fn).suffix() == "":
                fn += ".pdf"
            printer = QPrinter(QPrinter.HighResolution)
            printer.setOrientation(QPrinter.Landscape)
            printer.setOutputFormat(QPrinter.PdfFormat)
            printer.setOutputFileName(fn)
            painter = QPainter(printer)
            self.diagramScene.render(painter)
            painter.end()
            self.logger.info("File exported to %s" % fn)

    def openProject(self):
        self.projectPath = str(
            QFileDialog.getExistingDirectory(self, "Select Project Path"))
        if self.projectPath != "":
            test = self.parent()

            self.parent().newDia()
            self.PPL.setText(self.projectPath)
            loadPath = os.path.join(self.projectPath, "ddck")

            self.createConfigBrowser(self.projectPath)
            self.copyGenericFolder(self.projectPath)
            self.createHydraulicDir(self.projectPath)
            self.createWeatherAndControlDirs(self.projectPath)
            self.createDdckTree(loadPath)
            # todo : open diagram
            # todo : add files into list

    def createDdckTree(self, loadPath):
        treeToRemove = self.findChild(QTreeView, "ddck")
        try:
            # treeToRemove.hide()
            treeToRemove.deleteLater()
        except AttributeError:
            self.logger.debug("Widget doesnt exist!")
        else:
            self.logger.debug("Deleted widget")
        if self.projectPath == "":
            loadPath = os.path.join(loadPath, "ddck")
        if not os.path.exists(loadPath):
            os.makedirs(loadPath)
        self.model = MyQFileSystemModel()
        self.model.setRootPath(loadPath)
        self.model.setName("ddck")
        self.tree = MyQTreeView(self.model, self)
        self.tree.setModel(self.model)
        self.tree.setRootIndex(self.model.index(loadPath))
        self.tree.setObjectName("ddck")
        self.tree.setMinimumHeight(600)
        self.tree.setSortingEnabled(True)
        self.splitter.insertWidget(0, self.tree)

    def createConfigBrowser(self, loadPath):
        self.layoutToRemove = self.findChild(QHBoxLayout, "Config_Layout")
        try:
            # treeToRemove.hide()
            self.layoutToRemove.deleteLater()
        except AttributeError:
            self.logger.debug("Widget doesnt exist!")
        else:
            self.logger.debug("Deleted widget")

        runConfigData = self._getPackageResourceData("templates/run.config")
        runConfigPath = _pl.Path(loadPath) / "run.config"
        runConfigPath.write_bytes(runConfigData)

        self.HBox = QHBoxLayout()
        self.refreshButton = QPushButton(self)
        self.refreshButton.setIcon(_img.ROTATE_TO_RIGHT_PNG.icon())
        self.refreshButton.clicked.connect(self.refreshConfig)
        self.model = MyQFileSystemModel()
        self.model.setRootPath(loadPath)
        self.model.setName("Config File")
        self.model.setFilter(QDir.Files)
        self.tree = MyQTreeView(self.model, self)
        self.tree.setModel(self.model)
        self.tree.setRootIndex(self.model.index(loadPath))
        self.tree.setObjectName("config")
        self.tree.setFixedHeight(60)
        self.tree.setSortingEnabled(False)
        self.HBox.addWidget(self.refreshButton)
        self.HBox.addWidget(self.tree)
        self.HBox.setObjectName("Config_Layout")
        self.fileBrowserLayout.addLayout(self.HBox)

    def createProjectFolder(self):
        if not os.path.exists(self.projectFolder):
            os.makedirs(self.projectFolder)

    def refreshConfig(self):
        # configPath = os.path.dirname(__file__)
        # configPath = os.path.join(configPath, 'project')
        # configPath = os.path.join(configPath, self.date_time)
        # emptyConfig = os.path.join(configPath, 'run.config')
        if self.projectPath == "":
            localPath = self.projectFolder
        else:
            localPath = self.projectPath

        self.configToEdit = os.path.join(localPath, "run.config")
        os.remove(self.configToEdit)
        shutil.copy(self.emptyConfig, localPath)
        self.configToEdit = os.path.join(localPath, "run.config")

        localDdckPath = os.path.join(localPath, "ddck")
        with open(self.configToEdit, "r") as file:
            lines = file.readlines()
        localPathStr = "string LOCAL$ %s" % str(localDdckPath)
        # localPathStr.replace('/', '\\')
        lines[21] = localPathStr + "\n"

        with open(self.configToEdit, "w") as file:
            file.writelines(lines)

        # print(localPathStr)
        self.userInputList()

    def userInputList(self):
        self.logger.debug(self.fileList)
        dia = FileOrderingDialog(self.fileList, self)

    def copyGenericFolder(self, loadPath):
        genericFolderPath = _pl.Path(loadPath) / "ddck" / "generic"

        if not genericFolderPath.exists():
            self.logger.info("Creating %s", genericFolderPath)
            genericFolderPath.mkdir()

        headData = self._getPackageResourceData("templates/generic/head.ddck")
        self.logger.info("Copying head.ddck")
        (genericFolderPath / "head.ddck").write_bytes(headData)

        endData = self._getPackageResourceData("templates/generic/end.ddck")
        self.logger.info("Copying end.ddck")
        (genericFolderPath / "end.ddck").write_bytes(endData)

    @staticmethod
    def _getPackageResourceData(resourcePath):
        data = _pu.get_data(_tgui.__name__, resourcePath)
        assert data, f"{resourcePath} package resource not found"
        return data

    def createHydraulicDir(self, projectPath):

        self.hydraulicFolder = os.path.join(projectPath, "ddck")
        self.hydraulicFolder = os.path.join(self.hydraulicFolder, "hydraulic")

        if not os.path.exists(self.hydraulicFolder):
            self.logger.info("Creating " + self.hydraulicFolder)
            os.makedirs(self.hydraulicFolder)

    def createWeatherAndControlDirs(self, projectPath):

        ddckFolder = os.path.join(projectPath, "ddck")
        weatherFolder = os.path.join(ddckFolder, "weather")
        controlFolder = os.path.join(ddckFolder, "control")

        if not os.path.exists(weatherFolder):
            self.logger.info("Creating " + weatherFolder)
            os.makedirs(weatherFolder)

        if not os.path.exists(controlFolder):
            self.logger.info("Creating " + controlFolder)
            os.makedirs(controlFolder)

    def editHydraulicLoop(self, singlePipeConnection: SinglePipeConnection):
        assert isinstance(singlePipeConnection.fromPort, SinglePipePortItem)

        hydraulicLoop = self.hydraulicLoops.getLoopForExistingConnection(
            singlePipeConnection)
        _hledit.edit(hydraulicLoop, self.hydraulicLoops, self.fluids)
class PrioritizeDialog(QDialog):
    def __init__(self, parent, app, **kwargs):
        flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint
        super().__init__(parent, flags, **kwargs)
        self._setupUi()
        self.model = PrioritizeDialogModel(app=app.model)
        self.categoryList = ComboboxModel(model=self.model.category_list, view=self.categoryCombobox)
        self.criteriaList = ListviewModel(model=self.model.criteria_list, view=self.criteriaListView)
        self.prioritizationList = PrioritizationList(
            model=self.model.prioritization_list, view=self.prioritizationListView
        )
        self.model.view = self

        self.addCriteriaButton.clicked.connect(self.model.add_selected)
        self.removeCriteriaButton.clicked.connect(self.model.remove_selected)
        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.rejected.connect(self.reject)

    def _setupUi(self):
        self.setWindowTitle(tr("Re-Prioritize duplicates"))
        self.resize(700, 400)

        #widgets
        msg = tr(
            "Add criteria to the right box and click OK to send the dupes that correspond the "
            "best to these criteria to their respective group's "
            "reference position. Read the help file for more information."
        )
        self.promptLabel = QLabel(msg)
        self.promptLabel.setWordWrap(True)
        self.categoryCombobox = QComboBox()
        self.criteriaListView = QListView()
        self.addCriteriaButton = QPushButton(self.style().standardIcon(QStyle.SP_ArrowRight), "")
        self.removeCriteriaButton = QPushButton(self.style().standardIcon(QStyle.SP_ArrowLeft), "")
        self.prioritizationListView = QListView()
        self.prioritizationListView.setAcceptDrops(True)
        self.prioritizationListView.setDragEnabled(True)
        self.prioritizationListView.setDragDropMode(QAbstractItemView.InternalMove)
        self.prioritizationListView.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.buttonBox = QDialogButtonBox()
        self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok)

        # layout
        self.mainLayout = QVBoxLayout(self)
        self.mainLayout.addWidget(self.promptLabel)
        self.splitter = QSplitter()
        sp = self.splitter.sizePolicy()
        sp.setVerticalPolicy(QSizePolicy.Expanding)
        self.splitter.setSizePolicy(sp)
        self.leftSide = QWidget()
        self.leftWidgetsLayout = QVBoxLayout()
        self.leftWidgetsLayout.addWidget(self.categoryCombobox)
        self.leftWidgetsLayout.addWidget(self.criteriaListView)
        self.leftSide.setLayout(self.leftWidgetsLayout)
        self.splitter.addWidget(self.leftSide)
        self.rightSide = QWidget()
        self.rightWidgetsLayout = QHBoxLayout()
        self.addRemoveButtonsLayout = QVBoxLayout()
        self.addRemoveButtonsLayout.addItem(verticalSpacer())
        self.addRemoveButtonsLayout.addWidget(self.addCriteriaButton)
        self.addRemoveButtonsLayout.addWidget(self.removeCriteriaButton)
        self.addRemoveButtonsLayout.addItem(verticalSpacer())
        self.rightWidgetsLayout.addLayout(self.addRemoveButtonsLayout)
        self.rightWidgetsLayout.addWidget(self.prioritizationListView)
        self.rightSide.setLayout(self.rightWidgetsLayout)
        self.splitter.addWidget(self.rightSide)
        self.mainLayout.addWidget(self.splitter)
        self.mainLayout.addWidget(self.buttonBox)
Exemple #9
0
class MainWindow(QWidget):

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

        self.playlistView = QListView()
        self.switch_status = 2
        self.video_widget = QVideoWidget()
        self.playlist = QMediaPlaylist()
        self.model = PlaylistModel(self.playlist)
        self.titleBar = TitleBar(self)
        self.currentTimeLabel = QLabel()
        self.timeSlider = QSlider()
        self.totalTimeLabel = QLabel()
        self.mediaPlayer = QMediaPlayer()
        self.open_btn = QPushButton('Open File')
        self.play_btn = QPushButton()
        self.prev_btn = QPushButton()
        self.stop_btn = QPushButton()
        self.next_btn = QPushButton()
        self.switch_media_widgets_btn = QPushButton()
        self.pseudo_label = QLabel()

        self.vol_label = QLabel()
        self.volume_slider = Slider(Qt.Horizontal)
        self.gui()
        self.set_children_focus_policy(Qt.NoFocus)

    def gui(self):
        self.currentTimeLabel.setMinimumSize(QSize(80, 0))
        self.currentTimeLabel.setAlignment(Qt.AlignRight | Qt.AlignTrailing | Qt.AlignVCenter)

        self.timeSlider.setOrientation(Qt.Horizontal)
        self.totalTimeLabel.setMinimumSize(QSize(80, 0))
        self.totalTimeLabel.setAlignment(Qt.AlignLeading | Qt.AlignLeft | Qt.AlignVCenter)

        self.playlistView.setAcceptDrops(True)
        self.playlistView.setProperty("showDropIndicator", True)
        self.playlistView.setDragDropMode(QAbstractItemView.DropOnly)
        self.playlistView.setAlternatingRowColors(True)
        self.playlistView.setUniformItemSizes(True)

        self.setWindowFlags(Qt.FramelessWindowHint)
        self.setWindowTitle('Media Player')
        self.titleBar.label.setText('Media Player')
        self.setWindowIcon(QIcon('icon_png/media_player.png'))

        self.setGeometry(600, 200, 850, 600)
        self.timeSlider.setRange(0, 0)
        self.play_btn.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
        self.prev_btn.setIcon(self.style().standardIcon(QStyle.SP_MediaSkipBackward))
        self.next_btn.setIcon(self.style().standardIcon(QStyle.SP_MediaSkipForward))
        self.stop_btn.setIcon(self.style().standardIcon(QStyle.SP_MediaStop))
        self.switch_media_widgets_btn.setIcon(self.style().standardIcon(QStyle.SP_FileDialogDetailedView))
        self.vol_label.setText("")
        self.vol_label.setPixmap(QPixmap("icon_png/speaker-volume.png"))
        self.currentTimeLabel.setText("00:00")
        self.totalTimeLabel.setText("00:00")
        self.volume_slider.setValue(self.mediaPlayer.volume())
        self.mediaPlayer.setVideoOutput(self.video_widget)
        self.mediaPlayer.setPlaylist(self.playlist)
        self.playlistView.setModel(self.model)
        self.video_widget.hide()

        sizegrip = QSizeGrip(self)

        self.setAcceptDrops(True)

        inner_h_box = QHBoxLayout()
        inner_h_box.addWidget(self.prev_btn)
        inner_h_box.addWidget(self.stop_btn)
        inner_h_box.addWidget(self.next_btn)

        vol_h_box = QHBoxLayout()
        vol_h_box.addWidget(self.vol_label, 0)
        vol_h_box.addWidget(self.volume_slider, 1)

        h_box = QHBoxLayout()
        h_box.addWidget(self.open_btn)
        h_box.addWidget(self.play_btn, 0)
        h_box.addLayout(inner_h_box, 0)
        h_box.addWidget(self.switch_media_widgets_btn, 0)
        h_box.addWidget(self.pseudo_label, 1)
        h_box.addLayout(vol_h_box, 0)
        h_box.addWidget(sizegrip, 0, Qt.AlignBottom | Qt.AlignRight)

        video_slider_h_box = QHBoxLayout()
        video_slider_h_box.addWidget(self.currentTimeLabel)
        video_slider_h_box.addWidget(self.timeSlider)
        video_slider_h_box.addWidget(self.totalTimeLabel)

        v_box = QVBoxLayout()
        v_box.addWidget(self.titleBar, 0)
        v_box.addWidget(self.video_widget, 1)
        v_box.addWidget(self.playlistView, 1)
        v_box.addLayout(video_slider_h_box, 0)
        v_box.addLayout(h_box, 0)

        inner_h_box.setContentsMargins(20, 0, 10, 0)
        vol_h_box.setContentsMargins(0, 0, 20, 0)
        h_box.setContentsMargins(20, 0, 0, 0)
        v_box.setContentsMargins(0, 0, 0, 0)
        video_slider_h_box.setSpacing(10)
        h_box.setSpacing(0)
        v_box.setSpacing(0)

        self.setLayout(v_box)
        self.enabler()

        # connections
        self.open_btn.clicked.connect(self.open_file)
        self.play_btn.clicked.connect(self.play_media)
        self.stop_btn.clicked.connect(self.stop_media)

        self.prev_btn.pressed.connect(self.play_prev)
        self.next_btn.pressed.connect(self.play_next)
        self.switch_media_widgets_btn.pressed.connect(self.switch_media)

        self.playlist.currentIndexChanged.connect(self.playlist_position_changed)
        selection_model = self.playlistView.selectionModel()
        selection_model.selectionChanged.connect(self.playlist_selection_changed)

        self.mediaPlayer.durationChanged.connect(self.update_duration)
        self.mediaPlayer.positionChanged.connect(self.update_position)
        self.timeSlider.valueChanged.connect(self.mediaPlayer.setPosition)

        self.mediaPlayer.stateChanged.connect(self.media_state)

        self.mediaPlayer.volumeChanged.connect(self.volume_changed)
        self.volume_slider.valueChanged.connect(self.set_volume)

    def set_children_focus_policy(self, policy):
        def recursive_set_child_focus_policy(parent_q_widget):
            for childQWidget in parent_q_widget.findChildren(QWidget):
                childQWidget.setFocusPolicy(policy)
                recursive_set_child_focus_policy(childQWidget)

        recursive_set_child_focus_policy(self)

    def enabler(self, state=False):
        if state is False:
            self.play_btn.setEnabled(False)
            self.prev_btn.setEnabled(False)
            self.stop_btn.setEnabled(False)
            self.next_btn.setEnabled(False)
        else:
            self.play_btn.setEnabled(True)
            self.stop_btn.setEnabled(True)
            self.prev_btn.setEnabled(True)
            self.next_btn.setEnabled(True)

    def switch_media(self):
        if self.switch_status == 0:
            self.video_widget.hide()
            self.playlistView.show()
            self.switch_status = 1
            self.switch_media_widgets_btn.setEnabled(True)
        elif self.switch_status == 1:
            self.video_widget.show()
            self.playlistView.hide()
            self.switch_status = 0
            self.switch_media_widgets_btn.setEnabled(True)
        else:
            self.video_widget.hide()
            self.playlistView.show()
            self.switch_media_widgets_btn.setEnabled(False)

    def play_media(self):
        if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
            self.mediaPlayer.pause()
        else:
            self.mediaPlayer.play()
        self.ui_handler()

    def ui_handler(self):
        if not self.playlist.isEmpty():
            self.enabler(True)
        file_path = QFileInfo(self.mediaPlayer.currentMedia().canonicalUrl().toString()).fileName()
        ext = os.path.splitext(file_path)[-1].lower()
        audio_ext = ['.flac', '.mp3']
        video_ext = ['.mp4', '.m4a', '.mov', '.flv', 'avi', '3gp', '.mkv', '.wmv']

        if ext in audio_ext:
            self.switch_status = 2
            self.switch_media()
            if self.isFullScreen():
                self.fullscreen()
        elif ext in video_ext:
            self.switch_status = 1
            self.switch_media()
        self.setWindowTitle(file_path + ' - Media Player')
        self.titleBar.label.setText(file_path + ' - Media Player')

    def stop_media(self):
        if self.mediaPlayer.state() != QMediaPlayer.StoppedState:
            self.mediaPlayer.stop()
            self.setWindowTitle('Media Player')
            self.titleBar.label.setText('Media Player')

    def fullscreen(self):
        if self.switch_status == 2 or self.isFullScreen():
            self.titleBar.show()
            self.timeSlider.show()
            self.currentTimeLabel.show()
            self.totalTimeLabel.show()
            self.volume_slider.show()
            self.open_btn.show()
            self.play_btn.show()
            self.prev_btn.show()
            self.stop_btn.show()
            self.next_btn.show()
            self.switch_media_widgets_btn.show()
            self.pseudo_label.show()
            self.vol_label.show()
            self.showNormal()
        else:
            self.titleBar.hide()
            self.timeSlider.hide()
            self.currentTimeLabel.hide()
            self.totalTimeLabel.hide()
            self.volume_slider.hide()
            self.open_btn.hide()
            self.play_btn.hide()
            self.prev_btn.hide()
            self.stop_btn.hide()
            self.next_btn.hide()
            self.switch_media_widgets_btn.hide()
            self.pseudo_label.hide()
            self.vol_label.hide()
            self.showFullScreen()

    def mouseDoubleClickEvent(self, event: QMouseEvent):
        event.accept()
        if event.button() == Qt.LeftButton:
            self.fullscreen()

    def media_state(self):

        os_sleep = WindowsInhibitor()
        if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
            self.play_btn.setIcon(self.style().standardIcon(QStyle.SP_MediaPause))
            if os.name == 'nt':
                os_sleep = WindowsInhibitor()
                os_sleep.inhibit()
        else:
            self.play_btn.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
            if os_sleep:
                os_sleep.uninhibit()

    def play_next(self):
        self.playlist.next()

    def media_seek(self, seek):
        if not self.playlist.isEmpty():
            player = self.mediaPlayer
            if (player.duration() - seek) > player.position():
                player.setPosition(player.position() + seek)

    def play_prev(self):
        self.playlist.previous()

    def dragEnterEvent(self, e):
        if e.mimeData().hasUrls():
            e.acceptProposedAction()

    def dropEvent(self, e):
        for url in e.mimeData().urls():
            ext = os.path.splitext(url.fileName())[-1].lower()
            allowed_ext = ['.flac', '.mp3', '.mp4', '.m4a', '.mov', '.flv', 'avi', '3gp', '.mkv', '.wmv']
            if ext in allowed_ext:
                self.playlist.addMedia(
                    QMediaContent(url)
                )

        self.model.layoutChanged.emit()

        if self.mediaPlayer.state() != QMediaPlayer.PlayingState:
            i = self.playlist.mediaCount() - len(e.mimeData().urls())
            self.playlist.setCurrentIndex(i)
            if not self.playlist.isEmpty():
                self.play_media()

    def open_file(self):
        filter_files = "Media (*.mp3 *.mp4 *.mkv);; Videos files (*.mp4 *.mkv);; Music Files(*.mp3)"
        paths, _ = QFileDialog.getOpenFileNames(self, "Open file", "", filter_files)

        if paths:
            self.mediaPlayer.pause()
            for path in paths:
                self.playlist.addMedia(
                    QMediaContent(
                        QUrl.fromLocalFile(path)
                    )
                )
            i = self.playlist.mediaCount() - len(paths)
            self.playlist.setCurrentIndex(i)
            self.play_media()

        self.model.layoutChanged.emit()

    def update_duration(self, duration):
        self.mediaPlayer.duration()

        self.timeSlider.setMaximum(duration)

        if duration >= 0:
            self.totalTimeLabel.setText(hhmmss(duration))

    def update_position(self, position):
        if position >= 0:
            self.currentTimeLabel.setText(hhmmss(position))

        self.timeSlider.blockSignals(True)
        self.timeSlider.setValue(position)
        self.timeSlider.blockSignals(False)

    def playlist_selection_changed(self, ix):
        i = ix.indexes()[0].row()
        self.playlist.setCurrentIndex(i)
        self.ui_handler()

    def playlist_position_changed(self, i):
        if i > -1:
            ix = self.model.index(i)
            self.playlistView.setCurrentIndex(ix)

    def set_volume(self, value):
        self.mediaPlayer.setVolume(value)

    def volume_changed(self, value):
        self.volume_slider.setValue(value)

    def keyPressEvent(self, event):
        key = event.key()
        modifiers = int(event.modifiers())
        if (modifiers and modifiers & MOD_MASK == modifiers and
                key > 0 and key != Qt.Key_Shift and key != Qt.Key_Alt and
                key != Qt.Key_Control and key != Qt.Key_Meta):
            key_name = QKeySequence(modifiers + key).toString()
            if key_name == 'Ctrl+Right':
                self.media_seek(30000)
            elif key_name == 'Ctrl+Left':
                self.media_seek(-30000)
            elif key_name == 'Ctrl+Up':
                self.mediaPlayer.setVolume(self.mediaPlayer.volume() + 5)
            elif key_name == 'Ctrl+Down':
                self.mediaPlayer.setVolume(self.mediaPlayer.volume() - 5)
            elif key_name == 'Ctrl+O':
                self.open_file()

        else:
            if event.key() == Qt.Key_Space:
                self.play_media()
            elif event.key() == Qt.Key_MediaPlay:
                self.play_media()
            elif event.key() == Qt.Key_MediaNext:
                self.play_next()
            elif event.key() == Qt.Key_MediaPrevious:
                self.play_prev()
            elif event.key() == Qt.Key_Escape:
                self.close()
            elif event.key() == Qt.Key_F:
                self.fullscreen()
            elif event.key() == Qt.Key_Right:
                self.media_seek(5000)
            elif event.key() == Qt.Key_Left:
                self.media_seek(-5000)
Exemple #10
0
class DiaMorView(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):

        system = platform.system()

        self.projectPath_label = QLabel(self)
        self.projectPath_label.setText("Select a project folder")
        self.projectPath_label.move(22, 30)
        self.projectPath_label.resize(400, 30)

        self.xml_lineEdit = ClickableLineEdit(self)
        self.xml_lineEdit.move(20, 70)
        self.xml_lineEdit.resize(400, 30)
        self.xml_lineEdit.setPlaceholderText("Choose xml file")
        self.xml_lineEdit.setReadOnly(True)

        self.xml_button = QPushButton(self)
        self.xml_button.move(430, 74)
        self.xml_button.resize(20, 20)
        icon = QIcon()
        icon.addPixmap(QPixmap("assets/folder.png"))
        self.xml_button.setIcon(icon)
        self.xml_button.setIconSize(QSize(20, 20))

        self.lexiconFile_lineEdit = ClickableLineEdit(self)
        self.lexiconFile_lineEdit.move(20, 110)
        self.lexiconFile_lineEdit.resize(400, 30)
        #self.lexiconFile_lineEdit.clicked.connect(self.showFileSelection)
        self.lexiconFile_lineEdit.setPlaceholderText("Choose lexicon file")
        self.lexiconFile_lineEdit.setReadOnly(True)

        self.lexiconFile_button = QPushButton(self)
        self.lexiconFile_button.move(430, 114)
        self.lexiconFile_button.resize(20, 20)
        icon = QIcon()
        icon.addPixmap(QPixmap("assets/folder.png"))
        self.lexiconFile_button.setIcon(icon)
        self.lexiconFile_button.setIconSize(QSize(20, 20))

        self.twolFileList_Label = QLabel(self)
        self.twolFileList_Label.setText("Found .twol files: ")
        self.twolFileList_Label.move(22, 160)
        self.twolFileList_Label.resize(400, 30)

        self.twolRefresh_button = QPushButton("Refresh", self)
        self.twolRefresh_button.move(350, 160)

        self.twolFileList_ListWidget = QListView(self)
        self.twolFileList_ListWidgetModel = QStandardItemModel(
            self.twolFileList_ListWidget)
        self.twolFileList_ListWidget.setModel(
            self.twolFileList_ListWidgetModel)
        self.twolFileList_ListWidget.move(20, 200)
        self.twolFileList_ListWidget.resize(430, 300)
        self.twolFileList_ListWidget.setDragDropMode(
            QAbstractItemView.InternalMove)
        self.twolFileList_ListWidget.show()
        #self.twolFileList_ListWidget.selectionModel().selectionChanged.connect(self.t)

        self.generate_Button = QPushButton("Generate", self)
        self.generate_Button.move(350, 510)
        self.generate_Button.setEnabled(False)

        if system == "Darwin":
            self.projectPath_label.move(22, 10)
            self.xml_lineEdit.move(20, 50)
            self.xml_button.move(430, 54)
            self.lexiconFile_lineEdit.move(20, 90)
            self.lexiconFile_button.move(430, 94)
            self.twolFileList_Label.move(22, 140)
            self.twolRefresh_button.move(350, 140)
            self.twolFileList_ListWidget.move(20, 180)
            self.generate_Button.move(350, 490)

        self.statusBar()
        #openFile.setShortcut('Ctrl+O')
        #createProjectMenuBtn.triggered.connect(self.createProject)
        self.openProjectMenuBtn = QAction('Open', self)
        self.openProjectMenuBtn.setShortcut('Ctrl+O')
        self.openProjectMenuBtn.setStatusTip('Open existing project')
        self.saveProjectMenuBtn = QAction('Save', self)
        self.saveProjectMenuBtn.setShortcut('Ctrl+S')
        self.saveProjectMenuBtn.setStatusTip('Save existing project')

        menubar = self.menuBar()
        fileMenu = menubar.addMenu('&Project')
        fileMenu.addAction(self.openProjectMenuBtn)
        fileMenu.addAction(self.saveProjectMenuBtn)

        self.setGeometry(50, 50, 470, 550)
        self.setWindowTitle('DiaMor')
        self.show()

    def t(self):
        print("order changed")

    def showWarning(self, message):
        QMessageBox.about(self, "Warning", message)

    def showDirectoryDialog(self):
        return QFileDialog.getExistingDirectory(self, "Select Directory")

    def showFileDialog(self, path, fileType):
        return QFileDialog.getOpenFileName(self, 'Open file', path, fileType)

    def clearTwolList(self):
        self.twolFileList_ListWidgetModel.clear()

    def addToTwolList(self, fileName, state):
        item = QStandardItem(fileName)
        item.setCheckable(True)
        item.setCheckState(state)
        item.setEditable(False)
        item.setDropEnabled(False)
        self.twolFileList_ListWidgetModel.appendRow(item)
        '''
Exemple #11
0
class MediaPlayerView(AppWidget):

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

        # controls
        self.previousButton = QPushButton(self.style().standardIcon(QStyle.SP_MediaSeekBackward), "") # TODO change to icons
        self.playButton = QPushButton(self.style().standardIcon(QStyle.SP_MediaPlay), "")
        self.pauseButton = QPushButton(self.style().standardIcon(QStyle.SP_MediaPause), "")
        self.nextButton = QPushButton(self.style().standardIcon(QStyle.SP_MediaSeekForward), "")
        self.stopButton = QPushButton(self.style().standardIcon(QStyle.SP_MediaStop), "")
        self.removeButton = QPushButton(self.style().standardIcon(QStyle.SP_DialogCancelButton), "")
        self.volumeSlider = QSlider()
        self.initControls()

        # seek bar
        self.timeSlider = QSlider()
        self.currentTimeLabel = QLabel()
        self.totalTimeLabel = QLabel()
        self.initSeekBar()

        # playlist
        self.playlistView = QListView()
        self.initPlaylistView()

        self.assemble()

    def initControls(self):
        self.volumeSlider.setMaximum(100)
        self.volumeSlider.setProperty("value", R.initVolume)
        self.volumeSlider.setOrientation(Qt.Horizontal)
        self.volumeSlider.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
        self.playButton.setDisabled(True)
        self.pauseButton.setDisabled(True)
        self.stopButton.setDisabled(True)
        self.previousButton.setDisabled(True)
        self.nextButton.setDisabled(True)
        self.removeButton.setDisabled(True)

    def initSeekBar(self):
        self.timeSlider.setOrientation(Qt.Horizontal)
        self.currentTimeLabel.setMinimumSize(QSize(R.timeLabelWidth, 0))
        self.currentTimeLabel.setAlignment(Qt.AlignRight | Qt.AlignTrailing | Qt.AlignVCenter)
        self.totalTimeLabel.setMinimumSize(QSize(R.timeLabelWidth, 0))
        self.totalTimeLabel.setAlignment(Qt.AlignLeading | Qt.AlignLeft | Qt.AlignVCenter)
        self.currentTimeLabel.setText(R.initTimeLabel)
        self.totalTimeLabel.setText(R.initTotalTimeLabel)

    def initPlaylistView(self):
        self.playlistView.setAcceptDrops(True)
        self.playlistView.setProperty("showDropIndicator", True)
        self.playlistView.setDragDropMode(QAbstractItemView.DropOnly)
        self.playlistView.setAlternatingRowColors(True)
        self.playlistView.setUniformItemSizes(True)

    def assemble(self):
        # controls
        buttonGroup = QHBoxLayout()
        buttonGroup.addWidget(self.previousButton)
        buttonGroup.addWidget(self.playButton)
        buttonGroup.addWidget(self.pauseButton)
        buttonGroup.addWidget(self.stopButton)
        buttonGroup.addWidget(self.nextButton)
        buttonGroup.addWidget(self.volumeSlider)
        buttonGroup.addWidget(self.removeButton)

        # seek bar
        timerLayout = QHBoxLayout()
        timerLayout.addWidget(self.currentTimeLabel)
        timerLayout.addWidget(self.timeSlider)
        timerLayout.addWidget(self.totalTimeLabel)

        # main layout
        self.mainLayout.addWidget(self.playlistView)
        self.mainLayout.addLayout(timerLayout)
        self.mainLayout.addLayout(buttonGroup)