How can I set just `Widget` size? - qt

How can I set just Widget size?
My code:
from PySide.QtGui import QApplication, QWidget, QLabel
import sys
app = QApplication(sys.argv)
mainWindow = QWidget()
gameWidget = QWidget(mainWindow)
#gameWidget.setGeometry(gameWidth, gameHeight) <-- I want to set size of gameWidget such like this. How can I do this.
gameWidget.move(100,0)
gameLabel = QLabel("This is game widget", gameWidget)
mainWindow.show()
Output:
Description:
This will create Window that contain Widget.
I want to set this Widget size. I know There is a method Widget.setGeometry, but it takes 4 parameter (x, y, width, height). I want a method like Widget.setGeometry which takes just size parameter (width, height).
P.S.
Feel free to modify my question. Because I'm always learning English!!
Thanks.

Just use QWidget.resize(weight, height).
For example:
gameLabel.resize(200, 100);
Besides, you can also use QWidget.sizeHint() to get a proper size automatically, for example:
gameLabel.resize(gameLabel.sizeHint());

Related

Window resizing based on label size doesn't consider title bar in pyQT5

I'm coding a simple image viewer and would like for the window to resize based on the image that I open.
The window I'm using is a QMainWindow and has a toolbar. The only widget I have is a QLabel which is set as the central widget. When I open the image I use self.resize(self.label.sizeHint()), but the window size doesn't take into account the size of the title bar and the toolbar, so for example if I open a 400x400 image the window will be of the correct width, but a little bit too short.
What would be the correct way to calculate the correct window size so that it resizes correctly on every platform? (Windows, macOS, Linux)
EDIT: the minimal code is:
import PyQt5.QtWidgets as w
import PyQt5.QtGui as g
import PyQt5.QtCore as c
import sys
class ImageViewerWindow(w.QMainWindow):
def __init__(self):
super().__init__()
self.loadedImagePaths = []
self.imageIndex = 0
self.scrollArea = w.QScrollArea()
self.label = w.QLabel()
self.setCentralWidget(self.scrollArea)
self.scrollArea.setWidgetResizable(True)
self.label.setAlignment(c.Qt.AlignCenter)
self.label.setMinimumSize(1,1)
# Actions
self.openAction = w.QAction("Open...", self)
self.openAction.setShortcut(g.QKeySequence.Open)
self.openAction.triggered.connect(self.openMenuDialog)
# Toolbar elements
toolbar = w.QToolBar("Top toolbar")
toolbar.setMovable(False)
toolbar.setContextMenuPolicy(c.Qt.PreventContextMenu)
self.addToolBar(toolbar)
# Status bar elements
self.setStatusBar(w.QStatusBar(self))
# Add actions to toolbar and menu
toolbar.addAction(self.openAction)
def showImageAtIndex(self, index):
image = g.QPixmap(self.loadedImagePaths[index])
self.label.setPixmap(image)
self.scrollArea.setWidget(self.label)
self.imageIndex = index
self.angle = 0
self.label.adjustSize()
self.resize(self.label.sizeHint())
def openMenuDialog(self, firstStart = False):
self.loadedImagePaths, _ = w.QFileDialog.getOpenFileNames(parent=self, caption="Select one or more JPEG files to open:", filter="JPEG Image(*.jpg *.jpeg)")
if self.loadedImagePaths:
if firstStart:
self.show()
self.imageIndex = 0
self.showImageAtIndex(self.imageIndex)
elif firstStart:
sys.exit()
a = w.QApplication([])
ivw = ImageViewerWindow()
ivw.openMenuDialog(firstStart = True)
a.exec()
If you try and open an image and then resize the window you will notice that some of the image is covered by the title bar and the status bar.
The main problem is that using setWidgetResizable():
the scroll area will automatically resize the widget in order to avoid scroll bars where they can be avoided
So you have to remove that line, or use setFixedSize() on the label using the image size.
Then, calling adjustSize() on the label is not enough, as you actually need to call adjustSize() against the top level window: this is because calling resize() with the image size won't consider all other widgets in the window (in your case, the toolbar and status bar).
Unfortunately, that won't be enough, as QScrollArea caches the size hint of the widget, and calling again setWidget() with the same widget is useless.
The easiest solution is to use a subclass of QScrollArea and reimplement the sizeHint().
Finally, the alignment only has effect on the label contents, but when the widget is added to a container the alignment has to be set for the widget.
class ScrollAreaAdjust(w.QScrollArea):
def sizeHint(self):
if not self.widget():
return super().sizeHint()
frame = self.frameWidth() * 2
return self.widget().sizeHint() + c.QSize(frame, frame)
class ImageViewerWindow(w.QMainWindow):
def __init__(self):
super().__init__()
self.loadedImagePaths = []
self.imageIndex = 0
self.scrollArea = ScrollAreaAdjust()
self.label = w.QLabel()
self.setCentralWidget(self.scrollArea)
self.scrollArea.setWidget(self.label)
self.scrollArea.setAlignment(c.Qt.AlignCenter)
# ...
def showImageAtIndex(self, index):
image = g.QPixmap(self.loadedImagePaths[index])
self.label.setPixmap(image)
self.label.setFixedSize(image.size())
self.imageIndex = index
self.angle = 0
self.scrollArea.updateGeometry()
self.adjustSize()
Note that the size hint of a top level window will only be respected until the size doesn't exceed 2/3 of the screen size. This means that if the image will force the window to a slightly bigger size, at least one scroll bar will be shown, even if it's not strictly necessary.
There is no obvious nor universal solution for that, and you need to find your own way. For instance, you can check if the scroll bars are visible after adjusting the size and eventually compare the size of the image and that of the scroll area's viewport, then if one of the image dimensions is just smaller by the size of the opposite scroll bar, force a resizing of the top level window by that scroll bar size.

How to support high-res pixmap in qgraphicsproxywidget when zooming

I have a qgraphicsview with a scene that contains a qgraphicsproxywidget. The widget currently shows some hi-res images in a square about 25x25 via QPixmap. I'm looking for the proper approach to support zooming in on the image without deteriorating its resolution so much. I've found hints that it might be possible by overriding the paint method (ie derive from QPixmap then override paint method), or by using QImage, or some configuration options (I have tried setSmoothRendering on the QGraphicsView but this only helps a little), but it's not clear if these techniques apply when the image is in a widget in a graphics view.
I wrote the following program (actually, my colleague Colin did, I simplified it for posting) that shows the technique I use. Once you save the attached image and run the program, position the mouse over the pixmap, and press + several times to zoom in: notice how the pixmap is pixelated, whereas the text is perfect.
from PyQt5.QtCore import Qt
from PyQt5.Qt import QPixmap
from PyQt5.QtGui import QPainter
from PyQt5.QtWidgets import (QApplication, QGraphicsScene, QWidget,
QGraphicsView, QGraphicsProxyWidget, QLabel, QVBoxLayout, QHBoxLayout)
class TestGraphicsWidget(QWidget):
def __init__(self):
super().__init__()
self.setupUi(self)
def setupUi(self, TEST):
TEST.resize(400, 300)
self.verticalLayout = QVBoxLayout(TEST)
self.widget = QWidget(TEST)
self.horizontalLayout = QHBoxLayout(self.widget)
self.image_label = QLabel(self.widget)
self.image_label.setStyleSheet("border: 2px solid red;")
self.horizontalLayout.addWidget(self.image_label)
self.text_label1 = QLabel(self.widget)
self.text_label1.setStyleSheet("border: 2px solid red;")
self.horizontalLayout.addWidget(self.text_label1)
self.verticalLayout.addWidget(self.widget)
self.text_label2 = QLabel(TEST)
self.text_label2.setStyleSheet("border: 2px solid red;")
self.verticalLayout.addWidget(self.text_label2)
TEST.setWindowTitle("Form")
self.text_label1.setText("TEXT LABEL 1")
self.text_label2.setText("TEXT LABEL 2")
class TestView(QGraphicsView):
def __init__(self):
super().__init__()
scene = QGraphicsScene(self)
scene.setItemIndexMethod(QGraphicsScene.NoIndex)
scene.setSceneRect(-400, -400, 800, 800)
self.setScene(scene)
self.setCacheMode(QGraphicsView.CacheBackground)
self.setViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate)
self.setRenderHint(QPainter.Antialiasing)
self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QGraphicsView.AnchorViewCenter)
test_widget = TestGraphicsWidget()
test_widget.image_label.setFixedSize(25, 25)
test_widget.image_label.setScaledContents(True)
test_widget.image_label.setPixmap(QPixmap(r"chicken.jpg"))
proxy = QGraphicsProxyWidget()
proxy.setWidget(test_widget)
proxy.setPos(-100, -100)
scene.addItem(proxy)
self.scale(0.8, 0.8)
self.setMinimumSize(400, 400)
def keyPressEvent(self, event):
key = event.key()
if key == Qt.Key_Plus:
self.scaleView(1.2)
elif key == Qt.Key_Minus:
self.scaleView(1 / 1.2)
else:
super().keyPressEvent(event)
def scaleView(self, scaleFactor):
self.scale(scaleFactor, scaleFactor)
if __name__ == '__main__':
import sys
app = QApplication([])
widget = TestView()
widget.show()
sys.exit(app.exec_())
Although this is not all that surprising because the pixmap is downsampled into a 25x25 square of pixels then scaled up by the view, I wouldn't be surprised if there is a qt-specific technique I'm missing, like perhaps something that can be done by overriding the paint of QPixmap to take into account the current scale of the view. I have no choice about using QGraphicsView or embedding an image in a QGraphicsProxyWidget, but I have freedom on image format, the configuration of view or the class to use to load the image into qt, etc.
Any help would be really appreciated.
You appear to be asking: how do I downscale a 600x600 jpg to 25x25 without pixelation? The answer to which is obviously: you can't.
If you put a crappy little 25x25 jpg image in a graphics-view and wind the scale in and out, it's just like standing closer or further away from it. The image doesn't change at all: only your view of it does. And the closer you are to it, the more its intrinsic crappiness is revealed.
So one solution would appear to be: start with a much bigger subject. Resize the widget to, say, four times its original size (and likewise the image label), and then scale down the graphics view to get back to the widget's original starting size.

Autodesk Maya model panel resize event

I'm writing a simple tool menu for Maya, and I'd like to stick it to the border of model panel (perspective).
from PySide import QtCore, QtGui
from maya import OpenMayaUI as omui
from shiboken import wrapInstance
class TestWidget(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent = self.getMayaWindow())
self.setWindowFlags(QtCore.Qt.Tool | QtCore.Qt.FramelessWindowHint)
self.setFixedSize(100, 100)
panelPtr = omui.MQtUtil.findControl('modelPanel4')
panel = wrapInstance(long(panelPtr), QtGui.QWidget)
position = panel.mapToGlobal(panel.pos())
self.move(position.x(), position.y() + panel.geometry().height() / 2 - self.geometry().height() / 2)
mainLayout = QtGui.QVBoxLayout(self)
button = QtGui.QPushButton('CLOSE')
button.setFixedSize(80, 80)
button.clicked.connect(self.deleteLater)
mainLayout.addWidget(button)
def getMayaWindow(self):
omui.MQtUtil.mainWindow()
ptr = omui.MQtUtil.mainWindow()
return wrapInstance(long(ptr), QtGui.QWidget)
w = TestWidget()
w.show()
The main widget is positioned exactly where I want when it is created (horizontally on the left side of model panel, vertically - in the middle of model panel).
I need to reposition it accordingly when the model panel is resized, but model panel does not emit resized() signal. I'd appreciate any advise.
I've been trying many things to get this working yesterday. I did some additionnal researches today and came to this topic: cgsociety: Creating a floating button inside the viewport
In case of broken link, this is one of the answer:
You can use geometry but there are some issues with triggering
commands based on selection and the undo queue. If you want to go that
route, I would suggest looking into zooHud and zooTriggers (Part of
the zooToolbox)
If you are wanting actual GUI control parented to the viewport, mel
only offers hudslider, hudbutton, and headsUpMessage.
You can also use PyQt and parent in your own custom widgets/layouts or
whatever you want using something like this:
import maya.OpenMayaUI as apiUI import sip
from PyQt4 import QtGui
view = apiUI.M3dView()
apiUI.M3dView.getM3dViewFromModelPanel('modelPanel4', view)
viewWidget = sip.wrapinstance(long(view.widget()), QtCore.QObject)
global myBtn
myBtn = QtGui.QPushButton(viewWidget)
myBtn.setText('testing!')
myBtn.move(100, 100) #Relative to top-left corner of viewport myBtn.show()
You can do anything a full qt widget can do with that, so it's
extremely flexible. but it would require having PyQt installed, which
can be a barrier depending on your tools distribution.
I did a mix of this answer and your code:
from PySide import QtCore, QtGui
from maya import OpenMayaUI as omui
from shiboken import wrapInstance
class CustomQWidget(QtGui.QWidget):
def __init__(self, *args, **kwargs):
QtGui.QWidget.__init__(self, *args, **kwargs)
mainLayout = QtGui.QVBoxLayout(self)
closeButton = QtGui.QPushButton('CLOSE')
closeButton.setFixedSize(80, 40)
closeButton.clicked.connect(self.deleteLater)
helloButton = QtGui.QPushButton('HELLO')
helloButton.setFixedSize(80, 40)
helloButton.clicked.connect(self.printHello)
#Trying to fix glitchy background / Doesn't work, why?
#Is it because layouts don't have background?
p = self.palette()
p.setColor(self.backgroundRole(), QtCore.Qt.red)
self.setPalette(p)
self.setAttribute(QtCore.Qt.WA_StyledBackground, True)
##############################
mainLayout.addWidget(closeButton)
mainLayout.addWidget(helloButton)
def printHello(self):
print "Hello"
view = omui.M3dView()
omui.M3dView.getM3dViewFromModelPanel('modelPanel4', view) #Given the name of a model panel,
#get the M3dView used by that panel. If this fails, then a panel with the given name could not be located.
viewWidget = wrapInstance(long(view.widget()), QtGui.QWidget)
position = viewWidget.mapToGlobal(viewWidget.pos())
w = CustomQWidget(viewWidget)
w.move(0, viewWidget.geometry().height() / 2 - 100 / 2) #Relative to middle-left corner of viewport
w.show()
One of the issue I have it that the background of the widget is glitched:
If anyone knows why and how to fix it, I'll edit my answer with pleasure.
Else, when running this script from Maya's script editor, the widget follows the panel when it is resized.
I did fix such a problem, but not using Python/PyQt.
The problem itself is, that your Qt Widget is there. I have not found a way to make it not paint its background.
My solution was different: I derived from a Qt Layout, pushed all my widgets into that layout and used MQtUtil to get the QWidget of that modelPanel's modelEditor to attach the "real Qt layout" to it.
Heavy caveat that may make Python not suited: Maya doesn't expect "non-Maya" Layouts to be bound to "real-Maya" Widgets like modelEditors. So you need to listen to QEvents and find out when to destroy your layout, so Maya doesn't crash trying.
set autofillbackground True to fix your background painting issue

qt grow-only size policy

I have QLabel and this width shrink to content size.
How can I set size policy to grow-only policy?
for example, I set text to "12345" and QLabel should grow to minimal size to show this content, but when I set "12" this wouldnt shrink and stay old size.
Well, unless you don't need to use the space you want to recover for others widgets, you can use a QtGui.QSpacerItem; see QSpacerItem Class Reference.
You can set the size policy to the spacer item.
Note: This gets done automatically when you're using QtDesigner.
In QtDesigner, it should look like this:
The label has a cyan background for ilustrative purposes.
Here, you have some code sample:
Warning: the code has been written in Python. I don't know your lenguage preferences. But the interface is pretty much the same. Goog luck.
import PyQt4.QtGui as gui
import PyQt4.QtCore as core
import sys
if __name__ == "__main__":
app = gui.QApplication(sys.argv)
widget = gui.QWidget(None)
label = gui.QLabel(widget)
label.setStyleSheet("background-color: cyan;")
label.setMinimumWidth(10)
label.setText("Write some text above")
text = gui.QLineEdit(widget)
layout = gui.QVBoxLayout(widget)
hlayout = gui.QHBoxLayout(widget) # This layout will contains
# the label and a sacer item.
layout.addWidget(text)
hlayout.addWidget(label) # Add the label.
# Create the spacer.
spacerItem = gui.QSpacerItem(40, 20, gui.QSizePolicy.Expanding, gui.QSizePolicy.Minimum)
hlayout.addItem(spacerItem) # Add the spacer.
layout.addItem(hlayout)
# Slot for change the text of the label.
#core.pyqtSlot()
def on_textChanged():
label.setText(text.text())
text.textChanged.connect(on_textChanged)
widget.show()
sys.exit(app.exec_())

QListWidget adjust size to content

Is it possible to adjust QListWidget height and width to it's content?
sizeHint() always returns 256, 192 no matter what its content is.
QListWidgetItem's sizeHint() returns -1, -1, so I can not get content width.
Problem the same as here - http://www.qtcentre.org/threads/31787-QListWidget-width , but there is no solution.
import sys
from PyQt4.QtGui import *
class MainWindow(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
list = QListWidget()
list.addItem('111111111111111')
vbox = QVBoxLayout(self)
vbox.addWidget(list)
app = QApplication(sys.argv)
myapp = MainWindow()
myapp.show()
sys.exit(app.exec_())
sizeHint() always returns 256, 192 no
matter what its content is.
Thats because this is the size of the QListWidget, the viewport, not the items. sizeHintForColumn() will give you the max size over all items, so you can resize the widget like this:
list.setMinimumWidth(list.sizeHintForColumn(0))
If you don't want to force minimum width, then subclass and provide this as the size hint instead. E.g.:
class ListWidget(QListWidget):
def sizeHint(self):
s = QSize()
s.setHeight(super(ListWidget,self).sizeHint().height())
s.setWidth(self.sizeHintForColumn(0))
return s
Using takois answer I played around with the sizeHintForColumn or sizeHintForRow and found that you have to add slightly larger numbers, because there might be some style dependent margins still. ekhumoros comment then put me on the right track.
In short the full size of the list widget is:
list.sizeHintForColumn(0) + 2 * list.frameWidth()
list.sizeHintForRow(0) * list.count() + 2 * list.frameWidth())
According to the comment by Violet it may not work in Qt 5.
Also be aware that setting the size to the content, you don't need scrollbars, so I turn them off.
My full example for a QListWidget ajusted to its content size:
from PySide import QtGui, QtCore
app = QtGui.QApplication([])
window = QtGui.QWidget()
layout = QtGui.QVBoxLayout(window)
list = QtGui.QListWidget()
list.addItems(['Winnie Puh', 'Monday', 'Tuesday', 'Minnesota', 'Dracula Calista Flockhart Meningitis', 'Once', '123345', 'Fin'])
list.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
list.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
list.setFixedSize(list.sizeHintForColumn(0) + 2 * list.frameWidth(), list.sizeHintForRow(0) * list.count() + 2 * list.frameWidth())
layout.addWidget(list)
window.show()
app.exec_()
In order to effectively use sizeHint, you have to override it, at least in c++. In my experience, the default implementations for widgets can be pretty useless when you want a specific behavior. Attempts to force what you want with spacers or layouts end in disaster. If you can derive from QListWidget and override sizeHint, you can iterate through your items and find the longest string, then do some kind of magic to determine how wide it should be.
That's what I'd do, anyway.
First you should get your largest string in the list, that is easy to obtain.
After you get that string, do the following:
QFontMetrics * fm = new QFontMetrics(widget->font());
QRect rect;
rect = fm->boundingRect(string);
rect.width() has the width in pixels of the largest string
rect.height() has it's height.
Set the QListWidget width to that rect's width (plus the margins)
and it's height to that rect's height times the number of items
I didn't test the code, but hope it puts you on the right track
QListWidget *valList;
valList = new QListWidget(this);
valList->setSizePolicy (QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored));
valList->setMinimumSize (QSize(1111, 111));
You need to get the QHeaderView of your QListWidget and adjust its resize mode.
Read this documentation for more information
http://doc.qt.nokia.com/latest/qheaderview.html#ResizeMode-enum

Resources