Is it possible to resize a QColorDialog? I have been unable to get the window to resize appropriately. After the dialog is shown, it reverts to the default size.
An example:
import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class Window(QWidget):
def __init__(self):
super().__init__()
layout = QVBoxLayout()
push_Button = QPushButton()
layout.addWidget(push_Button)
push_Button.clicked.connect(self.button)
self.setLayout(layout)
def button(self):
color = QColorDialog(self)
color.resize(100,100)
print(color.size()) #Prints 100, 100
color.show()
print(color.size()) #Prints 551, 431
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
The QColorDialog has a fixed size, because it contains several custom widgets which aren't designed to be resizable. It is possble to override these constraints and allow for manual resizing like this:
color = QColorDialog(self)
color.setSizeGripEnabled(True)
color.layout().setSizeConstraint(QLayout.SetNoConstraint)
color.show()
However, as you will see, the layout quickly becomes messed up with even a little bit of resizing. I also found that beyond a certain point, the dialog will actually crash due to floating point exceptions. So I think you will either have to accept it as it is, or perhaps write your own color dialog.
Related
I am currently exploring the possibilities of displaying and working with PowerPoint presentations in a GUI using PyQt5/PyQt6 and Python. For that I found the most promising solution to be using a QAxWidget. Loading and displaying the pptx-file works fine, but unfortunately I noticed glitches when resizing the window of the GUI.
As a minimal example I used the following tutorial from the official Qt docs:
https://doc.qt.io/qtforpython/examples/example_axcontainer__axviewer.html?highlight=qaxwidget
After the QAxWidget()-initialization i just added the following line:
self.axWidget.setControl(r"C:\path\to\file\presentation.pptx")
Full code (taken from: https://doc.qt.io/qtforpython/examples/example_axcontainer__axviewer.html?highlight=qaxwidget):
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
"""PySide6 Active Qt Viewer example"""
import sys
from PyQt5.QtWidgets import qApp
from PySide6.QtAxContainer import QAxSelect, QAxWidget
from PySide6.QtGui import QAction
from PySide6.QtWidgets import (QApplication, QDialog,
QMainWindow, QMessageBox, QToolBar)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
toolBar = QToolBar()
self.addToolBar(toolBar)
fileMenu = self.menuBar().addMenu("&File")
loadAction = QAction("Load...", self, shortcut="Ctrl+L", triggered=self.load)
fileMenu.addAction(loadAction)
toolBar.addAction(loadAction)
exitAction = QAction("E&xit", self, shortcut="Ctrl+Q", triggered=self.close)
fileMenu.addAction(exitAction)
aboutMenu = self.menuBar().addMenu("&About")
aboutQtAct = QAction("About &Qt", self, triggered=qApp.aboutQt)
aboutMenu.addAction(aboutQtAct)
self.axWidget = QAxWidget()
self.axWidget.setControl(r"C:\path\to\file\presentation.pptx")
self.setCentralWidget(self.axWidget)
def load(self):
axSelect = QAxSelect(self)
if axSelect.exec() == QDialog.Accepted:
clsid = axSelect.clsid()
if not self.axWidget.setControl(clsid):
QMessageBox.warning(self, "AxViewer", f"Unable to load {clsid}.")
if __name__ == '__main__':
app = QApplication(sys.argv)
mainWin = MainWindow()
availableGeometry = mainWin.screen().availableGeometry()
mainWin.resize(availableGeometry.width() / 3, availableGeometry.height() / 2)
mainWin.show()
sys.exit(app.exec())
When resizing the window of the GUI, there appear some glitches underneath:
An animation that shows the result while resizing:
Unfortunately I haven't found many resources I could use where a QAxWidget is used in combination with Python to figure this out myself. That's why I'm here to ask if anyone out there might have a solution for getting rid of those glitches.
I got rid of the glitches by installing an event filter to the QAxWidget using self.axWidget.installEventFilter(self).
This will call the eventFilter()-method of the QMainWindow which I set up like this: (ReportDefinitionTool is the subclass of QMainWindow here.)
def eventFilter(self, widget: QWidget, event: QEvent):
if event.type() == QEvent.Resize and widget is self.pptx_axwidget:
self.pptx_axwidget.setFixedHeight(int(self.pptx_axwidget.width() / 16 * 9))
return super(ReportDefinitionTool, self).eventFilter(widget, event)
Since the PowerPoint-presentation is displayed in a 16:9 format, this will make sure the QAxWidget does only occupy this space. The glitchy space from the initial question came from the unused space of the QAxWidget.
I'm currently doing a tutorial on how to create GUI apps from a book called "Modern PyQt" by Joshua Willman and I'm stuck right out of the gate:
I've copy pasted a basic code snippet from the book and tried to tweak it bit by bit instead of reading a bunch of text without any practical trial and error. I can display a window and adjust it's size and properties, but when I try to attach other widgets to the main window, they just don't show up.
Here's what I've got so far:
# First practical example from the book: Pomodoro time manager
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
from PyQt5.QtGui import QIcon
class Pomodoro(QWidget):
def __init__(self): # Create default constructor
super().__init__()
self.initializeUI()
def initializeUI(self):
"""Initialize the window and display its contents to the screen."""
self.setGeometry(int((SCREEN_WIDTH-WINDOW_WIDTH)/2),int((SCREEN_HEIGHT-WINDOW_HEIGHT)/2),WINDOW_WIDTH,WINDOW_HEIGHT) # x,y,w,h
self.setWindowTitle('Bortism')
# self.setWindowIcon(QIcon("Borticon.png"))
self.button1 = QPushButton()
self.button1.setText('Button')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
WINDOW_WIDTH, WINDOW_HEIGHT = 1000,600
SCREEN_X, SCREEN_Y, SCREEN_WIDTH, SCREEN_HEIGHT = app.desktop().screenGeometry().getRect()
window = Pomodoro()
sys.exit(app.exec_())
For a widget to be shown as part of another then the requirement is that the first is the child of the second. This can be done by passing the parent or by using a layout (which is also passed to it by the parent). So in your case you must change to:
self.button1 = QPushButton(self)
#!/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 created a QLabel and set it's movie to a QMovie object with an animated gif. Then in the resizeEvent of my app I resize and move the label, so that it is centered/fit to the layout. This works fine but the movie has a lot of fine lines which get totally garbled in the resize operation, it seems there is no anti-aliasing. So either I am using the wrong resize method, or I need to set anti-aliasing somewhere right? There is nothing in the QMovie or QLabel documentation to suggest how to do this. I did read that the QMovie is inherited from a QImageReader, though that too has no property for anti-aliasing that I could find.
EDIT
I did somewhat get this to work, but it's still not quite right. I found that QMovie has a setScaledSize method which actually scales the underlying QImageViewer. Then I just have the label adjust to it's contents, namely the movie. Using the following code I am able to resize the movie with proper anti-aliasing, however it is quite "jumpy" and "flickery" during resize so obviously I'm not doing this quite "right". Also it somehow loses it's aspect ratio sometimes. Still looking for the correct way to do this... Maybe a QLabel is the wrong way to go?
Here's a working example
import sys
from PyQt4 import QtGui
class MovieTest(QtGui.QDialog):
def __init__(self):
super(MovieTest, self).__init__()
layout = QtGui.QVBoxLayout()
self.setLayout(layout)
self.loading_lbl = QtGui.QLabel()
self.loading_lbl.setSizePolicy(QtGui.QSizePolicy.Ignored, QtGui.QSizePolicyIgnored)
self.loading_lbl.setScaledContents(True)
layout.addWidget(self.loading_lbl)
loading_movie = QtGui.QMovie("loading-radial_loop.gif") # some gif in here
self.loading_lbl.setMovie(loading_movie)
loading_movie.start()
self.setGeometry(50,50,100,100)
self.setMinimumSize(10,10)
def resizeEvent(self, event):
rect = self.geometry()
size = min(rect.width(), rect.height())
movie = self.loading_lbl.movie()
movie.setScaledSize(QtCore.QSize(size, size))
self.loading_lbl.adjustSize()
def main():
app = QtGui.QApplication(sys.argv)
ex = MovieTest()
ex.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Ok I have this figured out now, with just a few tweaks to the code in my edited post. The secret is in keeping the label the full size of it's parent rect (in this case the size of the whole layout) and then scaling the movie within the label. Essentially you are internally scaling the movie, instead of having it automatically fill the contents of the label. From what I can tell this changes around the order of operations a bit and allows the movie to scale itself on render, instead of rendering the frame and then scaling it to the label size.
Working code:
import sys
from PyQt4 import QtGui, QtCore
class MovieTest(QtGui.QDialog):
def __init__(self):
super(MovieTest, self).__init__()
layout = QtGui.QVBoxLayout()
self.setLayout(layout)
self.loading_lbl = QtGui.QLabel()
self.loading_lbl.setStyleSheet('border: 1px solid red') # just for illustration
self.loading_lbl.setAlignment(QtCore.Qt.AlignCenter)
layout.addWidget(self.loading_lbl)
loading_movie = QtGui.QMovie("loading-radial_loop.gif")
self.loading_lbl.setMovie(loading_movie)
loading_movie.start()
self.setGeometry(50,50,100,100)
self.setMinimumSize(10,10)
def resizeEvent(self, event):
rect = self.geometry()
size = QtCore.QSize(min(rect.width(), rect.height()), min(rect.width(), rect.height()))
movie = self.loading_lbl.movie()
movie.setScaledSize(size)
def main():
app = QtGui.QApplication(sys.argv)
ex = MovieTest()
ex.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
A QtGui.QLineEdit line_edit widget is placed inside of QtGui.QFormLayout Form layout using .addRow() method.
my_formLayout.addRow(my_label, my_lineEdit)
To make a line_edit widget to stick to a dialog window's edges (so it re-sizes with the dialog) tried using sizePolicy:
sizePolicy = my_lineEdit.sizePolicy()
sizePolicy.setHorizontalStretch(1)
my_lineEdit.setSizePolicy( sizePolicy )
There are no errors. But the line_edit widget still doesn't stick to the edges of the dialog... What could be wrong?
You shouldn't need to do anything.
This simple example resizes as necessary:
from PyQt4 import QtGui
class Dialog(QtGui.QDialog):
def __init__(self):
super(Dialog, self).__init__()
form = QtGui.QFormLayout(self)
label = QtGui.QLabel('Label', self)
edit = QtGui.QLineEdit(self)
form.addRow(label, edit)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Dialog()
window.setGeometry(500, 300, 300, 50)
window.show()
sys.exit(app.exec_())
UPDATE:
Okay, it seems the behaviour of QFormaLayout is platform-dependent. To quote from the docs:
Style based on the Mac OS X Aqua guidelines. Labels are right-aligned, the fields don't grow beyond their size hint, and the form is horizontally centered.
However, there is a setFieldGrowthPolicy method, which could be used to over-ride the default behaviour on Mac OSX. So try:
my_formLayout.setFieldGrowthPolicy(QtGui.QFormLayout.ExpandingFieldsGrow)
or:
my_formLayout.setFieldGrowthPolicy(QtGui.QFormLayout.AllNonFixedFieldsGrow)
Try this: sizePolicy.setHorizontalPolicy(QSizePolicy.Expanding)