How to add stretch for QGridLayout in PyQt5? - python

I created widgets in a grid-layout. The widgets are stretching based on the window. Is it possible to avoid the stretching and align them as shown in picture below? I created a code to achieve this, but I feel it is not a straightforward solution. If there are any better solutions to achieve this, please share them.
Grid layout result:
from PyQt5.QtWidgets import *
app =QApplication([])
window=QWidget()
GL=QGridLayout(window)
GL.addWidget(QPushButton('R1C1'),0,0)
GL.addWidget(QPushButton('R1C2'),0,1)
GL.addWidget(QPushButton('R2C1'),1,0)
GL.addWidget(QPushButton('R1C1'),1,1)
window.showMaximized()
app.exec_()
Required Result:
My code:
from PyQt5.QtWidgets import *
app =QApplication([])
window=QWidget()
VL=QVBoxLayout(window);HL=QHBoxLayout();VL.addLayout(HL)
GL=QGridLayout();HL.addLayout(GL)
GL.addWidget(QPushButton('R1C1'),0,0)
GL.addWidget(QPushButton('R1C2'),0,1)
GL.addWidget(QPushButton('R2C1'),1,0)
GL.addWidget(QPushButton('R1C1'),1,1)
HL.addStretch();VL.addStretch()
window.showMaximized()
app.exec_()

The QGridLaout class doesn't have any simple convenience methods like QBoxLayout.addStretch() to do this. But the same effect can be achieved by adding some empty, stretchable rows/columns, like this:
GL.setRowStretch(GL.rowCount(), 1)
GL.setColumnStretch(GL.columnCount(), 1)

Related

How PySide6 paintEvent function for a PushButton works?

A long time ago, I wanted to make a logo appear on top of the text in a QPushButton stacked on top of each other, but I couldn't find anyway
I read some stylesheets (couldn't find a single doc to read it all about all styles I can apply to a button)
tried the setLayoutDirection (RightToLeft and LeftToRight were there, but no UpToDown direction)
In my (I wish) last attempt I tried to inherit a QAbstractButton (I didn't find QAbstractPushButton, so I guess QAbstractButton is the answer) and change its paintEvent/paintEngine to draw an image or maybe add a vbox inside it as a layout to draw to components, but I can't find anything in python (specially PySide) which has an example in any possible way close to that. The best thing I found was the analogue clock example which was not very helpful because it was trying to work a QWidget and not a QAbstractButton and I want to keep the feel of a Native looking button.
I like my final product to be something like this.
source of the implemention of that
Python Enaml toolkit supported this feature out of the box (in one of its widgets), and I know it is QT based, so I really wish to know how it is possible?
p.s.: Also, is there a market for qt widgets? e.g.: a plugin system. Because rewriting an android like switch doesn't seem like the correct thing that I should do! even a good tutorial or doc would be appreicated (excluding official doc)
It is easier than you think, you can use QToolButton() like this:
import sys
from PySide6.QtCore import Qt, QSize
from PySide6.QtWidgets import QApplication, QVBoxLayout,QStyle, QWidget,
QToolButton
class Window(QWidget):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
button = QToolButton()
# here you choose the position of the icon and its text
button.setToolButtonStyle(
Qt.ToolButtonStyle.ToolButtonTextUnderIcon)
# here I just use built-in icon by PySide6 for this example
name = 'SP_DialogSaveButton'
pixmapi = getattr(QStyle, name)
icon = self.style().standardIcon(pixmapi)
# here we set text and icon of size 32x32 to the button
button.setIcon(icon)
button.setText("Sample text")
button.setIconSize(QSize(32, 32))
# finally we add our button to the layout
lay = QVBoxLayout(self)
lay.addWidget(button, alignment=Qt.AlignCenter)
if __name__ == "__main__":
app = QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec())

Modify Qdial with a QLabel

I have a QDial widget that I want to beautify the circular edge of this widget by adding a QLable as the following figure. However, I think this makes the QLabel the parent widget, and the QDial no further works!
Below is also my simple code.
from PyQt5.QtWidgets import *
from PyQt5 import QtCore, QtGui
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.setGeometry(200, 200, 500, 500)
self.UiComponents()
self.show()
def UiComponents(self):
dial = QDial(self)
dial.setGeometry(150, 150, 200, 200)
label_1 = QLabel('', self)
label_1.move(168, 168)
label_1.resize(164, 164)
label_1.setStyleSheet("border: 4px solid gray; border-radius: 82px;")
App = QApplication(sys.argv)
window = Window()
sys.exit(App.exec())
The "main" problem is that you're adding the label over the dial, so it won't be able to receive mouse events.
A theoretical solution could be to use label_1.setAttribute(Qt.WA_TransparentForMouseEvents), but that won't be a good idea, for the following reasons:
widget geometries should normally be managed by a layout manager, so you cannot rely on a "guess" done by trial and error: as soon as the window is resized, all geometries will change and you'll end up with a floating circle that will make everything worse;
even assuming you get the positioning right by intercepting the resize event with an event filter, you'd need to manually reset the stylesheet everytime and ensure that it's properly aligned, but that cannot be guaranteed because different size policies and other widgets could change the final radius of the dial;
what you see on your screen is almost never what users will see in theirs, due to lots of reasons including the current OS and QStyle in use; see the following screenshots taken with 3 Qt common styles (Breeze, Oxygen and Windows):
Unfortunately, QDial has never received lots of care from developers, as it's a scarcely used widget that is hard to implement for custom usage. As such, it doesn't support many any appearance features, and there's also no stylesheet configuration.
If you want to change the look of the dial, the only safe possibility is to subclass it, override its paintEvent() and paint it on your own.

PyQt5: Progamically adding QWidget to layout, doesn't show Qwidget when a spacer is added

Edit: I have tried a few more things. If I move the spacer to a layout below the spacer I am adding to it doesn't exibit the same exact behavior. Its still not optimal, and isn't going to work because my end goal is to have a scrollArea with a spacer inside that i add my widgets to, but this would not look right. I think the problem is the widgets are getting to a size zero I just do not know why or how to fix it.
I have two ui files made in QtDesigner. The first file is my main window at the start of the program I load the second ui file and place it into a vertical spacer in the middle of the first one. Additional copies are placed each time a button is clicked.
This all works great until I add a vertical spacer to push the items to the top. I have tired adding it from Designer and in the code. Both have the same result.
I have looked on google quite a bit and tried a lot of suggestions.
I tried setting the second ui files parent as a Qwidget I added on the first that contained the vertical layout.
I tried setting the minimum sizes and sizing polices to various things.
Below is my current code, any ideas or suggestions would be appreciated!
#!python3
import sys
from PyQt5 import QtWidgets, QtCore, uic
class TimesheetWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(TimesheetWidget, self).__init__(parent)
self.parent = parent
self.tableRows = dict()
def setup(self):
self.labelSaved.hide()
self.addTableRow()
def addTableRow(self):
thisRow = len(self.tableRows)
self.tableRows[thisRow] = uic.loadUi("gui/tableRow.ui")
self.tableRows[thisRow].addButton.clicked.connect(self.addTableRow)
self.spacer.addWidget(self.tableRows[thisRow])
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
timesheet = TimesheetWidget()
Mytimesheet = uic.loadUi("gui/timesheet.ui", baseinstance=timesheet)
Mytimesheet.setup()
Mytimesheet.show()
sys.exit(app.exec_())
here is a link to the ui files (they are to long to post):
gist link for ui files
I finally fixed this by trying random things over and over until something worked.
It turned out that on my second ui file I did not have a top level layout. (I am not sure if that's what its called.)
To fix this I right clicked on the top level widget and choose layout and selected horizontal layout, although I think any would have worked.
Here is a picture that shows the top level widget with the cancel symbol on it. Once I added the layout that went away and everything worked!

How to embed vispy graph in PyQt?

I'm trying to embed a vispy plot (more specifically, a Vispy SceneCanvas) as a QWidget into PyQt4. I would presume the answer would be something like this:
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import vispy.mpl_plot as plt
app = QApplication(sys.argv)
win = QMainWindow()
plt.plot([1,2,3,4], [1,4,9,16])
vispyCanvas=plt.show()[0]
win.setCentralWidget(vispyCanvas)
However, when I try this the last line gives me the expected error that vispyCanvas is type SceneCanvas and not of type QWidget. When I print(vispyCanvas), it prints out <Vispy canvas (PyQt4 (qt) backend) at 0x142bcb00L>, which is why I suspect that it should be possible to treat it or one of its attributes as a QWidget object.
The answer is simple:
win.setCentralWidget(vispyCanvas.native)
As long as vispy is using Qt as its backend, then Canvas.native refers to the underlying QGLWidget.

Generate PyQt Legend

I'm trying to build myself a simple graphics calculator, as a way of teaching myself PyQt. I'd like the calculator to have a pane which lists all of the equations plotted and shows the line style used.
I began with a QListWidget to achieve these ends, the pane contains a scrollable list of equations, but does not show the line style because the QListWidget only allows strings or icons.
So I tried using a QAbstractScrollArea widget instead. For each equation I use QHBoxLayout to produce two Widgets, a label containing the equation string, and a QWidget within which I draw the line style. Then I stack all of the equation QHBoxLayouts within the ScrollArea using QVBoxLayout.
The problem is that QVBoxLayout uses all of the space available to it. So if I have only three equations, they are spread throughout the pane and not listed at the top as I'd like them to be, while if I have too many equations to fit in the pane they are stacked on top of one another rather than causing the area to be scrollable.
This is how the calculator appears with too many equations...
And this is how it looks with too few...
Does anyone have any suggestions of better ways to get around these issues? One idea is to generate icons programmatically to have the properties of the lines and to use these in the listwidget, is that possible?
It sounds like what you want is a QListWidget with multiple columns - and a QTreeWidget can be adapted to do exactly that.
Here's a simple demo:
from PyQt4 import QtGui, QtCore
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.list = QtGui.QTreeWidget(self)
self.list.setRootIsDecorated(False)
self.list.setUniformRowHeights(True)
self.list.setAllColumnsShowFocus(True)
self.list.setItemsExpandable(False)
self.list.header().hide()
self.list.setColumnCount(2)
for label, color in (
('v=x**2', 'red'),
('v=x/2', 'blue'),
('v=2*x', 'green'),
('v=3*2', 'orange'),
('v=5-x', 'purple'),
):
item = QtGui.QTreeWidgetItem([label, '------------'])
item.setForeground(1, QtGui.QColor(color))
self.list.addTopLevelItem(item)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.list)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 300, 200)
window.show()
sys.exit(app.exec_())

Categories