I'm trying to incorporate a QSplitter. The code works perfectly from a functionality standpoint, but the QSplitter itself doesn't appear correctly under the default PyQt style... possibly because it is itself embedded within a vertical splitter. This is confusing for the user.
If you uncomment out the line (and thus change the default PyQt style), the QSplitter visualizes correctly only when hovered over... however, I also don't want this other style.
Can anyone provide any guidance on this matter?
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class Example(QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
hbox = QHBoxLayout(self)
L_layout = QGridLayout()
R_layout = QGridLayout()
L_widgets = QWidget()
L_widgets.setLayout(L_layout)
R_widgets = QWidget()
R_widgets.setLayout(R_layout)
topleft = QFrame()
topleft.setFrameShape(QFrame.StyledPanel)
btn1 = QPushButton('btn1')
bottom = QFrame()
bottom.setFrameShape(QFrame.StyledPanel)
textedit = QTextEdit()
L_layout.addWidget(topleft, 0, 0, 1, 1)
L_layout.addWidget(btn1, 1, 0, 1, 1)
R_layout.addWidget(textedit)
splitter1 = QSplitter(Qt.Horizontal,frameShape=QFrame.StyledPanel,frameShadow=QFrame.Plain)
splitter1.addWidget(L_widgets)
splitter1.addWidget(R_widgets)
splitter1.setStretchFactor(1,1)
splitter2 = QSplitter(Qt.Vertical)
splitter2.addWidget(splitter1)
splitter2.addWidget(bottom)
hbox.addWidget(splitter2)
self.setLayout(hbox)
#QApplication.setStyle(QStyleFactory.create('Cleanlooks'))
self.setGeometry(300, 300, 300, 200)
self.setWindowTitle('QSplitter demo')
self.show()
def main():
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
EDIT: This is apparently a known macOS bug. When viewed on Linux, the bar of splitter1 has the same look as splitter2. I'll leave this topic open in case anyone else knows of a suitable workaround for mac.
Because the QPushButton has default minimum size, when you want to move splitter to left,
the button has reached its minimum size. So you can not move left anymore, otherwise the left will will collapse.
So if you want the left showing as you want, you can set the minimum size off button widget.
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class Example(QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
hbox = QHBoxLayout(self)
L_layout = QGridLayout()
R_layout = QGridLayout()
L_widgets = QWidget()
L_widgets.setLayout(L_layout)
R_widgets = QWidget()
R_widgets.setLayout(R_layout)
topleft = QFrame()
topleft.setFrameShape(QFrame.StyledPanel)
btn1 = QPushButton('btn1')
btn1.setMinimumWidth(1) # For example : set the minimum width to 1, then you can move left until the btn1 width is 1
bottom = QFrame()
bottom.setFrameShape(QFrame.StyledPanel)
textedit = QTextEdit()
L_layout.addWidget(topleft, 0, 0, 1, 1)
L_layout.addWidget(btn1, 1, 0, 1, 1)
R_layout.addWidget(textedit)
splitter1 = QSplitter(Qt.Horizontal,frameShape=QFrame.StyledPanel,frameShadow=QFrame.Plain)
splitter1.addWidget(L_widgets)
splitter1.addWidget(R_widgets)
splitter1.setStretchFactor(1,1)
splitter2 = QSplitter(Qt.Vertical)
splitter2.addWidget(splitter1)
splitter2.addWidget(bottom)
hbox.addWidget(splitter2)
self.setLayout(hbox)
#QApplication.setStyle(QStyleFactory.create('Cleanlooks'))
self.setGeometry(300, 300, 300, 200)
self.setWindowTitle('QSplitter demo')
self.show()
def main():
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Related
I'm trying to create a layout where is existing a row, and this row should contain an absolute positioned button which should be placed outside of this row.
Here is the simple schema
I did it by just pushing a child button into the parent button (I'm not sure that it's a correct solution) and moved it to some absolute coordinates.
It works but, unfortunately, the child button is clipping by the parent. So it's like overflow: hidden in CSS. But in case of QT I couldn't found how to disable this behavior.
Here is the demo of my current QUI
Is there exists any way to solve it? Or should I just use some widget combination with the empty spacer etc.?
btn = QPushButton("button")
test = QPushButton("X")
test.setParent(btn)
test.move(200, 5)
self.layout.addWidget(btn)
Full code of the UI class (minimal reproducible example)
class MainWindow(QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.layout = QVBoxLayout()
btn = QPushButton("button")
test = QPushButton("X")
test.setParent(btn)
test.move(200, 5)
self.layout.addWidget(btn)
self.setLayout(self.layout)
self.layout.setContentsMargins(0,0,0,74)
self.layout.setSpacing(0)
# self.layout.addStretch(-1)
self.setMinimumSize(640,400)
self.setWindowFlags(Qt.FramelessWindowHint)
Sorry, but the advice of #Heike is absolutely correct and you should not look for wrong solutions.
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
class MainWindow(QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.setMinimumSize(640,400)
self.setWindowFlags(Qt.FramelessWindowHint)
btn = QPushButton("button")
test = QPushButton("X")
test.setParent(btn)
# test.move(200, 5)
# self.layout = QVBoxLayout()
self.layout = QGridLayout()
self.layout.addWidget(btn, 0, 0, 1, 10)
self.layout.addWidget(test, 0, 11, 1, 1)
self.layout.setContentsMargins(0,0,0,74)
self.layout.setSpacing(0)
self.setLayout(self.layout)
if __name__ == '__main__':
import sys
application = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(application.exec_())
I have 2 QPushButton in the app window: btn1 needs to be 5x the height of btn2.
Problem: Tried setting the row span of self.btn1 to 5 using layout.addWidget but the height remains unchanged. did I miss out on a setting?
import sys
from PyQt4 import QtGui, QtCore
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.initUI()
def initUI(self):
layout = QtGui.QGridLayout()
self.btn1 = QtGui.QPushButton('Hello')
self.btn2 = QtGui.QPushButton('World')
layout.addWidget(self.btn1, 1, 1, 5, 1)
layout.addWidget(self.btn2, 6, 1, 1, 1)
centralWidget = QtGui.QWidget()
centralWidget.setLayout(layout)
self.setCentralWidget(centralWidget)
def main():
app = QtGui.QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
You'll need to change the buttons' size policy:
self.btn1.setSizePolicy(
QtGui.QSizePolicy.Preferred,
QtGui.QSizePolicy.Expanding)
self.btn2.setSizePolicy(
QtGui.QSizePolicy.Preferred,
QtGui.QSizePolicy.Preferred)
From Qt doc, by default:
Button-like widgets set the size policy to specify that they may
stretch horizontally, but are fixed vertically.
i.e. The default size policy of QPushButton is Minimum horizontally, and Fixed vertically.
In addition, a simpler way to achieve what you want in the example is to use a QVBoxLayout, and set the stretch factor when calling addWidget(). i.e.
def initUI(self):
layout = QtGui.QVBoxLayout()
self.btn1 = QtGui.QPushButton('Hello')
self.btn2 = QtGui.QPushButton('World')
self.btn1.setSizePolicy(
QtGui.QSizePolicy.Preferred,
QtGui.QSizePolicy.Expanding)
self.btn2.setSizePolicy(
QtGui.QSizePolicy.Preferred,
QtGui.QSizePolicy.Preferred)
layout.addWidget(self.btn1, 5)
layout.addWidget(self.btn2, 1)
centralWidget = QtGui.QWidget()
centralWidget.setLayout(layout)
self.setCentralWidget(centralWidget)
I have 2 issues with QGraphicsView.
I can't get the size of the QGraphicsView object. All methods I'm using is giving me values I wouldn't expect.
If I print out the mouse's position on the area's lower-right corner (scrollbars included), I get a random 400 value. After setting sceneRect to 500 I was expecting to get that back.
from PySide import QtGui, QtCore
class View(QtGui.QGraphicsView):
def __init__(self, parent = None):
super(View, self).__init__(parent)
self.setScene( QtGui.QGraphicsScene(self) )
self.setSceneRect( 0, 0, 500, 500 )
print self.viewport().width() # outputs 96
print self.width() # outputs 100
print self.rect() # outputs QRect(0, 0, 100, 30)
print self.size() # outputs QSize(100, 30)
def mouseMoveEvent(self, event):
print event.pos().toTuple() # prints (413, 423) at lower-right corner
class MainWindow(QtGui.QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.resize(500, 500)
self.view = View(self)
hLayout = QtGui.QHBoxLayout()
hLayout.addWidget(self.view)
buttonsLayout = QtGui.QVBoxLayout()
buttonsLayout.setSpacing(0)
for i in range(10):
newButton = QtGui.QPushButton()
buttonsLayout.addWidget(newButton)
hLayout.addLayout(buttonsLayout)
self.tempButton = QtGui.QPushButton()
mainLayout = QtGui.QVBoxLayout()
mainLayout.addLayout(hLayout)
mainLayout.addWidget(self.tempButton)
self.setLayout(mainLayout)
def run(self):
self.show()
win = MainWindow()
win.run()
Thank you!
Regarding your first issue, I believe you are not getting the sizes you are expecting for two reasons:
You are not explicitly setting the size of the QGraphicsView widget to 500, but the QGraphicsScene instead.
You are fetching the sizes too early in the construction of your application, before the layout of the MainWindow has been properly painted.
Regarding your second issue, depending of what is desired, it is possible to use the method mapFromScene to get the position of the mouse event in regard to the QGraphicsScene instead of the QGraphicsView widget.
More specifically, this can be achieve in your code by:
Setting the size of the QGraphicsView widget with setFixedSize ;
Moving the "size-fetching" calls in the run method, after the MainWindow has been painted ;
Adding a mapToScene transformation on the mouseMoveEvent coordinate.
Below is the code that was modified accordingly to the points listed above:
from PySide import QtGui, QtCore
import sys
class View(QtGui.QGraphicsView):
def __init__(self, parent = None):
super(View, self).__init__(parent)
self.setScene(QtGui.QGraphicsScene(self) )
self.setSceneRect( 0, 0, 1000, 1000 )
self.setFixedSize(500, 500)
def mouseMoveEvent(self, event):
print
print self.mapToScene(event.pos()).toTuple()
# prints (1000, 1000) at lower-right corner
print event.pos().toTuple()
# prints (500, 500) at lower-right corner
class MainWindow(QtGui.QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.view = View(self)
hLayout = QtGui.QHBoxLayout()
hLayout.addWidget(self.view)
buttonsLayout = QtGui.QVBoxLayout()
buttonsLayout.setSpacing(0)
for i in range(10):
newButton = QtGui.QPushButton()
buttonsLayout.addWidget(newButton)
hLayout.addLayout(buttonsLayout)
self.tempButton = QtGui.QPushButton()
mainLayout = QtGui.QVBoxLayout()
mainLayout.addLayout(hLayout)
mainLayout.addWidget(self.tempButton)
self.setLayout(mainLayout)
def run(self):
self.show()
print
print self.view.viewport().width() # outputs 485
print self.view.width() # outputs 500
print self.view.rect() # outputs QRect(0, 0, 500, 500)
print self.view.size() # outputs QSize(500, 500)
print self.view.sceneRect() #outputs QRect(0, 0, 1000, 1000)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
win = MainWindow()
win.run()
sys.exit(app.exec_())
With the code above, the value returned for the size of QGraphicView is 500x500, while it is 1000x1000 for the QGraphicsScene, as expected.
I am trying to create a simple application with PySide, but it seems that I didn't read the docs properly. That is my code:
from PySide.QtGui import *
from PySide.QtCore import Qt
class Window(QMainWindow):
def __init__(self, parent = None):
QMainWindow.__init__(self, parent)
self.scene = QGraphicsScene()
self.view1 = QGraphicsView(self.scene, self)
self.view2 = QGraphicsView(self.scene, self)
self.gridLayout = QGridLayout()
self.gridLayout.addWidget(self.view1, 0, 0, Qt.AlignLeft)
self.gridLayout.addWidget(self.view2, 0, 1, Qt.AlignRight)
self.gridLayout.setColumnMinimumWidth(0, 300)
self.gridLayout.setColumnMinimumWidth(1, 300)
self.setLayout(self.gridLayout)
self.view1.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.view2.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.scene.addLine(0, 0, 1000, 1000)
if __name__ == "__main__":
app = QApplication(())
window = Window()
window.showMaximized()
app.exec_()
The code executes, but it should display a window with two QGraphicsViews, which should divide the window in half, but I only get one QGraphicsView in its minimum size. Can someone help me with this?
Thanks in advance.
You need to create a central widget for a QMainWindow, and then set the layout on that. Simplifying your example:
from PySide.QtGui import *
class Window(QMainWindow):
def __init__(self, parent = None):
QMainWindow.__init__(self, parent)
self.scene = QGraphicsScene()
self.view1 = QGraphicsView(self.scene, self)
self.view2 = QGraphicsView(self.scene, self)
self.view1.setFrameShape(QFrame.NoFrame)
self.view2.setFrameShape(QFrame.NoFrame)
widget = QWidget(self)
layout = QGridLayout(widget)
layout.addWidget(self.view1, 0, 0)
layout.addWidget(self.view2, 0, 1)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
self.setCentralWidget(widget)
self.scene.addLine(0, 0, 1000, 1000)
if __name__ == "__main__":
app = QApplication(())
window = Window()
window.showMaximized()
app.exec_()
I'm having an issue where a custom widget I am making appears just fine when added to a QGridLayout without specifying an alignment, but when I specify an Alignment it disappears.
Here is the custom widget:
class OutlineLabel(QWidget):
def __init__(self, text, parent=None):
QWidget.__init__(self, parent)
self.text = text
def paintEvent(self, event=None):
painter = QPainter(self)
path = QPainterPath()
pen = QPen();
font = QFont("Helvetica", 14, 99, False)
brush = QBrush(QtCore.Qt.white)
pen.setWidth(1);
pen.setColor(QtCore.Qt.black);
painter.setFont(font);
painter.setPen(pen);
painter.setBrush(brush)
path.addText(0, 20, font, self.text);
painter.drawPath(path);
Here is an example where it will display just fine:
app = QApplication(sys.argv)
wnd = QWidget()
wnd.resize(400,400)
grid = QGridLayout()
test = OutlineLabel("hello")
grid.addWidget(test, 0, 0)
wnd.setLayout(grid)
wnd.show()
sys.exit(app.exec_())
But if I change it to the following, it no longer shows:
app = QApplication(sys.argv)
wnd = QWidget()
wnd.resize(400,400)
grid = QGridLayout()
test = OutlineLabel("hello")
grid.addWidget(test, 0, 0, QtCore.Qt.AlignHCenter)
wnd.setLayout(grid)
wnd.show()
sys.exit(app.exec_())
Is there some information I need to set in the OutlineLabel class to work correctly with Alignments?
It could be a problem with size hint (your widget is empty).
If you reimplement the sizeHint() method to return a valid QSize, your widget should appear:
def sizeHint( self ):
return QSize( 200, 200 )