I use PyQt5.QtWebEngineWidgets to display a PDF and I'm trying to get the selected text.
Using hasSelection() and selectedText() works on HTML files, but it fails when the web view displays a PDF file. Any idea how to copy the selected text (apart from using CTRL+C)?
from PyQt5.QtCore import QUrl
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QMenu
from PyQt5.QtWebEngineWidgets import QWebEngineSettings, QWebEngineView
class Browser(QWebEngineView):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def get_selection(self):
if self.hasSelection():
print(self.selectedText())
else:
print('No text selected')
def contextMenuEvent(self, event):
self.menu = QMenu()
get_selection = self.menu.addAction('Copy')
get_selection.triggered.connect(self.get_selection)
self.menu.popup(event.globalPos())
class MainWindow(QMainWindow):
def __init__(self):
super(QMainWindow, self).__init__()
self.setWindowTitle("PDF Viewer")
self.setGeometry(0, 28, 1000, 750)
self.webView = QWebEngineView()
self.webView = Browser()
self.webView.settings().setAttribute(QWebEngineSettings.PluginsEnabled, True)
self.webView.settings().setAttribute(QWebEngineSettings.PdfViewerEnabled, True)
self.setCentralWidget(self.webView)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
win = MainWindow()
win.show()
win.webView.setUrl(QUrl.fromLocalFile("d:/test.pdf"))
sys.exit(app.exec_())
Related
I am using the code below. Mainwindow's state is preserved but qtreeviw's is not.
import sys
from PyQt5.QtCore import QSettings, QByteArray
from PyQt5.QtWidgets import QApplication, QTreeView, QFileSystemModel, QVBoxLayout, QWidget
from PyQt5 import QtCore
class MyApp(QWidget):
def __init__(self):
super().__init__()
self.tree = QTreeView()
self.settings = QSettings('testorg', 'testapp')
try:
self.tree.header().restoreState(self.settings.value("estado_header"))
self.resize(self.settings.value('window size'))
self.move(self.settings.value('window position'))
except:
pass
self.model = QFileSystemModel()
self.model.setRootPath(r"C:\Users\dan-s>")
self.tree.setModel(self.model)
#self.tree.header().restoreState(self.settings.value("estado_header"))
#self.tree.collapseAll()
layout = QVBoxLayout()
layout.addWidget(self.tree)
self.setLayout(layout)
def closeEvent(self, event):
self.settings.setValue('window size', self.size())
self.settings.setValue('window position', self.pos())
state = self.tree.header().saveState()
self.settings.setValue('estado_header', state)
super().closeEvent(event)
app = QApplication(sys.argv)
demo = MyApp()
demo.show()
sys.exit(app.exec_())
I've tried other ways but I can't solve it.
I need to execute a method whenever the user clicks the down arrow on the combo box. I've tried the signals listed in the documentations but none of the worked.
from PyQt5.QtWidgets import *
import sys
class Window(QWidget):
def __init__(self):
super().__init__()
self.combo = QComboBox(self)
self.combo.signal.connect(self.mymethod)
self.show()
def mymethod(self):
print('hello world')
app = QApplication(sys.argv)
win = Window()
sys.exit(app.exec_())
There is no signal that is emitted when the down arrow is pressed but you can create override the mousePressEvent method and verify that this element was pressed:
import sys
from PyQt5.QtCore import pyqtSignal, Qt
from PyQt5.QtWidgets import (
QApplication,
QComboBox,
QStyle,
QStyleOptionComboBox,
QVBoxLayout,
QWidget,
)
class ComboBox(QComboBox):
arrowClicked = pyqtSignal()
def mousePressEvent(self, event):
super().mousePressEvent(event)
opt = QStyleOptionComboBox()
self.initStyleOption(opt)
sc = self.style().hitTestComplexControl(
QStyle.CC_ComboBox, opt, event.pos(), self
)
if sc == QStyle.SC_ComboBoxArrow:
self.arrowClicked.emit()
class Window(QWidget):
def __init__(self):
super().__init__()
self.combo = ComboBox()
self.combo.arrowClicked.connect(self.mymethod)
lay = QVBoxLayout(self)
lay.addWidget(self.combo)
lay.setAlignment(Qt.AlignTop)
def mymethod(self):
print("hello world")
if __name__ == "__main__":
app = QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec_())
I have a simple application where the back and forward buttons are enabled/disabled based on the history of the visited web pages. For this, I have found the canGoForward and canGoBack functions
of the QWebEngineHistory. However, the functions return True only after there are at least three items in the history. Normally, browsers work right after visiting the second different page.
Is this supposed to be working like that? I there any way to change it to the 2 web pages? I have looked at the QWebEngineSettings, but there is nothing related to this.
Here is a working example:
#!/usr/bin/python
import sys
from PyQt5.QtCore import QUrl
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import (QApplication, QLineEdit, QMainWindow,
QPushButton, QToolBar)
from PyQt5.QtWebEngineWidgets import QWebEnginePage, QWebEngineView
class Example(QMainWindow):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.toolBar = QToolBar(self)
self.addToolBar(self.toolBar)
self.backBtn = QPushButton(self)
self.backBtn.setEnabled(False)
self.backBtn.setIcon(QIcon(':/qt-project.org/styles/commonstyle/images/left-32.png'))
# self.backBtn.setIcon(QIcon('stock_left.png'))
self.backBtn.clicked.connect(self.back)
self.toolBar.addWidget(self.backBtn)
self.forBtn = QPushButton(self)
self.forBtn.setEnabled(False)
# self.forBtn.setIcon(QIcon('stock_right.png'))
self.forBtn.setIcon(QIcon(':/qt-project.org/styles/commonstyle/images/right-32.png'))
self.forBtn.clicked.connect(self.forward)
self.toolBar.addWidget(self.forBtn)
self.address = QLineEdit(self)
self.address.returnPressed.connect(self.load)
self.toolBar.addWidget(self.address)
self.webEngineView = QWebEngineView(self)
self.setCentralWidget(self.webEngineView)
self.webEngineView.page().urlChanged.connect(self.onLoadFinished)
print(self.webEngineView.history().backItem().url())
print(self.webEngineView.history().forwardItem().url())
self.setGeometry(300, 300, 500, 400)
self.setWindowTitle('QWebEnginePage')
self.show()
# self.webEngineView.page().urlChanged.connect(self.urlChanged)
def onLoadFinished(self):
print(dir(self.webEngineView.history()))
print('load finished')
# print(self.webEngineView.history().backItem().url())
# print(self.webEngineView.history().forwardItem().url())
# print(self.webEngineView.history().backItem())
# print(self.webEngineView.history().forwardItem())
# print(self.webEngineView.history().count())
# print(self.webEngineView.history().items())
# print(self.webEngineView.history().canGoForward())
# print(self.webEngineView.history().canGoBack())
if self.webEngineView.history().canGoBack():
self.backBtn.setEnabled(True)
else:
self.backBtn.setEnabled(False)
if self.webEngineView.history().canGoForward():
self.forBtn.setEnabled(True)
else:
self.forBtn.setEnabled(False)
def load(self):
url = QUrl.fromUserInput(self.address.text())
if url.isValid():
self.webEngineView.load(url)
def back(self):
self.webEngineView.page().triggerAction(QWebEnginePage.Back)
def forward(self):
self.webEngineView.page().triggerAction(QWebEnginePage.Forward)
def urlChanged(self, url):
self.address.setText(url.toString())
def main():
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I'm making a simple browser with a search box in PyQt5. This is what I've written:
import PyQt5
from PyQt5.QtCore import QUrl
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QAction, QLineEdit, QMessageBox, QMainWindow
from PyQt5.QtWebKitWidgets import QWebView , QWebPage
from PyQt5.QtWebKit import QWebSettings
from PyQt5.QtNetwork import *
import sys
from optparse import OptionParser
class App(QMainWindow):
def initiate(self):
super().initiate()
self.InitUI()
def initUI(self):
self.setWindowTitle('Browser')
self.setGeometry(100, 200, 1000, 2000)
self.searchbox = QLineEdit(self)
self.searchbox.move(20, 20)
self.searchbox.resize(1500,40)
self.go = QPushButton('Go', self)
self.go.move(1810, 20)
self.go.connect(self.gourl)
self.show()
def gourl(self):
url = self.searchbox.text()
class Browser(QWebView):
def __init__(self):
self.view = QWebView.__init__(self)
self.setWindowTitle('Loading...')
self.titleChanged.connect(self.adjustTitle)
def load(self,url):
self.setUrl(QUrl(url))
App.searchbox.setText(url)
def adjustTitle(self):
self.setWindowTitle(self.title())
app = QApplication(sys.argv)
view = Browser()
box = App()
box.show()
view.show()
view.load("https://duckduckgo.com")
app.exec_()
The browser part loads, but the textbox doesn't show. Python also throws this error:
Traceback (most recent call last):
File "C:\Users\Sid\Desktop\browser.py", line 43, in <module>
view.load("https://duckduckgo.com")
File "C:\Users\Sid\Desktop\browser.py", line 34, in load
App.searchbox.setText(url)
AttributeError: type object 'App' has no attribute 'searchbox'
I don't know why the textbox doesn't show, and I can't understand why the error is being thrown. Can someone please point out the error?
Thanks in advance. :)
What's New in Qt 5.6
https://doc.qt.io/qt-5/whatsnew56.html#removed-functionality
Porting from QtWebKit to QtWebEngine
https://doc.qt.io/qt-5/qtwebenginewidgets-qtwebkitportingguide.html
import sys
#import PyQt5
from PyQt5.QtCore import QUrl
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QAction, QLineEdit, QMessageBox, QMainWindow
#from PyQt5.QtWebKitWidgets import QWebView , QWebPage
#from PyQt5.QtWebKit import QWebSettings
from PyQt5.QtWebEngine import *
from PyQt5.QtWebEngineWidgets import *
#from PyQt5.QtNetwork import *
#from optparse import OptionParser
class App(QMainWindow):
# def initiate(self):
# super().initiate()
def __init__(self, parent=None):
super().__init__(parent)
self.initUI() # - InitUI -> + initUI
def initUI(self):
self.setWindowTitle('Browser')
self.setGeometry(100, 200, 500, 400)
self.searchbox = QLineEdit("https://stackoverflow.com/questions/57841281/not-sure-why-textbox-isnt-showing", self)
self.searchbox.move(20, 20)
self.searchbox.resize(460,40)
self.go = QPushButton('Go', self)
self.go.move(370, 100)
self.go.clicked.connect(self.gourl) # clicked
self.show()
def gourl(self):
url = self.searchbox.text()
print(f"url = {url}")
self.webview = Browser()
self.webview.load(QUrl(url))
self.webview.show()
class Browser(QWebEngineView): #(QWebView):
windowList = []
def createWindow(self, QWebEnginePage_WebWindowType):
new_webview = Browser()
new_window = App()
new_window.setCentralWidget(new_webview)
#new_window.show()
self.windowList.append(new_window)
return new_webview
"""
def __init__(self, parent=None):
super().__init__(parent)
# self.view = QWebView.__init__(self)
self.setWindowTitle('Loading...')
self.titleChanged.connect(self.adjustTitle)
def load(self,url):
self.setUrl(QUrl(url))
App.searchbox.setText(url)
def adjustTitle(self):
self.setWindowTitle(self.title())
"""
if __name__ == "__main__":
app = QApplication(sys.argv)
# view = Browser()
box = App()
box.show()
# view.show()
# view.load("https://duckduckgo.com")
sys.exit(app.exec_())
Update
without a new window?? if possible
import sys
from PyQt5.QtCore import QUrl
from PyQt5.QtWidgets import (QApplication, QWidget, QPushButton, QAction, QLineEdit,
QMessageBox, QMainWindow, QGridLayout)
from PyQt5.QtWebEngine import *
from PyQt5.QtWebEngineWidgets import *
class App(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
centralWidget = QWidget()
self.setCentralWidget(centralWidget)
self.searchbox = QLineEdit("https://stackoverflow.com/questions/57841281/not-sure-why-textbox-isnt-showing", self)
self.go = QPushButton('Go', self)
self.go.clicked.connect(self.gourl)
self.webview = Browser()
self.grid = QGridLayout(centralWidget)
self.grid.addWidget(self.webview, 0, 0, 1, 2)
self.grid.addWidget(self.searchbox, 1, 0)
self.grid.addWidget(self.go, 1, 1)
def gourl(self):
url = self.searchbox.text()
self.webview.load(QUrl(url))
class Browser(QWebEngineView): #(QWebView):
windowList = []
def createWindow(self, QWebEnginePage_WebWindowType):
new_webview = Browser()
new_window = App()
new_window.setCentralWidget(new_webview)
#new_window.show()
self.windowList.append(new_window)
return new_webview
if __name__ == "__main__":
app = QApplication(sys.argv)
box = App()
box.setWindowTitle('Browser')
box.resize(600, 500)
box.show()
sys.exit(app.exec_())
In order to track progress, this is the third question about practicing with different classes in PyQt5 .Here are the links to my previous questions:opening a new window, Open a file from main window to a new window in PyQt5 (in different files).
I'm trying to work with two classes, one with one button and when it's pressed it will load a file and show the text in a QTextEdit in other class.
In the first questions I was suggested that as an alternative to work with more classes, they can inherit from QMainWindow so I looked for more info for doing this: PyQt class inheritance
The second question code did worked but it would show both windows at the same time, so this question: PyQt: How to hide QMainWindow guided me to write this code (I attatch this here because it's a little bit different from the one in the link, plus I apply what it says in the answer):
import sys, os
from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton
class Dialog_02(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Dialog_02, self).__init__(parent, QtCore.Qt.Window)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
myBoxLayout = QVBoxLayout()
Button_02 = QPushButton ("Show Dialog 01")
myBoxLayout.addWidget(Button_02)
self.setLayout(myBoxLayout)
self.setWindowTitle('Dialog 02')
Button_02.clicked.connect(self.closeAndReturn)
def closeAndReturn(self):
self.close()
self.parent().show()
class Dialog_01(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Dialog_01, self).__init__()
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
myBoxLayout = QVBoxLayout()
Button_01 = QPushButton ("Show Dialog 02")
myBoxLayout.addWidget(Button_01)
self.setLayout(myBoxLayout)
self.setWindowTitle('Dialog 01')
Button_01.clicked.connect(self.callAnotherQMainWindow)
def callAnotherQMainWindow(self):
self.hide()
self.dialog_02 = Dialog_02(self)
self.dialog_02.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
dialog_1 = Dialog_01()
dialog_1.show()
sys.exit(app.exec_())
In this code I'm not inheriting, but it works fine.
The issue is that when I try to follow the same syntax in the original question code, it won't run, I'm not sure I'm getting inheritances fine.
import sys
import os
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtWidgets import QPushButton, QVBoxLayout, QTextEdit, QHBoxLayout, QLabel, QMainWindow, QAction, QFileDialog
class SecondWindow(QWidget):
def __init__(self, Window):
super(SecondWindow, self).__init__(parent, QtCore.Qt.Window)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.text = QTextEdit(self)
self.btn_return= QPushButton("Return")
self.init_ui()
def init_ui(self):
v_layout = QVBoxLayout(self)
v_layout.addWidget(self.text)
v_layout.addWidget(self.btn_return)
self.setLayout(v_layout)
self.setWindowTitle('Opened Text')
self.btn_return.clicked.connect(self.closeAndReturn)
def closeAndReturn(self):
self.close()
self.parent().show()
class Window(QMainWindow):
textChanged = QtCore.pyqtSignal(str)
def __init__(self, *args):
super(Window, self).__init__()
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.img = QLabel()
self.load_file= QPushButton('Load')
self.width = 400
self.height = 150
self.init_ui()
def init_ui(self):
self.img.setPixmap(QtGui.QPixmap("someimage.png"))
h_layout = QHBoxLayout()
v_layout = QVBoxLayout()
h_final = QHBoxLayout()
h_layout.addWidget(self.img)
v_layout.addWidget(self.load_file)
h_final.addLayout(h_layout)
h_final.addLayout(v_layout)
self.load_file.clicked.connect(self.loadafile)
self.setLayout(h_final)
self.setWindowTitle('Main Window')
self.setGeometry(600,150,self.width,self.height)
#QtCore.pyqtSlot()
def loadafile(self):
filename = QFileDialog.getOpenFileName(self, 'Open File', os.getenv('HOME'))
with open(filename[0], 'r') as f:
file_text = f.read()
self.textChanged.emit(file_text)
self.hide()
self.dialog_02 = SecondWindow(self)
self.dialog_02.show()
def main():
app = QApplication(sys.argv)
main = Window()
s = SecondWindow(main)
main.textChanged.connect(s.text.append)
main.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
You are coupling many classes: What happens if at a moment SecondWindow does not have a parent? Well, your code will have problems and you will have to modify it a lot so that it works correctly. So first it is to design the behavior of each class, for example SecondWindow has to warn the other windows that it was clicked, it has to have a method that updates the text. Similarly, Window must notify that there is new text available.
On the other hand QMainWindow already has a predefined layout so you must create a centralwidget where you place the other widgets.
import os
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class SecondWindow(QtWidgets.QWidget):
closed = QtCore.pyqtSignal()
def __init__(self, parent=None):
super(SecondWindow, self).__init__(parent, QtCore.Qt.Window)
self.text = QtWidgets.QTextEdit()
self.btn_return= QtWidgets.QPushButton("Return")
self.init_ui()
def init_ui(self):
v_layout = QtWidgets.QVBoxLayout(self)
v_layout.addWidget(self.text)
v_layout.addWidget(self.btn_return)
self.setWindowTitle('Opened Text')
self.btn_return.clicked.connect(self.close)
self.btn_return.clicked.connect(self.closed)
#QtCore.pyqtSlot(str)
def update_text(self, text):
self.text.setText(text)
self.show()
class Window(QtWidgets.QMainWindow):
textChanged = QtCore.pyqtSignal(str)
def __init__(self, *args):
super(Window, self).__init__()
self.img = QtWidgets.QLabel()
self.load_file= QtWidgets.QPushButton('Load')
self.width = 400
self.height = 150
self.init_ui()
def init_ui(self):
self.img.setPixmap(QtGui.QPixmap("someimage.png"))
self.load_file.clicked.connect(self.loadafile)
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
h_layout = QtWidgets.QHBoxLayout(central_widget)
h_layout.addWidget(self.img)
h_layout.addWidget(self.load_file)
self.setWindowTitle('Main Window')
self.setGeometry(600,150,self.width,self.height)
#QtCore.pyqtSlot()
def loadafile(self):
filename, _ = QtWidgets.QFileDialog.getOpenFileName(self, 'Open File', os.getenv('HOME'))
if filename:
with open(filename, 'r') as f:
file_text = f.read()
self.textChanged.emit(file_text)
self.close()
def main():
app = QtWidgets.QApplication(sys.argv)
main = Window()
s = SecondWindow()
main.textChanged.connect(s.update_text)
s.closed.connect(main.show)
main.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()