Changing kivy widget properties for multiple screens from a loop - python

I have a bunch of screens in my app that all have the same icon that I always want to change together. So I currently have it hard coded and everytime I add a new screen I have to add a new line and it's getting cumbersome:
self.tcs_screen.ids.statusicon.source = "/imgs/..."
self.eclss_screen.ids.statusicon.source = "/imgs/..."
self.gnc_screen.ids.statusicon.source = "/imgs/..."
...
Is it possible to do this from a loop over a list of the screens? I've been trying the following with no success (how do you insert a variable into a property assignment?):
ScreenList = ['tcs_screen', 'eclss_screen', 'gnc_screen']
for x in xrange(len(ScreenList)):
self.ScreenList[x].ids.statusicon.source = "/imgs/..."
Or is there a better way to accomplish this?
Thanks

You have to use getattr() to get the property using the name.
screen_list = ['tcs_screen', 'eclss_screen', 'gnc_screen']
for e in screen_list:
getattr(self, e).ids.statusicon.source = "/imgs/..."

Related

tkinter .insert adds lines to two listboxes at once

I'm creating a tkinter GUI and I have two list boxes that are supposed to have the same single line of text inserted into them upon initialization. The listboxes have separate names, but each time I run .insert for either of the listboxes, both of them get the line added to them and I can't figure out why. The same thing happens when they get their unique data. Here is example code copied from my program:
names = ["+ Add New Condition"]
bcList_lsb = tk.Listbox(bc_frm,listvariable=names)
bcList_lsb.grid(row = 1, column =1)
bcList_lsb.bind('<<ListboxSelect>>',command = bcLbClick)
bcList_lsb.insert("end",names[0])
solNames = ["+ Add New Condition"]
solBcList_lsb = tk.Listbox(solBc_frm,listvariable = solNames)
solBcList_lsb.grid(row = 1, column =1)
solBcList_lsb.bind('<<ListboxSelect>>',command = solBcLbClick)
solBcList_lsb.insert("end",solNames[0])
The setup for the two frames is identical except for their names and their commands.
Here is what it looks like in the GUI upon initialization:
Any suggestions would be appreciated.
You have in effect given both listboxes the same listvariable which forces them to be in sync. When you specify the listvariable option, the string representation of what you give it becomes an internal variable name. Both widgets end up with a variable named "['+ Add New Condition']" (eg: str(["+ Add New Condition"])) which means they are the same variable internally.

KivyMD - How can I update label texts that are StringProperties of objects set in a different screen?

It appears that when a line of code like the following:
class PostStatsPage(Screen, MDAdaptiveWidget):
name1 = StringProperty(pg1.name)
name2 = StringProperty(sg1.name)
name3 = StringProperty(sf1.name)
name4 = StringProperty(pf1.name)
name5 = StringProperty(c1.name)
pts1 = StringProperty(str(pg1.stats))
pts2 = StringProperty(str(sg1.stats))
is written for a screen, the variables are set as soon as the app is started. My app lets the user set the variables in screens leading up to this screen, so when I set my labels to reference these StringProperties, they call the default values written into the code.
My question is: Is there a simple way to update a screen at once? I know that I could set the button that takes me to this screen to update every variable (essentially writing this code twice), but that doesn't seen very "pythonic," and I'm sure it isn't the best way to do it. That would be a nightmare in a large scale application.
I'd like to think there is a function like app.update(PostStatsPage) that would recall all of the objects inside of the StringProperties of the screen given as an argument. Is there anything like that? I've tried searching, and all I can find is articles about threading, but that looks like its used for running multiple code simultaneously. I don't believe I need that for this simple use case. I can't be the only person who finds this a necessity. Thanks in advance!
Colton

Creating and using a large number of objects with a non-preset name in Tkinter, Python

What I`m using: Tkinter lib, setattr() function
What I want: by using a for loop create some number of widgets with some parameters and roughly similar names. Then, I want to be able to set their location using the same loop
How it looks for me for now:
class Window(Tk):
def __init__(self):
Tk.__init__(self)
for i in range(n):
setattr(Label(self, text=f"name{i}", OTHER_OPTIONS), f"name{i}", _value)
name{i}.place(y=0+10*i)
What I expect to see: a column of text from name0 to nameN with 10 units between each
For what I need it?: to create a function in which I can send parameters like name of object, text, background and other of label options
Thanks in advance for the answer!
You're making this more difficult than it needs to be. Just store the labels in a list or dictionary.
self.labels = []
for i in range(n):
label = Label(self, ...)
self.labels.append(label)
I also strongly recommend against place. It's much easier to create responsive applications using pack and grid. With place you have to a lot more work, and it's easy to create a UI that looks good only looks good for one specific configuration (resolution, default font, window size).

PyQt: removing custom widgets from a QListWidget

I am loading in a picture into a QGraphicsScene then when you click on the photo it will place circles in the area that is clicked. Then I am adding a custom widget that will keep a list of the points. This widget has a couple of buttons. one of them will be able to move the circle and the other will be to delete it. I am currently stuck on the delete part.
Problem
I can delete the circle just fine and I can delete the widget from the list. The problem is there is still a space in the list from where the widget once was and since I'm using the button from the widget and not selecting the item I'm not sure how to delete that spot. Also if I delete a bunch and then try and add some Python its self will crash and I have no idea why.
I'm not sure if what I want can be done since there really is no reference or if I'm better off moving the buttons to the main window and removing them from the custom widget. If it is possible I would like to keep it the way I have it.
There are a few files so I will put it on GitHub so I am not to take up a ton of space. Any help is much appreciated.
GitHub Link
Link to GitHub Project
The relevant code (from Main.py):
class MainWindow(QMainWindow, Ui_MainWindow):
...
def Add_History(self,pos):
self.HistoryWidget = HistoryList()
self.HistoryWidget.setObjectName("HistoryWidget_"+ str(Constents.analysisCount))
myQListWidgetItem = QListWidgetItem(self.History_List)
myQListWidgetItem.setSizeHint(self.HistoryWidget.sizeHint())
self.History_List.addItem(myQListWidgetItem)
self.History_List.setItemWidget(myQListWidgetItem, self.HistoryWidget)
self.HistoryWidget.buttonPushed.connect(self.deleteObject)
self.HistoryWidget.setXpoint(str(pos.x()))
self.HistoryWidget.setYpoint(str(pos.y()))
self.HistoryWidget.setHistoryName("Point "+ str(Constents.analysisCount))
Constents.analysisCount = Constents.analysisCount + 1
def deleteObject(self):
sender = self.sender() #var of object that sent the request
row_number = sender.objectName().split("_")
number = row_number[1]
x,y = Constents.analysisDetails[str(number)]# getting xy for object
self.loadPicture.findAndRemoveAnalysisPoint(x,y) #calling the class with the scense to delete it
Constents.analysisDetails.pop(str(number)) # get rid of the object in the variables
HistoryWidget = self.findChildren(HistoryList, "HistoryWidget_"+number)[0] #find the actual object
HistoryWidget.deleteLater() #delete that object
#Simport pdb; pdb.set_trace()
#self.History_List.takeItem(HistoryWidget)
Pictures
You must delete both the item-widget and the item itself. To do that, a method is required for getting an item from an item-widget (or one of its child widgets). A clean way to do this is to use the list-widget's itemAt method, which can get an item from a point on the screen. The major benefit of doing it this way is that it does not require knowledge of the item's index, which can of course change when other items are deleted. It also means the item-widgets don't need to know anything about the specific list-widget items they are associated with.
Here is a re-write of your deleteObject method, which implements that:
def deleteObject(self):
sender = self.sender() #var of object that sent the request
row_number = sender.objectName().split("_")
number = row_number[1]
x,y = Constents.analysisDetails[str(number)]# getting xy for object
self.loadPicture.findAndRemoveAnalysisPoint(x,y) #calling the class with the scense to delete it
Constents.analysisDetails.pop(str(number)) # get rid of the object in the variables
# get the viewport coords of the item-widget
pos = self.History_List.viewport().mapFromGlobal(
sender.mapToGlobal(QtCore.QPoint(1, 1)))
if not pos.isNull():
# get the item from the coords
item = self.History_List.itemAt(pos)
if item is not None:
# delete both the widget and the item
widget = self.History_List.itemWidget(item)
self.History_List.removeItemWidget(item)
self.History_List.takeItem(self.History_List.row(item))
widget.deleteLater()

Why aren't my old boxes disappearing?

So I did earlier, a few months ago, ask a similar question but I can't seem to apply the same rules/laws as were applied in a previous question of mine. Like my name suggest my understanding of Python is very basic and so I am confused at why his code won't work...
Every time I load a new screen, the old set of boxes should disappear and new only the new ones should be left there. However this is not what happens. Here are pictures to demonstrate:
Link to the full code.
However I think the problem may lie here in this part of the code:
def deletePanes(self):
print("delete panes")
Panes = []
self.NoOfPanes = 0
I also call Panes = [] in an earlier section of the code which lies in class Application():. Is the problem possibly that I am calling it twice? What I mean by this is that is it possible that one Panes list has the old boxes in it and one Panes has the new set in it and so when it comes to draw on the PyGame screen it draw both old and new boxes? (All of this might make more sense if you have a look at the full code)
Thank you for any help in advance.
When you call
Panes = []
inside a function, it creates a new local variable Panes and sets it to an empty list.
If it is a global variable, you should call
Panes[:] = []
If it is in the class, you should call
self.Panes = []
Panes is a local variable and will not change the value of your other use of Panes. You need to either make Panes global or an instance variable. Do the same in each place in your code that references Panes

Categories

Resources