python: Using the choice box to get data besides getString() - python

I have defined an object that has several attribute..
class thing(object):
def __init__(self, type, name, attrA, attrB, attrC):
self.type = type
self.name = name
self.attrA = attrA
self.attrB = attrB
self.attrC = attrC
lets say then I have a list of things
self.things=[thing('car','fred',1,2,3),
thing('car','george',a,b,c),
thing('truck','bob',6,7,8),
thing('truck','tom',x,y,z)
]
I then populate a choice box with SOME of the items from that list
for each in self.things:
if each.type == 'car':
self.choiceCar.Append(item=each.name)
When the user selects Bob from the dropdown I have an event for that
def EvtChoice(self,event):
self.Name = event.GetString()
This captures the name of the selection, but how do I get the other attributes? What I am currently doing is
for each in self.items:
if self.Name == each.name
#Get other things here
My thought is that if my list grows large then this loop through my entire list will become very inefficient and really unneeded since the user has already selected the specific item I want. What I think I should be able to do is to get the index of the selected item, but im not sure how to do that, or even if that is the correct way to go about it.

Associating data or objects with wx.Choice or wx.ComboBox is pretty easy. You can see an example using the latter here:
http://www.blog.pythonlibrary.org/2010/12/16/wxpython-storing-object-in-combobox-or-listbox-widgets/
The basic idea is to pass an empty list to the control's constructor and then iterate over the objects and Append them to the control. So something like this:
for obj in self.things:
self.choiceCar.Append(obj.name, obj)
Then in the event handler for the widget, you can get the object back by doing this:
obj = self.choiceCar.GetClientData(self.choiceCar.GetSelection())

Related

Python: how to utilize instances of a class

New to OOP and python, I am struggling enormously to grasp what good classes actually are for. I tried to ask help from a lecturer who said "oh, then you should read about general methods to classes". Been putting in a days work but get no where.
I get it that a class allow you to collect an instance structure and methods to it, like this:
class Items:
def __init__(self, item_id, item_name):
self.item_id = item_id
self.item_name = item_name
def show_list(self):
print(self.item_id, self.item_name)
idA = Items("idA", "A")
idA.show_list()
But what is even the point of a class if there were not MANY instances you would classify? If I have a method within the class, I must hard code the actual instance to call the class for. What if you want a user to search and select an instance, to then do operations to (e.g. print, compute or whatever)??
I thought of doing it like this:
class Items:
def __init__(self, item_id, item_name):
self.item_id = item_id
self.item_name = item_name
def show_list(self):
print(self.item_id, self.item_name)
idA = Items("idA", "A")
idB = Items("idB", "B")
select_item = input("enter item id")
select_item.show_list()
Replacing hard coded variable with input variable doesn't work, probably logically. I then played with the idea of doing it like this:
class Items:
def __init__(self, item_id, item_name):
self.item_id = item_id
self.item_name = item_name
iL = [Items('idA', 'A'), Items('idB', 'B')]
selected_item = input("enter item id")
for selected_item in iL:
print(f'{selected_item.item_id} {selected_item.item_name}')
Now all are called thanks to making it a list instead of separate instances, but how do I actually apply code to filter and only use one instance in the list (dynamically, based on input)?
I would love the one who brought me sense to classes. You guys who work interactively with large data sets must do something what I today believe exist in another dimension.
See examples above^^
It seems you want to find all the instances of a certain element within a class.
This is as simple as:
print([x for x in iL if x.item_id == selected_item])
Now, you may ask why you can't just store the elements of iL as tuples instead of classes. The answer is, you can, but
("idA", "A")
is much less descriptive than:
item_id = "idA"
item_name = "A"
Any code you write with classes, you should in theory be able to write without classes. Classes are for the benefit of the coder, not the end-user of the program. They serve to make the program more readable, which I'm sure you'll find is a desirable property.
Your point here is to lookup for Items instances based on their item_id attribute.
That's a thing to create instances of a class.
It's a completely different thing to search for items objects stored in memory - that is not directly linked to the concept of OOP, classes and instances.
You could use dictionary to store references of your objects and then lookup in your dictionary.
class Items:
def __init__(self, item_id, item_name):
self.item_id = item_id
self.item_name = item_name
def show_list(self):
print(self.item_id, self.item_name)
idA = Items("idA", "A")
idB = Items("idB", "B")
lookup_dict = {"idA": idA, "idB": idB}
select_item = input("enter item id")
found_item = lookup_dict.get(select_item)
if found_item:
found_item.show_list()
else:
print(f"item {select_item} not found")

Can I dynamically generate boolean options on a custom Blender operator?

I'm trying to write a custom operator for Blender that:
Gets the objects currently in the scene (may be lots)
Filters them based on some criteria
Prompts the user to select/deselect any of the filtered objects (using checkboxes or similar)
Does something with the final selection.
I'm stuck on number 3. I'd like to show the user a window with checkboxes beside each of the filtered options, but to do that I'd have to be able to generate the properties dynamically.
The closest thing I've found so far is a bpy.props.EnumProperty, which takes callable to set its items. But it only supports 1 selection, whereas I need the user to be able to select multiple options.
Example:
def filter_objects(self, context):
return [obj for obj in bpy.data.objects if obj.name.startswith('A')]
class TurnObjectsBlue(bpy.types.Operator):
'TurnObjectsBlue'
bl_idname = 'object.turnobjectsblue'
bl_label = 'TurnObjectsBlue'
bl_options = {'REGISTER'}
# MultiSelectCheckboxes doesn't exist :(
chosen_objects: bpy.props.MultiSelectCheckboxes(
name='Select Objects',
)
def execute(self, context):
from coolmodule import turn_blue
for obj in self.user_selected_objects:
turn_blue(obj)
return {'FINISHED'}
def invoke(self, context, event):
return context.window_manager.invoke_props_dialog(self)
It looks like you want a CollectionProperty. These are basically native blender lists that can store blender properties, and can have as many items as you like.
To set one up, start by creating a new class that will represent one of the items in the CollectionProperty.
It should inherit from bpy.types.PropertyGroup, and in it you can define any properties that you want to be able to access (also make sure to register that class):
class MyCollectionItem(bpy.types.PropertyGroup):
# This will tell us which object this item is for
object: bpy.props.PointerProperty(type=bpy.types.Object)
# This is whether this item (and by extension the object) is enabled/disabled
is_item_selected: bpy.props.BoolProperty()
def register():
# Don't forget to register it!
bpy.utils.register_class(MyCollectionItem)
Then you can rewrite your operator to do something like this:
class TurnObjectsBlue(bpy.types.Operator):
'TurnObjectsBlue'
bl_idname = 'object.turnobjectsblue'
bl_label = 'TurnObjectsBlue'
bl_options = {'REGISTER'}
# This acts as a list, where each item is an instance of MyCollectionItem
chosen_objects: bpy.props.CollectionProperty(
type=MyCollectionItem,
)
def execute(self, context):
from coolmodule import turn_blue
# Loop through all items in the CollectionProperty, and if they are selected, do something
for item in self.chosen_objects:
if item.is_item_selected:
turn_blue(item.object)
return {'FINISHED'}
def invoke(self, context, event):
objects = filter_objects(context)
# For each object, add a new item to the CollectionProperty, and set it's object property
for object in objects:
# Note how items are created with CollectionProperty.add()
obj_item = self.chosen_objects.add()
obj_item.object = object
return context.window_manager.invoke_props_dialog(self)
def draw(self, context):
layout = self.layout
# Loop through all of the CollectionProperty items and draw the "is_item_selected" property
for item in self.chosen_objects:
layout.prop(item, "is_item_selected")
While collection properties can act like Blender lists, they work fairly differently to python lists, so I'd suggest reading a bit about them to try and understand how they work:
https://docs.blender.org/api/current/bpy.props.html#collection-example
Hope that helps!

Why can't I add elements to this list? (Object oriented programming in python)

Hi everyone I am currently creating a student class to record down exam results and find the average score. Here's the code I have right now:
class Student:
def __init__(self, name):
self.name = name
self.all_grades = []
def add_exam(self, newest_grade):
self.newest_grade = newest_grade
self.all_grades = self.all_grades.append(newest_grade)
def get_mean(self):
self.average = sum(self.all_grades)/len(self.all_grades)
Josh = Student()
Josh.add_exam(72)
However, when I try to put print(Josh.all_grades), it doesn't return anything and type(Josh.all_grades) returns a none type.
I am really lost and I don't understand why it doesn't return [72]
You've to just type self.all_grades.append(newest_grade) instead of typing self.all_grades=self.all_grades.append(newest_grade). When you type append(), it adds element to list. But it returns None. So when you type <list_variable>.append(<element>) it adds the element, but when you type <list_variable>=<list_variable>.append(<element>) then the value of whole list is now None

List Manipulation Based on Class Variables

Apologies if I explain something wrong or use the wrong wording, my programmer vocabulary isn't the best. If anyone understands my problem and has better ways of explaining it feel free to do so. I have a problem similar to a problem here. I want to remove items from a list that occur in another list. But one list will have strings that reference the variable "name" within class objects.
class sword:
name = 'swordName'
class bow:
name = 'bowName'
class axe:
name = 'axeName'
inventory = [sword, bow, sword, axe]
select = ['bowName', 'swordName']
I want to be able to create a list "selectedItems" with the class objects out of inventory based off of the strings in "select" that are equal to the "name" of the class objects. It also needs to work if "inventory" and "select" both have duplicates in them.
Output:
>> inventory = [bow, axe]
>> selectedItems = [bow, sword]
One other thing I would like the program to ignore if there are more "name"s in select than there are corresponding class objects in "inventory", and to ignore if a string in "select" has no corresponding class objects.
For example, if "inventory" is [sword, axe] and "select" is ['bowName', 'non-existent', 'axeName'], the result is that "inventory" is [sword] and "selectedItems" is [axe].
A simple way of explaining this is that select will take from inventory, but if select can't take from inventory nothing happens.
You may make base class with magic methods __eq__ and __hash__ which can allow you to manage comparing your objects as you want:
class BaseItem:
name = None
def __init__(self):
self.__name = self.name
def __eq__(self, other):
return self.__name == other
def __hash__(self):
return id(self.__name)
def __repr__(self):
return f"'{self.__name}'"
class Sword(BaseItem):
name = "swordName"
class Bow(BaseItem):
name = "bowName"
class Axe(BaseItem):
name = "axeName"
inventory = [Sword(), Bow()]
select = ["swordName", "bowName", "axeName", "swordName", "bowName"]
# casting lists into sets and getting difference between them
result = set(inventory) - set(select)
print(result) # output {'swordName', 'bowName'}
eq - actually is unused here but i added that you can compare your objects with strings, lists etc:
Sword() in ["swordName"] # true
Sword() in ["bowName"] # false
Sword() == "swordName" # true
Sword() == "bowName" # false
hash - need to comparing two objects, actually it use for getting difference between two sets
repr - it is not really required method, it needs just for pretty displaying of objects
selectedItems = list()
# make a new list of the names of the objects in the inventory
# inventory and inventory names have the same index for the same item
inventory_names = [x.name for x in inventory]
for s in select:
if s in inventory_names:
index = inventory_names.index(s)
inventory_names.pop(index)
selectedItems.append(inventory.pop(index))

Something like setter and getter for a python dictionary

I have the following problem:
A class contains a dict of dicts of dicts ... e.g.
class Try():
def __init__(self):
self._tryDict = {'first':{'b':{'bla':'x'},'c':1},'second':{'b':15,'c':1}}
#getter
def tryDict....
#tryDict.setter
def tryDict....
I would like to have now something like setter and getter to change a variable at a certain level of the dict and retrun the corresponding dict a view levels above (and only if i set a new value)
e.g.
try = Try()
try.tryDict['first']['b']['bla']
returs: 'x'
try.tryDict['first']['b']['bla']='z'
changes 'x' to 'z' and returns the content of try.tryDict['first'] ({'b':{'bla':'x'},'c':1})
To understand why I need this:
The dict is actually a list of file-praser-functions and the content is also stored as a dict.
i.e.
dict = {'file-1':dict-1, 'file-1':dict-1, 'file-1':dict-1, ...}
with dict['file-1']['content-3']=1 I set the new content
and with dict['file-1'].write(), the content of the file is updated in the file. The dict is a class instance and I would like to immediately wirte the change to the file.
many thx for your help!
BR, maths
OK so what I implemented it now this way:
Each file got setter and getter function
class dicts():
.....
# to show the content
#getter
def tryDict(self):
return tryDict.file
# to sett some content
#tryDict.setter
def tryDict(self, entry):
if len(entry)==2:
name, newEntry = entry
tryDict.file[name]= newEntry
if len(entry)==3:
....
if i whan to get the entry:
tryDicis = dicts()
tryDicis.trydict # shows the dictionary
i can now set in different levels like:
tryDicis = dicts()
tryDicis.trydict = (name-level-1, name-level-2,..., newEntry)
This is maybe not the most elegant way, however it is easy to implement and good enough in my case.
BR, maths

Categories

Resources