Prevent clipping the absolute positioned child widget by the parent - python

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_())

Related

PyQt5 - Custom widgets open in separate windows rather than in same window

I'm new at PyQt, and I'm trying to create a main window containing two custom widgets, the first being a data grapher, the second being a QGridLayout containing QLabels. Problem is: the two widgets open in separate windows and have no content.
I've found multiple posts with a similar problem:
PyQt5 Custom Widget Opens in Another Window
Custom widget does not appear on Main Window
PyQt5 Custom Widget Opens in Another Window
And even a FAQ on this specific problem: https://www.pythonguis.com/faq/pyqt-widgets-appearing-as-separate-windows/
But I haven't been able to figure out why my code doesn't work. My aim is to obtain a result as shown below on the left, but instead I'm getting a result as shown on the right:
My code is the following (can be copied and run as it is):
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QLabel, QVBoxLayout, QWidget, QGridLayout
from PyQt5.QtGui import QFont
import sys
import pyqtgraph as pg
class CustomWidget_1(QWidget):
def __init__(self):
super(CustomWidget_1, self).__init__()
self.channels = [1, 2, 3, 4, 5, 6, 7, 8]
self.win = pg.GraphicsLayoutWidget(title='Plot', size=(800, 600))
self.plots = list()
self.curves = list()
for i in range(len(self.channels)):
p = self.win.addPlot(row=i, col=0)
p.showAxis('left', False)
p.setMenuEnabled('left', False)
p.showAxis('bottom', False)
p.setMenuEnabled('bottom', False)
self.plots.append(p)
curve = p.plot()
self.curves.append(curve)
self.win.show()
print('CustomWidget_1 initialized.')
class CustomWidget_2(QWidget):
def __init__(self, labelnames):
super(CustomWidget_2, self).__init__()
self.grid = QGridLayout()
self.labelnames = labelnames
self.qlabels = []
for label in self.labelnames:
labelBox = QLabel(label)
labelBox.setFont(QFont('Arial', 16))
labelBox.setStyleSheet('border: 2px solid black;')
labelBox.setAlignment(Qt.AlignCenter)
self.qlabels.append(labelBox)
index = self.labelnames.index(label)
q, r = divmod(index, 6)
self.grid.addWidget(labelBox, q, r)
print('CustomWidget_2 initialized.')
class MainWindow(QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.labelnames = ['label 1', 'label 2', 'label 3']
self.CustomWidget_1 = CustomWidget_1()
self.CustomWidget_1.setParent(self)
self.CustomWidget_1.show()
self.CustomWidget_2 = CustomWidget_2(self.labelnames)
self.CustomWidget_2.setParent(self)
self.CustomWidget_2.show()
self.mainLayout = QVBoxLayout()
self.mainLayout.addWidget(self.CustomWidget_1)
self.mainLayout.addWidget(self.CustomWidget_2)
self.setLayout(self.mainLayout)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
predictVisualizer = MainWindow()
sys.exit(app.exec())
Could anyone tell me what I'm doing wrong and how I could fix it? Any pointers towards tutorials and/or templates would be greatly appreciated as well! Thanks!
you should write fewer lines of code and debug slowly, if you are new to pyqt5 you should read carefully the basic Layout creation, like you are creating a website interface, link: https://www.pythonguis.com/tutorials/pyqt-layouts/
This is the code I have edited, you can can refer:
import sys
from PyQt5.QtCore import QSize,Qt
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget,QGridLayout,QVBoxLayout,QLabel,QHBoxLayout
from PyQt5.QtGui import QPalette, QColor
class CustomWidget_1(QWidget):
def __init__(self,color):
super(CustomWidget_1, self).__init__()
self.setAutoFillBackground(True)
layout = QGridLayout()
self.setLayout(layout)
self.setFixedSize(QSize(400,300))
palette = self.palette()
palette.setColor(QPalette.Window, QColor(color))
self.setPalette(palette)
class CustomWidget_2(QWidget):
def __init__(self,color):
super(CustomWidget_2, self).__init__()
self.setAutoFillBackground(True)
layout = QHBoxLayout()
self.setLayout(layout)
self.setFixedSize(QSize(400,134))
layout.setContentsMargins(70,0,0,0)
palette = self.palette()
palette.setColor(QPalette.Window, QColor(color))
self.setPalette(palette)
Label1 = QLabel()
Label1.setText('abc')
Label2 = QLabel()
Label2.setText('sad')
Label3 = QLabel()
Label3.setText('qv')
layout.addWidget(Label1)
layout.addWidget(Label2)
layout.addWidget(Label3)
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setWindowTitle("My App")
layout = QVBoxLayout()
layout.addWidget(CustomWidget_1("blue"))
layout.addWidget(CustomWidget_2("red"))
widget = QWidget()
widget.setLayout(layout)
self.setCentralWidget(widget)
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()

Use a variable/data in two tabs in PyQt5 / Python

I am trying to get input from a user in one tab then show it in the second tab. I could not find a question or an example about how to do that. This is an example of the code that I want to use, How can I show the data from Tab(1) Qlabel in QTextEdit box in Tab(2), I am a beginner in pyqt5 and not sure how this would work:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class TabWidget(QDialog):
def __init__(self):
super().__init__()
self.setWindowTitle('Tab Widget Application')
tabwidget = QTabWidget()
tabwidget.addTab(FirstTab(), 'First Tab')
tabwidget.addTab(SecondTab(),'Second Tab')
vbox=QVBoxLayout()
vbox.addWidget(tabwidget)
self.setLayout(vbox)
class FirstTab(QWidget):
def __init__(self):
super().__init__()
self.nameLabel = QLabel(self)
self.nameLabel.setText('Name:')
self.line = QLineEdit(self)
self.line.move(80, 20)
self.line.resize(200, 32)
self.nameLabel.move(20, 20)
self.btn=QPushButton('switch',self)
self.btn.move(80, 50)
self.btn.clicked.connect(lambda: SecondTab.display(SecondTab(),self.nameLabel.text()))
class SecondTab(QWidget):
def __init__(self):
super().__init__()
self.layout = QVBoxLayout()
self.editor=QTextEdit()
self.layout.addWidget(self.editor)
self.setLayout(self.layout)
def display(self,text):
self.editor.setText(text)
if __name__ == '__main__':
app=QApplication(sys.argv)
tabwidget = TabWidget()
tabwidget.resize(500,500)
tabwidget.show()
app.exec()
In order to track changes amongst child widgets, you'll need a main "controller".
Your QTabWidget will suffice, as long as you implement it in the correct way:
class TabWidget(QDialog):
def __init__(self):
super().__init__()
self.setWindowTitle('Tab Widget Application')
# if the target widget of the layout is provided as an init argument, the
# layout will be automatically set to it
vbox = QVBoxLayout(self)
tabwidget = QTabWidget()
vbox.addWidget(tabwidget)
firstTab = FirstTab()
tabwidget.addTab(firstTab, 'First Tab')
secondTab = SecondTab()
tabwidget.addTab(secondTab,'Second Tab')
firstTab.line.textChanged.connect(secondTab.editor.setPlainText)
firstTab.btn.clicked.connect(lambda: tabwidget.setCurrentWidget(secondTab))

Cannot create QScrollArea with QWidget and QVBoxLayout to QWidget with QVBoxLayout

I have this code:
class Window(QWidget):
def __init__(self):
super().__init__()
def init_gui(self):
self.layout = QVBoxLayout()
self.setLayout(self.layout)
self.new1()
self.new2()
self.showMaximized()
def create_scroll_area(self):
scroll_area = QScrollArea()
widget = QWidget()
scroll_area.setWidget(widget)
layout = QVBoxLayout()
widget.setLayout(layout)
button = QPushButton("Ahoj")
layout.addWidget(button)
self.layout.addLayout(layout)
def new1(self):
self.create_scroll_area()
def new2(self):
self.create_scroll_area()
I get this error message:
QLayout::addChildLayout: layout "" already has a parent
What's wrong?
Who is layout's parent? Widget? I also tried self.widget instead of widget and it still does not work.
Please try this code.
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class Window(QWidget):
def __init__(self, parent=None):
super().__init__(parent=None)
self.init_gui()
def init_gui(self):
self.create_scroll_area()
self.showMaximized()
def create_scroll_area(self):
scroll_area = QScrollArea()
widget = QWidget()
layout = QVBoxLayout()
button = QPushButton("Ahoj")
layout.addWidget(button)
widget.setLayout(layout)
scroll_area.setWidget(widget)
self.setLayout(layout)
def main():
app = QApplication([])
window = Window()
window.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Let's revise from A to Z.
1 to write self.init_gui() in __init__ constructor.
If you don't do it, you can't execute init_gui method at the first time.
2.setLayout() or setWidget() should be written at the last place at least.
In python, we prepare the things we want to show, and set them on the mainwidget, and show them at the last time.
3.please pay attention to self.layout name.
Widget has originally setLayout() method. and layout() method.
If you make self.layout = ***, you crush the original method of QWidget.
4. it may as well delete new1 and new2 method.
please call them directly.
5. please look create_scroll_area method.
You make three widget.QScrollArea,QWidget,QPushButton.
and make a layout object.and you set the layout into QWidget.
But you set the QWidget before the widget set the layout.
It is not good order for coding.
You make QPushButton but the button doesn't belong to any widget.
Because you set the button on the self.layout certainly, but if you want to show it, you must setLayout(self.layout) at the last position.

Pyqt QSplitter not visualizing correctly

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()

How to fit widgets within frame in python

I am pretty new at python but currently I am getting some problem here with the part where I am unable to get my stuff fit within the width of the window.
I am trying to set it in such a way that it is:
Eg. Name Button
by the way, I am using Maya to integrate and run my stuff.
If I set it to central, it fits but it is all over the place as I only wanted it to occupy a portion only. So are there any ways for me to fit it nicely into it?
By the way, if its possible, can it be done using my current codings?
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sip
import maya.OpenMayaUI as mui
import os
class MainWindow(QMainWindow):
def __init__(self, parent = None):
QMainWindow.__init__(self,parent)
self.resize(400,800)
self.setWindowTitle("GetShots")
self.pubDock = SetShotInfo()
self.pubDW = QDockWidget(self.tr(""), self)
self.pubDW.setWidget(self.pubDock)
# self.setCentralWidget(self.pubDW)
def getMayaWindow():
ptr = mui.MQtUtil.mainWindow()
return sip.wrapinstance(long(ptr), QObject)
def main():
global app
global form
app = qApp
form = MainWindow(getMayaWindow())
form.show()
class GetShot(QFrame):
def __init__(self, parent = None, display=None):
QFrame.__init__(self, parent)
self.createWidgets()
self.createLayout()
def createWidgets(self):
self.showLabel = QLabel('Show')
self.showName = QLineEdit()
self.showName.setText(str(os.environ['SHOW']))
self.shotLabel = QLabel('Shot Filter')
self.shotName = QLineEdit()
self.showButton = QPushButton('Set Show')
self.showButton.setMaximumWidth(200)
self.shotButton = QPushButton('Filter Shots')
self.shotButton.setMaximumWidth(200)
self.rootLabel = QLabel('Change Root')
self.rootButton = QComboBox()
def createLayout(self):
# Sets the Layout of Show and Shot
setShowLayout = QHBoxLayout()
setShowLayout.addWidget(self.showLabel)
setShowLayout.addWidget(self.showName)
setShowLayout.addWidget(self.showButton)
setShotLayout = QHBoxLayout()
setShotLayout.addWidget(self.shotLabel)
setShotLayout.addWidget(self.shotName)
setShotLayout.addWidget(self.shotButton)
# Sets the Change Root Layout
chgRootLayout = QHBoxLayout()
chgRootLayout.addWidget(self.rootLabel)
chgRootLayout.addWidget(self.rootButton)
mainLayout = QVBoxLayout()
mainLayout.addLayout(setShowLayout)
mainLayout.addLayout(setShotLayout)
mainLayout.addLayout(chgRootLayout)
self.setLayout(mainLayout)
if __name__ == '__main__':
main()
You need to use Layouts, combine vertical and horizontal and play with the size policy of the widgets to fit them as you need.
Here's a quick example:
import sys
from PyQt4 import QtCore, QtGui
class ButtonContainer(QtGui.QWidget):
def __init__(self):
super(ButtonContainer, self).__init__()
self.initUI()
def initUI(self):
self.setGeometry( 150, 150, 650, 350)
btn = QtGui.QPushButton('Name button', self)
btn.setSizePolicy( QtGui.QSizePolicy( QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed ) )
vbox = QtGui.QVBoxLayout()
vbox.addWidget( btn )
self.setLayout(vbox)
self.show()
app = QtGui.QApplication(sys.argv)
ex = ButtonContainer()
sys.exit(app.exec_())
The commenters are right in suggesting QtDesigner, if you'd rather code it yourself at least have a mock up ui file where you can play interactively with the layouts and size policies, it's really helpful.

Categories

Resources