Backstory: I'm new at writing GUIs and I'm attempting to make a grid of radio buttons. I have this so far but for some reason when I pass in the arguments it hits the line "self.radio_button_list[0] = setChecked.(True)" and then throws a list index out of range error. Any idea why this is happening and how to fix it? Is there a better way to make a grid of radio buttons (or a dropdown menu, there are 51 options)?
I initialize with the line:
self.radio_button_widget = RadioButtonWidget('File Types', "Please select the type of file to edit", ('adhdrs_on_bd', 'adhdrs_on_bm', 'alabama', 'asr', 'atq', 'bsa', 'caars_on_bd'))
which then calls:
from PyQt4 import *
class RadioButtonWidget(QtGui.QWidget):
def __init__(self, label, instruction, button_list):
super(RadioButtonWidget,self).__init__()
self.titleLabel = QtGui.QLabel(label)
self.radio_group_box= QtGui.QGroupBox(instruction)
self.radio_button_group = QtGui.QButtonGroup()
self.radio_button_list = []
for each in self.radio_button_list:
self.radio_button_list.append(QtGui.QRadioButton(each))
self.radio_button_list[0].setChecked(True)
self.radio_button_layout = QtGui.QVBoxLayout()
counter = 1
for each in self.radio_button_list:
self.radio_button_layout.addWidget(each)
self.radio_button_group.addButton(each)
self.radio_button_group.setID(each, counter)
counter += 1
self.radio_group_box.setLayout(self.radio_button_layout)
self.main_layout = QtGui.QVBoxLayout()
self.main_layout.addWidget(self.titleLabel)
self.main_layout.addWidget(self.radio_group_box)
self.setLayout(self.main_layout)
def selected_button(self):
return self.radio_button_group.checkedId()
Related
I'm trying to create an automated way of adding elements to my UI interface and deleting them when needed.
The functionality is there, on button press I create an instance of the translateUI class and with the other button I can remove it.
The issue lies within the def setBoolOn(self, *args).
btnList.append(self.delBtnFetch) is creating a second button in the UI and I cannot figure out why. Clicking the 'Add' button once generates 2 buttons, even though the function only creates one.
When commenting out the btnList.append, it only creates a single button, but the rest of the code breaks, when trying to remove it.
import maya.cmds as cmds
import maya.mel as mel
class UI(object):
#Constructor
def __init__(self):
#global counter, classList, sequence
self.btnList = []
global counter
counter = 1
classList.clear()
btnList.clear()
#define name, size and title
self.window = 'Animation_Window'
self.title = 'Animation Automator'
self.size = (400,560)
#close window if open
if cmds.window(self.window, exists=True) :
cmds.deleteUI(self.window, window=True)
#create new window
self.window = cmds.window(self.window, title=self.title, widthHeight=self.size, sizeable=False)
#specify layout for window
cmds.columnLayout(adjustableColumn=True)
#test text
cmds.text('Text in class')
#add button
self.btnChckBoxOn = cmds.button(label='Add', c=self.setBoolOn)
#remove button
self.btnChckBoxOff = cmds.button(label='Remove', c=self.setBoolOff)
#display window
cmds.showWindow()
############################
##Add button functionality##
def setBoolOn(self, *args):
#button check
btnChckOn = cmds.button(self.btnChckBoxOn, query=True)
global counter
self.classList = []
#add 1 to the counter
if counter >= 0:
counter = counter + 1
#create, based on sequence, the translateUI class
self.sequence = translateUI(counter)
#add the sequence class to a list
classList.append(self.sequence)
#get the button from translate
self.delBtnFetch = translateUI(0).delBtn
#add the delBtn to a list
btnList.append(self.delBtnFetch)
print(btnList)
###############################
##remove button functionality##
def setBoolOff(self, *args):
#button check
btnChckOff = cmds.button(self.btnChckBoxOff, query=True)
global counter, classList
#substract one off the counter
if counter >= 1:
counter = counter - 1
#get the last class in the list
lastSequence = classList[-1]
#set the last class instance to null
lastSequence = Null()
#get the last button in the list
lastButton = btnList[-1]
#remove the last button from list
btnList.remove(lastButton)
#delete last button from the UI
cmds.deleteUI(lastButton)
else :
print('You cannot remove all Sequences')
#initiate window class
myWindow = UI()
#######################
##Additional UI class##
#######################
class translateUI():
def __init__(self, counter):
global delBtn
self.delBtn = cmds.button(l='Animate', c=self.AnimatePress)
def AnimatePress(self, *args):
btnAnimate = cmds.button(self.delBtn, query=True)
print('Animation Pressed')
##############
##VOID class##
class Null():
pass
I am not that insanely experienced with Python yet and am at a loss here, any help is appreciated!
The issue is in your setBoolOn() method, the translateUI class is initialized twice as you can see here
# >> first translate UI instance was created
self.sequence = translateUI(counter)
self.classList.append(self.sequence)
# >> second translate UI instance was created
self.delBtnFetch = translateUI(0).delBtn
now each time it gets initiated, it will create a button for you, as that's how you declared it.
class translateUI():
def __init__(self, counter):
global delBtn
# >> you're creating a button during the initialization
self.delBtn = cmds.button(l='Animate', c=self.AnimatePress)
I didn't use your code but I guess what you're trying to do is something like this:
from maya import cmds
import sys
# I'm trying to create an automated way of adding elements to my UI interface and deleting them when needed.
def userInput():
user_input = cmds.promptDialog(title="Control Info", message="name,label", style="text",
button=['OK', 'Cancel'], defaultButton='OK',
cancelButton='Cancel', dismissString='Aborted'
)
if user_input == 'OK':
ranges = cmds.promptDialog(query=True, text=True)
else:
print('closed')
# force to stop the script
sys.exit()
return ranges.split(",")
def setParentLayout(*args):
""" Set the parent one level up in the current active window"""
cmds.setParent('..')
print ("MOVED the parent to: {0}".format(cmds.setParent(q=True)))
def addButtons(*args):
""" Add a button to the current parent """
# get control type and name
description = userInput()
# a dynamic dictionnary to extend for more buttons
ctrl_dict = {"button": cmds.button, "checkBox": cmds.checkBox}
# create the control
ctrl_cmd = ctrl_dict[description[0]]
the_ctrl = ctrl_cmd()
# If it's not a layout, use the label
if not cmds.layout(the_ctrl, q=True, ex=True):
ctrl_cmd(the_ctrl, e=True, l=description[1])
print("ADDED A BUTTON: {0}".format(the_ctrl))
# update the list
controls.append(the_ctrl)
def addLayout(*args):
""" Add a layout to the current parent """
lay = cmds.columnLayout()
controls.append(lay)
print("ADDED A LAYOUT: {0}".format(controls))
def removeButtons(*args):
""" remove one by one anything that the user created"""
for button_name in controls.__reversed__():
if cmds.control(button_name, q=True, ex=True):
cmds.deleteUI(button_name)
print("DELETED CONTROL: {0}".format(button_name))
# update my list
controls.remove(button_name)
break
# A simple window for testing the methods
controls = []
windowed = 'myWindow'
if cmds.window(windowed, q=True, exists=True):
cmds.deleteUI(windowed)
cmds.windowPref(windowed, remove=True)
cmds.window(windowed, title='Updater UI', w=297, h=113)
cmds.showWindow(windowed)
cmds.frameLayout("main_layout", lv=False, mh=7)
# create the updater button
edge = 7
layout2 = cmds.rowColumnLayout(nc=2, adj=1, columnOffset=[(1, "left", edge), (2, "right", edge)],
columnSpacing=(2, 3),
parent=windowed)
add_btn = cmds.button(l="Add more buttons", c=addButtons, p=layout2)
remove_btn = cmds.button(l="Remove buttons", c=removeButtons, p=layout2)
# change parent to the top level layout of the window
cmds.setParent('|')
layout1 = cmds.columnLayout(adj=1, columnOffset=("both", edge), rs=5, parent=windowed)
cmds.button(l="Add Layout", c=addLayout, p=layout1)
cmds.button(l="Set parent Layout", c=setParentLayout, p=layout1)
# change parent to the top level layout of the window
cmds.setParent('|')
i have a comboBox inside of a tableWidget and the verticalHeader DefaultSectionSize is 60.
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
QtWidgets.QMainWindow.__init__(self,parent)
self.table = QTableWidget()
self.setCentralWidget(self.table)
self.table.verticalHeader().setDefaultSectionSize(60)
self.table.setColumnCount(2)
self.table.setRowCount(2)
data = ["1","2"]
for i in range(2):
item = QTableWidgetItem(data[i])
self.table.setItem(i,0,item)
self.combo_sell = QComboBox()
self.combo_sell.setMaximumHeight(30)
self.table.setCellWidget(i,1,self.combo_sell)
But since i set the maximum size of the comboBox to 30, it stays in the top of the item.
I want to know if there's a way to align it to the center.
When setting an index widget, the view tries to set the widget geometry based on the visualRect() of the index. Setting a fixed dimension forces the widget to align itself to the default origin, which is usually the top left corner.
The only way to vertically center a widget with a fixed height is to use a container with a vertical box layout and add the combo to it:
for i in range(2):
item = QTableWidgetItem(data[i])
item.setTextAlignment(Qt.AlignCenter)
self.table.setItem(i,0,item)
container = QWidget()
layout = QVBoxLayout(container)
combo_sell = QComboBox()
layout.addWidget(combo_sell)
combo_sell.setMaximumHeight(30)
self.table.setCellWidget(i, 1, container)
Note: setting instance attributes in a for loop is pointless, as the reference is lost every time the cycle loops.
If you need a simple reference to the combo, you can set it as an attribute of the widget:
container.combo_sell = QComboBox()
In this way you can easily access it when required:
widget = self.table.cellWidget(row, column)
if widget and hasattr(widget, 'combo'):
combo = widget.combo
print(combo.currentIndex())
Note that the reference is created for the python wrapper of the widget, and that behavior might change in future versions of Qt. A better and safer way to achieve this would be to use a subclass, which would also allow easier access to the combo:
class TableCombo(QWidget):
def __init__(self):
super().__init__()
layout = QVBoxLayout(self)
self.combo = QComboBox()
layout.addWidget(self.combo)
self.combo.setMaximumHeight(30)
self.currentIndex = self.combo.currentIndex
self.setCurrentIndex = self.combo.setCurrentIndex
self.addItems = self.combo.addItems
# ...
combo_sell = TableCombo()
self.table.setCellWidget(i, 1, combo_sell)
# ...
combo = self.table.cellWidget(row, column)
print(combo.currentIndex())
I have a column of auto-generated buttons which, if there are too many of, can squash UI elements in the window. Therefore, I want to automatically convert the single column of buttons - nominally inside of a QVBoxLayout referred to as self.main_layout - into a multi-column affair by:
Removing the buttons from self.main_layout
Adding them to alternating new columns represented by QVBoxLayouts
Changing self.main_layout to a QHBoxLayout
Adding the new columns to this layout
My attempt simply results in the buttons staying in a single column but now don't even resize to fill the QSplitter frame they occupy:
app = QApplication(sys.argv)
window = TestCase()
app.exec_()
class TestCase(QMainWindow):
def __init__(self):
super().__init__()
test = QWidget()
self.layout = QVBoxLayout()
test.setLayout(self.layout)
for i in range(10):
temp_btn = QPushButton(str(i))
temp_btn.pressed.connect(self.multi_col)
self.layout.addWidget(temp_btn)
self.setCentralWidget(test)
#pyqtSlot()
def multi_col(self):
cols = [QVBoxLayout(), QVBoxLayout()]
while self.layout.count():
child = self.layout.takeAt(0)
if child.widget():
self.layout.removeItem(child)
cols[0].addItem(child)
cols[1], cols[0] = cols[0], cols[1]
self.layout = QHBoxLayout()
self.layout.addLayout(cols[0])
self.layout.addLayout(cols[1])
Any glaringly obvious thing I'm doing wrong here?
Replacing a layout of a QWidget is not so simple with assigning another object to the variable that stored the reference of the other layout. In a few lines of code you are doing:
self.layout = Foo()
widget.setLayout(self.layout)
self.layout = Bar()
An object is not the same as a variable, the object itself is the entity that performs the actions but the variable is only a place where the reference of the object is stored. For example, objects could be people and variables our names, so if they change our name it does not imply that they change us as a person.
The solution is to remove the QLayout using sip.delete and then set the new layout:
import sys
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets import (
QApplication,
QHBoxLayout,
QMainWindow,
QPushButton,
QVBoxLayout,
QWidget,
)
import sip
class TestCase(QMainWindow):
def __init__(self):
super().__init__()
test = QWidget()
self.setCentralWidget(test)
layout = QVBoxLayout(test)
for i in range(10):
temp_btn = QPushButton(str(i))
temp_btn.pressed.connect(self.multi_col)
layout.addWidget(temp_btn)
#pyqtSlot()
def multi_col(self):
cols = [QVBoxLayout(), QVBoxLayout()]
old_layout = self.centralWidget().layout()
while old_layout.count():
child = old_layout.takeAt(0)
widget = child.widget()
if widget is not None:
old_layout.removeItem(child)
cols[0].addWidget(widget)
cols[1], cols[0] = cols[0], cols[1]
sip.delete(old_layout)
lay = QHBoxLayout(self.centralWidget())
lay.addLayout(cols[0])
lay.addLayout(cols[1])
def main():
app = QApplication(sys.argv)
window = TestCase()
window.show()
app.exec_()
if __name__ == "__main__":
main()
I'd like to propose an alternative solution, which is to use a QGridLayout and just change the column of the widgets instead of setting a new layout everytime. The "trick" is that addWidget() always adds the widget at the specified position, even if it was already part of the layout, so you don't need to remove layout items.
Obviously, the drawback of this approach is that if the widgets have different heights, every row depends on the minimum required height of all widgets in that row, but since the OP was about using buttons, that shouldn't be the case.
This has the major benefit that the switch can be done automatically with one function, possibly by setting a maximum column number to provide further implementation.
In the following example the multi_col function actually increases the column count until the maximum number is reached, then it resets to one column again.
class TestCase(QMainWindow):
def __init__(self):
super().__init__()
test = QWidget()
self.layout = QGridLayout()
test.setLayout(self.layout)
for i in range(10):
temp_btn = QPushButton(str(i))
temp_btn.clicked.connect(self.multi_col)
self.layout.addWidget(temp_btn)
self.setCentralWidget(test)
self.multiColumns = 3
self.columns = 1
def multi_col(self):
maxCol = 0
widgets = []
for i in range(self.layout.count()):
item = self.layout.itemAt(i)
if item.widget():
widgets.append(item.widget())
row, col, rSpan, cSpan = self.layout.getItemPosition(i)
maxCol = max(col, maxCol)
if maxCol < self.multiColumns - 1:
self.columns += 1
else:
self.columns = 1
row = col = 0
for widget in widgets:
self.layout.addWidget(widget, row, col)
col += 1
if col > self.columns - 1:
col = 0
row += 1
Note: I changed to the clicked signal, as it's usually preferred against pressed due to the standard convention of buttons (which "accept" a click only if the mouse button is released inside it), and in your case it also creates two issues:
visually, the UI creates confusion, with the pressed button becoming unpressed in a different position (since the mouse button is released outside its actual and, at that point, different geometry);
conceptually, because if the user moves the mouse again inside the previously pressed button before releasing the mouse, it will trigger pressed once again;
I have a RadioButtonWidget class that receives a list of names (button_list) and a QtWidgets.QGroupBox (radio_group_box) and creates a radio button for each name. The problem I have is that after creating the buttons, I cannot change them. That is if I call the class again with another list of names, nothing changes. I need to create a function inside my class to remove any existing radio buttons so that I can add a new list inside it.
I tried to do radio_group_box.deleteLater() outside the class but this removes the whole box.
class RadioButtonWidget(QtWidgets.QWidget):
def __init__(self, radio_group_box, button_list):
super().__init__()
self.radio_group_box = radio_group_box
self.radio_button_group = QtWidgets.QButtonGroup()
#create the radio buttons
self.radio_button_list = []
for each in button_list:
self.radio_button_list.append(QtWidgets.QRadioButton(each))
if button_list != []:
#set the default checked item
self.radio_button_list[0].setChecked(True)
#create layout for radio buttons and add them
self.radio_button_layout = QtWidgets.QVBoxLayout()
# add buttons to the layout and button group
counter = 1
for each in self.radio_button_list:
self.radio_button_layout.addWidget(each)
self.radio_button_group.addButton(each)
self.radio_button_group.setId(each,counter)
counter += 1
# add radio buttons to the group box
self.radio_group_box.setLayout(self.radio_button_layout)
def selected_button(self):
return self.radio_button_group.checkedId()
Instead of removing the radio buttons, you can create a whole new radio button layout and set it for the group box exactly as you did in the constructor. Here is an example where the function set_group_box_buttons will remove the existing layout from radio_group_box (which is done by setting it to a temp widget), and add a new one with the new buttons.
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class RadioButtonWidget(QWidget):
def __init__(self, radio_group_box, button_list):
super().__init__()
self.radio_group_box = radio_group_box
self.set_group_box_buttons(button_list)
grid = QGridLayout(self)
grid.addWidget(self.radio_group_box)
def selected_button(self):
return self.radio_button_group.checkedId()
def set_group_box_buttons(self, button_list):
self.radio_button_group = QButtonGroup()
self.radio_button_list = [QRadioButton(x) for x in button_list]
if button_list:
self.radio_button_list[0].setChecked(True)
if self.radio_group_box.layout():
QWidget().setLayout(self.radio_group_box.layout())
self.radio_button_layout = QVBoxLayout()
for i, v in enumerate(self.radio_button_list):
self.radio_button_layout.addWidget(v)
self.radio_button_group.addButton(v)
self.radio_button_group.setId(v, i)
self.radio_group_box.setLayout(self.radio_button_layout)
class Template(QWidget):
def __init__(self):
super().__init__()
self.rbw = RadioButtonWidget(QGroupBox('Radio Buttons'), ['Radio 1', 'Radio 2', 'Radio 3'])
self.box = QLineEdit()
self.box.returnPressed.connect(self.replace_buttons)
grid = QGridLayout(self)
grid.addWidget(self.rbw, 0, 0)
grid.addWidget(self.box, 0, 1)
def replace_buttons(self):
self.rbw.set_group_box_buttons(self.box.text().split(', '))
if __name__ == '__main__':
app = QApplication(sys.argv)
gui = Template()
gui.show()
sys.exit(app.exec_())
To demonstrate, I added a QLineEdit which will update the names when you press enter. Before:
After:
There's a conceptual error in your code: you are creating a new RadioButtonGroup, which is a widget, but you are not using it.
As long as each group box will only contain the radio buttons, there is no need to create a new widget (especially if you're not actually using it); you just have to create a layout if the groupbox doesn't have one yet.
There are at least two possible approaches to your question.
For both of them I always use existing radios if possible, to avoid unnecessary object destruction each time the options change, so that they are removed only when the number of options decreases. This also avoids unnecessary layout updates (especially if the number of options is the same).
I also kept the logical "interface" consistent, providing the same method and behavior of update_options(groupBox, options).
QObject based group
With this implementation, I'm creating an object that acts as an interface responsible of creating a QButtonGroup and setting the options, while also providing signals for the state change or the current checked radio.
class RadioButtonGroup(QtCore.QObject):
optionToggled = QtCore.pyqtSignal(object, int, bool)
optionChanged = QtCore.pyqtSignal(object, int)
def __init__(self, radio_group_box, button_list):
super().__init__()
self.groupBox = radio_group_box
layout = radio_group_box.layout()
self.buttonGroup = QtWidgets.QButtonGroup(self)
self.buttonGroup.buttonToggled[int, bool].connect(self.changed)
if layout is None:
layout = QtWidgets.QVBoxLayout(radio_group_box)
for i, text in enumerate(button_list, 1):
radio = QtWidgets.QRadioButton(text)
layout.addWidget(radio)
self.buttonGroup.addButton(radio, i)
def button(self, id):
return self.buttonGroup.button(id)
def changed(self, i, state):
self.optionToggled.emit(self, i, state)
if state:
self.optionChanged.emit(self, i)
def selected_button(self):
return self.buttonGroup.checkedId()
def update_options(self, button_list):
layout = self.groupBox.layout()
# this method will keep the current checked radio as checked, if you want
# to reset it everytime, just uncomment the next commented lines
#self.buttonGroup.setExclusive(False)
for i, text in enumerate(button_list, 1):
radio = self.buttonGroup.button(i)
if radio:
#radio.setChecked(False)
radio.setText(text)
else:
radio = QtWidgets.QRadioButton(text)
layout.addWidget(radio)
self.buttonGroup.addButton(radio, i)
#self.buttonGroup.setExclusive(True)
if len(button_list) == len(self.buttonGroup.buttons()):
return
# there are more radios than needed, remove them
for radio in self.buttonGroup.buttons():
id = self.buttonGroup.id(radio)
if id > i:
self.buttonGroup.removeButton(radio)
radio.deleteLater()
class ObjectBased(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
uic.loadUi('buttongroup.ui', self)
self.pushButton.clicked.connect(self.setOptions)
self.groupBoxes = self.groupBox1, self.groupBox2, self.groupBox3
self.radioButtonGroups = []
for box in self.groupBoxes:
group = RadioButtonGroup(box,
['Option {}'.format(o + 1) for o in range(randrange(1, 10))])
self.radioButtonGroups.append(group)
group.optionChanged.connect(self.optionChanged)
def setOptions(self):
buttonGroup = self.radioButtonGroups[self.comboBox.currentIndex()]
options = ['Option {}'.format(o + 1) for o in range(self.spinBox.value())]
buttonGroup.update_options(options)
def optionChanged(self, radioButtonGroup, id):
groupBox = radioButtonGroup.groupBox
print('{} checked {} ({})'.format(
groupBox.title(), id, radioButtonGroup.button(id).text()))
Self contained
In this mode, the logic is all within the window class. While this approach is slightly simpler than the other one, we're missing an unique "interface", which might be useful for access from external objects instead.
class SelfContained(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
uic.loadUi('buttongroup.ui', self)
self.pushButton.clicked.connect(self.setOptions)
self.radioButtonGroups = []
for g, groupBox in enumerate((self.groupBox1, self.groupBox2, self.groupBox3)):
buttonGroup = QtWidgets.QButtonGroup(self)
self.radioButtonGroups.append((groupBox, buttonGroup))
buttonGroup.buttonToggled[int, bool].connect(lambda id, state, g=g: self.optionChanged(g, id, state))
self.update_options(g, ['Option {}'.format(o + 1) for o in range(randrange(1, 10))])
def update_options(self, groupId, button_list):
groupBox, buttonGroup = self.radioButtonGroups[groupId]
layout = groupBox.layout()
if layout is None:
layout = QtWidgets.QVBoxLayout(groupBox)
# as above...
#buttonGroup.setExclusive(False)
for i, text in enumerate(button_list, 1):
radio = buttonGroup.button(i)
if radio:
#radio.setChecked(False)
radio.setText(text)
else:
radio = QtWidgets.QRadioButton(text)
layout.addWidget(radio)
buttonGroup.addButton(radio, i)
#buttonGroup.setExclusive(True)
if len(button_list) == len(buttonGroup.buttons()):
return
for radio in buttonGroup.buttons():
id = buttonGroup.id(radio)
if id > i:
buttonGroup.removeButton(radio)
radio.deleteLater()
def setOptions(self):
groupId = self.comboBox.currentIndex()
options = ['Option {}'.format(o + 1) for o in range(self.spinBox.value())]
self.update_options(groupId, options)
def optionChanged(self, groupId, id, state):
if state:
groupBox, buttonGroup = self.radioButtonGroups[groupId]
print('{} checked {} ({})'.format(groupBox.title(), id, buttonGroup.button(id).text()))
I m able to dynamically add a layout to QHBoxLayout in pyqt , however I m unable to remove them once added.
Main aim is to dynamically add and remove a layout based on the Radio Button selected.
def SearchTab(self):
self.layout = QVBoxLayout()
button_layout = QHBoxLayout()
radio_button_1 = QRadioButton("Search")
radio_button_2 = QRadioButton("Update")
button_layout.addWidget(radio_button_1)
button_layout.addWidget(radio_button_2)
self.layout.addItem(button_layout)
radio_button_1.toggled.connect(lambda :self.SelectButtonCheck(radio_button_1))
radio_button_1.toggled.connect(lambda :self.UpdateButtonCheck(radio_button_2))
self.setTabText(0,"Search")
self.tab1.setLayout(self.layout)
def SelectButtonCheck(self,b):
if b.text() == "Search":
if b.isChecked():
print(b.text()+ "is selected")
self.pg_details = pgd.PGDetails()
layout = self.pg_details.returnLayout()
self.layout.addLayout(layout)
def UpdateButtonCheck(self,b):
if b.text() == "Update":
if b.isChecked():
print(b.text()+ " is selected")
for i in range(self.layout.count()):
print(self.layout.itemAt(i))
temp_layout = self.layout.itemAt(i)
widget = temp_layout.widget()
temp_layout.removeItem(temp_layout)
if widget is not None:
widget.deleteLater()
Initial Screen-
Currently I m able to add the layout when "Search" Radio Button is selected --
But Nothing happens when I select "Update" RadioButton
Also find the layouts that have been added-
for i in range(self.layout.count()):
print(self.layout.itemAt(i))
<PyQt5.QtWidgets.QHBoxLayout object at 0x1180ec438>
<PyQt5.QtWidgets.QFormLayout object at 0x1180ff828>
Layouts are being added but not getting removed.
Any leads would be helpful , in what I m missing here
I was able to solve this. Took me a while but understood what is the arrangement of widgets within the layouts.
I assumed removing the layout will cascade delete the widgets itself.
I used the below function to remove the layout and its widgets corresponding to it.
def SearchTab(self):
self.layout = QVBoxLayout()
button_layout = QHBoxLayout()
radio_button_1 = QRadioButton("Search")
radio_button_2 = QRadioButton("Update")
button_layout.addWidget(radio_button_1)
button_layout.addWidget(radio_button_2)
self.layout.addItem(button_layout)
#createDB()
radio_button_1.toggled.connect(lambda :self.SelectButtonCheck(radio_button_1,self.layout))
radio_button_1.toggled.connect(lambda :self.UpdateButtonCheck(radio_button_2,self.layout))
#layout.addRow("Address",QLineEdit())
self.setTabText(0,"Search")
update_layout = QHBoxLayout()
#update_layout.set
#update_btn = QDialogButtonBox(QDialogButtonBox)
#update_btn.setAlignment(Qt.AlignBottom)
update_layout.setAlignment(QtCore.Qt.AlignTop)
update_btn = QPushButton('Update')
reset_btn = QPushButton('Reset')
#self.layout.addRow(update_layout)
update_layout.addWidget(update_btn)
update_layout.addWidget(reset_btn)
update_btn.clicked.connect(self.createDB)
self.tab1.setLayout(self.layout)
def SelectButtonCheck(self,b,stacklayout):
if b.text() == "Search":
if b.isChecked():
print(b.text()+ "is selected")
self.pg_details = pgd.PGDetails()
layout = self.pg_details.returnLayout()
self.layout.addLayout(layout)
def removeLayout(self,layout):
for i in range(layout.count()):
temp_layout = layout.itemAt(i)
if temp_layout is not None:
widget = temp_layout.widget()
if widget is not None:
widget.deleteLater()
else:
return
if temp_layout.layout() is not None:
self.removeLayout(temp_layout.layout())
def removeFormLayout(self,layout):
if layout is not None:
for i in range(layout.count()):
temp_layout = layout.itemAt(i)
if isinstance(temp_layout.layout(),type(QFormLayout())):
self.removeLayout(temp_layout.layout())
else:
next
else:
return
def UpdateButtonCheck(self,b,stacklayout):
if b.text() == "Update":
if b.isChecked():
print(b.text()+ " is selected")
self.removeFormLayout(stacklayout)
The removeFormLayout function picks out the formlayout that I added with the Search radio button and removeLayout removes all the widgets under it as well.
Open to suggestions and improvements in the method used, I tried it with a couple of layout removals other then FormLayout as well. Currently it is working fine.
Also is anybody aware of how to align the HBoxLayout at the top , the radio button starts from the middle again , I want to align them at the top of the screen
Another way is to do the following. QWidget and thus QGroupBox have a show/hide option.
Note, its best to always hide first, otherwise things get wonky
I have a test function that has the following control logic
if isOn:
self.gb1.hide()
self.gb2.show()
else:
self.gb2.hide()
self.gb1.show()
I created a layout that contains both group boxes above. see the sample logic below. I am sure there is a way to do this without storing the variables in the window class.
def create_layout(self):
ly = QHBoxLayout()
self.gb1 = self.create_gb_test1()
self.gb2 = self.create_gb_test2()
ly.addWidget(self.gb1)
ly.addWidget(self.gb2)
return ly
def create_gb_test1(self):
my_name = inspect.currentframe().f_code.co_name
gb = QGroupBox("Find")
btn_find = QPushButton()
ly_horiz = QHBoxLayout()
ly_horiz.addWidget(QLabel("Find:"))
ly_horiz.addWidget(QLineEdit("Some Text", ))
ly_horiz.addWidget(btn_find)
self.ly_find_only = ly_horiz
gb.setLayout(ly_horiz)
return gb
def btn_open_click(self):
pass
def create_gb_test2(self):
my_name = inspect.currentframe().f_code.co_name
gb = QGroupBox("View")
btn_open = QPushButton
cbo = QComboBox()
cbo.addItems(['a', 'b'])
ly_horiz = QHBoxLayout()
ly_horiz.addWidget(QLabel("Find:"))
ly_horiz.addWidget(cbo)
ly_horiz.addWidget(btn_open)
self.ly_find_only = ly_horiz
gb.setLayout(ly_horiz)
return gb