I had an application that contained a lot of widgets with stylesheet on them, However, I did not add any layout to interface, It neither had central widget included, But the application was running without any problems.
However, whenever i tried to resize the application (scaling it down) the widgets would not scale, of course.
I had an little research (Because i could not find anything else related to my problem) and i found this on Qt Documentation, stylesheet reference:
"The actual image that is drawn is determined using the same algorithm as QIcon (i.e) the image is never scaled up but always scaled down if necessary."
How can i make stylesheet scale down with window? (If stylesheet has background image on)
For example i have button with stylesheet:
btn = QtGui.QPushButton(self)
btn.move(0, 0)
btn.setObjectName('btn)
btn.setStyleSheet("#btn {background-image: url(':/images/somepicture.png'); border: none; }")
How can i make this button scale down with window, Can i achieve this without layouts? If not how can i do it with layouts? (without it limiting too much)
If you add the button as the central widget to a QMainWindow it should automatically adjust it's size to fit the available space. However, to get the button image to scale, you need to set the image as a border-image stylesheet property (a little strange). A working example for PyQt4:
from PyQt4 import QtGui, QtCore
class MainWindow(QtGui.QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
btn = QtGui.QPushButton(self)
btn.setStyleSheet("border-image: url('somepicture.png');") # Scaled
#btn.setStyleSheet("background-image: url('somepicture.png');") # Not scaled
self.setCentralWidget(btn)
self.show()
app = QtGui.QApplication([])
window = MainWindow()
app.exec_()
Note that you don't need to set an id (objectName) to assign the CSS to a specific widget, you can simply pass in the CSS rule via .setStyleSheet().
You cannot set a layout on QMainWindow as it already has a complex layout system to accommodate docking widgets and toolbars. Therefore, if you want to use a layout to add more than one widget to the window, you need to use a container widget to hold it. The following working example demonstrates this:
from PyQt4 import QtGui, QtCore
class MainWindow(QtGui.QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
w = QtGui.QWidget() # container widget
l = QtGui.QVBoxLayout() # your layout
w.setLayout(l) # set the layout on your container widget
btn = QtGui.QPushButton(self)
btn.setStyleSheet("border-image: url('somepicture.png');")
label = QtGui.QLabel('Hello!')
l.addWidget(btn) # add your widget to the layout
l.addWidget(label) # add the label to the layout
self.setCentralWidget(w) # add the container widget to the QMainWindow
self.show()
app = QtGui.QApplication([])
window = MainWindow()
app.exec_()
If you want to be able to position widgets absolutely, rather than adding them to a layout (which will control their size/position) you can pass the parent element (relative to which x,y coords are taken) when creating it:
from PyQt4 import QtGui, QtCore
class MainWindow(QtGui.QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
w = QtGui.QWidget() # container widget
btn = QtGui.QPushButton(w)
btn.move(100,100)
btn.setStyleSheet("border-image: url('somepicture.png');")
self.setCentralWidget(w) # add the container widget to the QMainWindow
self.show()
app = QtGui.QApplication([])
window = MainWindow()
app.exec_()
But positioning a widget absolutely like this loses you the ability to auto-scale it to fit the parent widget. If you just want some padding/spacing around the element in the window, take a look at .setContentsMargins on the QLayouts, e.g. l.setContentsMargins(50,50,50,50) will put a 50px margin around the button.
Related
I want the child widget to just appear in the center of the parent widget with a horizontal pink stripe. But the widget of the parent becomes very small.
from PySide6 import QtWidgets, QtGui
import sys
class WidgetA(QtWidgets.QWidget):
def __init__(self):
super(WidgetA, self).__init__()
self.wb = WidgetB()
vbox = QtWidgets.QVBoxLayout()
hbox = QtWidgets.QHBoxLayout()
hbox.addWidget(self.wb)
vbox.addLayout(hbox)
self.setLayout(vbox)
class WidgetB(QtWidgets.QWidget):
def __init__(self):
super(WidgetB, self).__init__()
palette = self.palette()
palette.setColor(QtGui.QPalette.Window, QtGui.QColor("#ff00ff"))
self.setPalette(palette)
app = QtWidgets.QApplication()
window = WidgetA()
window.show()
sys.exit(app.exec())
If I have not written something, or something is not clear in my question, then ask, I will supplement it.
The window appears very small, because your example script never sets an explicit size for either the parent or the child. Directly setting the geometry of the child won't help, because it's in a layout, and the layout automatically manages the geometry of the widgets it contains. If you don't want to give the child widget a minimum size, you can use spacers and stretch factors to control the proportion of the space the child widget takes up. See the Layout Management article in the Qt docs for an exellent overview of all the possibilities.
Below is a rewrite of your example that should do what you asked for (i.e. make "the child widget ... appear in the centre of the parent widget with a horizontal pink stripe"). But note that the height of the child widget is proportional, so the layout will adjust its size accordingly whenever the parent widget is manually resized. If you want different proportions, change the stretch factors to suit. I have also reimplemented sizeHint. This provides a sensible initial size for the child, whilst allowing it to remain freely resizeable - but you could also just explicitly set the geometry of the parent window to achieve much the same thing.
from PySide6 import QtCore, QtWidgets, QtGui
class WidgetA(QtWidgets.QWidget):
def __init__(self):
super(WidgetA, self).__init__()
self.wb = WidgetB()
vbox = QtWidgets.QVBoxLayout()
vbox.addStretch(1)
vbox.addWidget(self.wb, 2)
vbox.addStretch(1)
self.setLayout(vbox)
class WidgetB(QtWidgets.QWidget):
def __init__(self):
super(WidgetB, self).__init__()
palette = self.palette()
palette.setColor(QtGui.QPalette.Window, QtGui.QColor("#ff00ff"))
self.setAutoFillBackground(True)
self.setPalette(palette)
def sizeHint(self):
return QtCore.QSize(300, 200)
app = QtWidgets.QApplication(['Test'])
window = WidgetA()
window.show()
app.exec()
I'm getting this weird result when using QMenuBar I've used this exact code before for the QMenuBar and it worked perfectly. But it doesn't show more than 1 QMenu
This is my code:
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import sys
from functools import partial
class MainMenu(QWidget):
def __init__(self, parent = None):
super(MainMenu, self).__init__(parent)
# background = QWidget(self)
lay = QVBoxLayout(self)
lay.setContentsMargins(5, 35, 5, 5)
self.menu()
self.setWindowTitle('Control Panel')
self.setWindowIcon(self.style().standardIcon(getattr(QStyle, 'SP_DialogNoButton')))
self.grid = QGridLayout()
lay.addLayout(self.grid)
self.setLayout(lay)
self.setMinimumSize(400, 320)
def menu(self):
menubar = QMenuBar(self)
viewMenu = menubar.addMenu('View')
viewStatAct = QAction('Dark mode', self, checkable=True)
viewStatAct.setStatusTip('enable/disable Dark mode')
viewMenu.addAction(viewStatAct)
settingsMenu = menubar.addMenu('Configuration')
email = QAction('Set Email', self)
settingsMenu.addAction(email)
if __name__ == '__main__':
app = QApplication(sys.argv)
main = MainMenu()
main.show()
sys.exit(app.exec_())
Result:
I am aware that I am using QWidget when I should be using QMainWindow But is there a workaround???
(I apologize in advance for the terrible quality of the image, there is no good way to take a picture of a QMenuBar)
The problem is that with a QWidget you are not using the "private" layout that a QMainWindow has, which automatically resizes specific children widgets (including the menubar, the statusbar, the dock widgets, the toolbars and, obviously, the "centralWidget").
Remember that a QMainWindow has its own layout (which can't and shouldn't be changed), because it needs that specific custom layout to lay out the aforementioned widgets. If you want to set a layout for the main window, you'll need to apply it to its centralWidget.
Read carefully how the Main Window Framework behaves; as the documentation reports:
Note: Creating a main window without a central widget is not supported. You must have a central widget even if it is just a placeholder.
In order to work around that when using a basic QWidget, you'll have to manually resize the children widgets accordingly. In your case, you only need to resize the menubar, as long as you have a reference to it:
def menu(self):
self.menubar = QMenuBar(self)
# any other function has to be run against the *self.menubar* object
viewMenu = self.menubar.addMenu('View')
# etcetera...
def resizeEvent(self, event):
# calling the base class resizeEvent function is not usually
# required, but it is for certain widgets (especially item views
# or scroll areas), so just call it anyway, just to be sure, as
# it's a good habit to do that for most widget classes
super(MainMenu, self).resizeEvent(event)
# now that we have a direct reference to the menubar widget, we are
# also able to resize it, allowing all actions to be shown (as long
# as they are within the provided size
self.menubar.resize(self.width(), self.menubar.height())
Note: you can also "find" the menubar by means of self.findChild(QtWidgets.QMenuBar) or using the objectName, but using an instance attribute is usually an easier and better solution.
Set minimum width
self.setMinimumSize(320,240)
I've got a PyQt GUI with a QTextEdit in it. I have set a few of the widget settings to play with things like the font size. What I'm seeing is that when I initially type in the field, the settings are applied, but if I delete all text and start typing again, the settings have reset to the default ones. Below is a MWE where I can see this behavior. Just in case it matters, I'm using Python 3.5.1 with PyQt4 4.8.7.
from PyQt4 import QtCore, QtGui
class App(object):
def __init__(self):
self.app = QtGui.QApplication([]) # The main application
self.win = QtGui.QMainWindow() # The main window
self.widget = QtGui.QWidget() # The central widget in the main window
self.grid = QtGui.QVBoxLayout() # The layout manager of the central widget
self.textArea = QtGui.QTextEdit()
self.grid.addWidget(self.textArea)
self.textArea.setMinimumSize(600,300)
self.textArea.setLineWrapMode(QtGui.QTextEdit.NoWrap)
self.textArea.setFontPointSize(12)
self.widget.setLayout(self.grid)
self.win.setCentralWidget(self.widget)
self.win.show()
self.app.exec_()
App()
You can create a new QFont item and then you can use QTextEdit.setFont()
This way it will not reset after all text is deleted.
I want to implement a GUI program like the blueprint editor in the Unreal game engine with PyQt4. Here is an example of the blueprint editor:
First I create a simple container widget to place all the components(The rectangles). In order to allow the user place the rectangles wherever they want(by drag & drop), I can't place my widgets in a layout. Then when the content of the rectangle is changed, the rectangle widget can't auto adjust the size itself.
Following is an example code:
import sys
from PyQt4 import QtCore
from PyQt4 import QtGui
class ChangeableChild(QtGui.QWidget):
def __init__(self, parent=None):
super(ChangeableChild, self).__init__(parent)
self.setLayout(QtGui.QVBoxLayout())
def addWidget(self, widget):
self.layout().addWidget(widget)
class MainWidget(QtGui.QWidget):
def __init__(self, child, parent=None):
super(MainWidget, self).__init__(parent)
child.setParent(self)
def main():
app = QtGui.QApplication(sys.argv)
changeable_child = ChangeableChild()
button = QtGui.QPushButton("Add label")
changeable_child.addWidget(button)
win = MainWidget(changeable_child)
win.show()
button.clicked.connect(
lambda: changeable_child.addWidget(QtGui.QLabel("A label.")))
app.exec_()
if __name__ == "__main__":
main()
When I hit "Add label" button to add a new label. The size of ChangeableChild wouldn't change automatically. If I put the ChangeableChild in a layout, it's all good.
So is there a way to auto adjust my widget when it's not in a layout? Or is there a way I can place my widget in a layout and still can place it in a absolute position?
I am currently working on a small application to do with arranging music lessons and am trying to add a background image. When I add an image it also repeats the image in the menu bar and toolbar. How can I stop this from happening?
Thanks in advance!
import sys
from PyQt4 import QtGui
class windowMain(QtGui.QMainWindow):
def __init__(self):
super(windowMain, self).__init__()
self.initUI()
def initUI(self):
#Layout of window
self.resize(700, 500) #Maximizing main window
self.center()
self.setWindowTitle('Lesson Planner') #Setting window title
self.setStyleSheet("border-image: url(p.jpg);")
self.show() #Showing the window
If you don't specify which widget(s) the stylesheet applies to, it will cascade to all child widgets (i.e. just like CSS). So, as with CSS, you need to use the right selector syntax to apply the background image to the appropriate widget.
For you example, one way to do that would be to set the objectName for the central widget of the main window, and then use that name in the selector:
def initUI(self):
self.centralWidget().setObjectName('CentralWidget')
self.setStyleSheet("""
#CentralWidget { background-image: url(./image.png) }
""")