How to get list of widgets in pyqt? - pyqt

I am designing a interface with QtDesigner and editing its functionalities with PyQt. One of the widgets that i created has several pushButtons and i want them all to have the property Checkable = True.
So currently what i am doing is:
class MyWidget(QWidget):
def __init__(self):
super(MyWidget, self).__init__()
uic.loadUi('./my_widget.ui', self)
self.pushButton_X.setCheckable(True)
self.pushButton_Y.setCheckable(True)
self.pushButton_Z.setCheckable(True)
self.pushButton_ETC.setCheckable(True)
self.show()
Is there any way i can do something like:
pushbuttons_list = self.get_all_pushbuttons()
for i in pushbuttons_list:
i.setCheckable(True)
?
Im trying the answers to this question but i keep getting
> File "./testing_class.py", line 12, in __init__
items = (self.layout.itemAt(i) for i in range(self.layout.count()))
AttributeError: 'builtin_function_or_method' object has no attribute 'count'

Your example failed because all Qt properties can be accessed by calling the attribute (see the parentheses used for self.layout()):
items = (self.layout().itemAt(i) for i in range(self.layout().count()))
Note that this will only get access to the layout items, not the widgets they might (or might not) contain. For this, using comprehensions will only complicate things unnecessarily, as one-liners should be used only as long as they keep the code readable; the comprehension above is already complex enough, and since you will need to cycle through all items anyway, there's little use in it. Just use a for loop, which is far more readable:
for i in range(self.layout().count()):
widget = self.layout().itemAt(i).widget()
if isinstance(widget, QPushButton):
widget.setCheckable(True)
Note that if you have several buttons that you want checkable and you are doing this only to avoid manually setting the checkable property for all of them, you can just use the extended selection mode that is common for all UI elements that allow selection by keeping pressed Ctrl when clicking multiple objects.
The property editor will automatically show all common properties for the selected widgets, and apply the changed property to all of them.
Another option is to use findChildren():
for button in self.findChildren(QPushButton, Qt.FindDirectChildrenOnly):
button.setCheckable(True)
The Qt.FindDirectChildrenOnly flag is required whenever you have complex layouts that have nested widgets containing other buttons, if you remove that you will find any push button that exists inside the window (or the widget referenced as self).
Finally, buttons can be part of a QButtonGroup, which can also be created in Designer. Just select all buttons (as explained above), right click on one of them and select "Assign to button group", and then:
for button in self.buttonGroup.buttons():
button.setCheckable(True)
Note that the self.buttonGroup above is the object name assigned by Designer, if you change it or you create more than one, ensure that the reference matches it.

Related

Make QTableWidgetItem non-editable, but still able to select parts of a string

I have a QTableWidget with data that I would like the user to be able to double-click on, and copy a specific part of the text. The only thing I want to disable is the user's ability to change that text, meaning that setting the flag ~Qt.ItemIsEditable is too restrictive. How can I achieve this in PyQt5?
Note: This solution works for all kinds of classes that inherit from QAbstractItemView like QTableWidget or QTreeWidget.
A possible solution is to use a modify the editor to be read-only through a delegate.
class StyledItemDelegate(QStyledItemDelegate):
def createEditor(self, *args):
editor = super().createEditor(*args)
if isinstance(editor, QLineEdit):
editor.setReadOnly(True)
return editor
delegate = StyledItemDelegate(view)
view.setItemDelegate(delegate)

How to I fix my code to only access one item in the list on each iteration?

I have been working with pygame for Python these past few days but I have encountered an issue that I can not seem to overcome. First let me talk about the issue and provide the code. The code with the issue is in the WIP branch of my repo. That is were new features are tested before been deployed.
https://github.com/Aeryes/Demented/blob/WIP/menu_testing.py
I have been trying to place my game menus in a class like structure to make it easier to create menus in the future by having a universal class for the menu creation. This has worked except for accessing individual members of my button list located in the menu class. When I check to see if a button is been hovered over to change color the check works but the new color is applied to all buttons in the list not just the one that is been hovered over.
I have tried many things thus far including the following:
I have tried to reference the individual item like this:
--> main_menu.buttons[0].hovered = True
When I do this the code does not work nor does it break the program. It just makes no color change at all.
I have tried to remove the buttons = [] list from the Menu() class and create each button as an individual instance by doing the following:
play_button = main_menu.add_button(button info here)
This just give me a NullType error when I call hovered = True.
I have been using this as a reference for help with this issue:
https://python-forum.io/Thread-PyGame-Creating-a-state-machine
Here is my direct question: How do I get hovering to work for an individual button and not all of them at the same time?
Thank you for your help.
Your problem is here:
if main_menu.buttons[0].rect.collidepoint(pygame.mouse.get_pos()):
Button.hovered = True
else:
Button.hovered = False
You're not setting the hovered flag to true on the actual button, but rather the class (which, as far as I know, doesn't generally serve a function)
To correct it, you should be using
if main_menu.buttons[0].rect.collidepoint(pygame.mouse.get_pos()):
main_menu.buttons[0].hover = True
else:
main_menu.buttons[0].hover = False
The bigger problem is that you never redraw your buttons after the update. The way I solved this (using your preexisting code) was to update your conditional to this:
if main_menu.buttons[0].rect.collidepoint(pygame.mouse.get_pos()):
main_menu.buttons[0].hovered = True
main_menu.buttons[0].draw()
else:
main_menu.buttons[0].hovered = False
main_menu.buttons[0].draw()
There are a couple things to think about.
A for loop iterating over indices would probably be much nicer than three individual conditionals.
You have a bug in your menu class, where you write "for button in buttons:" instead of "for button in self.buttons:"
Finally, you might want to look into your else condition. Given that you're currently recreating your menu continuously, it might not be necessary to un-hover the button, but I'll leave that to you

PyQt: how to create a dialog as a child of another dialog

In PyQt, I have a dialog that spawns another dialog (when you click a button to do so in the first dialog). I want to maintain a strong parent-child relationship, for garbage collection purposes, and to make the .findChild and .findChildren functions usable.
The root of the question may be: how do you use .setParent() but still have the object in question be shown as a separate window, rather than shown within the parent widget?
The 'parent' dialog (actually a container widget within a tab within a dialog) is 'newEntryWidget'. It spawns 'clueDialog' when a signal (not shown here) calls newEntryWidget.quickTextClueAction as a slot. Visually, clueDialog should be a "top level window" with its own banner, its own window attributes (I want to keep it on top of everything else), etc.
class newEntryWidget(QWidget,Ui_newEntryWidget):
def __init__(self,parent,sec=0,formattedLocString='',fleet='',dev='',origLocString='',amendFlag=False,amendRow=None):
QDialog.__init__(self)
self.parent=parent # just an attribute; not the same as setParent
...
...
def quickTextClueAction(self):
self.newClueDialog=clueDialog(self,self.ui.timeField.text(),self.ui.teamField.text(),self.ui.radioLocField.text(),lastClueNumber+1)
self.newClueDialog.show()
class clueDialog(QDialog,Ui_clueDialog):
def __init__(self,parent,t,callsign,radioLoc,newClueNumber):
QDialog.__init__(self)
self.parent=parent # just an attribute; not the same as setParent
...
...
Right now, since I am using self.parent=parent, which is just an attribute and not true "parent/child relationship" in Qt terms, clueDialog does display as a top level window, which is what I want:
But, if I add 'self.setParent(parent)' in the clueDialog init function, I get this:
How can I preserve the top-level-window behavior, and have the real-honest-parent-child-relationship so that .findChild(clueDialog) will work from within the newEntryWidget object?
Ultimately, I want to enforce that the newEntryWidget object should not be closed if it still has and 'child' clueDialogs open.
Instead of calling .setParent, call QDialog.__init__(self, parent) which constructs the clue dialog with a parent from the beginning. Setting it this way allows Qt establishes the parent-child relationship at the beginning of clueDialog's lifetime.
I believe this will fix both your issues: 1) the clue window frame, caption, etc. will be painted, 2) you will be able to iterate for proper children of newEntry.

Element not recognized

Working on Coded UI testing and for scripts developed using Record Capture and playback feature (ctrl +I).
The problem is when the page has sub-menus (e.g. I need to hover over menu link then click sub-menu). When I record and capture element using Ctrl+I and executed a script it recognizes, but when I ran the script for the second time the element gets changed and it's not recognized.
I have tried simple x path utility posted here but coudn't able to use this feature. What would be the problem for always element id's getting changed. How to resolve it ?
Are you sure it isn't a nested object?
See http://executeautomation.com/blog/how-to-identify-all-the-child-elements-within-a-page-in-coded-ui-test-cuit/
You could also try EnsureClickable()
There could few reasons behind not recognizing an element:
List item Element is not visible when you are trying to click on it.
If Type of Parent Element is e.g. winclient then it’s difficult in coded UI to identify its child elements.
There could various solutions, you can try:
First Click on Menu Item and then click on Sub Menu Item, if you are directly clicking on sub menu item in your recorded script, this will make sub menu element visible.
Also you can check the visibility from Coded UI Test Builder-> Add Assertion button then going to UI Control Map, then select the element in tree and click on Refresh. It will show if element is visible or not.
If Ids are changing, then you can various other properties like Name, ClassName, InnerText, ControlType, TagInstance, ControlName etc. whichever is supported by Element.

Redraw widgets with update

Let's say I have a button. In it's constructor I use variables to feed the values(before running I set some default values to these variables), so later they can be changed and within the program the look of the GUI can be modified. What I need is to update the widgets whenever I change these values.
For example I have some options to change a certain color, I press the button it calls a certain command defined in the constructor, changes the color variable and after that, it needs to be updated. Here it says the update() redraws widgets as needed. How do I tell it I need the widgets to be redrawn ?
http://effbot.org/tkinterbook/widget.htm
I might be mistaken on what the redrawing actually means. In any case I need it to update with the new values. I have a quite dumb solution for this, that is destroying everything and rebuilding it. I feel like there is a smarter way of doing things.
All widgets have a configure method which can be called to change any of its attributes. All you have to do is keep a reference to the widget(s), and call the method:
def update_the_widgets():
the_label.configure(background="red")
a_button = tk.Button(..., command=update_the_widgets)
the_label = tk.Label(..., background="green")
This is much easier if you use an object oriented style of coding. Otherwise these references need to be global variables.
When your GUI is properly coded you should almost never need to call update.

Resources