I'm trying to create a print preview of a QWebEngineView but I can't get it to work.
Here's my code:
...
self.view = QWebEngineView()
...
def handle_preview(self):
dialog = QPrintPreviewDialog()
dialog.paintRequested.connect(self.view.print_)
dialog.exec_()
The code gives me this error:
AttributeError: 'QWebEngineView' object has no attribute 'print_'
The code works perfectly when I use QTextEdit. But that's not what I want. I want to use QWebEngineView.
Based on the official example: WebEngine Widgets PrintMe Example you can implement the preview using the following code.
from PyQt5.QtCore import (QCoreApplication, QEventLoop, QObject, QPointF, Qt,
QUrl, pyqtSlot)
from PyQt5.QtGui import QKeySequence, QPainter
from PyQt5.QtPrintSupport import QPrintDialog, QPrinter, QPrintPreviewDialog
from PyQt5.QtWebEngineWidgets import QWebEnginePage, QWebEngineView
from PyQt5.QtWidgets import QApplication, QDialog, QLabel, QProgressBar, QProgressDialog, QShortcut
class PrintHandler(QObject):
def __init__(self, parent = None):
super().__init__(parent)
self.m_page = None
self.m_inPrintPreview = False
def setPage(self, page):
assert not self.m_page
self.m_page = page
self.m_page.printRequested.connect(self.printPreview)
#pyqtSlot()
def print(self):
printer = QPrinter(QPrinter.HighResolution)
dialog = QPrintDialog(printer, self.m_page.view())
if dialog.exec_() != QDialog.Accepted:
return
self.printDocument(printer)
#pyqtSlot()
def printPreview(self):
if not self.m_page:
return
if self.m_inPrintPreview:
return
self.m_inPrintPreview = True
printer = QPrinter()
preview = QPrintPreviewDialog(printer, self.m_page.view())
preview.paintRequested.connect(self.printDocument)
preview.exec()
self.m_inPrintPreview = False
#pyqtSlot(QPrinter)
def printDocument(self, printer):
loop = QEventLoop()
result = False
def printPreview(success):
nonlocal result
result = success
loop.quit()
progressbar = QProgressDialog(self.m_page.view())
progressbar.findChild(QProgressBar).setTextVisible(False)
progressbar.setLabelText("Wait please...")
progressbar.setRange(0, 0)
progressbar.show()
progressbar.canceled.connect(loop.quit)
self.m_page.print(printer, printPreview)
loop.exec_()
progressbar.close()
if not result:
painter = QPainter()
if painter.begin(printer):
font = painter.font()
font.setPixelSize(20)
painter.setFont(font)
painter.drawText(QPointF(10, 25), "Could not generate print preview.")
painter.end()
def main():
import sys
QCoreApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
app = QApplication(sys.argv)
app.setApplicationName("Previewer")
view = QWebEngineView()
view.setUrl(QUrl("https://stackoverflow.com/questions/59438021"))
view.resize(1024, 750)
view.show()
handler = PrintHandler()
handler.setPage(view.page())
printPreviewShortCut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_P), view)
printShortCut = QShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_P), view)
printPreviewShortCut.activated.connect(handler.printPreview)
printShortCut.activated.connect(handler.print)
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Note: For PySide2 you only have to change pyqtSlot to Slot.
Related
I would like when I click on a buttom from a toolbar created with PyQt get the selected items in a QListWidget created in other class (LisWorkDirectory class).
In the ToolBar.py in the compilation_ function, I would like to get all selected items. I want to get the instance of the ListWorkDirectory class to get the QListWidget that I created the first time I have launched my app.
If I instanciate ListWorkDirectory, I get a new instance, and the selectedItems return a empty list, it is a normal behaviour.
Maybe my architecture is not correct, so if you have any suggestion or remarks don't' hesitate to learn me.
Below my code so that you understand my request :
main.py
from MainWindow import MainWindow
from MenuBar import MenuBar
from ToolBar import ToolBar
from PyQt5.QtWidgets import QApplication
import sys
app = QApplication(sys.argv)
#Window
windowApp = MainWindow("pyCompile")
#MenuBar
menuBar = MenuBar()
#ToolBar
toolBar = ToolBar()
windowApp.setMenuBar(menuBar)
windowApp.addToolBar(toolBar)
windowApp.show()
sys.exit(app.exec_())
MainWindow.py
from PyQt5.QtWidgets import QMainWindow, QWidget, QLineEdit, QPushButton, QListWidget,QListWidgetItem,QPlainTextEdit, QFileDialog, QStatusBar ,QVBoxLayout, QHBoxLayout
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import Qt
from ListWorkDirectory import ListWorkDirectory
from Logger import MyDialog
import logging
import os
class MainWindow(QMainWindow):
def __init__(self, windowTitle):
super().__init__()
#Logger
self.logger = MyDialog()
logging.info("pyCompile version 0.1")
self.setGeometry(150,250,600,350)
self.setWindowTitle(windowTitle)
self.workDirectoryField = QLineEdit()
self.workDirectoryField.setPlaceholderText("Select your work directory ...")
self.workDirectoryField.setText("F:/WORKSPACE/Projects")
self.workDirectoryButton = QPushButton()
self.workDirectoryButton.setIcon(QIcon(":folder.svg"))
self.workDirectoryButton.clicked.connect(self.launchDialog)
self.hBoxLayout = QHBoxLayout()
self.hBoxLayout.addWidget(self.workDirectoryField)
self.hBoxLayout.addWidget(self.workDirectoryButton)
#List folder in work directory
self.myListFolder = ListWorkDirectory()
print(self.myListFolder)
self.workDirectoryField.textChanged[str].connect(self.myListFolder.update)
self.hBoxLayoutLogger = QHBoxLayout()
self.hBoxLayoutLogger.addWidget(self.myListFolder)
self.hBoxLayoutLogger2 = QHBoxLayout()
self.hBoxLayoutLogger2.addWidget(self.logger)
self.centralWidget = QWidget(self)
self.setCentralWidget(self.centralWidget)
self.vBoxLayout = QVBoxLayout(self.centralWidget)
self.vBoxLayout.addLayout(self.hBoxLayout)
self.vBoxLayout.addLayout(self.hBoxLayoutLogger)
self.vBoxLayout.addLayout(self.hBoxLayoutLogger2)
#Status Bar
self.statusBar = QStatusBar()
self.statusBar.showMessage("Welcome in pyCompile", 5000)
self.setStatusBar(self.statusBar)
def launchDialog(self):
workDirectory = QFileDialog.getExistingDirectory(self, caption="Select work directory")
print(workDirectory)
self.workDirectoryField.setText(workDirectory)
MenuBar.py
from PyQt5.QtWidgets import QMenuBar
class MenuBar(QMenuBar):
def __init__(self):
super().__init__()
self.fileMenu = "&File"
self.editMenu = "&Edit"
self.helpMenu = "&Help"
self.initUI()
def initUI(self):
self.addMenu(self.fileMenu)
self.addMenu(self.editMenu)
self.addMenu(self.helpMenu)
ToolBar.py
from PyQt5.QtWidgets import QMainWindow, QToolBar, QAction
from PyQt5.QtGui import QIcon
import qrc_resources
from ListWorkDirectory import ListWorkDirectory
class ToolBar(QToolBar, ListWorkDirectory):
def __init__(self):
super().__init__()
self._createActions()
self.initUI()
def initUI(self):
self.setMovable(False)
self.addAction(self.compileAction)
self.addAction(self.settingsAction)
self.addAction(self.quitAction)
def _createActions(self):
self.compileAction = QAction(self)
self.compileAction.setStatusTip("Launch compilation")
self.compileAction.setText("&Compile")
self.compileAction.setIcon(QIcon(":compile.svg"))
self.compileAction.triggered.connect(self.compilation_)
self.settingsAction = QAction(self)
self.settingsAction.setText("&Settings")
self.settingsAction.setIcon(QIcon(":settings.svg"))
self.quitAction = QAction(self)
self.quitAction.setText("&Quit")
self.quitAction.setIcon(QIcon(":quit.svg"))
def compilation_(self):
"""
Get the instance of ListWorkDirectory to get selected items and launch the
compilation
"""
ListWorkDirectory.py
from PyQt5.QtWidgets import QListWidget,QListWidgetItem
from PyQt5.QtCore import Qt, QDir
import os
class ListWorkDirectory(QListWidget):
def __init__(self):
super().__init__()
self.clear()
def update(self, workDirectoryField):
isPathCorrect = self.checkPath(workDirectoryField)
if(isPathCorrect):
listOfDirectory = self.getFolderList(workDirectoryField, os.listdir(workDirectoryField))
for folder in listOfDirectory:
self.item = QListWidgetItem(folder)
self.item.setCheckState(Qt.Unchecked)
self.addItem(self.item)
else:
self.clear()
def checkPath(self, path):
QPath = QDir(path)
isQPathExist = QPath.exists()
isPathEmpty = self.isPathEmpty(path)
if(isQPathExist and not isPathEmpty):
return True
else:
return False
def getFolderList(self, path, listOfFiles):
listOfFolders=[]
for file_ in listOfFiles:
if(os.path.isdir(os.path.join(path, file_))):
listOfFolders.append(file_)
else:
pass
return listOfFolders
def isPathEmpty(self, path):
if(path != ""):
return False
else:
return True
Thank you for your help.
When you add widget to window (or to other widget) then this window (or widget) is its parent and you can use self.parent() to access element in window (or widget). When widgets are nested then you may even use self.parent().parent()
def compilation_(self):
"""
Get the instance of ListWorkDirectory to get selected items and launch the
compilation
"""
print(self.parent().myListFolder)
EDIT:
Class ListWorkDirectory has function item(number) to get item from list - but you overwrite it with line self.item = QListWidgetItem(folder). If you remove self. and use
item = QListWidgetItem(folder)
item.setCheckState(Qt.Unchecked)
self.addItem(item)
then this will show only checked items
def compilation_(self):
"""
Get the instance of ListWorkDirectory to get selected items and launch the
compilation
"""
lst = self.parent().myListFolder
for x in range(lst.count()):
item = lst.item(x)
#print(item.checkState(), item.text())
if item.checkState() :
print(item.text())
Full working code - everyone can simply copy all to one file and run it.
from PyQt5.QtWidgets import QMainWindow, QWidget, QLineEdit, QPushButton, QListWidget,QListWidgetItem,QPlainTextEdit, QFileDialog, QStatusBar ,QVBoxLayout, QHBoxLayout
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import Qt
import logging
import os
# ---
from PyQt5.QtWidgets import QMenuBar
class MenuBar(QMenuBar):
def __init__(self):
super().__init__()
self.fileMenu = "&File"
self.editMenu = "&Edit"
self.helpMenu = "&Help"
self.initUI()
def initUI(self):
self.addMenu(self.fileMenu)
self.addMenu(self.editMenu)
self.addMenu(self.helpMenu)
# ---
from PyQt5.QtWidgets import QMainWindow, QToolBar, QAction
from PyQt5.QtGui import QIcon
class ToolBar(QToolBar):
def __init__(self):
super().__init__()
self._createActions()
self.initUI()
def initUI(self):
self.setMovable(False)
self.addAction(self.compileAction)
self.addAction(self.settingsAction)
self.addAction(self.quitAction)
def _createActions(self):
self.compileAction = QAction(self)
self.compileAction.setStatusTip("Launch compilation")
self.compileAction.setText("&Compile")
self.compileAction.setIcon(QIcon(":compile.svg"))
self.compileAction.triggered.connect(self.compilation_)
self.settingsAction = QAction(self)
self.settingsAction.setText("&Settings")
self.settingsAction.setIcon(QIcon(":settings.svg"))
self.quitAction = QAction(self)
self.quitAction.setText("&Quit")
self.quitAction.setIcon(QIcon(":quit.svg"))
def compilation_(self):
"""
Get the instance of ListWorkDirectory to get selected items and launch the
compilation
"""
lst = self.parent().myListFolder
for x in range(lst.count()):
item = lst.item(x)
#print(item.checkState(), item.text())
if item.checkState() :
print(item.text())
# ---
from PyQt5.QtWidgets import QListWidget, QListWidgetItem
from PyQt5.QtCore import Qt, QDir
import os
class ListWorkDirectory(QListWidget):
def __init__(self):
super().__init__()
self.clear()
def update(self, workDirectoryField):
isPathCorrect = self.checkPath(workDirectoryField)
if(isPathCorrect):
listOfDirectory = self.getFolderList(workDirectoryField, os.listdir(workDirectoryField))
for folder in listOfDirectory:
item = QListWidgetItem(folder)
item.setCheckState(Qt.Unchecked)
self.addItem(item)
else:
self.clear()
def checkPath(self, path):
QPath = QDir(path)
isQPathExist = QPath.exists()
isPathEmpty = self.isPathEmpty(path)
if(isQPathExist and not isPathEmpty):
return True
else:
return False
def getFolderList(self, path, listOfFiles):
listOfFolders=[]
for file_ in listOfFiles:
if(os.path.isdir(os.path.join(path, file_))):
listOfFolders.append(file_)
else:
pass
return listOfFolders
def isPathEmpty(self, path):
if(path != ""):
return False
else:
return True
class MainWindow(QMainWindow):
def __init__(self, windowTitle):
super().__init__()
#Logger
#self.logger = MyDialog()
logging.info("pyCompile version 0.1")
self.setGeometry(150,250,600,350)
self.setWindowTitle(windowTitle)
self.workDirectoryField = QLineEdit()
self.workDirectoryField.setPlaceholderText("Select your work directory ...")
self.workDirectoryField.setText("F:/WORKSPACE/Projects")
self.workDirectoryButton = QPushButton()
self.workDirectoryButton.setIcon(QIcon(":folder.svg"))
self.workDirectoryButton.clicked.connect(self.launchDialog)
self.hBoxLayout = QHBoxLayout()
self.hBoxLayout.addWidget(self.workDirectoryField)
self.hBoxLayout.addWidget(self.workDirectoryButton)
#List folder in work directory
self.myListFolder = ListWorkDirectory()
print(self.myListFolder)
self.workDirectoryField.textChanged[str].connect(self.myListFolder.update)
self.hBoxLayoutLogger = QHBoxLayout()
self.hBoxLayoutLogger.addWidget(self.myListFolder)
self.hBoxLayoutLogger2 = QHBoxLayout()
#self.hBoxLayoutLogger2.addWidget(self.logger)
self.centralWidget = QWidget(self)
self.setCentralWidget(self.centralWidget)
self.vBoxLayout = QVBoxLayout(self.centralWidget)
self.vBoxLayout.addLayout(self.hBoxLayout)
self.vBoxLayout.addLayout(self.hBoxLayoutLogger)
self.vBoxLayout.addLayout(self.hBoxLayoutLogger2)
#Status Bar
self.statusBar = QStatusBar()
self.statusBar.showMessage("Welcome in pyCompile", 5000)
self.setStatusBar(self.statusBar)
def launchDialog(self):
workDirectory = QFileDialog.getExistingDirectory(self, caption="Select work directory")
print(workDirectory)
self.workDirectoryField.setText(workDirectory)
# ---
from PyQt5.QtWidgets import QApplication
import sys
app = QApplication(sys.argv)
#Window
windowApp = MainWindow("pyCompile")
#MenuBar
menuBar = MenuBar()
#ToolBar
toolBar = ToolBar()
windowApp.setMenuBar(menuBar)
windowApp.addToolBar(toolBar)
windowApp.show()
sys.exit(app.exec_())
from PyQt5 import Qt
from PyQt5.QtCore import QUrl
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWidgets import *
from sys import argv
class Window(QMainWindow):
def __init__(self, *args, **kwargs):
super(Window, self).__init__(*args, **kwargs)
self.browser = QWebEngineView()
self.browser.setUrl(QUrl('https://www.duckduckgo.com'))
self.browser.urlChanged.connect(self.update_AddressBar)
self.setCentralWidget(self.browser)
self.navigation_bar = QToolBar('Navigation Toolbar')
self.addToolBar(self.navigation_bar)
self.navigation_bar.setAttribute(Qt.Qt.WA_StyledBackground, True)
self.navigation_bar.setStyleSheet('background-color: white;')
self.navigation_bar.setMinimumSize(0, 75)
self.navigation_bar.setMovable(False)
back_button = QAction("←", self)
back_button.setStatusTip('Go to previous page you visited')
back_button.triggered.connect(self.browser.back)
self.navigation_bar.addAction(back_button)
next_button = QAction("→", self)
next_button.setStatusTip('Go to next page')
next_button.triggered.connect(self.browser.forward)
self.navigation_bar.addAction(next_button)
refresh_button = QAction("⟳", self)
refresh_button.setStatusTip('Refresh this page')
refresh_button.triggered.connect(self.browser.reload)
self.navigation_bar.addAction(refresh_button)
home_button = QAction("⌂", self)
home_button.setStatusTip('Go to home page (Google page)')
home_button.triggered.connect(self.go_to_home)
self.navigation_bar.addAction(home_button)
#self.navigation_bar.addSeparator()
self.URLBar = QLineEdit()
self.URLBar.returnPressed.connect(lambda: self.go_to_URL(QUrl(self.URLBar.text()))) # This specifies what to do when enter is pressed in the Entry field
self.navigation_bar.addWidget(self.URLBar)
self.addToolBarBreak()
self.show()
def go_to_home(self):
self.browser.setUrl(QUrl('https://www.duckduckgo.com/'))
def go_to_URL(self, url: QUrl):
if url.scheme() == '':
url.setScheme('http://')
self.browser.setUrl(url)
self.update_AddressBar(url)
def update_AddressBar(self, url):
self.URLBar.setText(url.toString())
self.URLBar.setCursorPosition(0)
app = QApplication(argv)
app.setApplicationName('Libertatem Browser')
window = Window()
app.exec_()
As you can see in my code, I have 4 QActions. I want to make these bigger. How can I do that? I have seen others using back_button.geometry(), but you can't do that with QActions.
I would prefer to keep them as QActions, because it took me a long while to set them up. As a side note, would changing the font size make the button bigger?
If you change the size of the button associated with the QAction it would not make a big difference, you could do it in the following way:
button = toolbar.widgetForAction(action)
button.setFixedSize(100, 100)
But in this case it is better to change the font size since the size of the button (and the height of the QToolBar) depends on it.
import sys
from PyQt5.QtCore import Qt, QUrl, QSize
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import QAction, QApplication, QLineEdit, QMainWindow, QToolBar
from PyQt5.QtWebEngineWidgets import QWebEngineView
class Window(QMainWindow):
def __init__(self, *args, **kwargs):
super(Window, self).__init__(*args, **kwargs)
self.browser = QWebEngineView()
self.browser.setUrl(QUrl("https://www.duckduckgo.com"))
self.browser.urlChanged.connect(self.update_AddressBar)
self.setCentralWidget(self.browser)
self.navigation_bar = QToolBar("Navigation Toolbar", movable=False)
self.addToolBar(self.navigation_bar)
self.navigation_bar.setAttribute(Qt.WA_StyledBackground, True)
self.navigation_bar.setStyleSheet("background-color: white;")
font = QFont()
font.setPixelSize(40)
back_action = QAction("←", self)
back_action.setStatusTip("Go to previous page you visited")
back_action.triggered.connect(self.browser.back)
back_action.setFont(font)
self.navigation_bar.addAction(back_action)
next_action = QAction("→", self)
next_action.setStatusTip("Go to next page")
next_action.triggered.connect(self.browser.forward)
next_action.setFont(font)
self.navigation_bar.addAction(next_action)
refresh_action = QAction("⟳", self)
refresh_action.setStatusTip("Refresh this page")
refresh_action.triggered.connect(self.browser.reload)
refresh_action.setFont(font)
self.navigation_bar.addAction(refresh_action)
home_action = QAction("⌂", self)
home_action.setStatusTip("Go to home page (Google page)")
home_action.setFont(font)
home_action.triggered.connect(self.go_to_home)
self.navigation_bar.addAction(home_action)
self.URLBar = QLineEdit()
self.URLBar.returnPressed.connect(self.handle_return_pressed)
self.navigation_bar.addWidget(self.URLBar)
def go_to_home(self):
self.browser.setUrl(QUrl("https://www.duckduckgo.com/"))
def go_to_URL(self, url: QUrl):
if url.scheme() == "":
url.setScheme("http://")
self.browser.setUrl(url)
self.update_AddressBar(url)
def update_AddressBar(self, url):
self.URLBar.setText(url.toString())
self.URLBar.setCursorPosition(0)
def handle_return_pressed(self):
url = QUrl.fromUserInput(self.URLBar.text())
self.go_to_URL(url)
def main():
app = QApplication(sys.argv)
app.setApplicationName("Libertatem Browser")
window = Window()
window.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
I am trying to display an image from a URL in pyside6, but can't get anything to work.
def getIcon(data):
iconID = data['weather'][0]['icon']
icon = QPixmap("http://openweathermap.org/img/w/" + iconID + ".png");
return icon
And
self.temperatureIcon = QtWidgets.QLabel(self).setPixmap(getIcon(self.weatherData))
is the code I have.
QPixmap does not download the image from a url so you must download it for example using QNetworkAccessManager:
import sys
from functools import cached_property
from PySide6.QtCore import Signal, QObject, Qt, QUrl
from PySide6.QtGui import QImage, QPixmap
from PySide6.QtNetwork import QNetworkAccessManager, QNetworkReply, QNetworkRequest
from PySide6.QtWidgets import (
QApplication,
QGridLayout,
QLabel,
QLineEdit,
QPushButton,
QWidget,
)
class ImageDownloader(QObject):
finished = Signal(QImage)
def __init__(self, parent=None):
super().__init__(parent)
self.manager.finished.connect(self.handle_finished)
#cached_property
def manager(self):
return QNetworkAccessManager()
def start_download(self, url):
self.manager.get(QNetworkRequest(url))
def handle_finished(self, reply):
if reply.error() != QNetworkReply.NoError:
print("error: ", reply.errorString())
return
image = QImage()
image.loadFromData(reply.readAll())
self.finished.emit(image)
class Widget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.lineedit = QLineEdit()
self.button = QPushButton("Start")
self.label = QLabel(alignment=Qt.AlignCenter)
lay = QGridLayout(self)
lay.addWidget(self.lineedit, 0, 0)
lay.addWidget(self.button, 0, 1)
lay.addWidget(self.label, 1, 0, 1, 2)
self.downloader = ImageDownloader()
self.downloader.finished.connect(self.handle_finished)
self.button.clicked.connect(self.handle_clicked)
self.lineedit.setText("http://openweathermap.org/img/wn/01d#2x.png")
self.resize(640, 480)
def handle_finished(self, image):
pixmap = QPixmap.fromImage(image)
self.label.setPixmap(pixmap)
def handle_clicked(self):
url = QUrl.fromUserInput(self.lineedit.text())
self.downloader.start_download(url)
def main():
app = QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()
Here's another simple solution\example:
import sys
import requests
from PySide6.QtGui import QPixmap, QScreen
from PySide6.QtWidgets import QApplication, QWidget, QLabel
URL = 'https://upload.wikimedia.org/wikipedia/en/7/7d/Lenna_(test_image).png'
class App(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle('getAndSetImageFromURL()')
self.label = QLabel(self)
self.pixmap = QPixmap()
self.getAndSetImageFromURL(URL)
self.resize(self.pixmap.width(),self.pixmap.height())
screenSize = QScreen.availableGeometry(QApplication.primaryScreen())
frmX = (screenSize.width () - self.width ())/2
frmY = (screenSize.height() - self.height())/2
self.move(frmX, frmY)
self.show()
def getAndSetImageFromURL(self,imageURL):
request = requests.get(imageURL)
self.pixmap.loadFromData(request.content)
self.label.setPixmap(self.pixmap)
#QApplication.processEvents() # uncoment if executed on loop
if __name__ == '__main__':
app = QApplication(sys.argv)
gui = App()
sys.exit(app.exec())
which outputs:
import sys
import requests
import PySide6
from PySide6.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
from PySide6.QtWidgets import QTableView, QWidget, QApplication, QGridLayout, QHeaderView
from PySide6.QtCore import Qt, QAbstractTableModel, QObject, Signal, QUrl
from PySide6.QtGui import QColor, QIcon, QPixmap, QImage
from datetime import datetime
class MagicIcon():
def __init__(self, link):
self.link = link
self.icon = QIcon()
try:
response = requests.get(self.link)
pixmap = QPixmap()
pixmap.loadFromData(response.content)
self.icon = QIcon(pixmap)
except:
pass
class MainWindow(QWidget):
def __init__():
super().__init__()
self.setWindowIcon(MagicIcon(
"https://img.icons8.com/external-flatarticons-blue-flatarticons/65/000000/external-analysis-digital-marketing-flatarticons-blue-flatarticons-1.png"
).icon)
if __name__ == "__main__":
app = QApplication(sys.argv)
wid = MainWindow()
wid.show()
sys.exit(app.exec())
Use requests module to fetch image and store them.
I have 2 QGraphicsView and one QWebEngineView widget on the form and I need to * (on the click of the button1)* make a screenshot of the content inside this QWebEngineView with some defined setback from the original QWebEngineView borders and save that screenshot as an image, and at the same time keep it as an object and paste this object to the first QGraphicsView and * (on the click of button2)* insert saved image into the second QGraphicsView.
Edit:
(I'd like to make a screenshot of the area inside the QtWebEngineWidgets and keep this area as an object. and a second problem I need to know how to paste this area in to the QGraphicsView in 2 different ways: (from the file) and display it as an object without saving. 2 QGraphicsView are just for the studying purpose it could be just one QGraphicsView and by hitting on button1 screenshot is making and pasting as an object to the QGraphicsView and by hitting on button2 -screenshot is making (saving as an png and loading to the QGraphicsView))
Here is the code I have:
import sys, os
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QBrush, QPen, QScreen, QPixmap
from PyQt5.QtWidgets import QApplication, QStyleFactory, QMainWindow, QWidget, QVBoxLayout, QLabel
from PyQt5.QtWidgets import QGraphicsScene, QGraphicsView, QGraphicsItem, QPushButton
from pyqtgraph.Qt import QtCore, QtGui
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWidgets import QApplication, QDialog
class Geometry(QGraphicsView):
def __init__(self):
QGraphicsView.__init__(self)
class CentralPanel(QWidget):
def __init__(self):
QWidget.__init__(self)
self.lblCoords = QLabel('MAP SELECTOR (object image): ')
self.lblCoords2 = QLabel('MAP SELECTOR (loaded from file image): ')
self.gvwShapes = Geometry()
self.gvwShapes2 = Geometry()
vbxDsply = QVBoxLayout()
vbxDsply.addWidget(self.lblCoords) # Capture coordinates of drawn line at window 1
vbxDsply.addWidget(self.gvwShapes) # add QGraphicsView #1
vbxDsply.addWidget(self.lblCoords2) # Capture coordinates of drawn line at window 2
vbxDsply.addWidget(self.gvwShapes2) # add QGraphicsView #2
self.webEngineView = QWebEngineView() # Add Google maps web window
self.webEngineView.load(QtCore.QUrl("https://www.google.com/maps/#36.797966,-97.1413048,3464a,35y,0.92h/data=!3m1!1e3"))
vbxDsply.addWidget(self.webEngineView)
self.Button1 = QPushButton('Do screenshot of webEngineView save it and paste it into QGraphicsView2', self) # Button to load image to graphics view
vbxDsply.addWidget(self.Button1)
self.Button1.clicked.connect(self.button_screenshot)
self.Button2 = QPushButton('Do screenshot of webEngineView and paste it into QGraphicsView1 ', self)
vbxDsply.addWidget(self.Button2)
self.Button2.clicked.connect(self.button_load_image)
self.setLayout(vbxDsply)
self.filename = "image.jpg"
def button_screenshot(self):
print('Screenshot is taken and saved as an image, Image loaded and inserted into the gvwShapes QGraphicsView1 ')
app = QApplication(sys.argv)
QScreen.grabWindow(app.primaryScreen(), QApplication.desktop().winId()).save(self.filename, 'png')
def button_load_image(self):
print('Screenshot is taken and inserted into the gvwShapes2 QGraphicsView2')
# pix = QPixmap()
# pix.load(self.filename)
# pix = QPixmap(self.filename)
# item = QGraphicsPixmapItem(pix)
# scene = QGraphicsScence(self)
# scene.addItem(item)
# self.graphicsView.setScene(scene)
scene = QtWidgets.QGraphicsScene(self)
pixmap = QPixmap(self.filename)
item = QtWidgets.QGraphicsPixmapItem(pixmap)
scene.addItem(item)
self.gvwShapes.setScene(scene)
# scene = self.gvwShapes
# self.image = QtGui.QPixmap(self.filename)
# self.gvwShapes.add
# scene.addItem(QtGui.QGraphicsPixmapItem(self.image))
# self.view = self.QGraphicsView
# self.view.setScene(self.image)
# self.view.show()
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setGeometry(200, 50, 700, 900)
self.setWindowTitle('MAP Selector REV01')
self.setStyle(QStyleFactory.create('Cleanlooks'))
self.CenterPane = CentralPanel()
self.setCentralWidget(self.CenterPane)
# Catching exceptions and running a main loop
#
import traceback
def except_hook(exc_type, exc_value, exc_tb):
tb = "".join(traceback.format_exception(exc_type, exc_value, exc_tb))
print("error cached")
print("error message:\n", tb)
if __name__ == "__main__":
MainEventThred = QApplication([])
os.environ["QTWEBENGINE_CHROMIUM_FLAGS"] = "--enable-logging --log-level=3"
sys.excepthook = except_hook
MainApp = MainWindow()
MainApp.show()
MainEventThred.exec()
Crop image in QWebEngineView
If you want to implement the crop of the QWebEngineView screenshot then you must use a QRubberBand that is on the focusProxy () of the QWebEngineView (it is the widget where the web page is rendered and receives the mouse event that is created after displaying the view)
from functools import cached_property
import sys
from PyQt5.QtCore import pyqtSignal, QEvent, QObject, QPoint, QRect, QSize, QUrl
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QApplication, QLabel, QRubberBand
from PyQt5.QtWebEngineWidgets import QWebEngineView
class RubberBandManager(QObject):
pixmap_changed = pyqtSignal(QPixmap, name="pixmapChanged")
def __init__(self, widget):
super().__init__(widget)
self._origin = QPoint()
self._widget = widget
self.widget.installEventFilter(self)
#property
def widget(self):
return self._widget
#cached_property
def rubberband(self):
return QRubberBand(QRubberBand.Rectangle, self.widget)
def eventFilter(self, source, event):
if self.widget is source:
if event.type() == QEvent.MouseButtonPress:
self._origin = event.pos()
self.rubberband.setGeometry(QRect(self._origin, QSize()))
self.rubberband.show()
elif event.type() == QEvent.MouseMove:
self.rubberband.setGeometry(
QRect(self._origin, event.pos()).normalized()
)
elif event.type() == QEvent.MouseButtonRelease:
rect = self.rubberband.geometry()
pixmap = self.widget.grab(rect)
self.pixmap_changed.emit(pixmap)
self.rubberband.hide()
return super().eventFilter(source, event)
if __name__ == "__main__":
app = QApplication(sys.argv)
view = QWebEngineView()
view.load(
QUrl(
"https://www.google.com/maps/#36.797966,-97.1413048,3464a,35y,0.92h/data=!3m1!1e3"
)
)
view.show()
rubberband_manager = RubberBandManager(view.focusProxy())
label = QLabel()
label.hide()
def on_pixmap_changed(pixmap):
label.setPixmap(pixmap)
label.adjustSize()
label.show()
rubberband_manager.pixmap_changed.connect(on_pixmap_changed)
ret = app.exec()
sys.exit(ret)
Show an Image in QGraphicsView
To display an image then you must use a QGraphicsPixmapItem where you load a QPixmap.
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPixmap, QPainter, QPalette
from PyQt5.QtWidgets import (
QApplication,
QGraphicsView,
QGraphicsScene,
QGraphicsPixmapItem,
)
class ImageViewer(QGraphicsView):
def __init__(self, parent=None):
super().__init__(parent)
self.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
self.setAlignment(Qt.AlignCenter)
self.setBackgroundRole(QPalette.Dark)
scene = QGraphicsScene()
self.setScene(scene)
self._pixmap_item = QGraphicsPixmapItem()
scene.addItem(self._pixmap_item)
def load_pixmap(self, pixmap):
self._pixmap_item.setPixmap(pixmap)
self.fitToWindow()
def fitToWindow(self):
self.fitInView(self.sceneRect(), Qt.KeepAspectRatio)
def resizeEvent(self, event):
super().resizeEvent(event)
self.fitToWindow()
if __name__ == "__main__":
app = QApplication(sys.argv)
view = ImageViewer()
view.resize(640, 480)
view.show()
pixmap = QPixmap("image.jpg")
view.load_pixmap(pixmap)
ret = app.exec()
sys.exit(ret)
The previous widget is used to load an image from a file: pixmap = QPixmap("/path/of/image") or use the QPixmap provided by pixmap_changed signal of RubberBandManager.
Set a scene for your QGraphicsViews.
self.gvwShapes = Geometry()
self.gvwShapes2 = Geometry()
self.gvwShapes.setScene(QGraphicsScene())
self.gvwShapes2.setScene(QGraphicsScene())
Use QWidget.grab() to render it into a QPixmap and QGraphicsScene.addPixmap() to add it to the scene. You can specify the area with a QRect.
def button_screenshot(self):
pixmap = self.webEngineView.grab(QRect(180, 100, 300, 280))
pixmap.save(self.filename)
self.gvwShapes2.scene().addPixmap(pixmap)
def button_load_image(self):
self.gvwShapes.scene().addPixmap(QPixmap(self.filename))
I am new to python and I am makeing a simple appliacation where the value of bitcoin is displayed in the system tray. I am useing PyQt5.
My question is: How do I refresh the value and icon every minute. When I try it my menu wont work anymore. Is there a simple fix?
This is my code:
import sys
import time
import math
import json
from PyQt5.QtWidgets import QApplication, QSystemTrayIcon, QMenu
from PyQt5.QtGui import QIcon
from urllib.request import urlopen
from time import sleep
app = QApplication(sys.argv)
while True:
# Connecting the api
with urlopen("https://api.alternative.me/v2/ticker/1/") as response:
source = response.read()
data = json.loads(source)
price = (json.dumps(data['data']['1']['quotes']['USD']['price']))
change = (json.dumps(data['data']['1']['quotes']['USD']['percentage_change_1h']))
intPrice = float(price)
intChange = float(change)
roundPrice = round(intPrice,2)
roundStringPrice = str(roundPrice)
# Icon change
if intChange <=0.00:
trayIcon = QSystemTrayIcon(QIcon('icons/down.png'), parent=app)
else:
trayIcon = QSystemTrayIcon(QIcon('icons/up.png'), parent=app)
trayIcon.setToolTip(roundStringPrice + ' USD')
trayIcon.show()
time.sleep(5)
trayIcon.update()
menu = QMenu()
exitAction = menu.addAction('Quit app')
exitAction.triggered.connect(app.quit)
trayIcon.setContextMenu(menu)
sys.exit(app.exec_())
Don't use while True, urlopen or time.sleep as they block the eventloop, instead use a QTimer with QNetworkAccessManager:
import os.path
import json
from functools import cached_property
from PyQt5.QtCore import pyqtSignal, QObject, QTimer, QUrl
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply, QNetworkRequest
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication, QMenu, QMessageBox, QSystemTrayIcon
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
class ApiManager(QObject):
infoChanged = pyqtSignal(float, float)
def __init__(self, parent=None):
super().__init__(parent)
self.manager.finished.connect(self.handle_finished)
#cached_property
def manager(self):
return QNetworkAccessManager()
def start_request(self):
url = QUrl("https://api.alternative.me/v2/ticker/1/")
qrequest = QNetworkRequest(url)
self.manager.get(qrequest)
def handle_finished(self, reply):
if reply.error() != QNetworkReply.NoError:
print(f"code: {reply.error()} message: {reply.errorString()}")
else:
print("successful")
source = reply.readAll().data()
data = json.loads(source)
r = data["data"]["1"]["quotes"]["USD"]
price = r["price"]
change = r["percentage_change_1h"]
self.infoChanged.emit(price, change)
class SystemTrayIcon(QSystemTrayIcon):
def __init__(self, parent=None):
super().__init__(parent)
self.setIcon(QIcon(self.get_resource("icons/up.png")))
self.menu = QMenu()
exitAction = self.menu.addAction("Quit app")
exitAction.triggered.connect(QApplication.quit)
self.setContextMenu(self.menu)
self.manager = ApiManager()
self.manager.infoChanged.connect(self.handle_info_changed)
timer = QTimer(self, timeout=self.manager.start_request, interval=60 * 1000)
timer.start()
self.manager.start_request()
def get_resource(self, path):
return os.path.join(CURRENT_DIR, path)
def handle_info_changed(self, price, change):
icon = (
QIcon(self.get_resource("icons/down.png"))
if change < 0
else QIcon(self.get_resource("icons/up.png"))
)
self.setIcon(icon)
self.setToolTip(f"{price:.2f} USD")
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
if not QSystemTrayIcon.isSystemTrayAvailable():
QMessageBox.critical(
None, "Systray", "I couldn't detect any system tray on this system."
)
sys.exit(1)
QApplication.setQuitOnLastWindowClosed(False)
trayIcon = SystemTrayIcon(parent=app)
trayIcon.show()
sys.exit(app.exec_())