How to iterate over different ids levels using weakref in kivy - python

I have this sequence of ids.
self.ids.cuarta_pantalla.ids.container.ids.pre_1.ids.Si
In this case, container has 70 different ids [from pre_1 until pre_70] and each pre_(x) has three different ids [Si, MasMenos, No] that correspondes to a group of CheckBoxes.
If I want to know the state of a single checkbox using its atribute value, I'm forced to write all the statement like this.
self.ids.cuarta_pantalla.ids.container.ids.pre_1.ids.Si.value.
So, How can I iterate over the ids?
I've tried using square brackets self.ids.cuarta_pantalla.ids.container.ids['pre_1'] but it returns something with which I can't call any method.
Print with Square brackets: <weakref at 0x125F7118; to 'BoxLayout' at 0x125F30D0>
Print with dot notation: <kivy.uix.boxlayout.BoxLayout object at 0x125F30D0>
This is the way I've created the objects:
for idx in range(70):
BoxContainer = BoxLayout()
# Repeat this two more times with MasMenos and No
check1 = CheckBox(group= f"p_{idx+1}")
BoxContainer.add_widget(check1)
BoxContainer.ids['Si'] = weakref.ref(check1)
#Adding the BoxContainer with CheckBoxes to the container
self.ids.cuarta_pantalla.ids.container.add_widget(BoxContainer)
self.ids.cuarta_pantalla.ids.container.ids[f'pre_{idx+1}'] = weakref.ref(BoxContainer)

There is no need to use weakref, every time that add_widget is used, an Observable List called children into the object in which was added the other one will contain the reference of that added object.
For example:
IDlist = self.ids.cuarta_pantalla.ids.container.children
The variable IDlist can be iterated to get each reference where you can call any method of that particular object

Related

Python Changes Object Data Inside List Syncronously With The Related Object

class RandomData:
def __init__(self):
self.id=0
def rand_prod(self):
self.id+=1
self.name,self.surname=unique_names_generator.get_random_name().split()
self.address=real_random_address()
self.birthday=datetime.date(randint(1950,2021),randint(1,12),randint(1,28))
self.latitude=(18,16)
self.longtitude=(18,16)
data_list=[]
current_rd=RandomData()
for i in range(5):
current_rd.rand_prod()
data_list.append(current_rd.__dict__)
I create a RandomData object and call the rand_prod() method inside the for loop, and in the loop I append this data to a list by saying objectname.dict,
but when I print the last list, all 5 dicts in the list have the data obtained in the last for loop. Every time I change the properties of the object, the old properties that I previously added in the list also change. What is the reason for this and how can I solve it?
You should really make a new object each time round the loop.
data_list=[]
for i in range(5):
current_rd=RandomData()
current_rd.rand_prod()
data_list.append(current_rd.__dict__)

Append an element to a double embedded dictionary adds to all parent elements

I'm a little bit stumped by a problem which I can't figure out and I feel it should be a trivial problem to solve.
I have an element budget of class Budget. In this class, it has a dictionary sections which contains Section class objects. In Section class, I have another dictionary which is called allowances of Allowance class objects. In the Allowance class, I have a list called operations to which I want to add Operation class objects. The hierarchy is as follows, for those a bit more visual :
budget -> sections -> allowances -> operations
I want to append an Operation class object to a specific combination of section and allowance (variables used are name_of_section and name_of_operation. My first try was
budget.sections[name_of_section].allowances[name_of_allowance].operations.append(Operation(name=name,
cost=cost, date=date)
For some unknown reason, it adds the Operation object to all my sections and allowances elements and I can't figure out why. I tried adding a method add_operation() to Section, which uses a method add_operation() to Allowance, but it just keeps adding every operation to all my Section and Allowance elements.
The weirdest thing is that I also have a list keywords in Allowance which is meant to be a list of strings. I append a string just a few lines later in exactly the same fashion:
budget.sections[name_of_section].allowances[name_of_allowance].keywords.append(keyword_str)
And it only adds to the appropriate section and allowance. Does anyone have an idea why when I'm trying to append my Operation object to a list, it adds to all the lists, although when I append to a list of str, it only adds to a single and the appropriate list?
Here's my Budget initialization and method to add an allowance which adds a section as needed.
def __init__(self):
self.sections = {"Income": Section(name = "Income")}
self.total_balance = 0.0
self.unsorted_operations = []
def add_allowance(self, name, section, projected_cost = 0.0, frequency = "monthly"):
if section in self.sections:
self.sections[section].new_allowance(name=name, section=section, projected_cost = projected_cost,
frequency = frequency)
else:
self.add_section(name = section)
self.sections[section].new_allowance(name=name, section=section, projected_cost=projected_cost,
frequency=frequency)
My Section class is initialized in this fashion and the _new_allowance()_ method is:
def __init__(self, name):
self.name = name
self.allowances = dict()
self.calculate_projected_cost()
def new_allowance(self, name, section, projected_cost, frequency = 'monthly'):
self.allowances[name] = Allowance(name = name, section = section, projected_cost = projected_cost,
frequency = frequency)
self.calculate_projected_cost()
My Allowance class is initialized this way:
def __init__(self, name, section, projected_cost = 0.0, frequency = "monthly"):
self.name = name
self.section = section
self.operations = []
self.cost = 0.0
self.frequency = frequency
self.calculate_projected_cost(projected_cost, frequency)
self.keywords = []
I don't know how you created your data structure,
as you didn't post that code.
But your symptom makes it clear that you did something like this:
>>> a = ['apple']
>>> fruit = [a, a, a]
>>> a.append('banana')
>>> fruit
[['apple', 'banana'], ['apple', 'banana'], ['apple', 'banana']]
That is, you have an inner mutable container (or similar object),
and your outer container has multiple references to that single inner container.
Suppose you display the outer container.
Now you change the inner container (a, above) --
that will affect the displayed output in multiple places,
since you have multiple references to what changed.
If you're curious about details you can use e.g.
print(list(map(id, fruit))) to see whether elements
are the same or different.
The python id(x) function is roughly equivalent
to a C program asking at which address was
storage for x allocated.
It turned out it might had been a problem with my saved JSON file which I loaded my budget object from. As J_H suggested to use id(x), the elements were one and the same. This tip was quite useful in debugging my problem.
Without any change to the code and by starting from scratch instead of loading my JSON file, I did not see that kind of behavior again. I saved it, stopped the program, ran it again and loaded my new saved file, and now, it is running as it should.

Django - Filter related objects

Let's say I have a list of locations where each location has a list of some objects. I want to make sure that I get these locations, but with a filtered list of objects.
Here's the structure of models.py:
class Location(models.Models):
# fields
class LocationObject(models.Models):
location = models.ForeignKey(Location, related_name="objects_list")
# other fields that are used in filtering
Here's how I do filtering:
locations = Location.objects.all()
if request_square_from:
locations = locations.filter(objects_list__size__gte=request_square_from)
if request_square_to:
locations = locations.filter(objects_list__size__lte=request_square_to)
# Other filters ...
The problem is that, by using this method of filtering, I get in each location a list of objects in which there is at least one object that satisfies the condition in locations.filter(). This is not what I actually need. I need to exclude every object (I mean LocationObject) that doesn't satisfy the condition in the filter() method.
Is there any idea to do that?
Update. A bit of clarification
Here's how this list looks:
Location #1
- Object 1 (size=40)
- Object 2 (size=45)
- Object 3 (size=30)
Location #2
- Object 4 (size=20)
- Object 5 (size=25)
I want to filter each object of location by, let's say, size property. Assume this condition: Location.objects.filter(objects_list__size__gte=40). This will match locations that contain even just a single list entry that has this property. This is not what I need. Expected result should be:
Location #1:
- Object 1 (size=40)
- Object 2 (size=45)
Assuming that you really want those locations that have at least one object that fulfills both conditions, you could do:
locations = locations.filter(objects_list__size__gte=request_square_from,
objects_list__size__lte=request_square_to)
But since you are not sure to have both parameters, you cannot do that. You can, however, achieve it using Q objects:
from django.db.models import Q
# ...
locations = Location.objects.all()
q = Q()
if request_square_from:
q &= Q(objects_list__size__gte=request_square_from)
if request_square_to:
q &= Q(objects_list__size__lte=request_square_to)
if q:
locations = locations.filter(q)
If I am not wrong, what you want is exclude().
filter() is used to filter out all objects that are required. So you would use:
locations = locations.filter(objects_list__size__gte=request_square_from)
This will give you all the objects that satisfy this condition.
But if you want to exclude the matching query. You need to use exclude()
locations = locations.exclude(objects_list__size__gte=request_square_from)
This will give you the objects that do not satisfy the condition and returns the rest of the objects.
According to your update, you want a list of location objects, not locations, so, instead of filtering your locations, filter the location objects!
objects = LocationObject.objects.all()
if request_square_from:
objects = objects.filter(size__gte=request_square_from)
if request_square_to:
objects = objects.filter(size__lte=request_square_to)
You are not restricted to filter foreign keys, you can filter over any Model instance.
If after this, you want the location of any object, you just...
objects[0].location

How To Iterate Through Objects (Saved in List) Using For Loop?

I am running an API to grab some information from a website where I am storing the information in a list '[]'. How can I run a for loop through this information to:
1) Iterate through the list of objects in a for loop (specifically comparing one objects text
2) If one value of the object equals a 1 word, save whole object into a new list
I have tried running a for loop through the list/objects but get the error ''list' object is not callable'
tree = Et.fromstring(response.content)
for child in tree.findall('meterConsumption'):
for audit in child.findall('audit'):
for creator in audit.findall('createdBy'):
for ID in child.findall('id'):
print ('Entry ID: ',ID.text)
for use in child.findall('usage'):
print ('Use: ',use.text)
for cost in child.findall('cost'):
print ('Cost: ',cost.text)
for startdate in child.findall('startDate'):
print ('Startdate: ',startdate.text)
for enddate in child.findall('endDate'):
print ('Enddate: ',enddate.text)
#save object to list
allentries.append(Entry(ID.text,
use.text,
cost.text,
startdate.text,
enddate.text,
creator.text))
for x in allentries():
print (x.entryid)
I am looking to get a list of all key value pairs in the object. For example it would like:
Id[1], use[1], cost[1], startdate[1], enddate[1], creator[1]
Id[2], use[2], cost[2], startdate[2], enddate[2], creator[2]
Id[3], use[3], cost[3], startdate[3], enddate[3], creator[3]
The say from this, if creator == "human".append to all info from this object to a new object list
Triple for loops followed a for loop for child.findall('id') will result in a compile-time error called as identation error.
for child in tree.findall('meterConsumption'):
for audit in child.findall('audit'):
for creator in audit.findall('createdBy'):
#identation error
for ID in child.findall('id'):
print ('Entry ID: ', ID.text)
List object is not callable means u trying to call list-objects.
allentries is a list & u are trying to call a list by using ().
Remove this ().
You'll get the 'list' object is not callable error when you try to call a list like you would call a function. That's happening in your second last line:
for x in allentries():
print (x.entryid)
Since allentries is your list, tacking on a () at the end of it is the syntax for "calling" it, which doesn't make sense for objects that aren't functions. So the correct syntax is:
for x in allentries:
print (x.entryid)
Per your second question, I'd suggest looking into pandas DataFrames as handy ways to work with tabular data in Python. Since child.findall() gives you a list of objects that you're extracting text from with .text, you can pass a dictionary of lists to the DataFrame constructor like so:
import pandas as pd
# Initialize empty dataframe
allentries = pd.DataFrame()
for child in tree.findall('meterConsumption'):
for audit in child.findall('audit'):
for creator in audit.findall('createdBy'):
# Also touched up your indentation
for ID in child.findall('id'):
print ('Entry ID: ',ID.text)
for use in child.findall('usage'):
print ('Use: ',use.text)
for cost in child.findall('cost'):
print ('Cost: ',cost.text)
for startdate in child.findall('startDate'):
print ('Startdate: ',startdate.text)
for enddate in child.findall('endDate'):
print ('Enddate: ',enddate.text)
# Use list comprehensions to extract text attributes
allentries.append({
'ID': [ID.text for ID in child.findall('id')],
'use': [use.text for use in child.findall('use')],
'cost': [cost.text for cost in child.findall('cost')],
'startdate': [startdate.text for startdate in child.findall('startDate')],
'enddate': [enddate.text for enddate in child.findall('endDate')]
})
# Display the final dataframe
print(allentries)

Compare attributes of objects inside a list?

I'm trying to sort a list of textMessage objects into several lists.
Each textMessage has three fields - the address of the sender, the time it was sent, and the body of the message.
I have listOfContacts which contains Contact objects. Each Contact object has two fields - the name of the contact and a listOfTexts from that contact. The listOfTexts is filled with a list of textMessage objects.
I'm having trouble with the function that does the sorting. Given a list of texts, I want it to add a new Contact object to listOfContacts if the contact isn't yet in the list. Otherwise I want to append the textMessage object to the listOfTexts inside of the Contact inside of listOfContacts
I'm having a hard time seeing if the contact is already present inside of listOfContacts
def sortObjectArray(textArray):
listOfContacts = []
emptyList = []
for text in TextArray: # For each text in the sorted array
if text.address in listOfContacts.name: # if the text's address is in list of contacts
addText(text) # add the text to the list of texts in contact's class
else:
listOfContacts.append(Contact(text.get("address"), emptyList) #if address not in list of contacts, construct one with an empty list of texts.
I thought that if text.address in listOfContacts.name should check if any Contact with a name field of n exists in listOfContacts, where n is the sender of the text message.
Am I going about this the wrong way?
A list object doesn't have the properties of the contained objects. Consider the following absolutely valid python list:
list_ = ["ah", 12, object(), lambda honey: str(honey) ]
and tell me why
if "ah" in list_.lower():
should have a defined behaviour.
What you want to do is make the objects in your list hashable by their names, and build a set out of them.
EDIT: hashable means you implement the __hash__ function, which in this case might just be
class Contact(object):
def __hash__(self):
return self.name.__hash__
Also, you should define an __eq__ function (eq like equality), so that python can check whether two contacts are equal:
def __eq__(self,other):
return self.name==other.name
As soon as you have done that, you can use the set type, which is just like the list type, but only allows a single "equal" object. That makes a lot of sense for a contact list! After having done that, you can just create a dummy Contact object and check whether it's in the set.
That, or you'll simply have to iterate over all of them:
if any(text.address == cont.name for contact in listOfContacts):
EDIT: that just checks if any of contacts has a name property that is equal to your text.address.
The first problem that you will likely encounter is that listOfContacts will not have a name attribute, since it is a list and not a Contact object. You can create a new list of contact names that will be used to check if the address exists already.
def sortObjectArray(textArray):
listOfContacts = []
contact_names = []
...
for text in TextArray: # For each text in the sorted array
if text.address in contact_names: # if the text's address is in list of contacts
...
else:
contact_names.append(text.address)
listOfContacts.append(Contact(text.get("address"), emptyList) #if address not in list of contacts, construct one with an empty list of texts.

Categories