This bad boy just does not want to change size to fill the dock widget area. I have tried all sorts of variations with QSizePolicy but nothing seems to work. The size of the QWebView always stays the same. Do I need to write a resize() callback?
Here's what I have right now:
self.helpwindow = QtGui.QDockWidget("Doc Browser")
self.helpwindow.setAllowedAreas(QtCore.Qt.RightDockWidgetArea)
self.helpwindow.setFeatures(QtGui.QDockWidget.DockWidgetClosable | QtGui.QDockWidget.DockWidgetFloatable)
helpAction = self.helpwindow.toggleViewAction()
helpAction.setText("&Help Browser")
helpAction.setShortcut(QtGui.QKeySequence("F1"))
helpMenu.addAction(helpAction)
self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.helpwindow)
helpbrowser = QtWebKit.QWebView(self.helpwindow)
indexpath = resource_filename(__name__,"help/index.html")
url = QtCore.QUrl("file://" + indexpath)
helpbrowser.load(url)
helpbrowser.show()
helpbrowser.updateGeometry()
helpbrowser.update()
helpbrowser.setMinimumWidth(400)
helpbrowser.setMinimumHeight( self.helpwindow.height())
sizepolicy = helpbrowser.sizePolicy()
sizepolicy.setVerticalPolicy(QtGui.QSizePolicy.MinimumExpanding)
sizepolicy.setHorizontalPolicy(QtGui.QSizePolicy.MinimumExpanding)
I'm using PyQT4 but C++ solutions gladly accepted.
Setting only paren't wont make QDockWidget manage child widget. You need to call:
helpwindow.setWidget(helpbrowser)
Related
I am messing around with the print preview of a QGraphicsView instance in Pyside6. I tried many things, but can't get it right.
def onPreview(self):
printer = QtPrintSupport.QPrinter(QtPrintSupport.QPrinter.HighResolution)
layout = QtGui.QPageLayout()
layout.setOrientation(QtGui.QPageLayout.Landscape)
layout.setPageSize(QtGui.QPageSize.A3)
printer.setPageLayout(layout)
preview = QtPrintSupport.QPrintPreviewDialog(printer, self.parent)
preview.paintRequested.connect(self.handlePaintRequest)
preview.exec_()
# handle paint request
def handlePaintRequest(self, printer):
# render QGraphicsView
self.parent.view.render(QtGui.QPainter(printer))
The snippet works without throwing an error, but the page orientation in the preview is always portrait. Also the page size doesn't work when printing. For sure I handle this in the wrong manner.
Currently working on a Python program that uses Qt Widgets from an .ui file to display an interactable GUI. However, i have not found a way to integrate the QQuickview widget to display any QML code, which i have read is possible.
I'm using PySide2 to convert the .ui file from Qt Designer and have both attempted to use the QQuickWidget found in Qt Designer, and manualy adding a QQuickView to the gridLayout in the .ui to no success.
The QQuickWidget i added in Qt Designer was, as far as i could tell transformed to a QWidget when run in python, so setSource(QUrl) or .load(QUrl) Made no sense when running the code.
My attempt at adding the QQuickView:
def connect_map_click(self):
# Function for handling the connect map button
engine = QQuickView()
ctx = engine.rootContext()
url = QUrl.fromLocalFile('QMLtest.qml')
engine.setSource(url)
container = QWidget.createWindowContainer(engine, self)
container.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
engine.show()
self.window.grd_map.addWidget(container, 0, 0)
The QML file:
import QtQuick 2.7
Rectangle {
id: rectangle
color: "red"
width: 200
height: 200
visible: true
Text {
id:text
text: "It's working!"
}
}
I'm attempting to run the qml window on the right side of the screen, shown below.
Solved it myself as one tends to do right after asking for help.
Ended up finding out that i had not imported the QQuickWidget to the Python file before. So my solution ended up being to create a QQuickWidget in python, setting the source to the qml file and adding it to the grid in the .ui GUI.
def connect_map_click(self):
# Function for handling the connect map button
qml_widget = QtQuickWidgets.QQuickWidget()
qml_widget.setSource(QUrl('QMLtest.qml'))
self.window.grd_map.addWidget(qml_widget)
resulting GUI:
Explanation:
QQuickView is a local variable that will be deleted when "connect_map_click" is finished executing nothing in the container.
Solution:
The solution is to extend the life cycle and for this there are the following alternatives:
Pass the QWindow associated with the window as parent:
def connect_map_click(self):
engine = QQuickView(self.window.grd_map.parentWidget().window().windowHandle())
# ...
Make the QQuickView an attribute of another object that has a longer life cycle, for example the container:
# ...
container = QWidget.createWindowContainer(engine, self)
container.engine = engine
# ...
or the self:
def connect_map_click(self):
# Function for handling the connect map button
self.engine = QQuickView()
ctx = self.engine.rootContext()
url = QUrl.fromLocalFile('QMLtest.qml')
self.engine.setSource(url)
container = QWidget.createWindowContainer(self.engine, self)
container.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
engine.show()
self.window.grd_map.addWidget(container, 0, 0)
Notes:
As you point out another solution is to use QQuickWidget since its life cycle depends on your parent who is self so he will live as long as the class. But QQuickWidget has limitations as the docs points out, including that you will not be able to record items that may be one of your requirements.
This behavior happens in PySide2 but in PyQt5 your initial code works since the container passes as parent to the QWindow of the window.
#!/usr/bin/env python3
from PyQt5.QtGui import *
from PyQt5.QtWidgets import QApplication, QWidget
import sys
app = QApplication(sys.argv)
screen = QApplication.primaryScreen()
widget = QWidget()
screenshot = screen.grabWindow(0, 0, 0, 100, 100)
screenshot.save('shot', 'jpg')
How can i use this to get a window? it only get a part of screen:
screenshot = screen.grabWindow( widget.winId() )
I need a crossplataform method..
Ref: http://doc.qt.io/qt-5/qscreen.html#grabWindow
You say you require a screenshot of a window, therefore
screenshot = screen.grabWindow(0, 0, 0, 100, 100)
is not the appropriate call here, since it captures the entire screen, cropped according to the final 4 parameters. (the 100 parameters are width and height).
screenshot = screen.grabWindow( widget.winId() )
captures the widget window. However, the reason you don't perhaps get what you expected on this call is that you don't create a solid widget and/or the widget hasn't been shown. Try the following example, making sure the app is on your primary display before clicking the button.
from PyQt5 import QtWidgets
import sys
app = QtWidgets.QApplication(sys.argv)
w = QtWidgets.QWidget()
grab_btn=QtWidgets.QPushButton('Grab Screen')
def click_handler():
screen = QtWidgets.QApplication.primaryScreen()
screenshot = screen.grabWindow( w.winId() )
screenshot.save('shot.jpg', 'jpg')
w.close()
grab_btn.clicked.connect(click_handler)
layout = QtWidgets.QVBoxLayout()
layout.addWidget(grab_btn)
w.setLayout(layout)
w.show()
sys.exit(app.exec_())
I've tested this on Windows.
If any one is coming here and having issues with the other answer, it might be a timing problem.
If you try to grab a screenshot directly during / after initializing the QWidget() instead of at the press of a button it might just make a screenshot of your desktop at the area of your window.
So if you want to grab a screenshot directly after calling __init__, call it after waiting some time with a QTimer (do not use time.sleep() as this would block the GUI).
def initUI(self):
(...)
QTimer.singleShot(1000, self.saveScreenshot)
def saveScreenshot(self):
screen = QApplication.primaryScreen()
screenshot = screen.grabWindow(self.winId() )
screenshot.save('screenshot.png', 'png')
I'm developing an application using the Qt Designer and PyQt4, I need to make several screens where each screen I capture user-specific data, for that I need to implement a next button and a back button similar to
where the current screen closes and the following opens when the user clicks next or if he clicks back, the screen closes and opens the previous screen, I made an example with only the next buttons and back to exemplify, if I was not clear:
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class Frm(QWidget):
def __init__(self, parent = None):
super(Frm, self).__init__(parent)
next = QPushButton('Next >', self)
back = QPushButton('< Back', self)
hbox = QHBoxLayout()
hbox.addWidget(back)
hbox.addWidget(next)
self.setLayout(hbox)
if __name__ == '__main__':
import sys
root = QApplication(sys.argv)
app = Frm(None)
app.show()
root.exec_()
In short: How do I implement a function that calls another screen and close the current at the click of a button?
First about a misconception: you do usually not create/show one screen (window) and close another, you usually only exchange the content of a wizard-like dialog window upon actions like pressing the buttons. The window is alive the whole time until the multiple page task is finished.
So I take it your question is really about:
How to exchange a widget in a layout?
Since you may still use PyQt4 which does not yet have QLayout.replaceWidget, it's best to just use methods removeWidget and addWidget of QLayout and since addWidget adds a widget to the end of the layout items list, I prefer a dedicated layout just for the interchangeable content of your wizard (see also: How to add an Item to the specific index in the layout).
Example code using PyQt5 but easily transferrable to PyQt4. Only the next button is implemented.
from PyQt5 import QtWidgets
class MyWizard(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# vertical layout, wraps content layout and buttons layout
vertical_layout = QtWidgets.QVBoxLayout()
self.setLayout(vertical_layout)
# content widget and layout
self.content_layout = QtWidgets.QVBoxLayout() # could be almost any layout actually
self.content = QtWidgets.QLabel('Page1') # customize with your content
self.content_layout.addWidget(self.content)
vertical_layout.addLayout(self.content_layout)
# back, forward buttons wraped in horizontal layout
button_layout = QtWidgets.QHBoxLayout()
button_layout.addStretch()
back_button = QtWidgets.QPushButton('Back')
back_button.clicked.connect(self.back_button_clicked)
button_layout.addWidget(back_button)
forward_button = QtWidgets.QPushButton('Forward')
forward_button.clicked.connect(self.forward_button_clicked)
button_layout.addWidget(forward_button)
vertical_layout.addLayout(button_layout)
def back_button_clicked(self):
"""
The back button is clicked.
"""
pass
def forward_button_clicked(self):
"""
The forward button is clicked.
"""
# remove old content
self.content_layout.removeWidget(self.content)
self.content.deleteLater()
# create new content
self.content = QtWidgets.QLabel('Another Page')
# add new content
self.content_layout.addWidget(self.content)
app = QtWidgets.QApplication([])
wizard = MyWizard()
wizard.setWindowTitle('MyWizard Example')
wizard.setFixedSize(600, 400)
wizard.show()
app.exec_()
And it looks like:
However, as already written in the comment by Marius, there is quite extensive support for such dialogs in Qt using QWizard. So I strongly recommend to use that instead. The example above is only to demonstrate the ability of inserting and removing widgets in layouts.
You should definitely use QWizard for such problems!
There is a QWizard class that allows you to create wizards in Qt and PyQt. It implements all the functionality you want, and lots more. All you do is design your pages by extending QWizardPage, and add them to the wizard. This is much simpler than doing the whole thing from scratch as you propose.
Old question but missing example with existing API
You don't need to create all structure of an Wizard by yourself. Qt (and PyQt) already provides a class called QWizard.
Basic example:
wizard = QtWidgets.QWizard()
page1 = QtWidgets.QWizardPage()
page1.setTitle('Page 1 is best!')
page1.setSubTitle('1111111111')
lineEdit = QtWidgets.QLineEdit()
hLayout1 = QtWidgets.QHBoxLayout(page1)
hLayout1.addWidget(lineEdit)
wizard.addPage(page1)
Complete example and some explanation:
https://www.youtube.com/watch?v=kTJ1QULxXjg
https://impatientprogrammer.net/2018/07/06/pyside-pyqt-qwizard-in-3-minutes/
I'm attempting to add a set of push buttons to a toolbar that can be toggled. When there are more buttons than can be displayed, the chevron appears, but it is greyed out and doesn't show the remaining contents.
Initially I had:
toolbar = QtGui.QToolbar()
newButton = QtGui.QPushButton('name')
newButton.toggled.connect(myAction)
toolbar.addWidget(newButton)
I read that I need to create a customWidgetAction, so I have tried the following:
toolbar = QtGui.QToolbar()
newButton = QtGui.QPushButton()
widgetAction = QtGui.QWidgetAction(newButton)
widgetAction.toggled.connect(myAction)
newWidget = widgetAction.createWidget(newButton)
toolbar.addWidget(newButton)
However using this code, the button doesn't appear in the toolbar. Any pointers on what I'm doing wrong?
I see the same behavior. If widgets are added to a toolbar and the toolbar cannot display them all, it will display a chevron but the chevron will be greyed out. The chevron will not be greyed out for actions though and they will be displayed in a drop down fashion.
My example just using standard QActions:
from PySide import QtGui
app = QtGui.QApplication([])
tb = QtGui.QToolBar()
#tb.addWidget(QtGui.QPushButton('AAA'))
#tb.addWidget(QtGui.QPushButton('BBB'))
#tb.addWidget(QtGui.QPushButton('CCC')) # will not be shown in case the toolbar is too short, chevron will be greyed
tb.addAction(QtGui.QAction('AAA', tb))
tb.addAction(QtGui.QAction('BBB', tb))
tb.addAction(QtGui.QAction('CCC', tb))
tb.addAction(QtGui.QAction('DDD', tb))
tb.addAction(QtGui.QAction('EEE', tb))
tb.resize(50, 40)
tb.show()
app.exec_()
and if you want to connect the action to something useful the pattern is:
toolbar = QtGui.QToolBar()
action = QtGui.QAction(icon, 'Create new scenario', toolbar)
action.triggered.connect(..)
toolbar.addAction(action)
QWidgetAction seems to be slightly more complex than just QAction although it should work as well. If you do not need the added functionality, rather use QAction and a simple icon.