def create_line_search(self, hostnames_list): """ Add all hosts to QLineEdit and set QCompleter :param hostnames_list: list of host names :type hostnames_list: list """ # Get QStringListModel model = self.completer.model() if not model: model = QStringListModel() model.setStringList(hostnames_list) # Configure QCompleter from model self.completer.setFilterMode(Qt.MatchContains) self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.completer.setModel(model) self.completer.popup().setObjectName('popup') # Add completer to QLineEdit self.line_search.setCompleter(self.completer) self.line_search.setPlaceholderText(_('Type a host name to display its data')) self.line_search.setToolTip(_('Type a host name to display its data'))
def create_line_search(self, hostnames_list): """ Add all hosts to QLineEdit and set QCompleter :param hostnames_list: list of host names :type hostnames_list: list """ # Get QStringListModel model = self.completer.model() if not model: model = QStringListModel() model.setStringList(hostnames_list) # Configure QCompleter from model self.completer.setFilterMode(Qt.MatchContains) self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.completer.setModel(model) self.completer.popup().setObjectName('popup') # Add completer to QLineEdit self.line_search.setCompleter(self.completer) self.line_search.setPlaceholderText( _('Type a host name to display its data')) self.line_search.setToolTip(_('Type a host name to display its data'))
def create_line_search(self): """ Add all hosts to QLineEdit and set QCompleter """ # Create list for QStringModel hosts_list = [] params = {'where': json.dumps({'_is_template': False})} all_hosts = self.app_backend.get('host', params, ['name']) if all_hosts: for host in all_hosts['_items']: hosts_list.append(host['name']) model = QStringListModel() model.setStringList(hosts_list) # Create completer from model completer = QCompleter() completer.setFilterMode(Qt.MatchContains) completer.setCaseSensitivity(Qt.CaseInsensitive) completer.setModel(model) # Add completer to "line edit" self.line_search.setCompleter(completer) self.line_search.setPlaceholderText( 'Type a host name to display its data') self.line_search.setToolTip('Type a host name to display its data')
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 FontFamilyDialog(QDialog): def __init__(self, current_family, parent=None): QDialog.__init__(self, parent) self.setWindowTitle(_('Choose font family')) self.setWindowIcon(QIcon(I('font.png'))) from calibre.utils.fonts.scanner import font_scanner self.font_scanner = font_scanner self.m = QStringListModel(self) self.build_font_list() self.l = l = QGridLayout() self.setLayout(l) self.view = FontsView(self) self.view.setModel(self.m) self.view.setCurrentIndex(self.m.index(0)) if current_family: for i, val in enumerate(self.families): if icu_lower(val) == icu_lower(current_family): self.view.setCurrentIndex(self.m.index(i)) break self.view.doubleClicked.connect(self.accept, type=Qt.QueuedConnection) self.view.changed.connect(self.current_changed, type=Qt.QueuedConnection) self.faces = Typefaces(self) self.bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel) self.bb.accepted.connect(self.accept) self.bb.rejected.connect(self.reject) self.add_fonts_button = afb = self.bb.addButton(_('Add &fonts'), self.bb.ActionRole) afb.setIcon(QIcon(I('plus.png'))) afb.clicked.connect(self.add_fonts) self.ml = QLabel(_('Choose a font family from the list below:')) self.search = QLineEdit(self) self.search.setPlaceholderText(_('Search')) self.search.returnPressed.connect(self.find) self.nb = QToolButton(self) self.nb.setIcon(QIcon(I('arrow-down.png'))) self.nb.setToolTip(_('Find next')) self.pb = QToolButton(self) self.pb.setIcon(QIcon(I('arrow-up.png'))) self.pb.setToolTip(_('Find previous')) self.nb.clicked.connect(self.find_next) self.pb.clicked.connect(self.find_previous) l.addWidget(self.ml, 0, 0, 1, 4) l.addWidget(self.search, 1, 0, 1, 1) l.addWidget(self.nb, 1, 1, 1, 1) l.addWidget(self.pb, 1, 2, 1, 1) l.addWidget(self.view, 2, 0, 1, 3) l.addWidget(self.faces, 1, 3, 2, 1) l.addWidget(self.bb, 3, 0, 1, 4) l.setAlignment(self.faces, Qt.AlignTop) self.resize(800, 600) def set_current(self, i): self.view.setCurrentIndex(self.m.index(i)) def keyPressEvent(self, e): if e.key() == Qt.Key_Return: return return QDialog.keyPressEvent(self, e) def find(self, backwards=False): i = self.view.currentIndex().row() if i < 0: i = 0 q = icu_lower(unicode(self.search.text())).strip() if not q: return r = (xrange(i-1, -1, -1) if backwards else xrange(i+1, len(self.families))) for j in r: f = self.families[j] if q in icu_lower(f): self.set_current(j) return def find_next(self): self.find() def find_previous(self): self.find(backwards=True) def build_font_list(self): try: self.families = list(self.font_scanner.find_font_families()) except: self.families = [] print ('WARNING: Could not load fonts') import traceback traceback.print_exc() self.families.insert(0, _('None')) self.m.setStringList(self.families) def add_fonts(self): families = add_fonts(self) if not families: return self.font_scanner.do_scan() self.m.beginResetModel() self.build_font_list() self.m.endResetModel() self.view.setCurrentIndex(self.m.index(0)) if families: for i, val in enumerate(self.families): if icu_lower(val) == icu_lower(families[0]): self.view.setCurrentIndex(self.m.index(i)) break info_dialog(self, _('Added fonts'), _('Added font families: %s')%( ', '.join(families)), show=True) @property def font_family(self): idx = self.view.currentIndex().row() if idx == 0: return None return self.families[idx] def current_changed(self): fam = self.font_family self.faces.show_family(fam, self.font_scanner.fonts_for_family(fam) if fam else None)
class OpdsDialog(QDialog): def __init__(self, gui, icon, do_user_config): QDialog.__init__(self, gui) self.gui = gui self.do_user_config = do_user_config self.db = gui.current_db.new_api # The model for the book list self.model = OpdsBooksModel(None, self.dummy_books(), self.db) self.searchproxymodel = QSortFilterProxyModel(self) self.searchproxymodel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.searchproxymodel.setFilterKeyColumn(-1) self.searchproxymodel.setSourceModel(self.model) self.layout = QGridLayout() self.setLayout(self.layout) self.setWindowTitle("OPDS Client") self.setWindowIcon(icon) labelColumnWidths = [] self.opdsUrlLabel = QLabel("OPDS URL: ") self.layout.addWidget(self.opdsUrlLabel, 0, 0) labelColumnWidths.append(self.layout.itemAtPosition(0, 0).sizeHint().width()) config.convertSingleStringOpdsUrlPreferenceToListOfStringsPreference() self.opdsUrlEditor = QComboBox(self) self.opdsUrlEditor.activated.connect(self.opdsUrlEditorActivated) self.opdsUrlEditor.addItems(prefs["opds_url"]) self.opdsUrlEditor.setEditable(True) self.opdsUrlEditor.setInsertPolicy(QComboBox.InsertAtTop) self.layout.addWidget(self.opdsUrlEditor, 0, 1, 1, 3) self.opdsUrlLabel.setBuddy(self.opdsUrlEditor) buttonColumnNumber = 7 buttonColumnWidths = [] self.about_button = QPushButton("About", self) self.about_button.setAutoDefault(False) self.about_button.clicked.connect(self.about) self.layout.addWidget(self.about_button, 0, buttonColumnNumber) buttonColumnWidths.append( self.layout.itemAtPosition(0, buttonColumnNumber).sizeHint().width() ) # Initially download the catalogs found in the root catalog of the URL # selected at startup. Fail quietly on failing to open the URL catalogsTuple = self.model.downloadOpdsRootCatalog( self.gui, self.opdsUrlEditor.currentText(), False ) print(catalogsTuple) firstCatalogTitle = catalogsTuple[0] self.currentOpdsCatalogs = catalogsTuple[1] # A dictionary of title->feedURL self.opdsCatalogSelectorLabel = QLabel("OPDS Catalog:") self.layout.addWidget(self.opdsCatalogSelectorLabel, 1, 0) labelColumnWidths.append(self.layout.itemAtPosition(1, 0).sizeHint().width()) self.opdsCatalogSelector = QComboBox(self) self.opdsCatalogSelector.setEditable(False) self.opdsCatalogSelectorModel = QStringListModel(self.currentOpdsCatalogs.keys()) self.opdsCatalogSelector.setModel(self.opdsCatalogSelectorModel) self.opdsCatalogSelector.setCurrentText(firstCatalogTitle) self.layout.addWidget(self.opdsCatalogSelector, 1, 1, 1, 3) self.download_opds_button = QPushButton("Download OPDS", self) self.download_opds_button.setAutoDefault(False) self.download_opds_button.clicked.connect(self.download_opds) self.layout.addWidget(self.download_opds_button, 1, buttonColumnNumber) buttonColumnWidths.append( self.layout.itemAtPosition(1, buttonColumnNumber).sizeHint().width() ) # Search GUI self.searchEditor = QLineEdit(self) self.searchEditor.returnPressed.connect(self.searchBookList) self.layout.addWidget(self.searchEditor, 2, buttonColumnNumber - 2, 1, 2) self.searchButton = QPushButton("Search", self) self.searchButton.setAutoDefault(False) self.searchButton.clicked.connect(self.searchBookList) self.layout.addWidget(self.searchButton, 2, buttonColumnNumber) buttonColumnWidths.append( self.layout.itemAtPosition(2, buttonColumnNumber).sizeHint().width() ) # The main book list self.library_view = QTableView(self) self.library_view.setAlternatingRowColors(True) self.library_view.setModel(self.searchproxymodel) self.library_view.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch) self.library_view.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch) self.library_view.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch) self.library_view.setSelectionBehavior(QAbstractItemView.SelectRows) self.resizeAllLibraryViewLinesToHeaderHeight() self.library_view.resizeColumnsToContents() self.layout.addWidget(self.library_view, 3, 0, 3, buttonColumnNumber + 1) self.hideNewsCheckbox = QCheckBox("Hide Newspapers", self) self.hideNewsCheckbox.clicked.connect(self.setHideNewspapers) self.hideNewsCheckbox.setChecked(prefs["hideNewspapers"]) self.layout.addWidget(self.hideNewsCheckbox, 6, 0, 1, 3) self.hideBooksAlreadyInLibraryCheckbox = QCheckBox("Hide books already in library", self) self.hideBooksAlreadyInLibraryCheckbox.clicked.connect(self.setHideBooksAlreadyInLibrary) self.hideBooksAlreadyInLibraryCheckbox.setChecked(prefs["hideBooksAlreadyInLibrary"]) self.layout.addWidget(self.hideBooksAlreadyInLibraryCheckbox, 7, 0, 1, 3) # Let the checkbox initial state control the filtering self.model.setFilterBooksThatAreNewspapers(self.hideNewsCheckbox.isChecked()) self.model.setFilterBooksThatAreAlreadyInLibrary( self.hideBooksAlreadyInLibraryCheckbox.isChecked() ) self.downloadButton = QPushButton("Download selected books", self) self.downloadButton.setAutoDefault(False) self.downloadButton.clicked.connect(self.downloadSelectedBooks) self.layout.addWidget(self.downloadButton, 6, buttonColumnNumber) buttonColumnWidths.append( self.layout.itemAtPosition(6, buttonColumnNumber).sizeHint().width() ) self.fixTimestampButton = QPushButton("Fix timestamps of selection", self) self.fixTimestampButton.setAutoDefault(False) self.fixTimestampButton.clicked.connect(self.fixBookTimestamps) self.layout.addWidget(self.fixTimestampButton, 7, buttonColumnNumber) buttonColumnWidths.append( self.layout.itemAtPosition(7, buttonColumnNumber).sizeHint().width() ) # Make all columns of the grid layout the same width as the button column buttonColumnWidth = max(buttonColumnWidths) for columnNumber in range(0, buttonColumnNumber): self.layout.setColumnMinimumWidth(columnNumber, buttonColumnWidth) # Make sure the first column isn't wider than the labels it holds labelColumnWidth = max(labelColumnWidths) self.layout.setColumnMinimumWidth(0, labelColumnWidth) self.resize(self.sizeHint()) def opdsUrlEditorActivated(self, text): prefs["opds_url"] = config.saveOpdsUrlCombobox(self.opdsUrlEditor) catalogsTuple = self.model.downloadOpdsRootCatalog( self.gui, self.opdsUrlEditor.currentText(), True ) firstCatalogTitle = catalogsTuple[0] self.currentOpdsCatalogs = catalogsTuple[1] # A dictionary of title->feedURL self.opdsCatalogSelectorModel.setStringList(self.currentOpdsCatalogs.keys()) self.opdsCatalogSelector.setCurrentText(firstCatalogTitle) def setHideNewspapers(self, checked): prefs["hideNewspapers"] = checked self.model.setFilterBooksThatAreNewspapers(checked) self.resizeAllLibraryViewLinesToHeaderHeight() def setHideBooksAlreadyInLibrary(self, checked): prefs["hideBooksAlreadyInLibrary"] = checked self.model.setFilterBooksThatAreAlreadyInLibrary(checked) self.resizeAllLibraryViewLinesToHeaderHeight() def searchBookList(self): searchString = self.searchEditor.text() print("starting book list search for: %s" % searchString) self.searchproxymodel.setFilterFixedString(searchString) def about(self): text = get_resources("about.txt") QMessageBox.about(self, "About the OPDS Client plugin", text.decode("utf-8")) def download_opds(self): opdsCatalogUrl = self.currentOpdsCatalogs.get(self.opdsCatalogSelector.currentText(), None) if opdsCatalogUrl is None: # Just give up quietly return self.model.downloadOpdsCatalog(self.gui, opdsCatalogUrl) if self.model.isCalibreOpdsServer(): self.model.downloadMetadataUsingCalibreRestApi(self.opdsUrlEditor.currentText()) self.library_view.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch) self.library_view.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch) self.library_view.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch) self.resizeAllLibraryViewLinesToHeaderHeight() self.resize(self.sizeHint()) def config(self): self.do_user_config(parent=self) def downloadSelectedBooks(self): selectionmodel = self.library_view.selectionModel() if selectionmodel.hasSelection(): rows = selectionmodel.selectedRows() for row in reversed(rows): book = row.data(Qt.UserRole) self.downloadBook(book) def downloadBook(self, book): if len(book.links) > 0: self.gui.download_ebook(book.links[0]) def fixBookTimestamps(self): selectionmodel = self.library_view.selectionModel() if selectionmodel.hasSelection(): rows = selectionmodel.selectedRows() for row in reversed(rows): book = row.data(Qt.UserRole) self.fixBookTimestamp(book) def fixBookTimestamp(self, book): bookTimestamp = book.timestamp identicalBookIds = self.findIdenticalBooksForBooksWithMultipleAuthors(book) bookIdToValMap = {} for identicalBookId in identicalBookIds: bookIdToValMap[identicalBookId] = bookTimestamp if len(bookIdToValMap) < 1: print("Failed to set timestamp of book: %s" % book) self.db.set_field("timestamp", bookIdToValMap) def findIdenticalBooksForBooksWithMultipleAuthors(self, book): authorsList = book.authors if len(authorsList) < 2: return self.db.find_identical_books(book) # Try matching the authors one by one identicalBookIds = set() for author in authorsList: singleAuthorBook = Metadata(book.title, [author]) singleAuthorIdenticalBookIds = self.db.find_identical_books(singleAuthorBook) identicalBookIds = identicalBookIds.union(singleAuthorIdenticalBookIds) return identicalBookIds def dummy_books(self): dummy_author = " " * 40 dummy_title = " " * 60 books_list = [] for line in range(1, 10): book = DynamicBook() book.author = dummy_author book.title = dummy_title book.updated = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S+00:00") book.id = "" books_list.append(book) return books_list def resizeAllLibraryViewLinesToHeaderHeight(self): rowHeight = self.library_view.horizontalHeader().height() for rowNumber in range(0, self.library_view.model().rowCount()): self.library_view.setRowHeight(rowNumber, rowHeight)
class FontFamilyDialog(QDialog): def __init__(self, current_family, parent=None): QDialog.__init__(self, parent) self.setWindowTitle(_('Choose font family')) self.setWindowIcon(QIcon(I('font.png'))) from calibre.utils.fonts.scanner import font_scanner self.font_scanner = font_scanner self.m = QStringListModel(self) self.build_font_list() self.l = l = QGridLayout() self.setLayout(l) self.view = FontsView(self) self.view.setModel(self.m) self.view.setCurrentIndex(self.m.index(0)) if current_family: for i, val in enumerate(self.families): if icu_lower(val) == icu_lower(current_family): self.view.setCurrentIndex(self.m.index(i)) break self.view.doubleClicked.connect(self.accept, type=Qt.QueuedConnection) self.view.changed.connect(self.current_changed, type=Qt.QueuedConnection) self.faces = Typefaces(self) self.bb = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.bb.accepted.connect(self.accept) self.bb.rejected.connect(self.reject) self.add_fonts_button = afb = self.bb.addButton( _('Add &fonts'), self.bb.ActionRole) afb.setIcon(QIcon(I('plus.png'))) afb.clicked.connect(self.add_fonts) self.ml = QLabel(_('Choose a font family from the list below:')) self.search = QLineEdit(self) self.search.setPlaceholderText(_('Search')) self.search.returnPressed.connect(self.find) self.nb = QToolButton(self) self.nb.setIcon(QIcon(I('arrow-down.png'))) self.nb.setToolTip(_('Find Next')) self.pb = QToolButton(self) self.pb.setIcon(QIcon(I('arrow-up.png'))) self.pb.setToolTip(_('Find Previous')) self.nb.clicked.connect(self.find_next) self.pb.clicked.connect(self.find_previous) l.addWidget(self.ml, 0, 0, 1, 4) l.addWidget(self.search, 1, 0, 1, 1) l.addWidget(self.nb, 1, 1, 1, 1) l.addWidget(self.pb, 1, 2, 1, 1) l.addWidget(self.view, 2, 0, 1, 3) l.addWidget(self.faces, 1, 3, 2, 1) l.addWidget(self.bb, 3, 0, 1, 4) l.setAlignment(self.faces, Qt.AlignTop) self.resize(800, 600) def set_current(self, i): self.view.setCurrentIndex(self.m.index(i)) def keyPressEvent(self, e): if e.key() == Qt.Key_Return: return return QDialog.keyPressEvent(self, e) def find(self, backwards=False): i = self.view.currentIndex().row() if i < 0: i = 0 q = icu_lower(unicode(self.search.text())).strip() if not q: return r = (xrange(i - 1, -1, -1) if backwards else xrange( i + 1, len(self.families))) for j in r: f = self.families[j] if q in icu_lower(f): self.set_current(j) return def find_next(self): self.find() def find_previous(self): self.find(backwards=True) def build_font_list(self): try: self.families = list(self.font_scanner.find_font_families()) except: self.families = [] print('WARNING: Could not load fonts') import traceback traceback.print_exc() self.families.insert(0, _('None')) self.m.setStringList(self.families) def add_fonts(self): families = add_fonts(self) if not families: return self.font_scanner.do_scan() self.m.beginResetModel() self.build_font_list() self.m.endResetModel() self.view.setCurrentIndex(self.m.index(0)) if families: for i, val in enumerate(self.families): if icu_lower(val) == icu_lower(families[0]): self.view.setCurrentIndex(self.m.index(i)) break info_dialog(self, _('Added fonts'), _('Added font families: %s') % (', '.join(families)), show=True) @property def font_family(self): idx = self.view.currentIndex().row() if idx == 0: return None return self.families[idx] def current_changed(self): fam = self.font_family self.faces.show_family( fam, self.font_scanner.fonts_for_family(fam) if fam else None)
class SideView(QObject): onBeforeItemDeletion = pyqtSignal(str) onEntrySelected = pyqtSignal(str) onDirectoryChanged = pyqtSignal(str) onRemoveRequested = pyqtSignal(str) def __init__(self, dirLister, entryProvider: IEntryProvider, newEntryText: str, itemNameNormalizer: IItemNameNormalizer): super().__init__() def initListView(): self.listView = QListView() self.listView.setEditTriggers(QAbstractItemView.NoEditTriggers) self.listView.setModel(self.model) self.listView.setMinimumWidth(200) actionRemove = QAction("Remove", None) self.listView.addAction(actionRemove) self.listView.selectionModel().currentChanged.connect( lambda selectedItem, unselectedItem: self.onEntrySelected.emit( self.itemNameNormalizer.normalizeName( self.currentDir.filePath(selectedItem.data())))) def contextMenu(position): menu = QMenu() index = self.listView.indexAt(position) entry = self.model.data(index, Qt.DisplayRole) deleteAction = None renameAction = None addAction = menu.addAction("Add") if entry is not None: deleteAction = menu.addAction("Delete") renameAction = menu.addAction("Rename") refreshAction = menu.addAction("Refresh") chosenAction = menu.exec_(self.listView.mapToGlobal(position)) if chosenAction is None: return if chosenAction == deleteAction: self.onRemoveRequested.emit(entry) elif chosenAction == renameAction: self.renameEntry(index, entry) elif chosenAction == addAction: self.onCreateNewEntry() elif chosenAction == refreshAction: self.refreshListViewEntries() self.listView.customContextMenuRequested.connect(contextMenu) self.listView.setContextMenuPolicy(Qt.CustomContextMenu) self.currentDir: QDir = None self.model = QStringListModel() self.directoryLister = dirLister self.entryProvider = entryProvider self.newEntryText = newEntryText self.itemNameNormalizer = itemNameNormalizer self.sortingParser: IEntrySorting initListView() def renameEntry(self, index, oldName): def onNewEntryCommited(editedLine: QLineEdit): self.listView.itemDelegate().commitData.disconnect( onNewEntryCommited) try: newName = editedLine.text() normalizedNameOld = self.itemNameNormalizer.normalizeName( oldName) normalizedNameNew = self.itemNameNormalizer.normalizeName( newName) self.emitOnItemDeletion(normalizedNameOld) self.entryProvider.renameEnty(normalizedNameOld, normalizedNameNew) self.sortingParser.rename(oldName, newName) self.refreshListViewEntries() except Exception: pass self.listView.itemDelegate().commitData.connect(onNewEntryCommited) self.listView.edit(index) def emitOnItemDeletion(self, normalizedName): self.onBeforeItemDeletion.emit( self.currentDir.filePath(normalizedName)) def removeEntry(self, entry): normalizedName = self.itemNameNormalizer.normalizeName(entry) self.emitOnItemDeletion(normalizedName) self.entryProvider.removeEntry(normalizedName) self.refreshListViewEntries() def setDirectory(self, dirPath: str): self.currentDir = QDir(dirPath) self.sortingParser = EntrySortingFile( self.currentDir.filePath('.sorting')) self.entryProvider.setContext(dirPath) self.refreshListViewEntries() self.onDirectoryChanged.emit(self.currentDir.absolutePath()) def refreshListViewEntries(self): if self.currentDir is None: return entries = self.directoryLister.listEntries(self.currentDir) sortedEntries = self.sortingParser.getSortedList(entries) self.model.setStringList(sortedEntries) def onCreateNewEntry(self): def onNewEntryCommited(editedLine: QLineEdit): self.listView.itemDelegate().commitData.disconnect( onNewEntryCommited) try: if editedLine.text() == self.newEntryText: raise Exception() normalizedName = self.itemNameNormalizer.normalizeName( editedLine.text()) self.entryProvider.addEntry(normalizedName) self.refreshListViewEntries() self.listView.setCurrentIndex(index) except Exception: print(f"Error adding entry {editedLine.text()}", file=sys.stderr) self.model.removeRow(self.model.rowCount() - 1) if not self.model.insertRow(self.model.rowCount()): return self.listView.itemDelegate().commitData.connect(onNewEntryCommited) index = self.model.index(self.model.rowCount() - 1, 0) self.model.setData(index, self.newEntryText) self.listView.edit(index) pass
class OpdsDialog(QDialog): def __init__(self, gui, icon, do_user_config): QDialog.__init__(self, gui) self.gui = gui self.do_user_config = do_user_config self.db = gui.current_db.new_api # The model for the book list self.model = OpdsBooksModel(None, self.dummy_books(), self.db) self.searchproxymodel = QSortFilterProxyModel(self) self.searchproxymodel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.searchproxymodel.setFilterKeyColumn(-1) self.searchproxymodel.setSourceModel(self.model) self.layout = QGridLayout() self.setLayout(self.layout) self.setWindowTitle('OPDS Client') self.setWindowIcon(icon) labelColumnWidths = [] self.opdsUrlLabel = QLabel('OPDS URL: ') self.layout.addWidget(self.opdsUrlLabel, 0, 0) labelColumnWidths.append(self.layout.itemAtPosition(0, 0).sizeHint().width()) config.convertSingleStringOpdsUrlPreferenceToListOfStringsPreference() self.opdsUrlEditor = QComboBox(self) self.opdsUrlEditor.activated.connect(self.opdsUrlEditorActivated) self.opdsUrlEditor.addItems(prefs['opds_url']) self.opdsUrlEditor.setEditable(True) self.opdsUrlEditor.setInsertPolicy(QComboBox.InsertAtTop) self.layout.addWidget(self.opdsUrlEditor, 0, 1, 1, 3) self.opdsUrlLabel.setBuddy(self.opdsUrlEditor) buttonColumnNumber = 7 buttonColumnWidths = [] self.about_button = QPushButton('About', self) self.about_button.setAutoDefault(False) self.about_button.clicked.connect(self.about) self.layout.addWidget(self.about_button, 0, buttonColumnNumber) buttonColumnWidths.append(self.layout.itemAtPosition(0, buttonColumnNumber).sizeHint().width()) # Initially download the catalogs found in the root catalog of the URL # selected at startup. Fail quietly on failing to open the URL catalogsTuple = self.model.downloadOpdsRootCatalog(self.gui, self.opdsUrlEditor.currentText(), False) print catalogsTuple firstCatalogTitle = catalogsTuple[0] self.currentOpdsCatalogs = catalogsTuple[1] # A dictionary of title->feedURL self.opdsCatalogSelectorLabel = QLabel('OPDS Catalog:') self.layout.addWidget(self.opdsCatalogSelectorLabel, 1, 0) labelColumnWidths.append(self.layout.itemAtPosition(1, 0).sizeHint().width()) self.opdsCatalogSelector = QComboBox(self) self.opdsCatalogSelector.setEditable(False) self.opdsCatalogSelectorModel = QStringListModel(self.currentOpdsCatalogs.keys()) self.opdsCatalogSelector.setModel(self.opdsCatalogSelectorModel) self.opdsCatalogSelector.setCurrentText(firstCatalogTitle) self.layout.addWidget(self.opdsCatalogSelector, 1, 1, 1, 3) self.download_opds_button = QPushButton('Download OPDS', self) self.download_opds_button.setAutoDefault(False) self.download_opds_button.clicked.connect(self.download_opds) self.layout.addWidget(self.download_opds_button, 1, buttonColumnNumber) buttonColumnWidths.append(self.layout.itemAtPosition(1, buttonColumnNumber).sizeHint().width()) # Search GUI self.searchEditor = QLineEdit(self) self.searchEditor.returnPressed.connect(self.searchBookList) self.layout.addWidget(self.searchEditor, 2, buttonColumnNumber - 2, 1, 2) self.searchButton = QPushButton('Search', self) self.searchButton.setAutoDefault(False) self.searchButton.clicked.connect(self.searchBookList) self.layout.addWidget(self.searchButton, 2, buttonColumnNumber) buttonColumnWidths.append(self.layout.itemAtPosition(2, buttonColumnNumber).sizeHint().width()) # The main book list self.library_view = QTableView(self) self.library_view.setAlternatingRowColors(True) self.library_view.setModel(self.searchproxymodel) self.library_view.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch) self.library_view.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch) self.library_view.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch) self.library_view.setSelectionBehavior(QAbstractItemView.SelectRows) self.resizeAllLibraryViewLinesToHeaderHeight() self.library_view.resizeColumnsToContents() self.layout.addWidget(self.library_view, 3, 0, 3, buttonColumnNumber + 1) self.hideNewsCheckbox = QCheckBox('Hide Newspapers', self) self.hideNewsCheckbox.clicked.connect(self.setHideNewspapers) self.hideNewsCheckbox.setChecked(prefs['hideNewspapers']) self.layout.addWidget(self.hideNewsCheckbox, 6, 0, 1, 3) self.hideBooksAlreadyInLibraryCheckbox = QCheckBox('Hide books already in library', self) self.hideBooksAlreadyInLibraryCheckbox.clicked.connect(self.setHideBooksAlreadyInLibrary) self.hideBooksAlreadyInLibraryCheckbox.setChecked(prefs['hideBooksAlreadyInLibrary']) self.layout.addWidget(self.hideBooksAlreadyInLibraryCheckbox, 7, 0, 1, 3) # Let the checkbox initial state control the filtering self.model.setFilterBooksThatAreNewspapers(self.hideNewsCheckbox.isChecked()) self.model.setFilterBooksThatAreAlreadyInLibrary(self.hideBooksAlreadyInLibraryCheckbox.isChecked()) self.downloadButton = QPushButton('Download selected books', self) self.downloadButton.setAutoDefault(False) self.downloadButton.clicked.connect(self.downloadSelectedBooks) self.layout.addWidget(self.downloadButton, 6, buttonColumnNumber) buttonColumnWidths.append(self.layout.itemAtPosition(6, buttonColumnNumber).sizeHint().width()) self.fixTimestampButton = QPushButton('Fix timestamps of selection', self) self.fixTimestampButton.setAutoDefault(False) self.fixTimestampButton.clicked.connect(self.fixBookTimestamps) self.layout.addWidget(self.fixTimestampButton, 7, buttonColumnNumber) buttonColumnWidths.append(self.layout.itemAtPosition(7, buttonColumnNumber).sizeHint().width()) # Make all columns of the grid layout the same width as the button column buttonColumnWidth = max(buttonColumnWidths) for columnNumber in range(0, buttonColumnNumber): self.layout.setColumnMinimumWidth(columnNumber, buttonColumnWidth) # Make sure the first column isn't wider than the labels it holds labelColumnWidth = max(labelColumnWidths) self.layout.setColumnMinimumWidth(0, labelColumnWidth) self.resize(self.sizeHint()) def opdsUrlEditorActivated(self, text): prefs['opds_url'] = config.saveOpdsUrlCombobox(self.opdsUrlEditor) catalogsTuple = self.model.downloadOpdsRootCatalog(self.gui, self.opdsUrlEditor.currentText(), True) firstCatalogTitle = catalogsTuple[0] self.currentOpdsCatalogs = catalogsTuple[1] # A dictionary of title->feedURL self.opdsCatalogSelectorModel.setStringList(self.currentOpdsCatalogs.keys()) self.opdsCatalogSelector.setCurrentText(firstCatalogTitle) def setHideNewspapers(self, checked): prefs['hideNewspapers'] = checked self.model.setFilterBooksThatAreNewspapers(checked) self.resizeAllLibraryViewLinesToHeaderHeight() def setHideBooksAlreadyInLibrary(self, checked): prefs['hideBooksAlreadyInLibrary'] = checked self.model.setFilterBooksThatAreAlreadyInLibrary(checked) self.resizeAllLibraryViewLinesToHeaderHeight() def searchBookList(self): searchString = self.searchEditor.text() print "starting book list search for: %s" % searchString self.searchproxymodel.setFilterFixedString(searchString) def about(self): text = get_resources('about.txt') QMessageBox.about(self, 'About the OPDS Client plugin', text.decode('utf-8')) def download_opds(self): opdsCatalogUrl = self.currentOpdsCatalogs.get(self.opdsCatalogSelector.currentText(), None) if opdsCatalogUrl is None: # Just give up quietly return self.model.downloadOpdsCatalog(self.gui, opdsCatalogUrl) if self.model.isCalibreOpdsServer(): self.model.downloadMetadataUsingCalibreRestApi(self.opdsUrlEditor.currentText()) self.library_view.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch) self.library_view.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch) self.library_view.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch) self.resizeAllLibraryViewLinesToHeaderHeight() self.resize(self.sizeHint()) def config(self): self.do_user_config(parent=self) def downloadSelectedBooks(self): selectionmodel = self.library_view.selectionModel() if selectionmodel.hasSelection(): rows = selectionmodel.selectedRows() for row in reversed(rows): book = row.data(Qt.UserRole) self.downloadBook(book) def downloadBook(self, book): if len(book.links) > 0: self.gui.download_ebook(book.links[0]) def fixBookTimestamps(self): selectionmodel = self.library_view.selectionModel() if selectionmodel.hasSelection(): rows = selectionmodel.selectedRows() for row in reversed(rows): book = row.data(Qt.UserRole) self.fixBookTimestamp(book) def fixBookTimestamp(self, book): bookTimestamp = book.timestamp identicalBookIds = self.findIdenticalBooksForBooksWithMultipleAuthors(book) bookIdToValMap = {} for identicalBookId in identicalBookIds: bookIdToValMap[identicalBookId] = bookTimestamp if len(bookIdToValMap) < 1: print "Failed to set timestamp of book: %s" % book self.db.set_field('timestamp', bookIdToValMap) def findIdenticalBooksForBooksWithMultipleAuthors(self, book): authorsList = book.authors if len(authorsList) < 2: return self.db.find_identical_books(book) # Try matching the authors one by one identicalBookIds = set() for author in authorsList: singleAuthorBook = Metadata(book.title, [author]) singleAuthorIdenticalBookIds = self.db.find_identical_books(singleAuthorBook) identicalBookIds = identicalBookIds.union(singleAuthorIdenticalBookIds) return identicalBookIds def dummy_books(self): dummy_author = ' ' * 40 dummy_title = ' ' * 60 books_list = [] for line in range (1, 10): book = DynamicBook() book.author = dummy_author book.title = dummy_title book.updated = datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S+00:00') book.id = '' books_list.append(book) return books_list def resizeAllLibraryViewLinesToHeaderHeight(self): rowHeight = self.library_view.horizontalHeader().height() for rowNumber in range (0, self.library_view.model().rowCount()): self.library_view.setRowHeight(rowNumber, rowHeight)
class LocationBar(LineEdit): # ProgressStyle _ProgressFilled = 0 _ProgressBottom = 1 _ProgressTop = 2 class LoadAction: # enum Type Invalid = 0 Search = 1 Bookmark = 2 Url = 3 def __init__(self): self.type = self.Invalid self.searchEngine = SearchEngine() self.bookmark = None # BookmarkItem self.loadRequest = LoadRequest() def __init__(self, parent=None): super().__init__(parent) self._completer = None # LocationCompleter self._domainCompleterModel = None # QStringListModel self._bookmarkIcon = None # BookmarksIcon self._goIcon = None # GoIcon self._siteIcon = None # SiteIcon self._autofillIcon = None # AutoFillIcon self._window = None # BrowserWindow self._webView = None # TabbedWebView self._holdingAlt = False self._oldTextLength = 0 self._currentTextLength = 0 self._loadProgress = 0 self._progressVisible = True self._progressStyle = 0 # ProgressStyle self._progressColor = QColor() self._progressTimer = None # QTimer self.setObjectName('locationbar') self.setDragEnabled(True) # Disable KDE QLineEdit transitions, it breaks with setText() && home() self._bookmarkIcon = BookmarksIcon(self) self._goIcon = GoIcon(self) self._siteIcon = SiteIcon(self) self._autofillIcon = AutoFillIcon(self) down = DownIcon(self) self.addWidget(self._siteIcon, LineEdit.LeftSide) self.addWidget(self._autofillIcon, LineEdit.RightSide) self.addWidget(self._bookmarkIcon, LineEdit.RightSide) self.addWidget(self._goIcon, LineEdit.RightSide) self.addWidget(down, LineEdit.RightSide) self._completer = LocationCompleter(self) self._completer.setLocationBar(self) self._completer.showCompletion.connect(self._showCompletion) self._completer.showDomainCompletion.connect( self._showDomainCompletion) self._completer.clearCompletion.connect(self._clearCompletion) self._completer.loadRequested.connect(self.loadRequest) self._completer.popupClosed.connect(self._updateSiteIcon) self._domainCompleterModel = QStringListModel(self) domainCompleter = QCompleter(self) domainCompleter.setCompletionMode(QCompleter.InlineCompletion) domainCompleter.setModel(self._domainCompleterModel) self.setCompleter(domainCompleter) self._progressTimer = QTimer(self) self._progressTimer.setInterval(700) self._progressTimer.setSingleShot(True) self._progressTimer.timeout.connect(self._hideProgress) self.editAction(self.PasteAndGo).setText(_('Paste And &Go')) self.editAction(self.PasteAndGo).setIcon(QIcon.fromTheme('edit-paste')) self.editAction(self.PasteAndGo).triggered.connect(self._pasteAndGo) self.textEdited.connect(self._textEdited) self._goIcon.clicked.connect(self._requestLoadUrl) down.clicked.connect(self._completer.showMostVisited) # TODO: #gVar.app.searchEnginesManager().activeEngineChanged.connect(self._updatePlaceHolderText) #gVar.app.searchEnginesManager().defaultEngineChanged.connect(self._updatePlaceHolderText) self._loadSettings() self._updateSiteIcon() # Hide icons by default self._goIcon.setVisible(gVar.appSettings.alwaysShowGoIcon) self._autofillIcon.hide() QTimer.singleShot(0, self._updatePlaceHolderText) def browserWindow(self): ''' @note: BrowserWindow can be null! @return: BrowserWindow ''' return self._window def setBrowserWindow(self, window): self._window = window self._completer.setMainWindow(self._window) self._siteIcon.setBrowserWindow(self._window) def webView(self): ''' @return TabbedWebView ''' return self._webView def setWebView(self, view): ''' @param: view TabbedWebView -> WebView -> QWebEngineView ''' self._webView = view self._bookmarkIcon.setWebView(self._webView) self._siteIcon.setWebView(self._webView) self._autofillIcon.setWebView(self._webView) self._webView.loadStarted.connect(self._loadStarted) self._webView.loadProgress.connect(self.__loadProgress) self._webView.loadFinished.connect(self._loadFinished) self._webView.urlChanged.connect(self.showUrl) self._webView.privacyChanged.connect(self._setPrivacyState) @classmethod def convertUrlToText(cls, url): ''' @note: It was most probably entered by user, so don't urlencode it Also don't urlencode JavaScript code @param: url QUrl @return: QString ''' if not url.scheme() or url.scheme() == 'javascript': return QUrl.fromPercentEncoding(url.toEncoded()) stringUrl = gVar.appTools.urlEncodeQueryString(url) if stringUrl == 'app:speeddial' or stringUrl == 'about:blank': stringUrl = '' return stringUrl @classmethod def searchEngine(cls): ''' @return: SearchEngine ''' if not gVar.appSettings.searchFromAddressBar: return SearchEngine() elif gVar.appSettings.searchWithDefaultEngine: return gVar.app.searchEnginesManager().defaultEngine() else: return gVar.app.searchEnginesManager().activeEngine() @classmethod def loadAction(cls, text): ''' @return: LoadAction ''' action = cls.LoadAction() t = text.strip() if not t: return action # Check for Search Engine shortcut firstSpacePos = t.find(' ') if gVar.appSettings.searchFromAddressBar and firstSpacePos != -1: shortcut = t[:firstSpacePos] searchedString = t[firstSpacePos:].strip() en = gVar.app.searchEnginesManager().engineForShortcut(shortcut) if en.isValid(): action.type = cls.LoadAction.Search action.searchEngine = en url = gVar.app.searchEnginesManager().searchResult( en, searchedString) action.loadRequest = LoadRequest(url) return action # Check for Bookmark keyword items = gVar.app.bookmarks().searchKeyword(t) if items: item = items[0] action.type = cls.LoadAction.Bookmark action.bookmark = item action.loadRequest.setUrl(item.url()) return action if not gVar.appSettings.searchFromAddressBar: guessedUrl = QUrl.fromUserInput(t) if guessedUrl.isValid(): action.type = cls.LoadAction.Url action.loadRequest = LoadRequest(guessedUrl) return action # Check for one word search if t != 'localhost' \ and gVar.appTools.containsSpace(t) \ and '.' not in t \ and ':' not in t \ and '/' not in t: action.type = cls.LoadAction.Search action.searchEngine = cls.searchEngine() url = gVar.app.searchEnginesManager().searchResult( cls.searchEngine(), t) action.loadRequest = LoadRequest(url) return action # Otherwise load as url guessedUrl = QUrl.fromUserInput(t) if guessedUrl.isValid(): # Always allow javascript: to be loaded forceLoad = guessedUrl.scheme() == 'javascript' # Only allow spaces in query urlRaw = guessedUrl.toString(QUrl.RemoveQuery) if forceLoad or not gVar.appTools.containsSpace(t) or \ not gVar.appTools.contiansSpace(urlRaw): # Only allow supported schemes if forceLoad or guessedUrl.scheme( ) in WebPage.supportedSchemes(): action.type = cls.LoadAction.Url action.loadRequest = LoadRequest(guessedUrl) return action # Search when creating url failed action.type = cls.LoadAction.Search action.searchEngine = cls.searchEngine() url = gVar.app.searchEnginesManager().searchResult( cls.searchEngine(), t) action.loadRequest = LoadRequest(url) return action # public Q_SLOTS def setText(self, text): # TODO: ? self._oldTextLength = len(text) self._currentTextLength = self._oldTextLength super().setText(text) self._refreshTextFormat() def showUrl(self, url): if self.hasFocus() or url.isEmpty(): return stringUrl = self.convertUrlToText(url) if self.text() == stringUrl: self.home(False) self._refreshTextFormat() return # Set converted url as text self.setText(stringUrl) # Move cursor to the start self.home(False) self._bookmarkIcon.checkBookmark(url) def loadRequest(self, request): ''' @param: request LoadRequest ''' if not self._webView.webTab().isRestored(): return urlString = self.convertUrlToText(request.url()) self._completer.closePopup() self._webView.setFocus() if urlString != self.text(): self.setText(urlString) self._webView.userLoadAction(request) # private Q_SLOTS def _textEdited(self, text): self._oldTextLength = self._currentTextLength self._currentTextLength = len(text) if text: self._completer.complete(text) icon = QIcon.fromTheme('edit-find', QIcon(':/icons/menu/search-icon.svg')) self._siteIcon.setIcon(icon) else: self._completer.closePopup() self._setGoIconVisible(True) def _requestLoadUrl(self): req = self.loadAction(self.text()).loadRequest self.loadRequest(req) self._updateSiteIcon() def _pasteAndGo(self): self.clear() self.paste() self._requestLoadUrl() def _updateSiteIcon(self): if self._completer.isVisible(): self._siteIcon.setIcon( QIcon.fromTheme('edit-find', QIcon(':/icons/menu/search-icon.svg'))) else: icon = IconProvider.emptyWebIcon() secured = self.property('secured') if secured: icon = QIcon.fromTheme('document-encrypted', icon) self._siteIcon.setIcon(icon.pixmap(16)) def _updatePlaceHolderText(self): if gVar.appSettings.searchFromAddressBar: self.setPlaceholderText( _('Enter address or search with %s') % self.searchEngine().name) else: self.setPlaceholderText(_('Enter address')) def _setPrivacyState(self, state): ''' @param: state bool ''' self._siteIcon.setProperty('secured', state) self._siteIcon.style().unpolish(self._siteIcon) self._siteIcon.style().polish(self._siteIcon) self.setProperty('secured', state) self.style().unpolish(self) self.style().polish(self) self._updateSiteIcon() def _setGoIconVisible(self, state): ''' @param: state bool ''' if state: self._bookmarkIcon.hide() self._goIcon.show() else: self._bookmarkIcon.show() if not gVar.appSettings.alwaysShowGoIcon: self._goIcon.hide() self.updateTextMargins() def _showCompletion(self, completion, completeDomain): ''' @param: completion QString @param: completeDomain bool ''' super().setText(completion) # Move cursor to the end self.end(False) if completeDomain: self.completer().complete() self._updateSiteIcon() def _showDomainCompletion(self, completion): ''' @param: completion QString ''' self._domainCompleterModel.setStringList([completion]) # We need to manually force the completion because model is updated # asynchronously, But only force completion when theuser actually added # new text if completion and self._oldTextLength < self._currentTextLength: self.completer().complete() def _clearCompletion(self): self._webView.setFocus() self.showUrl(self._webView.url()) def _loadStarted(self): self._progressVisible = True self._progressTimer.start() self._autofillIcon.hide() def __loadProgress(self, progress): ''' @param: progress int ''' if gVar.appSettings.showLoadingProgress: self._loadProgress = progress self.update() def _loadFinished(self): if gVar.appSettings.showLoadingProgress: self._progressTimer.start() page = self._webView.page() if isinstance(page, WebPage) and page.autoFillUsernames(): self._autofillIcon.setUsernames(page.autoFillUsernames()) self._autofillIcon.show() def _hideProgress(self): if gVar.appSettings.showLoadingProgress: self._progressVisible = False self.update() def _loadSettings(self): settings = Settings() settings.beginGroup('AddressBar') self._progressStyle = settings.value('ProgressStyle', 0) customColor = settings.value('UseCustomProgressColor', False) if customColor: self._progressColor = settings.value( 'CustomProgressColor', self.palette().color(QPalette.Highlight)) else: self._progressColor = QColor() settings.endGroup() # private: # override def contextMenuEvent(self, event): ''' @param: event QContextMenuEvent ''' menu = self._createContextMenu() menu.setAttribute(Qt.WA_DeleteOnClose) # Prevent choosing first option with double rightclick pos = event.globalPos() pos.setY(pos.y() + 1) menu.popup(pos) # override def showEvent(self, event): ''' @param: event QShowEvent ''' super().showEvent(event) self._refreshTextFormat() # override def focusInEvent(self, event): ''' @param: event QFocusEvent ''' if self._webView: stringUrl = self.convertUrlToText(self._webView.url()) # Text has been edited, let's show go button if stringUrl != self.text(): self._setGoIconVisible(True) self.clearTextFormat() super().focusInEvent(event) if self._window and Settings().value( 'Browser-View-Settings/instantBookmarksToolbar', type=bool): self._window.bookmarksToolbar().show() # override def focusOutEvent(self, event): ''' @param: event QFocusEvent ''' # Context menu or completer popup were opened # Let's block focusOutEvent to trick QLineEdit and point cursor properly if event.reason() == Qt.PopupFocusReason: # TODO: ? return super().focusOutEvent(event) self._setGoIconVisible(False) if not self.text().strip(): self.clear() self._refreshTextFormat() if self._window and Settings().value( 'Browser-View-Settings/instantBookmarksToolbar', type=bool): self._window.bookmarksToolbar().hide() # override def keyPressEvent(self, event): # noqa C901 ''' @param: event QKeyEvent ''' evtKey = event.key() evtModifiers = event.modifiers() if evtKey == Qt.Key_V: if event.modifiers() == (Qt.ControlModifier | Qt.ShiftModifier): self._pasteAndGo() event.accept() return elif evtKey == Qt.Key_Down: self._completer.complete(self.text()) elif evtKey == Qt.Key_Left: self._completer.closePopup() elif evtKey == Qt.Key_Escape: self._webView.setFocus() self.showUrl(self._webView.url()) event.accept() elif evtKey == Qt.Key_Alt: self._holdingAlt = True elif evtKey in (Qt.Key_Return, Qt.Key_Enter): if evtModifiers == Qt.ControlModifier: if not self.text().endswith('.com'): self.setText(self.text() + '.com') self.requestLoadUrl() elif evtModifiers == Qt.AltModifier: self._completer.closePopup() if self._window: req = self.loadAction(self.text()).loadRequest self._window.tabWidget().addViewByReq(req) else: self._requestLoadUrl() self._holdingAlt = False elif evtKey in ( Qt.Key_0, Qt.Key_1, Qt.Key_2, Qt.Key_3, Qt.Key_4, Qt.Key_5, Qt.Key_6, Qt.Key_7, Qt.Key_8, Qt.Key_9, ): if evtModifiers & Qt.AltModifier or evtModifiers & Qt.ControlModifier: event.ignore() self._holdingAlt = False return else: self._holdingAlt = False super().keyPressEvent(event) # override def dropEvent(self, event): ''' @param: event QDropEvent ''' if event.mimeData().hasUrls(): dropUrl = event.mimeData().urls()[0] if WebView.isUrlValid(dropUrl): self.setText(dropUrl.toString()) self.loadRequest(LoadRequest(dropUrl)) event = QFocusEvent(QFocusEvent.FocusOut) super().focusOutEvent(event) return elif event.mimeData().hasText(): dropText = event.mimeData().text().strip() dropUrl = QUrl(dropText) if WebView.isUrlValid(dropUrl): self.setText(dropUrl.toString()) self.loadRequest(LoadRequest(dropUrl)) event = QFocusEvent(QFocusEvent.FocusOut) super().focusOutEvent(event) return else: self.setText(dropText) self.setFocus() return super().dropEvent(event) # override def paintEvent(self, event): ''' @param: event QPaintEvent ''' super().paintEvent(event) # Show loading progress if gVar.appSettings.showLoadingProgress and self._progressVisible: option = QStyleOptionFrame() self.initStyleOption(option) lm, tm, rm, bm = self.getTextMargins() contentsRect = self.style().subElementRect( QStyle.SE_LineEditContents, option, self) contentsRect.adjust(lm, tm, -rm, -bm) bg = QColor(self._progressColor) if not bg.isValid() or bg.alpha() == 0: pal = self.palette() bg = Colors.mid(pal.color(QPalette.Base), pal.color(QPalette.Text), self._progressStyle > 0 and 4 or 8, 1) p = QPainter(self) p.setBrush(QBrush(bg)) # We are painting over text, make sure the text stays visible p.setOpacity(0.5) outlinePen = QPen(bg.darker(110), 0.8) p.setPen(outlinePen) if self._progressStyle == self._ProgressFilled: bar = contentsRect.adjusted(0, 1, 0, -1) bar.setWidth(int(bar.width() * self._loadProgress / 100)) roundness = bar.height() / 4.0 p.drawRoundedRect(bar, roundness, roundness) elif self._progressStyle == self._ProgressBottom: outlinePen.setWidthF(0.3) outlinePen.setColor(outlinePen.color().darker(130)) p.setPen(outlinePen) bar = QRect(contentsRect.x(), contentsRect.bottom() - 3, contentsRect.width() * self._loadProgress / 100.0, 3) p.drawRoundedRect(bar, 1, 1) elif self._progressStyle == self._ProgressTop: outlinePen.setWidthF(0.3) outlinePen.setColor(outlinePen.color().darker(130)) p.setPen(outlinePen) bar = QRect(contentsRect.x(), contentsRect.top() + 1, contentsRect.width() * self._loadProgress / 100.0, 3) p.drawRoundedRect(bar, 1, 1) def _refreshTextFormat(self): if not self._webView: return textFormat = [] # typedef QList<QTextLayout::FormatRange> if self._webView.url().isEmpty(): hostName = QUrl(self.text()).host() else: hostName = self._webView.url().host() if hostName: hostPos = self.text().find(hostName) if hostPos > 0: format_ = QTextCharFormat() palette = self.palette() color = Colors.mid(palette.color(QPalette.Base), palette.color(QPalette.Text), 1, 1) format_.setForeground(color) schemePart = QTextLayout.FormatRange() schemePart.start = 0 schemePart.length = hostPos schemePart.format = format_ hostPart = QTextLayout.FormatRange() hostPart.start = hostPos hostPart.length = len(hostName) remainingPart = QTextLayout.FormatRange() remainingPart.start = hostPos + len(hostName) remainingPart.length = len(self.text()) - remainingPart.start remainingPart.format = format_ textFormat.append(schemePart) textFormat.append(hostPart) textFormat.append(remainingPart) self.setTextFormat(textFormat)