import sys
from PyQt4 import QtCore, QtGui
class Class1(QtGui.QMainWindow):
def __init__(self):
super(Class1, self).__init__()
self.func()
def func(self):
r0=QtGui.QRadioButton("0",self)
r1=QtGui.QRadioButton("1",self)
ra=QtGui.QRadioButton("a",self)
rb=QtGui.QRadioButton("b",self)
r0.move(100,100)
r1.move(400,100)
ra.move(100,400)
rb.move(400,400)
number_layout=QtGui.QButtonGroup()
letter_layout=QtGui.QButtonGroup()
number_layout.addButton(r0)
number_layout.addButton(r1)
letter_layout.addButton(ra)
letter_layout.addButton(rb)
layout=QtGui.QHBoxLayout(self)
self.show()
def main():
app = QtGui.QApplication(sys.argv)
mw = Class1()
mw.show()
sys.exit(app.exec_())
if __name__=='__main__':
main()
I am trying to group r0,r1 and ra,rb i.e. when r0 is checked, r1 should be unchecked with no effect on states of ra or rb. How can I achieve this? The code indicates what I have tried so far.
A QMainWindow provides a layout already, you can't simply replace that with your own. Either inherit from a plain QWidget, or create a new widget and add the layout and buttons to that.
Your naming is confusing too, QButtonGroup isn't a layout. It doesn't actually provide any visible UI. If you need a UI element that groups buttons, you should look at QGroupBox instead.
Here's a simple variation on what you have above:
def func(self):
layout=QtGui.QHBoxLayout() # layout for the central widget
widget=QtGui.QWidget(self) # central widget
widget.setLayout(layout)
number_group=QtGui.QButtonGroup(widget) # Number group
r0=QtGui.QRadioButton("0")
number_group.addButton(r0)
r1=QtGui.QRadioButton("1")
number_group.addButton(r1)
layout.addWidget(r0)
layout.addWidget(r1)
letter_group=QtGui.QButtonGroup(widget) # Letter group
ra=QtGui.QRadioButton("a")
letter_group.addButton(ra)
rb=QtGui.QRadioButton("b")
letter_group.addButton(rb)
layout.addWidget(ra)
layout.addWidget(rb)
# assign the widget to the main window
self.setCentralWidget(widget)
self.show()
Grouping of radio buttons can be done by all containers. You don't necessarily need QGroupBox, you can use QFrame instead or a QTabWidget. Your choice. Here's a sample code.
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.layoutWidget1 = QtWidgets.QWidget(self.centralwidget)
self.frame_1 = QtWidgets.QFrame(self.layoutWidget1)
self.radio_btn_a = QtWidgets.QRadioButton(self.frame_1)
self.radio_btn_a.setGeometry(QtCore.QRect(160, 80, 40, 17))
self.radio_btn_a.setObjectName("radio_btn_a")
MainWindow.setCentralWidget(self.centralwidget)
Related
This question already has answers here:
PyQT Navigation between layouts
(2 answers)
Closed 1 year ago.
How to get back my MainWindow ?
From my MainWindow. If I Press either the "Open Left Box" button or "Open Right Box" button, it's worked and at the same time, If I press the "Back" Button from Left Box, nothing will happen. How to obtain the main window? (Simply, I want to know how to set Layouts and remove layouts in setcentral Widgets)
import sys,os
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class Class_MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Main Window")
self.initUI()
def initUI(self):
self.widgets()
self.layouts()
def widgets(self):
self.Masterbtn = QPushButton("Master")
self.transbtn = QPushButton("tanscation")
self.reportbtn = QPushButton("Reports")
self.masterlbl = QLabel("Master Label")
self.translbl = QLabel("transcation label")
self.reportlbl = QLabel("Report Label")
self.leftboxbtn = QPushButton("Open Left Box")
self.leftboxbtn.clicked.connect(self.leftboxopn)
self.rightboxbtn = QPushButton("Open Right Box")
self.rightboxbtn.clicked.connect(self.rightboxopn)
self.backbtn =QPushButton("Back")
self.backbtn.clicked.connect(self.mainwindow)
def layouts(self):
self.mainbox = QVBoxLayout()
self.mainbox.addWidget(self.leftboxbtn)
self.mainbox.addWidget(self.rightboxbtn)
self.leftbox = QVBoxLayout()
self.leftbox.addWidget(self.Masterbtn)
self.leftbox.addWidget(self.transbtn)
self.leftbox.addWidget(self.reportbtn)
self.leftbox.addWidget(self.backbtn)
self.rightbox = QVBoxLayout()
self.rightbox.addWidget(self.masterlbl)
self.rightbox.addWidget(self.translbl)
self.rightbox.addWidget(self.reportlbl)
# self.rightbox.addWidget(self.backbtn)
widget = QWidget()
widget.setLayout(self.mainbox)
self.setCentralWidget(widget)
def leftboxopn(self):
self.setWindowTitle("Left Box ")
widget = QWidget()
widget.setLayout(self.leftbox)
self.setCentralWidget(widget)
def rightboxopn(self):
self.setWindowTitle("Right Box")
widget = QWidget()
widget.setLayout(self.rightbox)
self.setCentralWidget(widget)
def mainwindow(self):
self.setWindowTitle("Main Window")
widget = QWidget()
widget.setLayout(self.mainbox)
self.setCentralWidget(widget)
def main():
app = QApplication(sys.argv)
mainwindow = Class_MainWindow()
mainwindow.show()
sys.exit(app.exec_())
if __name__ =="__main__":
main()
You cannot "get back" anything, because everytime you use setCentralWidget() the existing widget gets deleted, as the documentation explains:
Note: QMainWindow takes ownership of the widget pointer and deletes it at the appropriate time.
When you call setCentralWidget() on another widget, the previous is completely deleted, including all its children. When a Qt object is deleted, all its child objects gets deleted along with it, and the result is that the self.mainbox you created in the beginning doesn't exist any more (the python object exists, but remember that PyQt objects are only a reference to the actual Qt objects: a Qt object can be deleted if Qt requires it, even if the python reference still exists).
In substance (and, in any case), you should not replace the central widget everytime, but use a paged widget like QStackedWidget as the central widget instead, and then switch to the other interfaces using its functions: setCurrentIndex() or setCurrentWidget().
In order to properly use it, all child widgets must be added to a QWidget container, which will then be added as individual "pages" to QStackedWidget:
class Class_MainWindow(QMainWindow):
# ...
def layouts(self):
self.mainContainer = QWidget()
self.mainbox = QVBoxLayout(self.mainContainer)
self.mainbox.addWidget(self.leftboxbtn)
self.mainbox.addWidget(self.rightboxbtn)
self.leftContainer = QWidget()
self.leftbox = QVBoxLayout(self.leftContainer)
self.leftbox.addWidget(self.Masterbtn)
self.leftbox.addWidget(self.transbtn)
self.leftbox.addWidget(self.reportbtn)
self.leftbox.addWidget(self.backbtn)
self.rightContainer = QWidget()
self.rightbox = QVBoxLayout(self.rightContainer)
self.rightbox.addWidget(self.masterlbl)
self.rightbox.addWidget(self.translbl)
self.rightbox.addWidget(self.reportlbl)
# self.rightbox.addWidget(self.backbtn)
self.stackedWidget = QStackedWidget()
self.setCentralWidget(self.stackedWidget)
self.stackedWidget.addWidget(self.mainContainer)
self.stackedWidget.addWidget(self.leftContainer)
self.stackedWidget.addWidget(self.rightContainer)
def leftboxopn(self):
self.setWindowTitle("Left Box ")
self.stackedWidget.setCurrentWidget(self.leftContainer)
def rightboxopn(self):
self.setWindowTitle("Right Box")
self.stackedWidget.setCurrentWidget(self.rightContainer)
def mainwindow(self):
self.setWindowTitle("Main Window")
self.stackedWidget.setCurrentWidget(self.mainContainer)
Edits: Phrasing and included a minimum reproducible example
I have a question about how object names work in PyQt5 / how OOP works in PyQt5:
Background
I'm working on a project which includes a QTabWidget. This QTabWidget adds a new tab each time a '+' tab is clicked:
Before click:
After click:
When I add the new tab as just a blank widget using insertNewRegionTab(), everything works properly; however, when I instead use a custom class that is a derived class of QtWidgets.QWidget (customTab), the program crashes. I created this class so that each tab would have the same formatting / internal relationships between objects, and that I could just add as many as I needed with the '+' button.
I think the crashing may be due to name conflicts, e.g. a label called "label1" being created in two tabs simultaneously.
Question
The problem is that I don't see how name conflicts could be an issue if each customTab is it's own object. I'm wondering if any one knows why I can't implement this customTab class in the desired way.
Proposed Solution
My thoughts on a solution is to just define class counters and then increment every time a new tab is added so they all have unique names, but this is a bit of work so I wanted to make sure that this is actually what the problem is before I implement it.
Code
N.B. - The two original tabs using this code will not have names. The one on the left, 'Test 1', has a combo box in it and is in the customTab class. The one on the right, '+', adds a new tab, but a normal QWidget instead of customTab object
from PyQt5 import QtCore, QtGui, QtWidgets
class topLevelWindow(QtWidgets.QMainWindow):
def __init__(self):
# Function that is used to add tabs
def insertNewRegionTab(self):
if self.mainTabWidgetCount == 1 + self.mainTabWidget.currentIndex():
# Attempted method 1:
tab = QtWidgets.QWidget() # Want this to be a customTab not a QWidget
self.mainTabWidget.insertTab(self.mainTabWidget.currentIndex(),
tab, "Tab " + str(self.mainTabWidgetCount))
# Attempted method 2:
# self.tabInstaces.append(customTab(self.mainTabWidget))
self.mainTabWidgetCount += 1
super().__init__()
# Initialization of main window
self.setObjectName("MainWindow")
self.resize(500, 300)
self.centralwidget = QtWidgets.QWidget(self)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout_5 = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout_5.setObjectName("gridLayout_5")
# Create the main tab widget and set properties
self.mainTabWidget = QtWidgets.QTabWidget(self.centralwidget)
self.mainTabWidget.setObjectName("mainTabWidget")
self.mainTabWidget.setCurrentIndex(0)
self.gridLayout_5.addWidget(self.mainTabWidget, 1, 1, 1, 1)
self.setCentralWidget(self.centralwidget)
# Insert a tab (of the custom, pre-formatted tab class) into this tab widget
self.tabInstances = [customTab(self.mainTabWidget)]
self.mainTabWidgetCount = 2
# Add the tab which creates other tabs to the tan widget
self.addRegionTab = QtWidgets.QWidget()
self.addRegionTab.setObjectName("addRegionTab")
self.mainTabWidget.addTab(self.addRegionTab, "")
# Add functionality: When '+' tab is selected, add a tab
self.mainTabWidget.currentChanged.connect(lambda: insertNewRegionTab(self))
# Show window
self.show()
class customTab(QtWidgets.QWidget):
def __init__(self, parent=None):
# Initializes the object itself and renames it. Add vertical layout to it
super(customTab, self).__init__()
self.setObjectName("tabInstances")
self.verticalLayout = QtWidgets.QVBoxLayout(self)
self.verticalLayout.setObjectName("verticalLayout")
self.comboBox1 = QtWidgets.QComboBox(self)
self.comboBox1.setObjectName("comboBox1")
self.comboBox1.addItem("")
self.comboBox1.addItem("")
# Add self to parent
parent.addTab(self, "")
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = topLevelWindow()
sys.exit(app.exec_())
I do not understand the logic of OP since it asks about objectName() that has nothing to do with the problem, the objectName is only a name that allows us to identify elements and nothing else.
In my answer I show how to create the button that when pressing must add tabs:
from PyQt5 import QtCore, QtGui, QtWidgets
class TabWidget(QtWidgets.QTabWidget):
plusClicked = QtCore.pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent)
self.tabBar().installEventFilter(self)
self.add_button = QtWidgets.QToolButton(self, text="+")
self.add_button.clicked.connect(self.plusClicked)
def eventFilter(self, obj, event):
if obj is self.tabBar() and event.type() == QtCore.QEvent.Resize:
r = self.tabBar().geometry()
h = r.height()
self.add_button.setFixedSize((h - 1) * QtCore.QSize(1, 1))
self.add_button.move(r.right(), 0)
return super().eventFilter(obj, event)
class topLevelWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
# Initialization of main window
self.setObjectName("MainWindow")
self.resize(500, 300)
self.centralwidget = QtWidgets.QWidget(self)
self.setCentralWidget(self.centralwidget)
lay = QtWidgets.QGridLayout(self.centralwidget)
# Create the main tab widget and set properties
self.mainTabWidget = TabWidget()
self.mainTabWidget.setObjectName("mainTabWidget")
self.mainTabWidget.setCurrentIndex(0)
lay.addWidget(self.mainTabWidget, 1, 1, 1, 1)
self.mainTabWidget.addTab(CustomWidget(), "Tab1")
self.mainTabWidget.plusClicked.connect(self.add_clicked)
def add_clicked(self):
index = self.mainTabWidget.count() + 1
self.mainTabWidget.addTab(CustomWidget(), "Tab {}".format(index))
class CustomWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
# Initializes the object itself and renames it. Add vertical layout to it
super(CustomWidget, self).__init__(parent)
self.comboBox1 = QtWidgets.QComboBox()
self.comboBox1.addItems(list("abcdef"))
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.comboBox1)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = topLevelWindow()
w.show()
sys.exit(app.exec_())
I want to hide a QFrame from the start of the app to later on showing it as a warning message with a function.
But I can't find a solution. The frame keeps showing up without changing its start size.
I would also be happy with a solution in Qt Designer but I can't figure out how to do it either way.
self.frame_top_warning.setFixedHeight(0) seems to work but makes problems later on when animating that frame.
class SampleApp(ui_main.Ui_MainWindow, QtWidgets.QMainWindow):
def __init__(self):
super(SampleApp, self).__init__()
self.setupUi(self)
# Here I want to set the start size to 0 to, later on, animate it in.
self.frame_top_warning.resize(self.frame_top_warning.width(), 0)
One possibility could be to set the maximum height of the widget to 0 with self.frame_top_warning.setMaximumHeight(0) at the beginning to hide the QFrame. Then you can use QtCore.QParallelAnimationGroup to animate two properties at the same time minimumHeight and maximumHeight. In this way you are constraining the height of the widget to be what you want it to be.
I have put together a small example to show you what I mean
import sys
from PyQt5 import QtWidgets, QtCore
class Widget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
layout = QtWidgets.QVBoxLayout()
show_warning_button = QtWidgets.QPushButton('Show Warning')
layout.addWidget(show_warning_button)
show_warning_button.clicked.connect(self.showWarningLabel)
layout.addWidget(QtWidgets.QPushButton('Button'))
layout.addWidget(QtWidgets.QLabel('This is some text'))
self.frame_top_warning = QtWidgets.QFrame()
self.frame_top_warning.setStyleSheet('QFrame {background: red;}')
self.frame_top_warning.setMaximumHeight(0)
layout.addWidget(self.frame_top_warning)
min_height_animation = QtCore.QPropertyAnimation(self.frame_top_warning, b"minimumHeight")
min_height_animation.setDuration(600)
min_height_animation.setStartValue(0)
min_height_animation.setEndValue(400)
max_height_animation = QtCore.QPropertyAnimation(self.frame_top_warning, b"maximumHeight")
max_height_animation.setDuration(600)
max_height_animation.setStartValue(0)
max_height_animation.setEndValue(400)
self.animation = QtCore.QParallelAnimationGroup()
self.animation.addAnimation(min_height_animation)
self.animation.addAnimation(max_height_animation)
self.setLayout(layout)
self.resize(800, 600)
self.show()
def showWarningLabel(self):
self.animation.start()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = Widget()
sys.exit(app.exec_())
Hope this helps =)
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.
In my application,
I have three same QGroupBoxes with some pushbuttons.
I want to have same names for pushbuttons and acces to them through QGroupBox name.
Is that possible?
QGroupBox_one.button_export
QGroupBox_one.button_import
QGroupBox_two.button_export
QGroupBox_two.button_import
Than I could make method with QGroupBox as parameter and configure buttons easier.
Thanks.
I think a clean way of doing what you want is making a new class (MyQGroupBoxes) that has the push buttons and the configure method you need.
from PyQt4 import QtGui
class MyQGroupBoxes(QtGui.QGroupBox):
def __init__(self, parent):
QtGui.QGroupBox.__init__(self, parent)
self.button_export = QtGui.QPushButton("Export", self)
self.button_import = QtGui.QPushButton("Import", self)
layout = QtGui.QVBoxLayout(self)
self.setLayout(layout)
layout.addWidget(self.button_export)
layout.addWidget(self.button_import)
def config_export(self):
# config your button
pass
def config_import(self):
# config your button
pass
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
my_box1 = MyQGroupBoxes(None)
my_box2 = MyQGroupBoxes(None)
my_boxes = [my_box1, my_box2]
# Config all the boxes
for my_box in my_boxes:
my_box.config_export()
my_box.config_import()
my_box.show()
app.exec_()