def init_name_input_completers(self):
        self.name_input_completers = dict()
        for microbiome, strains in self.all_strains.items():
            self.name_input_completers[microbiome] = dict()

            # Determine which strains should be displayed
            strains_to_display = {taxon: set() for taxon in TAXA}
            strains_to_display[ASV_KEY] = set()
            for strain in strains:
                if sum(strain.abundances) > ABUNDANCE_DISPLAY_THRESHOLD:
                    for taxon in TAXA:
                        name = strain.get_taxon_name(taxon)
                        if name is not None:
                            strains_to_display[taxon].add(
                                strain.get_taxon_name(taxon))
                    strains_to_display[ASV_KEY].add(strain.id)

            for taxon, names in strains_to_display.items():
                if taxon == ASV_KEY:
                    sorted_names = sorted(names,
                                          key=lambda name: int(name[4:]))
                else:
                    sorted_names = sorted(names)
                completer = QCompleter(sorted_names)
                completer.setCaseSensitivity(
                    Qt.CaseSensitivity.CaseInsensitive)
                completer.setMaxVisibleItems(20)
                self.name_input_completers[microbiome][taxon] = completer
Esempio n. 2
0
    def __init__(self, parent, title: str, width: int,
                 completer_list: List[str], completer_max: int):
        super().__init__(parent)
        self.setWindowTitle(title)
        self.setMinimumWidth(width)

        # completer
        completer = QCompleter(completer_list)
        completer.setMaxVisibleItems(completer_max)
        self.ledit = QLineEdit()
        self.ledit.setCompleter(completer)
        self.ledit.editingFinished.connect(self.edit_finished)

        self.ok = False
        self.btn_ok = QPushButton("OK", self)
        self.btn_ok.clicked.connect(self.button_ok)
        self.btn_cancel = QPushButton("Cancel", self)
        self.btn_cancel.clicked.connect(self.button_cancel)
Esempio n. 3
0
class Main(QMainWindow):
    def __init__(self): 
        global downloads_list_file
        QMainWindow.__init__(self)
        self.setWindowIcon(QIcon(":/quartz.png")) 
        self.setWindowTitle("Quartz Browser - "+__version__)
        # Window Properties
        self.history = []
        self.downloads = []
        self.confirm_before_quit = True
        # Create required directories
        for folder in [configdir, icon_dir, thumbnails_dir]:
            if not os.path.exists(folder):
                os.mkdir(folder)
        # Import and Apply Settings
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.settings = QSettings(1, 0, "quartz-browser","Quartz", self)
        self.opensettings()
        self.websettings = QWebSettings.globalSettings()
        self.websettings.setAttribute(QWebSettings.DnsPrefetchEnabled, True)
        self.websettings.setMaximumPagesInCache(10)
        self.websettings.setIconDatabasePath(icon_dir)
        self.websettings.setAttribute(QWebSettings.JavascriptCanOpenWindows, True)
        self.websettings.setAttribute(QWebSettings.JavascriptCanCloseWindows, True)
        if webkit.enable_adblock:
            self.websettings.setUserStyleSheetUrl(QUrl.fromLocalFile(program_dir + 'userContent.css'))
        # Import Downloads and Bookmarks
        self.dwnldsmodel = DownloadsModel(self.downloads, QApplication.instance())
        self.dwnldsmodel.deleteDownloadsRequested.connect(self.deleteDownloads)
        imported_downloads = importDownloads(downloads_list_file)
        for [filepath, url, totalsize, timestamp] in imported_downloads:
            try :                                                  # Check if downloads.txt is valid
                tymstamp = float(timestamp)
            except :
                self.downloads = []
                exportDownloads(downloads_list_file, [])
                print("Error in importing Downloads.")
                break
            old_download = Download(networkmanager)
            old_download.loadDownload(filepath, url, totalsize, timestamp)
            old_download.datachanged.connect(self.dwnldsmodel.datachanged)
            self.downloads.append(old_download)
        self.bookmarks = importBookmarks(configdir+"bookmarks.txt")
        self.favourites = importFavourites(configdir + 'favourites.txt')
        # Find and set icon theme name
        for theme_name in ['Adwaita', 'Gnome', 'Tango']:
            if os.path.exists('/usr/share/icons/' + theme_name):
                QIcon.setThemeName(theme_name)
                break
        self.initUI()
        self.resize(1024,714)

    def initUI(self):
###############################  Create  Actions ##############################
        self.loadimagesaction = QAction("Load Images",self)
        self.loadimagesaction.setCheckable(True)
        self.loadimagesaction.triggered.connect(self.loadimages)

        self.javascriptmode = QAction("Enable Javascript",self)
        self.javascriptmode.setCheckable(True)
        self.javascriptmode.triggered.connect(self.setjavascript)

        self.useragent_mode_desktop = QAction("Desktop",self)
        self.useragent_mode_desktop.setCheckable(True)
        self.useragent_mode_desktop.triggered.connect(self.setUserAgentDesktop)

        self.useragent_mode_mobile = QAction("Mobile",self)
        self.useragent_mode_mobile.setCheckable(True)
        self.useragent_mode_mobile.triggered.connect(self.setUserAgentMobile)

        self.useragent_mode_custom = QAction("Custom",self)
        self.useragent_mode_custom.setCheckable(True)
        self.useragent_mode_custom.triggered.connect(self.setUserAgentCustom)

################ Add Actions to Menu ####################
        # This sub-menu sets useragent mode to desktop/mobile/custom
        self.useragentMenu = QMenu('UserAgent', self)
        self.useragentMenu.setIcon(QIcon(":/computer.png"))
        self.useragentMenu.addAction(self.useragent_mode_desktop)
        self.useragentMenu.addAction(self.useragent_mode_mobile)
        self.useragentMenu.addAction(self.useragent_mode_custom)

        # This is main menu
        self.menu = QMenu(self)
        self.menu.addAction(QIcon(":/edit-find.png"), "Find Text", self.findmode, "Ctrl+F")
        self.menu.addAction(QIcon(":/list-add.png"), "Zoom In", self.zoomin, "Ctrl++")
        self.menu.addAction(QIcon(":/list-remove.png"), "Zoom Out", self.zoomout, "Ctrl+-")
        self.menu.addAction(QIcon(":/view-fullscreen.png"), "Toggle Fullscreen", self.fullscreenmode, "F11")
        self.menu.addSeparator()

        self.menu.addAction(self.loadimagesaction)
        self.menu.addAction(self.javascriptmode)
        self.menu.addMenu(self.useragentMenu)
        self.menu.addAction(QIcon(":/applications-system.png"), "Settings", self.settingseditor, "Ctrl+,")
        self.menu.addSeparator()

        self.menu.addAction(QIcon(":/image-x-generic.png"), "Save as Image", self.saveAsImage, "Shift+Ctrl+S")
        self.menu.addAction(QIcon(":/text-html.png"), "Save as HTML", self.saveashtml, "Ctrl+S")
        self.menu.addAction(QIcon(":/document-print.png"), "Print to PDF", self.printpage, "Ctrl+P")
        self.menu.addSeparator()
        self.menu.addAction(QIcon(":/process-stop.png"), "Quit", self.forceClose, "Ctrl+Q")

        self.bmk_menu = QMenu(self)
        self.bmk_menu.addAction(QIcon(':/add-bookmark.png'), 'Add Bookmark', self.addbookmark)
        self.bmk_menu.addAction(QIcon(':/favourites.png'), 'Add to Home', self.addToFavourites)
        
###############################  Create Gui Parts ##############################
        self.centralwidget = QWidget(self)
        self.setCentralWidget(self.centralwidget)
        grid = QGridLayout(self.centralwidget)
        grid.setSpacing(1)
        grid.setContentsMargins(0,2,0,0)

        self.toolBar = QWidget(self)
        horLayout = QHBoxLayout(self.toolBar)
        horLayout.setSpacing(1)
        horLayout.setContentsMargins(0,2,0,0)

        self.addtabBtn = QPushButton(QIcon(":/add-tab.png"), "",self)
        self.addtabBtn.setToolTip("New Tab\n[Ctrl+Tab]")
        self.addtabBtn.setShortcut("Ctrl+Tab")
        self.addtabBtn.clicked.connect(self.addTab)

        self.reload = QPushButton(QIcon(":/refresh.png"), "",self) 
        self.reload.setMinimumSize(35,26) 
        self.reload.setToolTip("Reload/Stop\n  [Space]")
        self.reload.setShortcut("Space")
        self.reload.clicked.connect(self.Reload)

        self.back = QPushButton(QIcon(":/prev.png"), "", self) 
        self.back.setToolTip("Previous Page")
        self.back.setMinimumSize(35,26) 
        self.back.clicked.connect(self.Back)

        self.forw = QPushButton(QIcon(":/next.png"), "",self) 
        self.forw.setToolTip("Next Page")
        self.forw.setMinimumSize(35,26) 
        self.forw.clicked.connect(self.Forward)

        self.homeBtn = QPushButton(QIcon(":/home.png"), "",self) 
        self.homeBtn.setToolTip("Go Home")
        self.homeBtn.clicked.connect(self.goToHome)

        self.videoDownloadButton = QPushButton(QIcon(":/video-dwnld.png"), "", self)
        self.videoDownloadButton.setToolTip("Download this Video")
        self.videoDownloadButton.clicked.connect(self.downloadVideo)
        self.videoDownloadButton.hide()

        self.addbookmarkBtn = QToolButton(self)
        self.addbookmarkBtn.setIcon(QIcon(":/add-bookmark.png"))
        self.addbookmarkBtn.setToolTip("Add Bookmark")
        self.addbookmarkBtn.setMenu(self.bmk_menu)
        self.addbookmarkBtn.setPopupMode(QToolButton.InstantPopup)

        self.menuBtn = QToolButton(self)
        self.menuBtn.setIcon(QIcon(":/menu.png"))
        self.menuBtn.setMenu(self.menu)
        self.menuBtn.setPopupMode(QToolButton.InstantPopup)

        self.bookmarkBtn = QPushButton(QIcon(":/bookmarks.png"), "", self)
        self.bookmarkBtn.setToolTip("Manage Bookmarks\n         [Alt+B]")
        self.bookmarkBtn.setShortcut("Alt+B")
        self.bookmarkBtn.clicked.connect(self.managebookmarks)
        self.historyBtn = QPushButton(QIcon(":/history.png"), "", self)
        self.historyBtn.setShortcut("Alt+H")
        self.historyBtn.setToolTip("View History\n     [Alt+H]")
        self.historyBtn.clicked.connect(self.viewhistory)

        self.downloadsBtn = QPushButton(QIcon(":/download.png"), "", self)
        self.downloadsBtn.setToolTip("Download Manager")
        self.downloadsBtn.clicked.connect(self.download_manager)

        self.find = QPushButton(self)
        self.find.setText("Find/Next")
        self.find.clicked.connect(self.findnext)
        self.find.hide()
        self.findprev = QPushButton(self)
        self.findprev.setText("Backward")
        self.findprev.clicked.connect(self.findback)
        self.findprev.hide()
        self.cancelfind = QPushButton(self)
        self.cancelfind.setText("Cancel")
        self.cancelfind.clicked.connect(self.cancelfindmode)
        self.cancelfind.hide()

        self.pbar = QProgressBar(self) 
        self.pbar.setTextVisible(False)
        self.pbar.setStyleSheet("QProgressBar::chunk { background-color: #bad8ff; }")
        pbarLayout = QGridLayout(self.pbar)
        pbarLayout.setContentsMargins(0,0,0,0)

        self.line = webkit.UrlEdit(self.pbar)
        self.line.openUrlRequested.connect(self.Enter)
        self.line.textEdited.connect(self.urlsuggestions)
        self.line.downloadRequested.connect(self.download_requested_file)
        pbarLayout.addWidget(self.line)

        self.listmodel = QStringListModel(self)
        self.completer = QCompleter(self.listmodel, self.line)
        self.completer.setCompletionMode(1)
        self.completer.setMaxVisibleItems(10)
        self.line.setCompleter(self.completer)

        self.statusbar = QLabel(self)
        self.statusbar.setStyleSheet("QLabel { font-size: 12px; border-radius: 2px; padding: 2px; background: palette(highlight); color: palette(highlighted-text); }")
        self.statusbar.setMaximumHeight(16)
        self.statusbar.hide()

        self.tabWidget = QTabWidget(self)
        self.tabWidget.setTabsClosable(True)
        self.tabWidget.setDocumentMode(True)
        self.tabWidget.tabBar().setExpanding(True)
        self.tabWidget.tabBar().setElideMode(Qt.ElideMiddle)
        self.tabWidget.currentChanged.connect(self.onTabSwitch)
        self.tabWidget.tabCloseRequested.connect(self.closeTab)
        self.addTab()
        self.applysettings()
#       
        grid.addWidget(self.toolBar, 0,0, 1,1)
        for widget in [self.addtabBtn, self.back, self.forw, self.reload, self.homeBtn, self.videoDownloadButton,
                self.pbar, self.find, self.findprev, self.cancelfind, self.addbookmarkBtn, self.menuBtn,
                self.bookmarkBtn, self.historyBtn, self.downloadsBtn]:
            horLayout.addWidget(widget)
        grid.addWidget(self.tabWidget, 1, 0, 1, 1)

#------------------------------------------------------------------------------------------
#        Must be at the end, otherwise cause segmentation fault
#       self.status = self.statusBar() 

    def addTab(self, webview_tab=None):
        """ Creates a new tab and add to QTabWidget
            applysettings() must be called after adding each tab"""
        if not webview_tab:
            webview_tab = webkit.MyWebView(self.tabWidget, networkmanager) 
        webview_tab.windowCreated.connect(self.addTab)
        webview_tab.loadStarted.connect(self.onLoadStart) 
        webview_tab.loadFinished.connect(self.onLoadFinish) 
        webview_tab.loadProgress.connect(self.onProgress)
        webview_tab.urlChanged.connect(self.onUrlChange)
        webview_tab.titleChanged.connect(self.onTitleChange)
        webview_tab.iconChanged.connect(self.onIconChange)
        webview_tab.videoListRequested.connect(self.getVideos)
        webview_tab.page().printRequested.connect(self.printpage)
        webview_tab.page().downloadRequested.connect(self.download_requested_file)
        webview_tab.page().unsupportedContent.connect(self.handleUnsupportedContent)
        webview_tab.page().linkHovered.connect(self.onLinkHover)
        webview_tab.page().windowCloseRequested.connect(self.closeRequestedTab)

        self.tabWidget.addTab(webview_tab, "( Untitled )")
        if self.tabWidget.count()==1:
            self.tabWidget.tabBar().hide()
        else:
            self.tabWidget.tabBar().show()
        self.tabWidget.setCurrentIndex(self.tabWidget.count()-1)

    def closeTab(self, index=None):
        """ Closes tab, hides tabbar if only one tab remains"""
        if index==None:
            index = self.tabWidget.currentIndex()
        widget = self.tabWidget.widget(index)
        self.tabWidget.removeTab(index)
        widget.deleteLater()
        # Auto hide tab bar, when no. of tab widget is one
        if self.tabWidget.count()==1:
            self.tabWidget.tabBar().hide()

    def closeRequestedTab(self):
        """ Close tab requested by the page """
        webview = self.sender().view()
        index = self.tabWidget.indexOf(webview)
        self.closeTab(index)

    def Enter(self): 
        url = self.line.text()
        if url == 'about:home':
            self.goToHome()
        else:
            self.GoTo(url)

    def GoTo(self, url):
        URL = QUrl.fromUserInput(url)
        self.tabWidget.currentWidget().openLink(URL)
        self.line.setText(url)
        self.tabWidget.currentWidget().setFocus()

    def goToHome(self):
        self.GoTo(homepage)
        loop = QEventLoop()
        QTimer.singleShot(10, loop.quit)
        loop.exec_()
        document = self.tabWidget.currentWidget().page().mainFrame().documentElement()
        gallery = document.findFirst('div')
        for i, fav in enumerate(self.favourites):
            title, url, img = fav[0], fav[1], thumbnails_dir+fav[2]
            child = '<div class="photo"> <a href="{}"><img src="{}"></a><div class="desc">{}</div></div>'.format(url, img, title)
            gallery.appendInside(child)

    def onLoadStart(self):
        webview = self.sender()
        if webview is self.tabWidget.currentWidget():
          self.reload.setIcon(QIcon(":/stop.png"))

    def onProgress(self, progress):
        webview = self.sender()
        if webview is self.tabWidget.currentWidget() and webview.loading:
            self.pbar.setValue(progress)

    def onLoadFinish(self, ok):
        webview = self.sender()
        if webview is self.tabWidget.currentWidget():
          self.reload.setIcon(QIcon(":/refresh.png"))
          self.pbar.reset()
          url = self.line.text()
          self.handleVideoButton(url)

    def onTabSwitch(self, index):
        """ Updates urlbox, refresh icon, progress bar on switching tab"""
        webview = self.tabWidget.currentWidget()
        if webview.loading == True:
            self.reload.setIcon(QIcon(":/stop.png"))
            self.pbar.setValue(webview.progressVal)
        else:
            self.reload.setIcon(QIcon(":/refresh.png"))
            self.pbar.reset()
        url =  webview.url().toString()
        if url == homepage : url = 'about:home'
        self.line.setText(url)
        self.statusbar.hide()
        self.onIconChange(webview)
        self.handleVideoButton(url)

    def onUrlChange(self,url):
        url = url.toString()
        if url == homepage : url = 'about:home'
        webview = self.sender()
        if webview is self.tabWidget.currentWidget():
            self.line.setText(url)
            self.onIconChange(webview)
            self.handleVideoButton(url)

    def onTitleChange(self, title):
        webview = self.sender()
        index = self.tabWidget.indexOf(webview)
        if not title == '':
            self.tabWidget.tabBar().setTabText(index, title)
            url = webview.url().toString()
            for item in self.history:  # Removes the old item, inserts new same item on the top
                if url == item[1]:
                    self.history.remove(item)
            self.history.insert(0, [title, url])

    def onIconChange(self, webview=None):
        if not webview:
            webview = self.sender()
        icon = webview.icon()
        if icon.isNull():
            icon = QIcon(':/quartz.png')
        if webview is self.tabWidget.currentWidget():
            self.line.setIcon(icon)
        index = self.tabWidget.indexOf(webview)
        self.tabWidget.setTabIcon(index, icon)

    def onLinkHover(self, url):
        if url=="":
            self.statusbar.hide()
            return
        self.statusbar.setText(url)
        self.statusbar.adjustSize()
        self.statusbar.show()
        self.statusbar.move(QPoint(0, self.height()-self.statusbar.height()))

    def Back(self): 
        self.tabWidget.currentWidget().back() 
    def Forward(self): 
        self.tabWidget.currentWidget().forward()
    def Reload(self):
        if self.tabWidget.currentWidget().loading:
            self.tabWidget.currentWidget().stop()
        else:
            if self.line.text() == 'about:home':
                self.goToHome()
            else:
                self.tabWidget.currentWidget().reload()

    def urlsuggestions(self, text):
        """ Creates the list of url suggestions for URL box """
        suggestions = []
        if not webkit.find_mode_on:
            for [title, url] in self.history:
                if text in url:
                    suggestions.insert(0, url)
            for [title, address] in self.bookmarks:
                if text in address:
                    suggestions.insert(0, address)
        self.listmodel.setStringList( suggestions )

    def handleVideoButton(self, url):
        if youtube.validYoutubeUrl(url):
            self.videoDownloadButton.show()
            return
        frames = [self.tabWidget.currentWidget().page().mainFrame()]
        frames += frames[0].childFrames()
        for frame in frames:
            video = frame.findFirstElement('video')
            if not video.isNull():
                self.videoDownloadButton.show()
                return
        self.videoDownloadButton.hide()


##################### Downloading and Printing  ########################
    def download_requested_file(self, networkrequest):
        """ Gets called when the page requests a file to be downloaded """
        reply = networkmanager.get(networkrequest)
        self.handleUnsupportedContent(reply)

    def handleUnsupportedContent(self, reply, preset_filename=None, page_url=None):
        """ This is called when url content is a downloadable file. e.g- pdf,mp3,mp4 """
        if reply.rawHeaderList() == []:
            loop = QEventLoop()
            reply.metaDataChanged.connect(loop.quit)
            QTimer.singleShot(5000, loop.quit)
            loop.exec_()
        if reply.hasRawHeader(b'Location'):
            URL = QUrl.fromUserInput(str_(reply.rawHeader(b'Location')))
            reply.abort()
            reply = networkmanager.get(QNetworkRequest(URL))
            self.handleUnsupportedContent(reply, preset_filename)
            return
        for (title, header) in reply.rawHeaderPairs():
            print( str_(title) + "-> " + str_(header) )
        # copy url to clipboard
        QApplication.clipboard().setText(reply.url().toString())
        # Get filename and mimetype
        mimetype = None
        if reply.hasRawHeader(b'Content-Type'):
            mimetype = str_(reply.rawHeader(b'Content-Type')).split(';')[0] # eg - audio/mpeg; name=""
        content_name = str_(reply.rawHeader(b'Content-Disposition'))
        if preset_filename:
            filename = preset_filename
        else:
            filename = filenameFromHeader(content_name)
            if filename == '':
                filename = filenameFromUrl(reply.url().toString())
        filename = validateFileName(filename, mimetype)
        # Create downld Confirmation dialog
        dlDialog = DownloadDialog(self)
        dlDialog.filenameEdit.setText(filename)
        # Get filesize
        if reply.hasRawHeader(b'Content-Length'):
            filesize = reply.header(1)
            if filesize >= 1048576 :
                file_size = "{} M".format(round(float(filesize)/1048576, 2))
            elif 1023 < filesize < 1048576 :
                file_size = "{} k".format(round(float(filesize)/1024, 1))
            else:
                file_size = "{} B".format(filesize)
            dlDialog.labelFileSize.setText(file_size)
        # Get filetype and resume support info
        if mimetype:
            dlDialog.labelFileType.setText(mimetype)
        if reply.hasRawHeader(b'Accept-Ranges') or reply.hasRawHeader(b'Content-Range'):
            dlDialog.labelResume.setText("True")
        # Execute dialog and show confirmation
        if dlDialog.exec_()== QDialog.Accepted:
            filepath = dlDialog.folder + dlDialog.filenameEdit.text()
            url = reply.url().toString()
            if self.useexternaldownloader:
                download_externally(url, self.externaldownloader)
                reply.abort()
                reply.deleteLater()
                return

            global downloads_list_file
            newdownload = Download(networkmanager, page_url)
            newdownload.startDownload(reply, filepath)
            newdownload.datachanged.connect(self.dwnldsmodel.datachanged)
            self.downloads.insert(0, newdownload)
            imported_downloads = importDownloads(downloads_list_file)
            imported_downloads.insert(0, [filepath, url, str(newdownload.totalsize), newdownload.timestamp])
            exportDownloads(downloads_list_file, imported_downloads)
        else:
            reply.abort()
            reply.deleteLater()

    def download_manager(self):
        """ Opens download manager dialog """
        dialog = QDialog(self)
        downloads_dialog = Downloads_Dialog()
        downloads_dialog.setupUi(dialog, self.dwnldsmodel)
        dialog.exec_()

    def deleteDownloads(self, timestamps):
        global downloads_list_file
        imported_downloads = importDownloads(downloads_list_file)
        exported_downloads = []
        for download in imported_downloads:
            if download[-1] not in timestamps:
                exported_downloads.append(download)
        exportDownloads(downloads_list_file, exported_downloads)

    def downloadVideo(self):
        url = self.tabWidget.currentWidget().url().toString()
        # For youtube videos, parse youtube links in separate thread
        if youtube.validYoutubeUrl(url):
            vid_id = parse_qs(urlparse(url).query)['v'][0]
            ytThread = youtube.YoutubeThread(self)
            ytThread.ytParseFailed.connect(self.onYtParseFail)
            ytThread.ytVideoParsed.connect(self.onYtVideoParse)
            ytThread.finished.connect(ytThread.deleteLater)
            ytThread.vid_id = vid_id
            ytThread.start()
            return
        # For embeded HTML5 videos
        self.getVideos()

    def onYtVideoParse(self, videos):
        dialog = youtube.YoutubeDialog(videos, self)
        if dialog.exec_() == 1 :
            index = abs(dialog.buttonGroup.checkedId())-2
            vid = videos[index]
            reply = networkmanager.get( QNetworkRequest(QUrl.fromUserInput(vid.url)) )
            self.handleUnsupportedContent(reply, vid.filename + '.' + vid.extension)

    def onYtParseFail(self):
        # Show error on fail to parse youtube
        QMessageBox.warning(self, "Download Failed !","This Video can not be downloaded")

    def getVideos(self):
        dialog = youtube.Media_Dialog(self, self.tabWidget.currentWidget().page(), networkmanager)
        dialog.downloadRequested.connect(self.handleUnsupportedContent)
        dialog.exec_()

    def saveAsImage(self):
        """ Saves the whole page as PNG/JPG image"""
        title = self.tabWidget.currentWidget().page().mainFrame().title()
        title == validateFileName(title)
        filename = QFileDialog.getSaveFileName(self,
                                      "Select Image to Save", downloaddir + title +".jpg",
                                      "JPEG Image (*.jpg);;PNG Image (*.png)" )[0]
        if filename == '': return
        viewportsize = self.tabWidget.currentWidget().page().viewportSize()
        contentsize = self.tabWidget.currentWidget().page().mainFrame().contentsSize()
        self.tabWidget.currentWidget().page().setViewportSize(contentsize)
        img = QPixmap(contentsize)
        painter = QPainter(img)
        self.tabWidget.currentWidget().page().mainFrame().render(painter)
        painter.end()
        if img.save(filename):
            QMessageBox.information(self, "Successful !","Page has been successfully saved as\n"+filename)
        else:
            QMessageBox.warning(self, "Saving Failed !","Exporting page to Image hasbeen failed")
        self.tabWidget.currentWidget().page().setViewportSize(viewportsize)

    def saveashtml(self):
        """ Saves current page as HTML , bt does not saves any content (e.g images)"""
        title = self.tabWidget.currentWidget().page().mainFrame().title()
        title = validateFileName(title)
        filename = QFileDialog.getSaveFileName(self,
                                "Enter HTML File Name", downloaddir + title +".html",
                                "HTML Document (*.html)" )[0]
        if filename == '': return
        #html = self.tabWidget.currentWidget().page().mainFrame().toHtml()
        page_URL = self.tabWidget.currentWidget().url()
        useragent = self.tabWidget.currentWidget().page().userAgentForUrl(page_URL)
        doc = self.tabWidget.currentWidget().page().mainFrame().documentElement().clone()
        #doc.setInnerXml(html)
        SaveAsHtml(networkmanager, doc, filename, page_URL, useragent)

    def printpage(self, page=None):
        """ Prints current/requested page """
        if not page:
            page = self.tabWidget.currentWidget().page().currentFrame()
        printer = QPrinter(QPrinter.HighResolution)
        printer.setPaperSize(QPrinter.A4)
        printer.setPageSize(QPrinter.A4)
        printer.setColorMode(QPrinter.Color)
        printer.setCreator("Quartz Browser")
        title = self.tabWidget.currentWidget().page().mainFrame().title()
        title = validateFileName(title)
        printer.setDocName(title)
        printer.setOutputFileName(docdir + title + ".pdf")
        #printer.setOutputFormat(QPrinter.PdfFormat)
        print_dialog = QPrintPreviewDialog(printer, self)
        print_dialog.paintRequested.connect(page.print_)
        print_dialog.exec_()

##################################################################################################
    def addToFavourites(self):
        dialog = QDialog(self)
        addbmkdialog = Add_Bookmark_Dialog()
        addbmkdialog.setupUi(dialog)
        dialog.setWindowTitle('Add to HomePage')
        addbmkdialog.titleEdit.setMaxLength(31)
        addbmkdialog.titleEdit.setText(self.tabWidget.currentWidget().page().mainFrame().title())
        addbmkdialog.addressEdit.setText(self.line.text())
        if (dialog.exec_() == QDialog.Accepted):
            title = addbmkdialog.titleEdit.text()
            addr = addbmkdialog.addressEdit.text()
            imgfile = str(time.time()) + '.jpg'
            viewportsize = self.tabWidget.currentWidget().page().viewportSize()
            contentsize = QSize(640, 640)
            self.tabWidget.currentWidget().page().setViewportSize(contentsize)
            img = QPixmap(contentsize)
            painter = QPainter(img)
            self.tabWidget.currentWidget().page().mainFrame().render(painter, QWebFrame.AllLayers)
            painter.end()
            self.tabWidget.currentWidget().page().setViewportSize(viewportsize)
            icon = img.scaledToWidth(184, 1).copy(0,0, 180, 120)
            icon.save(thumbnails_dir + imgfile)
            self.favourites = importFavourites(configdir + 'favourites.txt')
            self.favourites.append([title, addr, imgfile])
            exportFavourites(configdir + 'favourites.txt', self.favourites)

    def addbookmark(self):
        """ Opens add bookmark dialog and gets url from url box"""
        dialog = QDialog(self)
        addbmkdialog = Add_Bookmark_Dialog()
        addbmkdialog.setupUi(dialog)
        addbmkdialog.titleEdit.setText(self.tabWidget.currentWidget().page().mainFrame().title())
        addbmkdialog.addressEdit.setText(self.line.text())
        if (dialog.exec_() == QDialog.Accepted):
            url = addbmkdialog.addressEdit.text()
            bmk = [addbmkdialog.titleEdit.text(), url]
            self.bookmarks = importBookmarks(configdir+"bookmarks.txt")
            self.bookmarks.insert(0, bmk)
            exportBookmarks(configdir+"bookmarks.txt", self.bookmarks)
            icon = self.tabWidget.currentWidget().icon()
            if not icon.isNull():
                icon.pixmap(16, 16).save(icon_dir + url.split('/')[2] + '.png')

    def managebookmarks(self):
        """ Opens Bookmarks dialog """
        dialog = QDialog(self)
        bmk_dialog = Bookmarks_Dialog()
        bmk_dialog.setupUi(dialog, self.bookmarks, self.favourites)
        bmk_dialog.bookmarks_table.doubleclicked.connect(self.GoTo)
        bmk_dialog.favs_table.doubleclicked.connect(self.GoTo)
        dialog.exec_()
        if bmk_dialog.bookmarks_table.data_changed:
            self.bookmarks = bmk_dialog.bookmarks_table.data
            exportBookmarks(configdir+"bookmarks.txt", self.bookmarks)
        if bmk_dialog.favs_table.data_changed:
            self.favourites = bmk_dialog.favs_table.data
            exportFavourites(configdir+"favourites.txt", self.favourites)

    def viewhistory(self):
        """ Open history dialog """
        dialog = QDialog(self)
        history_dialog = History_Dialog()
        history_dialog.setupUi(dialog, self.history)
        history_dialog.tableView.doubleclicked.connect(self.GoTo)
        dialog.exec_()

    def findmode(self):
        """ Starts find mode and unhides find buttons"""
        webkit.find_mode_on = True
        self.line.clear()
        self.find.show()
        self.findprev.show()
        self.cancelfind.show()
        self.line.setFocus()
    def cancelfindmode(self):
        """ Hides the find buttons, updates urlbox"""
        webkit.find_mode_on = False
        self.tabWidget.currentWidget().findText("")
        self.find.hide()
        self.findprev.hide()
        self.cancelfind.hide()
        self.line.setText(self.tabWidget.currentWidget().url().toString())
    def findnext(self):
        text = self.line.text()
        self.tabWidget.currentWidget().findText(text)
    def findback(self):
        text = self.line.text()
        self.tabWidget.currentWidget().findText(text, QWebPage.FindBackward)

#####################  View Settings  ###################
    def zoomin(self):
        zoomlevel = self.tabWidget.currentWidget().zoomFactor()
        self.tabWidget.currentWidget().setZoomFactor(zoomlevel+0.1) # Use setZoomFactor() to zoom text and images
    def zoomout(self):
        zoomlevel = self.tabWidget.currentWidget().zoomFactor()
        self.tabWidget.currentWidget().setZoomFactor(zoomlevel-0.1)
    def fullscreenmode(self):
        if self.isFullScreen():
            self.showNormal()
        else:
            self.showFullScreen()

    def loadimages(self, state):
        """ TOggles image loading on/off"""
        self.websettings.setAttribute(QWebSettings.AutoLoadImages, state)
        self.loadimagesval = bool(state)

    def setjavascript(self, state):
        """ Toggles js on/off """
        self.websettings.setAttribute(QWebSettings.JavascriptEnabled, state)
        self.javascriptenabledval = bool(state)

    def setUserAgentDesktop(self, checked):
        if bool(checked):
            webkit.useragent_mode = 'Desktop'
            self.useragent_mode_mobile.setChecked(False)
            self.useragent_mode_custom.setChecked(False)

    def setUserAgentMobile(self, checked):
        if bool(checked):
            webkit.useragent_mode = 'Mobile'
            self.useragent_mode_desktop.setChecked(False)
            self.useragent_mode_custom.setChecked(False)

    def setUserAgentCustom(self, checked):
        if bool(checked):
            webkit.useragent_mode = 'Custom'
            self.useragent_mode_mobile.setChecked(False)
            self.useragent_mode_desktop.setChecked(False)

########################## Settings Portion #########################
    def settingseditor(self):  
        """ Opens the settings manager dialog, then applies the change"""
        dialog = QDialog(self)
        websettingsdialog = Ui_SettingsDialog()
        websettingsdialog.setupUi(dialog)
        # Enable AdBlock
        websettingsdialog.checkAdBlock.setChecked(webkit.enable_adblock)
        # Fonts blocking
        websettingsdialog.checkFontLoad.setChecked(webkit.block_fonts)
        # Popups blocking
        websettingsdialog.checkBlockPopups.setChecked(webkit.block_popups)
        # Custom user agent
        websettingsdialog.useragentEdit.setText(webkit.useragent_custom)
        # External download manager
        websettingsdialog.checkDownMan.setChecked(self.useexternaldownloader)
        websettingsdialog.downManEdit.setText(self.externaldownloader)
        # RTSP media player command
        websettingsdialog.mediaPlayerEdit.setText(webkit.video_player_command)
        websettingsdialog.mediaPlayerEdit.setCursorPosition(0)
        # Font settings
        websettingsdialog.spinFontSize.setValue(self.minfontsizeval)
        websettingsdialog.standardfontCombo.setCurrentFont(QFont(self.standardfontval))
        websettingsdialog.sansfontCombo.setCurrentFont(QFont(self.sansfontval))
        websettingsdialog.seriffontCombo.setCurrentFont(QFont(self.seriffontval))
        websettingsdialog.fixedfontCombo.setCurrentFont(QFont(self.fixedfontval))
        # Clear Data buttons
        websettingsdialog.clearCacheButton.clicked.connect(self.websettings.clearMemoryCaches)
        websettingsdialog.cookiesButton.clicked.connect(cookiejar.clearCookies)
        websettingsdialog.iconDBButton.clicked.connect(self.websettings.clearIconDatabase)

        if dialog.exec_() == QDialog.Accepted:
            # Enable AdBlock
            webkit.enable_adblock = websettingsdialog.checkAdBlock.isChecked()
            # Block Fonts
            webkit.block_fonts = websettingsdialog.checkFontLoad.isChecked()
            # Block Popups
            webkit.block_popups = websettingsdialog.checkBlockPopups.isChecked()
            # User Agent
            webkit.useragent_custom = websettingsdialog.useragentEdit.text()
            # Download Manager
            self.useexternaldownloader = websettingsdialog.checkDownMan.isChecked()
            self.externaldownloader = websettingsdialog.downManEdit.text()
            # Media Player Command
            webkit.video_player_command = websettingsdialog.mediaPlayerEdit.text()

            self.minfontsizeval = websettingsdialog.spinFontSize.value()
            self.standardfontval = websettingsdialog.standardfontCombo.currentText()
            self.sansfontval = websettingsdialog.sansfontCombo.currentText()
            self.seriffontval = websettingsdialog.seriffontCombo.currentText()
            self.fixedfontval = websettingsdialog.fixedfontCombo.currentText()
            self.applysettings()
            self.savesettings()

    def opensettings(self): 
        """ Reads settings file in ~/.config/quartz-browser/ directory and
            saves values in settings variables"""
        webkit.enable_adblock = _bool(self.settings.value('EnableAdblock', True))
        self.loadimagesval = _bool(self.settings.value('LoadImages', True))
        self.javascriptenabledval = _bool(self.settings.value('JavaScriptEnabled', True))
        webkit.block_fonts = _bool(self.settings.value('BlockFontLoading', False))
        webkit.block_popups = _bool(self.settings.value('BlockPopups', False))
        webkit.useragent_mode = self.settings.value('UserAgentMode', webkit.useragent_mode)
        webkit.useragent_custom = self.settings.value('UserAgent', webkit.useragent_custom)
        self.useexternaldownloader = _bool(self.settings.value('UseExternalDownloader', False))
        self.externaldownloader = self.settings.value('ExternalDownloader', "x-terminal-emulator wget -c %u")
        webkit.video_player_command = self.settings.value('MediaPlayerCommand', webkit.video_player_command)
        self.maximize_window = _bool(self.settings.value('MaximizeWindow', False))
        self.minfontsizeval = int(self.settings.value('MinFontSize', 11))
        self.standardfontval = self.settings.value('StandardFont', 'Sans')
        self.sansfontval = self.settings.value('SansFont', 'Sans')
        self.seriffontval = self.settings.value('SerifFont', 'Serif')
        self.fixedfontval = self.settings.value('FixedFont', 'Monospace')

    def savesettings(self):
        """ Writes setings to disk in ~/.config/quartz-browser/ directory"""
        self.settings.setValue('EnableAdblock', webkit.enable_adblock)
        self.settings.setValue('LoadImages', self.loadimagesval)
        self.settings.setValue('JavaScriptEnabled', self.javascriptenabledval)
        self.settings.setValue('BlockFontLoading', webkit.block_fonts)
        self.settings.setValue('BlockPopups', webkit.block_popups)
        self.settings.setValue('UserAgent', webkit.useragent_custom)
        self.settings.setValue('UserAgentMode', webkit.useragent_mode)
        self.settings.setValue('UseExternalDownloader', self.useexternaldownloader)
        self.settings.setValue('ExternalDownloader', self.externaldownloader)
        self.settings.setValue('MediaPlayerCommand', webkit.video_player_command)
        self.settings.setValue('MaximizeWindow', self.isMaximized())
        self.settings.setValue('MinFontSize', self.minfontsizeval)
        self.settings.setValue('StandardFont', self.standardfontval)
        self.settings.setValue('SansFont', self.sansfontval)
        self.settings.setValue('SerifFont', self.seriffontval)
        self.settings.setValue('FixedFont', self.fixedfontval)

    def applysettings(self):
        """ Reads settings variables, and changes browser settings.This is run after
            changing settings by Settings Dialog"""
        if webkit.enable_adblock:
            self.websettings.setUserStyleSheetUrl(QUrl.fromLocalFile(program_dir + 'userContent.css'))
        else:
            self.websettings.setUserStyleSheetUrl(QUrl(''))
        self.websettings.setAttribute(QWebSettings.AutoLoadImages, self.loadimagesval)
        self.loadimagesaction.setChecked(self.loadimagesval)
        self.websettings.setAttribute(QWebSettings.JavascriptEnabled, self.javascriptenabledval)
        self.javascriptmode.setChecked(self.javascriptenabledval)
        if webkit.useragent_mode == 'Mobile':
            self.useragent_mode_mobile.setChecked(True)
        elif webkit.useragent_mode == 'Custom':
            self.useragent_mode_custom.setChecked(True)
        else:
            self.useragent_mode_desktop.setChecked(True)
        self.websettings.setFontSize(QWebSettings.MinimumFontSize, self.minfontsizeval)
        self.websettings.setFontFamily(QWebSettings.StandardFont, self.standardfontval)
        self.websettings.setFontFamily(QWebSettings.SansSerifFont, self.sansfontval)
        self.websettings.setFontFamily(QWebSettings.SerifFont, self.seriffontval)
        self.websettings.setFontFamily(QWebSettings.FixedFont, self.fixedfontval)
#        self.websettings.setFontSize(QWebSettings.DefaultFontSize, 14)

    def enableKiosk(self):
        webkit.KIOSK_MODE = True
        self.menu.clear()
        self.toolBar.hide()
        self.showFullScreen()

    def forceClose(self):
        self.confirm_before_quit = False
        self.close()

    def closeEvent(self, event):
        """This saves all settings, bookmarks, cookies etc. during window close"""
        if self.confirm_before_quit:
            confirm = QMessageBox.warning(self, 'Quit Browser ?', 'Are you sure to close the Browser',
                                                QMessageBox.Yes|QMessageBox.No, QMessageBox.Yes)
            if confirm == QMessageBox.No :
                event.ignore()
                return
        self.savesettings()
        cookiejar.exportCookies()
        # Delete excess thumbnails
        thumbnails = [ x for x in os.listdir(thumbnails_dir) ]
        for fav in self.favourites:
            if fav[2] in thumbnails:
                thumbnails.remove(fav[2])
        for f in thumbnails: os.remove(thumbnails_dir + f)
        # Delete excess icons
        icons = [ x for x in os.listdir(icon_dir) if x.endswith('.png') ]
        for bmk in self.bookmarks:
            host = QUrl(bmk[1]).host()
            if host + '.png' in icons:
                icons.remove(host + '.png')
        for f in icons: os.remove( icon_dir + f )
        super(Main, self).closeEvent(event)
Esempio n. 4
0
class DiscoveryPlugin:
    def __init__(self, _iface):
        # Save reference to the QGIS interface
        self.iface = _iface
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)

        # Variables to facilitate delayed queries and database connection management
        self.db_timer = QTimer()
        self.line_edit_timer = QTimer()
        self.line_edit_timer.setSingleShot(True)
        self.line_edit_timer.timeout.connect(self.reset_line_edit_after_move)
        self.next_query_time = None
        self.last_query_time = time.time()
        self.db_conn = None
        self.search_delay = 0.5  # s
        self.query_sql = ''
        self.query_text = ''
        self.query_dict = {}
        self.db_idle_time = 60.0  # s
        self.display_time = 5000  # ms
        self.bar_info_time = 30  # s

        self.search_results = []
        self.tool_bar = None
        self.search_line_edit = None
        self.completer = None
        self.conn_info = {}

        self.marker = QgsVertexMarker(iface.mapCanvas())
        self.marker.setIconSize(15)
        self.marker.setPenWidth(2)
        self.marker.setColor(QColor(226, 27, 28))  #51,160,44))
        self.marker.setZValue(11)
        self.marker.setVisible(False)
        self.marker2 = QgsVertexMarker(iface.mapCanvas())
        self.marker2.setIconSize(16)
        self.marker2.setPenWidth(4)
        self.marker2.setColor(QColor(255, 255, 255, 200))
        self.marker2.setZValue(10)
        self.marker2.setVisible(False)
        self.is_displayed = False

        self.rubber_band = QgsRubberBand(iface.mapCanvas(), False)
        self.rubber_band.setVisible(False)
        self.rubber_band.setWidth(3)
        self.rubber_band.setStrokeColor(QColor(226, 27, 28))
        self.rubber_band.setFillColor(QColor(226, 27, 28, 63))

    def initGui(self):

        # Create a new toolbar
        self.tool_bar = self.iface.addToolBar('Discovery')
        self.tool_bar.setObjectName('Discovery_Plugin')

        # Create action that will start plugin configuration
        self.action_config = QAction(
            QIcon(os.path.join(self.plugin_dir, "discovery_logo.png")),
            u"Configure Discovery", self.tool_bar)
        self.action_config.triggered.connect(self.show_config_dialog)
        self.tool_bar.addAction(self.action_config)

        # Add combobox for configs
        self.config_combo = QComboBox()
        settings = QgsSettings()
        settings.beginGroup("/Discovery")
        config_list = settings.value("config_list")

        if config_list:
            for conf in config_list:
                self.config_combo.addItem(conf)
        elif settings.childGroups():
            # support for prev version
            key = "Config1"
            config_list = []
            config_list.append(key)
            settings.setValue("config_list", config_list)
            self.config_combo.addItem(key)

            settings.setValue(key + "data_type", settings.value("data_type"))
            settings.setValue(key + "file", settings.value("file"))
            settings.setValue(key + "connection", settings.value("connection"))
            settings.setValue(key + "schema", settings.value("schema"))
            settings.setValue(key + "table", settings.value("table"))
            settings.setValue(key + "search_column",
                              settings.value("search_column"))
            settings.setValue(key + "echo_search_column",
                              settings.value("echo_search_column"))
            settings.setValue(key + "display_columns",
                              settings.value("display_columns"))
            settings.setValue(key + "geom_column",
                              settings.value("geom_column"))
            settings.setValue(key + "scale_expr", settings.value("scale_expr"))
            settings.setValue(key + "bbox_expr", settings.value("bbox_expr"))

            delete_config_from_settings("", settings)
        self.tool_bar.addWidget(self.config_combo)

        # Add search edit box
        self.search_line_edit = QgsFilterLineEdit()
        self.search_line_edit.setPlaceholderText('Search for...')
        self.search_line_edit.setMaximumWidth(768)
        self.tool_bar.addWidget(self.search_line_edit)

        self.config_combo.currentIndexChanged.connect(
            self.change_configuration)

        # Set up the completer
        self.completer = QCompleter([])  # Initialise with en empty list
        self.completer.setCaseSensitivity(Qt.CaseInsensitive)
        self.completer.setMaxVisibleItems(1000)
        self.completer.setModelSorting(
            QCompleter.UnsortedModel)  # Sorting done in PostGIS
        self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion
                                         )  # Show all fetched possibilities
        self.completer.activated[QModelIndex].connect(self.on_result_selected)
        self.completer.highlighted[QModelIndex].connect(
            self.on_result_highlighted)
        self.search_line_edit.setCompleter(self.completer)

        # Connect any signals
        self.search_line_edit.textEdited.connect(self.on_search_text_changed)

        # Search results
        self.search_results = []

        # Set up a timer to periodically perform db queries as required
        self.db_timer.timeout.connect(self.do_db_operations)
        self.db_timer.start(100)

        # Read config
        self.read_config(config_list[0] if config_list else "")

        self.locator_filter = locator_filter.DiscoveryLocatorFilter(self)
        self.iface.registerLocatorFilter(self.locator_filter)

        # Debug
        # import pydevd; pydevd.settrace('localhost', port=5678)

    def unload(self):
        # Stop timer
        self.db_timer.stop()
        # Disconnect any signals
        self.db_timer.timeout.disconnect(self.do_db_operations)
        self.completer.highlighted[QModelIndex].disconnect(
            self.on_result_highlighted)
        self.completer.activated[QModelIndex].disconnect(
            self.on_result_selected)
        self.search_line_edit.textEdited.disconnect(
            self.on_search_text_changed)
        # Remove the new toolbar
        self.tool_bar.clear()  # Clear all actions
        self.iface.mainWindow().removeToolBar(self.tool_bar)

        self.iface.deregisterLocatorFilter(self.locator_filter)
        self.locator_filter = None

    def clear_suggestions(self):
        model = self.completer.model()
        model.setStringList([])

    def on_search_text_changed(self, new_search_text):
        """
        This function is called whenever the user modified the search text

        1. Open a database connection
        2. Make the query
        3. Update the QStringListModel with these results
        4. Store the other details in self.search_results
        """

        self.query_text = new_search_text

        if len(new_search_text) < 3:
            # Clear any previous suggestions in case the user is 'backspacing'
            self.clear_suggestions()
            return

        if self.data_type == "postgres":
            query_text, query_dict = dbutils.get_search_sql(
                new_search_text, self.postgisgeomcolumn,
                self.postgissearchcolumn, self.echosearchcolumn,
                self.postgisdisplaycolumn, self.extra_expr_columns,
                self.postgisschema, self.postgistable)
            self.schedule_search(query_text, query_dict)

        elif self.data_type == "gpkg":
            query_text = (new_search_text, self.postgissearchcolumn,
                          self.echosearchcolumn,
                          self.postgisdisplaycolumn.split(","),
                          self.extra_expr_columns, self.layer)
            self.schedule_search(query_text, None)

        elif self.data_type == "mssql":
            query_text = mssql_utils.get_search_sql(
                new_search_text, self.postgisgeomcolumn,
                self.postgissearchcolumn, self.echosearchcolumn,
                self.postgisdisplaycolumn, self.extra_expr_columns,
                self.postgisschema, self.postgistable)
            self.schedule_search(query_text, None)

    def do_db_operations(self):
        if self.next_query_time is not None and self.next_query_time < time.time(
        ):
            # It's time to run a query
            self.next_query_time = None  # Prevent this query from being repeated
            self.last_query_time = time.time()
            self.perform_search()
        else:
            # We're not performing a query, close the db connection if it's been open for > 60s
            if time.time() > self.last_query_time + self.db_idle_time:
                self.db_conn = None

    def perform_search(self):
        db = self.get_db()
        self.search_results = []
        suggestions = []
        if self.data_type == "postgres":
            cur = db.cursor()
            try:
                cur.execute(self.query_sql, self.query_dict)
            except psycopg2.Error as e:
                err_info = "Failed to execute the search query. Please, check your settings. Error message:\n\n"
                err_info += f"{e.pgerror}"
                QMessageBox.critical(None, "Discovery", err_info)
                return
            result_set = cur.fetchall()
        elif self.data_type == "mssql":
            result_set = mssql_utils.execute(db, self.query_sql)
        elif self.data_type == "gpkg":
            result_set = gpkg_utils.search_gpkg(*self.query_sql)

        for row in result_set:
            geom, epsg, suggestion_text = row[0], row[1], row[2]
            extra_data = {}
            for idx, extra_col in enumerate(self.extra_expr_columns):
                extra_data[extra_col] = row[3 + idx]
            self.search_results.append(
                (geom, epsg, suggestion_text, extra_data))
            suggestions.append(suggestion_text)
        model = self.completer.model()
        model.setStringList(suggestions)
        self.completer.complete()

    def schedule_search(self, query_text, query_dict):
        # Update the search text and the time after which the query should be executed
        self.query_sql = query_text
        self.query_dict = query_dict
        self.next_query_time = time.time() + self.search_delay

    def show_bar_info(self, info_text):
        """Optional show info bar message with selected result information"""
        self.iface.messageBar().clearWidgets()
        if self.bar_info_time:
            self.iface.messageBar().pushMessage("Discovery",
                                                info_text,
                                                level=Qgis.Info,
                                                duration=self.bar_info_time)

    def on_result_selected(self, result_index):
        # What to do when the user makes a selection
        self.select_result(self.search_results[result_index.row()])

    def select_result(self, result_data):
        geometry_text, src_epsg, suggestion_text, extra_data = result_data
        location_geom = QgsGeometry.fromWkt(geometry_text)
        canvas = self.iface.mapCanvas()
        dst_srid = canvas.mapSettings().destinationCrs().authid()
        transform = QgsCoordinateTransform(
            QgsCoordinateReferenceSystem(src_epsg),
            QgsCoordinateReferenceSystem(dst_srid),
            canvas.mapSettings().transformContext())
        # Ensure the geometry from the DB is reprojected to the same SRID as the map canvas
        location_geom.transform(transform)
        location_centroid = location_geom.centroid().asPoint()

        # show temporary marker
        if location_geom.type() == QgsWkbTypes.PointGeometry:
            self.show_marker(location_centroid)
        elif location_geom.type() == QgsWkbTypes.LineGeometry or \
            location_geom.type() == QgsWkbTypes.PolygonGeometry:
            self.show_line_rubber_band(location_geom)
        else:
            #unsupported geometry type
            pass

        # Adjust map canvas extent
        zoom_method = 'Move and Zoom'
        if zoom_method == 'Move and Zoom':
            # with higher priority try to use exact bounding box to zoom to features (if provided)
            bbox_str = eval_expression(self.bbox_expr, extra_data)
            rect = bbox_str_to_rectangle(bbox_str)
            if rect is not None:
                # transform the rectangle in case of OTF projection
                rect = transform.transformBoundingBox(rect)
            else:
                # bbox is not available - so let's just use defined scale
                # compute target scale. If the result is 2000 this means the target scale is 1:2000
                rect = location_geom.boundingBox()
                if rect.isEmpty():
                    scale_denom = eval_expression(self.scale_expr,
                                                  extra_data,
                                                  default=2000.)
                    rect = canvas.mapSettings().extent()
                    rect.scale(scale_denom / canvas.scale(), location_centroid)
                else:
                    # enlarge geom bbox to have some margin
                    rect.scale(1.2)
            canvas.setExtent(rect)
        elif zoom_method == 'Move':
            current_extent = QgsGeometry.fromRect(
                self.iface.mapCanvas().extent())
            dx = location_centroid.x() - location_centroid.x()
            dy = location_centroid.y() - location_centroid.y()
            current_extent.translate(dx, dy)
            canvas.setExtent(current_extent.boundingBox())
        canvas.refresh()
        self.line_edit_timer.start(0)
        if self.info_to_clipboard:
            QApplication.clipboard().setText(suggestion_text)
            suggestion_text += ' (copied to clipboard)'
        self.show_bar_info(suggestion_text)

    def on_result_highlighted(self, result_idx):
        self.line_edit_timer.start(0)

    def reset_line_edit_after_move(self):
        self.search_line_edit.setText(self.query_text)

    def get_db(self):
        # Create a new new connection if required
        if self.db_conn is None:
            if self.data_type == "postgres":
                self.db_conn = dbutils.get_connection(self.conn_info)
            elif self.data_type == "mssql":
                self.db_conn = mssql_utils.get_mssql_conn(self.conn_info)
        return self.db_conn

    def change_configuration(self):
        self.search_line_edit.setText("")
        self.line_edit_timer.start(0)
        self.read_config(self.config_combo.currentText())

    def read_config(self, key=""):
        # the following code reads the configuration file which setups the plugin to search in the correct database,
        # table and method

        settings = QgsSettings()
        settings.beginGroup("/Discovery")

        connection = settings.value(key + "connection", "", type=str)
        self.data_type = settings.value(key + "data_type", "", type=str)
        self.file = settings.value(key + "file", "", type=str)
        self.postgisschema = settings.value(key + "schema", "", type=str)
        self.postgistable = settings.value(key + "table", "", type=str)
        self.postgissearchcolumn = settings.value(key + "search_column",
                                                  "",
                                                  type=str)
        self.echosearchcolumn = settings.value(key + "echo_search_column",
                                               True,
                                               type=bool)
        self.postgisdisplaycolumn = settings.value(key + "display_columns",
                                                   "",
                                                   type=str)
        self.postgisgeomcolumn = settings.value(key + "geom_column",
                                                "",
                                                type=str)
        if settings.value("marker_time_enabled", True, type=bool):
            self.display_time = settings.value("marker_time", 5000, type=int)
        else:
            self.display_time = -1
        if settings.value("bar_info_time_enabled", True, type=bool):
            self.bar_info_time = settings.value("bar_info_time", 30, type=int)
        else:
            self.bar_info_time = 0
        self.info_to_clipboard = settings.value("info_to_clipboard",
                                                True,
                                                type=bool)

        scale_expr = settings.value(key + "scale_expr", "", type=str)
        bbox_expr = settings.value(key + "bbox_expr", "", type=str)

        if self.is_displayed:
            self.hide_marker()
            self.hide_rubber_band()
            self.is_displayed = False

        self.make_enabled(False)  # assume the config is invalid first

        self.db_conn = None
        if self.data_type == "postgres":
            self.conn_info = dbutils.get_postgres_conn_info(connection)
            self.layer = None

            if len(connection) == 0 or len(self.postgisschema) == 0 or len(self.postgistable) == 0 or \
                    len(self.postgissearchcolumn) == 0 or len(self.postgisgeomcolumn) == 0:
                return

            if len(self.conn_info) == 0:
                iface.messageBar().pushMessage(
                    "Discovery",
                    "The database connection '%s' does not exist!" %
                    connection,
                    level=Qgis.Critical)
                return
        if self.data_type == "mssql":
            self.conn_info = mssql_utils.get_mssql_conn_info(connection)
            self.layer = None

            if len(connection) == 0 or len(self.postgisschema) == 0 or len(self.postgistable) == 0 or \
                    len(self.postgissearchcolumn) == 0 or len(self.postgisgeomcolumn) == 0:
                return

            if len(self.conn_info) == 0:
                iface.messageBar().pushMessage(
                    "Discovery",
                    "The database connection '%s' does not exist!" %
                    connection,
                    level=Qgis.Critical)
                return
        elif self.data_type == "gpkg":
            self.layer = QgsVectorLayer(
                self.file + '|layername=' + self.postgistable,
                self.postgistable, 'ogr')
            self.conn_info = None
        self.extra_expr_columns = []
        self.scale_expr = None
        self.bbox_expr = None

        self.make_enabled(True)

        # optional scale expression when zooming in to results
        if len(scale_expr) != 0:
            expr = QgsExpression(scale_expr)
            if expr.hasParserError():
                iface.messageBar().pushMessage("Discovery",
                                               "Invalid scale expression: " +
                                               expr.parserErrorString(),
                                               level=Qgis.Warning)
            else:
                self.scale_expr = scale_expr
                self.extra_expr_columns += expr.referencedColumns()

        # optional bbox expression when zooming in to results
        if len(bbox_expr) != 0:
            expr = QgsExpression(bbox_expr)
            if expr.hasParserError():
                iface.messageBar().pushMessage("Discovery",
                                               "Invalid bbox expression: " +
                                               expr.parserErrorString(),
                                               level=Qgis.Warning)
            else:
                self.bbox_expr = bbox_expr
                self.extra_expr_columns += expr.referencedColumns()

    def show_config_dialog(self):
        dlg = config_dialog.ConfigDialog()
        if (self.config_combo.currentIndex() >= 0):
            dlg.configOptions.setCurrentIndex(self.config_combo.currentIndex())

        if dlg.exec_():
            dlg.write_config()
            self.config_combo.clear()
            for key in [
                    dlg.configOptions.itemText(i)
                    for i in range(dlg.configOptions.count())
            ]:
                self.config_combo.addItem(key)

            self.config_combo.setCurrentIndex(dlg.configOptions.currentIndex())
            self.change_configuration()

    def make_enabled(self, enabled):
        self.search_line_edit.setEnabled(enabled)
        self.search_line_edit.setPlaceholderText(
            "Search for..."
            if enabled else "Search disabled: check configuration")

    def show_marker(self, point):
        for m in [self.marker, self.marker2]:
            m.setCenter(point)
            m.setOpacity(1.0)
            m.setVisible(True)
        if self.display_time == -1:
            self.is_displayed = True
        else:
            QTimer.singleShot(self.display_time, self.hide_marker)

    def hide_marker(self):
        opacity = self.marker.opacity()
        if opacity > 0.:
            # produce a fade out effect
            opacity -= 0.1
            self.marker.setOpacity(opacity)
            self.marker2.setOpacity(opacity)
            QTimer.singleShot(100, self.hide_marker)
        else:
            self.marker.setVisible(False)
            self.marker2.setVisible(False)

    def show_line_rubber_band(self, geom):
        self.rubber_band.reset(geom.type())
        self.rubber_band.setToGeometry(geom, None)
        self.rubber_band.setVisible(True)
        self.rubber_band.setOpacity(1.0)
        self.rubber_band.show()
        if self.display_time == -1:
            self.is_displayed = True
        else:
            QTimer.singleShot(self.display_time, self.hide_rubber_band)
        pass

    def hide_rubber_band(self):
        opacity = self.rubber_band.opacity()
        if opacity > 0.:
            # produce a fade out effect
            opacity -= 0.1
            self.rubber_band.setOpacity(opacity)
            QTimer.singleShot(100, self.hide_rubber_band)
        else:
            self.rubber_band.setVisible(False)
            self.rubber_band.hide()
Esempio n. 5
0
class FindTextEdit(QWidget):
    def __init__(self):
        super().__init__()
        self.isHide = False

        self.searchLineEdit = QLineEdit(placeholderText='search',
                                        returnPressed=self.handle_search)
        self.searchLineEdit.setStyleSheet('.QLineEdit {border-radius: 5px;}')
        self.searchCompleter = QCompleter()
        self.searchCompleter.setFilterMode(Qt.MatchContains)
        self.searchList = QStringListModel()
        self.searchCompleter.setModel(self.searchList)
        self.searchCompleter.setMaxVisibleItems(10)
        self.searchLineEdit.setCompleter(self.searchCompleter)

        self.prevButton = QPushButton('<', clicked=self.handle_backward)
        self.nextButton = QPushButton('>', clicked=self.handle_forward)
        self.textEdit = QTextEdit(readOnly=True)

        hlayout = QHBoxLayout()
        hlayout.setSpacing(0)
        hlayout.setContentsMargins(0, 0, 0, 0)
        hlayout.addWidget(self.searchLineEdit)
        hlayout.addSpacing(10)
        hlayout.addWidget(self.prevButton)
        hlayout.addWidget(self.nextButton)

        layout = QVBoxLayout()
        layout.setSpacing(0)
        layout.setContentsMargins(0, 0, 0, 0)

        layout.addLayout(hlayout)
        layout.addWidget(self.textEdit)
        self.hideSearch()
        self.setLayout(layout)

    def append(self, text):
        self.textEdit.append(text)

    def hideSearch(self):
        self.searchLineEdit.setVisible(not self.isHide)
        self.prevButton.setVisible(not self.isHide)
        self.nextButton.setVisible(not self.isHide)
        self.isHide = not self.isHide
        self.update()

    def handle_search(self):
        self.handle_find()

    def handle_forward(self, checked):
        self.handle_find()

    def handle_backward(self, checked):
        self.handle_find(QTextDocument.FindBackward)

    def handle_find(self, findflag=0):
        searchText = self.searchLineEdit.text()
        stringList = self.searchList.stringList()
        stringList.append(searchText)
        self.searchList.setStringList(list(set(stringList)))

        # _ = self.textEdit.find(searchText) if findflag == 0 else self.textEdit.find(searchText, findflag)

        textCurosr = self.textEdit.textCursor()
        document = self.textEdit.document()
        if findflag == 0:
            hi_cursor = document.find(searchText, textCurosr)
            textCurosr.setPosition(hi_cursor.position())
        else:
            hi_cursor = document.find(searchText, textCurosr, findflag)
            textCurosr.setPosition(hi_cursor.anchor() - 1)

        extraSelection = QTextEdit.ExtraSelection()
        extraSelection.cursor = hi_cursor
        extraSelection.format.setBackground(Qt.red)

        self.textEdit.setTextCursor(textCurosr)
        self.textEdit.setExtraSelections([extraSelection])

    def keyPressEvent(self, e):
        if e.modifiers() & Qt.CTRL and e.key() == Qt.Key_F:
            self.hideSearch()

        e.accept()
class DiscoveryPlugin:
    def __init__(self, _iface):
        # Save reference to the QGIS interface
        self.iface = _iface
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)

        # Variables to facilitate delayed queries and database connection management
        self.icon_loading = ':plugins/_Discovery_sqlite/icons/loading.gif'
        self.db_timer = QTimer()
        self.line_edit_timer = QTimer()
        self.line_edit_timer.setSingleShot(True)
        self.line_edit_timer.timeout.connect(self.reset_line_edit_after_move)
        self.next_query_time = None
        self.last_query_time = time.time()
        self.db_conn = None
        self.search_delay = 0.5  # s
        self.query_sql = ''
        self.query_text = ''
        self.query_dict = {}
        self.db_idle_time = 60.0  # s

        self.search_results = []
        self.tool_bar = None
        self.search_line_edit = None
        self.completer = None

        self.marker = QgsVertexMarker(iface.mapCanvas())
        self.marker.setIconSize(15)
        self.marker.setPenWidth(2)
        self.marker.setColor(QColor(226, 27, 28))  #51,160,44))
        self.marker.setZValue(11)
        self.marker.setVisible(False)
        self.marker2 = QgsVertexMarker(iface.mapCanvas())
        self.marker2.setIconSize(16)
        self.marker2.setPenWidth(4)
        self.marker2.setColor(QColor(255, 255, 255, 200))
        self.marker2.setZValue(10)
        self.marker2.setVisible(False)

    def initGui(self):

        # Create a new toolbar
        self.tool_bar = self.iface.addToolBar(u'Панель поиска')
        self.tool_bar.setObjectName('Discovery_sqlite_Plugin')

        # Add search edit box
        self.search_line_edit = QgsFilterLineEdit()
        self.search_line_edit.setSelectOnFocus(True)
        self.search_line_edit.setShowSearchIcon(True)
        self.search_line_edit.setPlaceholderText(
            u'Поиск адреса или участка...')
        # self.search_line_edit.setMaximumWidth(768)
        self.tool_bar.addWidget(self.search_line_edit)

        # loading indicator
        self.load_movie = QMovie()
        self.label_load = QLabel()
        self.tool_bar.addWidget(self.label_load)

        # Set up the completer
        model = QStandardItemModel()
        self.completer = QCompleter([])  # Initialise with en empty list
        self.completer.setCaseSensitivity(Qt.CaseInsensitive)
        self.completer.setMaxVisibleItems(30)
        self.completer.setModelSorting(
            QCompleter.UnsortedModel)  # Sorting done in PostGIS
        self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion
                                         )  # Show all fetched possibilities
        self.completer.setModel(model)
        tableView = QTableView()
        tableView.verticalHeader().setVisible(False)
        tableView.horizontalHeader().setVisible(False)
        tableView.setSelectionBehavior(QTableView.SelectRows)
        tableView.setShowGrid(False)
        # tableView.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
        tableView.verticalHeader().setSectionResizeMode(QHeaderView.Fixed)
        fontsize = QFontMetrics(
            tableView.verticalHeader().font()).height() + 2  #font size
        tableView.verticalHeader().setDefaultSectionSize(
            fontsize)  #font size 15
        tableView.horizontalHeader().setSectionResizeMode(
            QHeaderView.ResizeToContents)
        tableView.horizontalHeader().setStretchLastSection(True)
        tableView.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        self.completer.setCompletionColumn(0)
        self.completer.setPopup(tableView)

        self.completer.activated[QModelIndex].connect(self.on_result_selected)
        self.completer.highlighted[QModelIndex].connect(
            self.on_result_highlighted)
        self.search_line_edit.setCompleter(self.completer)

        # Connect any signals
        self.search_line_edit.textEdited.connect(self.on_search_text_changed)
        self.search_line_edit.returnPressed.connect(self.returnPressed)

        self.read_config()

        # Search results
        self.search_results = []

        # Set up a timer to periodically perform db queries as required
        self.db_timer.timeout.connect(self.schedule_search)

    def read_config(self):
        qstring = ''
        self.data = self.settings_path()
        try:
            for line in self.data[1:]:
                words = line.split(';')
                postgissearchcolumn = words[2].strip()
                postgistable = words[0].strip()
                geomcolumn = words[3].strip()
                layername = words[4].strip()
                isSelect = int(words[5].strip())

                connection = sqlite3.connect(os.path.join(self.dbfile.strip()))

                cur = connection.cursor()

                qstring = u'select {2} from {0} where length({1})>0 LIMIT 1'.format(
                    postgistable, postgissearchcolumn, geomcolumn)
                cur.execute(qstring)
                connection.close()
            self.make_enabled(True)  # assume the config is invalid first
        except Exception as E:
            print(E)
            self.make_enabled(False)

    # включить или выключить поисковую строку в зависимости от результата проверки настроек
    def make_enabled(self, enabled):
        self.search_line_edit.setEnabled(enabled)
        self.search_line_edit.setPlaceholderText(
            u"Поиск адреса или участка..."
            if enabled else u"Поиск отключен: проверьте конфигурацию")

    def settings_path(self):
        p = os.path.join(self.plugin_dir, 'layers.ini')
        f = codecs.open(p, 'r', encoding='cp1251')
        data = f.readlines()

        dbfileline = data[0]
        if dbfileline[:2] == u'\\\\':
            self.dbfile = dbfileline
        elif dbfileline[1] == u':':
            self.dbfile = dbfileline
        else:
            self.dbfile = os.path.join(self.plugin_dir, dbfileline)
        f.close()
        return data

    def unload(self):
        self.db_timer.stop()
        self.db_timer.timeout.disconnect(self.schedule_search)
        self.completer.highlighted[QModelIndex].disconnect(
            self.on_result_highlighted)
        self.completer.activated[QModelIndex].disconnect(
            self.on_result_selected)
        self.search_line_edit.textEdited.disconnect(
            self.on_search_text_changed)
        self.search_line_edit.returnPressed.disconnect(self.returnPressed)
        self.tool_bar.clear()  # Clear all actions
        self.iface.mainWindow().removeToolBar(self.tool_bar)

    def clear_suggestions(self):
        model = self.completer.model()
        model.clear()
        # model.setStringList([])

    def returnPressed(self):
        if self.completer.popup().isHidden():
            self.do_search(self.search_line_edit.text())

    # def setLoading(self, isLoading):
    #     if self.label_load is None:
    #         return
    #     if isLoading:
    #         load_movie = QMovie()
    #         load_movie.setFileName(self.icon_loading)
    #         self.label_load.setMovie(load_movie)
    #         load_movie.start()
    #     else:
    #         load_movie = QMovie()
    #         load_movie.stop()
    #         self.label_load.setMovie(load_movie)

    def schedule_search(self):

        if self.next_query_time is not None and self.next_query_time < time.time(
        ):
            self.next_query_time = None  # Prevent this query from being repeated
            self.last_query_time = time.time()
            self.do_search(self.search_line_edit.text())
            self.db_timer.stop()
            # self.setLoading(False)
            self.search_line_edit.setShowSpinner(False)
        else:
            # self.setLoading(True)
            self.search_line_edit.setShowSpinner(True)
            if time.time() > self.last_query_time + self.db_idle_time:
                self.db_conn = None

    # def on_search_text_changed(self, new_search_text):
    def on_search_text_changed(self, new_search_text):
        # self.setLoading(False)
        self.search_line_edit.setShowSpinner(False)
        if len(new_search_text) < 3:
            self.db_timer.stop()
            self.clear_suggestions()
            return
        self.db_timer.start(300)
        self.next_query_time = time.time() + self.search_delay

    def do_search(self, new_search_text):

        if len(new_search_text) < 3:
            self.clear_suggestions()
            return

        self.clear_suggestions()

        self.query_text = new_search_text

        self.search_results = []
        self.suggestions = []

        for index, line in enumerate(self.data[1:]):
            curline_layer = line
            words = curline_layer.split(';')
            searchcolumn = words[2].strip()  # поле со значением для поиска
            postgistable = words[0].strip()  # таблица
            geomcolumn = words[3].strip()  # поле с геометрией
            layername = words[4].strip(
            )  # имя слоя в легенде для соответствий и выделения
            isSelect = int(
                words[5].strip())  # выделять ли объект в слое layername
            descript = words[1].strip(
            )  # описание. Выводится в списке результатов

            query_text, query_dict = self.get_search_sql(
                new_search_text, searchcolumn, postgistable)

            query_sql = query_text
            query_dict = query_dict
            self.perform_search(query_sql, query_dict, descript, postgistable,
                                layername, isSelect, searchcolumn)

        # QStringList - просто одна строка в выводе
        # if len(self.suggestions) > 0:
        #     model = self.completer.model()
        #     model.setStringList(self.suggestions)
        #     print(model)
        #     self.completer.complete()

        if len(self.suggestions) > 0:
            # model = self.completer.model()
            model = QStandardItemModel()
            font = QFont()
            font.setItalic(True)
            font.setPointSize(7)
            # заполняем модель
            for i, line in enumerate(self.suggestions):
                #icon
                pixmap = QPixmap(':plugins/_Discovery_sqlite/icons/' +
                                 line[2] + '.png')
                pixmap = pixmap.scaledToHeight(10)
                pixmap = pixmap.scaledToWidth(10)
                # itemImage = QStandardItem()
                # itemImage.setData(pixmap, Qt.DecorationRole)
                # model.setItem(i, 0, itemImage)

                itemLayer = QStandardItem(u"{1}[{0}]".format(
                    line[1], u' ' * 50))
                itemLayer.setFont(font)
                itemValue = QStandardItem(line[0])
                itemValue.setData(pixmap, Qt.DecorationRole)
                model.setItem(i, 0, itemValue)
                model.setItem(i, 1, itemLayer)

            self.completer.setModel(model)
            self.completer.complete()

        else:
            model = self.completer.model()
            # self.suggestions.append(u"<Не найдено>")   # для QStringList
            # model.setStringList(self.suggestions)   # для QStringList
            model.setItem(
                0, 0, QStandardItem('<Не найдено>'))  # для QStandardItemModel
            self.completer.complete()

    def perform_search(self, query_sql, query_dict, descript, tablename,
                       layername, isSelect, searchcolumn):
        cur = self.get_db_cur()
        cur.execute(query_sql, query_dict)
        for row in cur.fetchall():
            geom, suggestion_text = row[0], row[1]
            self.search_results.append(geom)
            self.suggestions.append([
                suggestion_text, descript, tablename, layername, isSelect,
                searchcolumn
            ])
            # self.suggestions.append(suggestion_text)   # для QStringList

    def get_search_sql(self, search_text, search_column, table):

        wildcarded_search_string = ''
        for part in search_text.split():
            wildcarded_search_string += '%' + part  #.lower()
        wildcarded_search_string += '%'
        wildcarded_search_string = wildcarded_search_string
        query_dict = {'search_text': wildcarded_search_string}

        # wildcarded_search_string = wildcarded_search_string.encode('cp1251')
        query_text = u"SELECT WKT_GEOMETRY AS geom, {0} AS suggestion_string FROM {1} WHERE ({0}) LIKE '{2}' ORDER BY {0} LIMIT 1000".format(
            search_column, table, wildcarded_search_string)
        # query_text = query_text.decode('cp1251')
        return query_text, query_dict

    def on_result_selected(self, result_index):
        resultIndexRow = result_index.row()

        if len(self.search_results) < 1:
            self.search_line_edit.setPlaceholderText(u'')
            return
        # What to do when the user makes a selection
        geometry_text = self.search_results[resultIndexRow]
        location_geom = QgsGeometry.fromWkt(geometry_text)
        canvas = self.iface.mapCanvas()
        # dst_srid = canvas.mapRenderer().destinationCrs().authid()
        # Ensure the geometry from the DB is reprojected to the same SRID as the map canvas
        location_centroid = location_geom.centroid().asPoint()

        result_text = self.completer.completionModel().index(
            resultIndexRow, 0).data()

        if self.suggestions[resultIndexRow][2] in (
                u"adres_nd") and location_geom.type() == 0:  # point
            self.show_marker(location_centroid)
            self.iface.mapCanvas().setExtent(location_geom.boundingBox())
            self.iface.mapCanvas().zoomScale(1000)
            layer_build = self.find_layer(u"Здания")
            if layer_build != None:
                layer_build.selectByIds([])
                for feat in layer_build.getFeatures(
                        QgsFeatureRequest().setFilterRect(
                            QgsRectangle(self.iface.mapCanvas().extent()))):
                    if location_geom.intersects(feat.geometry()):
                        # self.show_marker_feature(feat.geometry())
                        self.iface.setActiveLayer(layer_build)
                        layer_build.selectByIds([feat.id()])
                        layer_build.triggerRepaint()
                        return

        else:  #not point
            layername = self.suggestions[resultIndexRow][3]
            isSelect = self.suggestions[resultIndexRow][4]
            searchcolumn = self.suggestions[resultIndexRow][5]

            box = location_geom.boundingBox()
            if box.height() > box.width():
                max = box.height()
            else:
                max = box.width()
            box.grow(max * 0.10)
            self.iface.mapCanvas().setExtent(box)

            if isSelect == 1:
                selLayer = self.find_layer(layername)
                if selLayer is not None:
                    for feat in selLayer.getFeatures(
                            QgsFeatureRequest().setFilterRect(box)):
                        # print(feat[searchcolumn], str(result_text).strip())
                        try:
                            if str(feat[searchcolumn]) == str(
                                    result_text).strip():
                                self.iface.setActiveLayer(selLayer)
                                selLayer.selectByIds([feat.id()])
                                selLayer.triggerRepaint()
                                break
                        except Exception as E:
                            print(E)
                            break

            self.show_marker_feature(location_geom)

        canvas.refresh()
        self.line_edit_timer.start(0)
        # self.db_timer.stop()

    def get_db_cur(self):
        # Create a new new connection if required
        if self.db_conn is None:
            self.db_conn = sqlite3.connect(os.path.join(self.dbfile.strip()))
        return self.db_conn.cursor()

    def on_result_highlighted(self, result_idx):
        self.line_edit_timer.start(0)

    def reset_line_edit_after_move(self):
        self.search_line_edit.setText(self.query_text)

    def find_layer(self, layer_name):
        for search_layer in self.iface.mapCanvas().layers():
            if search_layer.name() == layer_name:
                return search_layer
        return None

    def show_marker(self, point):
        for m in [self.marker, self.marker2]:
            m.setCenter(point)
            m.setOpacity(1.0)
            m.setVisible(True)
        QTimer.singleShot(4000, self.hide_marker)

    def hide_marker(self):
        opacity = self.marker.opacity()
        if opacity > 0.:
            # produce a fade out effect
            opacity -= 0.1
            self.marker.setOpacity(opacity)
            self.marker2.setOpacity(opacity)
            QTimer.singleShot(100, self.hide_marker)
        else:
            self.marker.setVisible(False)
            self.marker2.setVisible(False)

    def show_marker_feature(self, geom):
        if geom.type() == 2:  #poly
            self.r = QgsRubberBand(iface.mapCanvas(), True)
        elif geom.type() == 1:  #line
            self.r = QgsRubberBand(iface.mapCanvas(), False)
        self.r.setToGeometry(geom, None)
        self.r.setColor(QColor(255, 0, 0, 200))
        self.r.setFillColor(QColor(255, 0, 0, 50))
        self.r.setWidth(2)
        self.r.setZValue(9)

        QTimer.singleShot(4000, self.hide_marker_feature)

    def hide_marker_feature(self):
        opacity = self.r.opacity()
        if opacity > 0.:
            # produce a fade out effect
            opacity -= 0.1
            self.r.setOpacity(opacity)
            QTimer.singleShot(100, self.hide_marker_feature)
        else:
            iface.mapCanvas().scene().removeItem(self.r)
Esempio n. 7
0
class Window(main.Ui_MainWindow, QMainWindow, ApplicationContext):
    def __init__(self):
        """Window Object"""
        super(Window, self).__init__()

        # Inherit from UI file
        self.setupUi(self)

        #Default variables
        self.variables()

        # Load settings
        self.settingsbox = Settings()
        self.load_settings()

        # Initialize widget defaults
        self.widget_settings()

        # Class Variables
        self.oldPos = self.pos()  # variable for dragging frameless window

        # Set Flags (Frameless)
        flags = QtCore.Qt.WindowFlags(QtCore.Qt.FramelessWindowHint)
        self.setWindowFlags(flags)

        # Context Menu
        self.treeView.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.treeView.customContextMenuRequested.connect(self.context_menu)

        #Initiate the window
        self.show()

    class Folder(object):
        def __init__(self, folder):
            """Object for getting folder properties"""
            self.yearpref = exit_code.yearpref
            self.path = folder
            self.parts = self.path.split('\\')
            self.level = len(self.parts)
            if self.level >= 2:
                self.parent = os.path.dirname(self.path)
            else:
                self.parent = None
            if self.level >= 3:
                self.clientname = self.parts[2]
            else:
                self.clientname = None
            if self.level >= 4:
                if self.yearpref in self.parts[3]:
                    self.year = self.parts[3].strip(self.yearpref)
                else:
                    self.year = None
            else:
                self.year = None

    # TODO: Error message on tree view if no folder found         // ETA: 30 Minutes
    # TODO: Extract information from latest workpapers            // ETA: 4 Hours
    # TODO: For future update > Add XPM Links                     // ETA: 10 Hours
    # TODO: Context Menu > Add New  > Workpapers / Folder         // ETA: 30 Minutes
    # TODO: Add double click event to open files in tree view     // ETA: 50 Minutes
    # TODO: Add images for close and minimize button              // ETA: 20 Minutes
    # TODO: Dynamic font size, dependent of len()                 // ETA: 15 Minutes

    def variables(self):
        self.configpath = configpath
        self.configparam = configparam
        self.configfile = configfile
        self.wpfolder = 'Annual Workpapers'
        self.prevpath = []
        self.listyear = []
        self.clientname = ''
        self.count = 0
        self.counter = 0

    def widget_settings(self):
        self.populate()
        self.client_names()
        self.actions()
        self.toggle_year(False)
        self.lineEdit.hide()
        self.yearline.setText(self.year)
        self.treeView.setRootIndex(self.model.index(self.clientdir))
        self.backbutton.setEnabled(False)

    def load_settings(self):
        """Load settings from config file"""

        # Settings File
        if not os.path.exists(self.configpath):
            os.mkdir(self.configpath)
        if not os.path.exists(self.configfile):
            with open(self.configfile, 'w+') as f:
                separator = '=\n'
                f.write(separator.join(self.configparam) + '=')
                f.close

        # Read config file
        configuration = open(self.configfile, 'r')
        self.settings = {}
        for lines in configuration.readlines():
            line = lines.strip('\n').split('=')
            self.settings[line[0]] = line[1]

        # Declaring variables from config file
        if self.settings['clientdir']:
            self.clientdir = self.settings['clientdir']
        else:
            self.clientdir = f'{os.environ["USERPROFILE"]}'

        self.path = self.clientdir
        self.completer_pref = int(self.settings['autocomplete'])
        self.yearpref = self.settings['yearprefix']
        self.year = str(datetime.now().year)
        self.diryear = f'{self.yearpref}{self.year}'

    def select_client(self, event):
        """Hide label and show line edit"""
        self.clientlabel.hide()
        self.lineEdit.show()

    def hide_client(self):
        self.lineEdit.setText('')
        self.lineEdit.hide()
        self.clientlabel.show()

    def client_selected(self):
        """Show label, hide line edit and update label"""
        self.lineEdit.hide()
        self.clientlabel.show()
        self.clientname = self.lineEdit.text()
        self.folder(opt='selected')

    # def back_button
    def folder(self, opt=None):
        """Changing folder function, all of them passed here"""
        #Folder instance for prev folder
        self.starttime = time.time()
        print(self.starttime - time.time())
        self.prevfolder = self.Folder(self.path)
        print(self.starttime - time.time())
        self.prevpath.append(self.prevfolder.path)
        # Detect how folder function is initialized
        print(self.starttime - time.time())
        if opt == 'selected':
            self.clientname = self.lineEdit.text()
            self.path = '\\'.join([self.clientdir, self.clientname, self.year])
        # elif opt == 'back':
        #     pass

        elif opt == 'manual':
            self.path = self.openpath
        # elif opt == 'up':
        # elif opt == 'switch':
        print(self.starttime - time.time())
        #Folder instance for current folder
        self.currfolder = self.Folder(self.path)
        self.treeView.setRootIndex(self.model.index(self.path))  #Run folder
        print(self.starttime - time.time())
        #Detect year and declare year variables
        if self.currfolder.year:
            self.yearline.setText(self.currfolder.year)
            self.toggle_year(True)
        else:
            self.yearline.setText('-')
            self.toggle_year(False)
        print(self.starttime - time.time())
        #Detect if current index is inside client
        if self.path == self.clientdir:
            self.clientlabel.setText('Select a client...')
            self.lineEdit.setText('')
            self.lineEdit.hide()
            self.clientlabel.show()
        elif self.currfolder.clientname:  # Detect if on second level
            self.prevclient = self.clientname
            self.clientname = self.currfolder.clientname
            self.clientfolder = '\\'.join(self.path.split('\\')[0:3])
            clientlabel = textwrap.shorten(self.clientname,
                                           50,
                                           placeholder='...')
            self.clientlabel.setText(clientlabel)
            self.lineEdit.setText(self.clientname)
            self.lineEdit.hide()
            self.clientlabel.show()
            if self.prevclient != self.clientname:
                self.years(self.clientfolder, clear=True)
                print(self.listyear)
        print(self.starttime - time.time())
        # Storing paths to a list
        if not opt == 'back':
            self.counter += 1  # Usage Counter
            if self.counter == 1:
                self.prevpath.append(self.clientdir)
            else:
                self.prevpath.append(self.path)
        print(self.starttime - time.time())
        # Toggle year widget if current index contains year pref
        self.windowspath = self.path.replace('/', '\\')
        self.addressbar.setText(self.windowspath)
        self.backbutton.setEnabled(True)

    def years(self, yearfolder, clear=None):
        """Create a list of years from folder structure"""
        if clear:
            self.listyear = []
            self.index = 0

        folders = os.listdir(yearfolder)
        for folder in folders:
            if self.yearpref in folder:
                year = folder.lstrip(self.yearpref)
                self.listyear.append(year)

    def goback(self):
        # self.folder(opt='back')
        pass

    def go_up(self):
        self.folder(opt='up')

    def toggle_year(self, boolean):
        self.rightarrow.setEnabled(boolean)
        self.leftarrow.setEnabled(boolean)
        self.yearline.setEnabled(boolean)

    def switchyear(self, event):
        """Switch one year on yearline"""
        print(event)
        currfolder = os.path.basename(self.path)
        self.curryear = currfolder.strip(self.yearpref)
        self.index = self.listyear.index(self.curryear)
        # if event.key():
        #     self.index += 1
        # else:
        #     self.index -= 1
        self.newyear = self.listyear[self.index]
        self.yearline.setText(self.newyear)
        self.folder(opt='switch')

    def actions(self):
        """All actions for buttons"""
        self.closebutton.clicked.connect(self.close)
        self.minimizebutton.clicked.connect(self.showMinimized)
        self.completer.activated.connect(self.client_selected)
        self.settingsbutton.clicked.connect(self.open_settings)
        self.rightarrow.mousePressEvent = self.switchyear
        self.leftarrow.mousePressEvent = self.switchyear
        self.clientlabel.mousePressEvent = self.select_client
        self.backbutton.clicked.connect(self.goback)
        self.settingsbox.buttonBox.accepted.connect(self.save_settings)
        self.treeView.mouseDoubleClickEvent = self.open_file
        self.upper.clicked.connect(self.go_up)
        self.lineEdit.returnPressed.connect(self.client_selected)

    def save_settings(self):
        self.settingsbox.writer()
        self.load_settings()
        self.setEnabled(True)
        self.client_names()
        self.actions()

    def open_settings(self):
        """Open settings dialog box"""
        self.settingsbox.show()
        self.settingsbox.exec_()

    def populate(self):
        """Load file browser model on tree view"""
        self.model = QtWidgets.QFileSystemModel()
        self.model.setRootPath((QtCore.QDir.rootPath()))
        self.treeView.setModel(self.model)
        self.treeView.setColumnWidth(0, 350)
        self.treeView.setSortingEnabled(1)

    def client_names(self):
        """Auto complete list of entries"""
        self.clients = os.listdir(self.clientdir)
        self.completer = QCompleter(self.clients)
        self.completer.setCaseSensitivity(0)
        self.completer.setMaxVisibleItems(10)
        self.completer.setCompletionMode(self.completer_pref)
        self.lineEdit.setCompleter(self.completer)

    def context_menu(self):
        """Right Click Menu: [OPEN]"""
        menu = QtWidgets.QMenu()

        # Actions
        open = menu.addAction('Open')
        open.triggered.connect(self.open_file)
        cursor = QtGui.QCursor()
        menu.exec_(cursor.pos())

    def open_file(self, *event):
        """Open Function"""
        index = self.treeView.currentIndex()
        self.openpath = self.model.filePath(index)
        self.openpath = self.openpath.replace('/', '\\')
        if os.path.isdir(self.openpath):
            self.folder(opt='manual')
        else:
            os.startfile(self.file_path)

    """ Event Handling """

    def keyPressEvent(self, event):
        """Key press events"""
        #if Esc is pressed while on line edit
        if self.clientlabel.isHidden() and event.key() == 16777216:
            self.hide_client()

        if event.key(
        ) == QtCore.Qt.Key_Backspace:  # Go back on previous folder
            self.folder(opt='back')

        print(event.key())

    def mousePressEvent(self, event):
        """Record the position when mouse is pressed"""
        self.oldPos = event.globalPos()
        if self.clientlabel.isHidden():
            self.hide_client()

    def mouseMoveEvent(self, event):
        """Updating positions as we drag"""
        delta = QPoint(event.globalPos() - self.oldPos)
        self.move(self.x() + delta.x(), self.y() + delta.y())
        self.oldPos = event.globalPos()