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