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()))
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 two QTableViews inside a QMainWindow and I want to create a context menu on one of them and later another context menu on the other one.
Creating the context menu and define actions works so far. But the context menu pops up everywhere inside the whole application. I don't know how to limit it to only one specific table. I think it has to do with contextMenuEvent() which is a member of QMainWindow, but I don't know how to change this part. Trying to create a custom class that inherits QTableView didn't work, because I'm not sure where to start.
Here's what I tried:
The populate_table_1() and populate_table_2() methods are only for filling some data into the tables. The get_selected_item_TV1() method gets the necessary data from a row of table_1. The delete_file() method is an example for what I want do when calling the delete action from the contextMenuEvent() method. This code works so far, but I want the context menu to popup only if I right-click on a row of table_1 and that it don't appear at all when right-clicking elsewhere.
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
data_1 = ["file_name", "file_infos"]
data_2 = ["other_stuff_1", "other_stuff_2"]
class Ui_MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle("MyApp")
self.resize(450, 280)
centralwidget = QWidget(self)
#]===================================================================[#
# table_1
table_1 = QTableView(
centralwidget,
selectionBehavior=QAbstractItemView.SelectRows,
editTriggers=QAbstractItemView.NoEditTriggers
)
# table_1 models
self.modelTV1 = QStandardItemModel(0, 2, centralwidget)
self.modelTV1.setHorizontalHeaderLabels(["column 1", "column 2"])
table_1.setModel(self.modelTV1)
self.selectionModelTV1 = table_1.selectionModel()
#]===================================================================[#
# table_2
table_2 = QTableView(
centralwidget,
selectionBehavior=QAbstractItemView.SelectRows,
editTriggers=QAbstractItemView.NoEditTriggers,
)
# table_2 models
self.modelTV2 = QStandardItemModel(0, 2, centralwidget)
self.modelTV2.setHorizontalHeaderLabels(["column 1", "column 2"])
table_2.setModel(self.modelTV2)
self.selectionModelTV2 = table_2.selectionModel()
v_Layout1 = QVBoxLayout()
v_Layout1.addWidget(table_1)
v_Layout1.addWidget(table_2)
gridLayout = QGridLayout(centralwidget)
gridLayout.addLayout(v_Layout1, 0, 0, 1, 1)
self.setCentralWidget(centralwidget)
def populate_table_1(self):
self.modelTV1.setRowCount(0)
for item in data_1:
self.modelTV1.insertRow(0)
for i, text in enumerate(data_1):
self.modelTV1.setItem(0, i, QStandardItem(text))
def populate_table_2(self):
self.modelTV2.setRowCount(0)
for item in data_2:
self.modelTV2.insertRow(0)
for i, text in enumerate(data_2):
self.modelTV2.setItem(0, i, QStandardItem(text))
def contextMenuEvent(self, event):
self.contextMenu = QMenu(self)
deleteAction = QAction("Delete", self)
self.contextMenu.addAction(deleteAction)
deleteAction.triggered.connect(lambda: self.delete_file(event))
self.contextMenu.popup(QCursor.pos())
def get_selected_item_TV1(self):
# get the row's text from the first column in table_1
listed_items = self.selectionModelTV1.selectedRows()
for index in listed_items:
selected_item = index.data()
return f"table_1 - row_{index.row()} - {selected_item}"
def delete_file(self, event):
item = self.get_selected_item_TV1()
print(f"Deleting: {item}")
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
mainUI = Ui_MainWindow()
mainUI.populate_table_1()
mainUI.populate_table_2()
mainUI.show()
sys.exit(app.exec_())
There are many alternatives:
Detect that when the mouse is pressed it is in a certain area, for example the first QTableView. In this case the widget must be accessible in the contextMenuEvent() so you must change table_1 to self.table_1 and then use underMouse():
def contextMenuEvent(self, event):
if self.table_1.underMouse():
self.contextMenu = QMenu(self)
deleteAction = QAction("Delete", self)
self.contextMenu.addAction(deleteAction)
deleteAction.triggered.connect(lambda: self.delete_file(event))
self.contextMenu.popup(QCursor.pos())
Implement the contextMenuEvent method for each QTableView:
class TableView(QTableView):
def contextMenuEvent(self, event):
self.contextMenu = QMenu(self)
deleteAction = QAction("Delete", self)
self.contextMenu.addAction(deleteAction)
deleteAction.triggered.connect(lambda: self.delete_file(event))
self.contextMenu.popup(QCursor.pos())
def get_selected_item_TV1(self):
# get the row's text from the first column in table_1
listed_items = self.selectionModel().selectedRows()
for index in listed_items:
selected_item = index.data()
return f"table_1 - row_{index.row()} - {selected_item}"
def delete_file(self, event):
item = self.get_selected_item_TV1()
print(f"Deleting: {item}")
and then you must change table_1 = QTableView(... for table_1 = TableView(...
Another alternative is to use the customContextMenuRequested signal, for this you must enable the Qt::CustomContextMenu flag:
table_1 = QTableView(
centralwidget,
selectionBehavior=QAbstractItemView.SelectRows,
editTriggers=QAbstractItemView.NoEditTriggers,
contextMenuPolicy=Qt.CustomContextMenu
)
table_1.customContextMenuRequested.connect(self.on_customContextMenuRequested)
def on_customContextMenuRequested(self):
self.contextMenu = QMenu(self)
deleteAction = QAction("Delete", self)
self.contextMenu.addAction(deleteAction)
deleteAction.triggered.connect(lambda: self.delete_file())
self.contextMenu.popup(QCursor.pos())
def delete_file(self):
item = self.get_selected_item_TV1()
print(f"Deleting: {item}")
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
Here I have 2 different classes called c_elementOptFrame (lets call it class a) and c_elementFigFrame (and this is class b). In class a, I have radio button object, these button will change an image in which I construct with label in class b, in other words, I wanted to change this image when I click those radio button. The radio button has been set up with method called react , in this method I wanted to pass a value (string) to class b, so that class b will use this value to change the image. But it doesn't work as I expected. The value has been passed to class b (I printed the value) but the image doesn't come up, I tried several ways but I still couldn't do it yet. so this is my code, please let me know if you have couple of suggestions or an alternative to do it. Thank you
code:
class c_elementOptFrame(QFrame):
def __init__(self, parent=None):
super(c_elementOptFrame,self).__init__()
self.setVisible(True)
self.setMaximumWidth(150)
self.setFrameShadow(QFrame.Plain)
self.setFrameShape(QFrame.Box)
self.setLineWidth(1)
self.setFrameStyle(QFrame.StyledPanel)
#layoutmanager
layout = QVBoxLayout()
#all objects inside this frame
strel_lab = QLabel("Element Types")
strel_lab.setContentsMargins(0,0,10,10)
group_box = QGroupBox('Structural Element')
opts_group = QButtonGroup()
opts_group.setExclusive(True)
beam_but = QRadioButton("Beams")
slab_but = QRadioButton("Slabs")
beam_but.toggled.connect(self.react_1)
slab_but.toggled.connect(self.react_2)
opts_group.addButton(beam_but)
opts_group.addButton(slab_but)
group_box.setLayout(layout)
concls_lab = QLabel("Concrete Class")
concls_lab.setContentsMargins(0,15,10,10)
concls_opt = QComboBox()
concls_opt.addItem("C30")
concls_opt.addItem("C40")
concls_opt.addItem("C50")
mod_but = QPushButton("Modify Parameter", self)
mod_but.clicked.connect(self.modifyParam)
create_but = QPushButton("Create Model", self)
create_but.setDisabled(False)
create_but.clicked.connect(self.nextFrame)
#add objects to layout
layout.addWidget(strel_lab)
layout.addWidget(beam_but)
layout.addWidget(slab_but)
layout.addWidget(concls_lab)
layout.addWidget(concls_opt)
layout.addWidget(mod_but)
layout.addStretch()
layout.addWidget(create_but)
self.setLayout(layout)
def react_1(self, enabled):
if enabled:
pict_path = 1
c_elementFigFrame(pict_path)
class c_elementFigFrame(QFrame):
def __init__(self, pict_path):
super(c_elementFigFrame,self).__init__()
self.setFrameShadow(QFrame.Plain)
self.setFrameShape(QFrame.Box)
self.setLineWidth(1)
self.setFrameStyle(QFrame.StyledPanel)
layout = QVBoxLayout()
pict = QLabel('Test')
pict.sizeHint()
pixmap = QPixmap()
if pict_path == 1:
print(True)
pixmap.load('beam.png')
pict.setPixmap(pixmap)
#else:
#pixmap = QPixmap(pict_path)
#pict.setPixmap(pixmap)
layout.addWidget(pict)
self.setLayout(layout)
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()