PyQT: how to dump all items into a file? - python

Okay there is a QListView with a standard model.
How to iterate over ALL the items in the list (or get them all at once), and transform their content into Python's list of strings?
There are tons of receipts for PyQT4, and hours of googling gave me nothing for PyQT5. That's just terrible! I'm really upset we chose version 5, but that wasn't my choice =(
(somewhere in the UI)
self.listView = QtWidgets.QListView(self.centralwidget)
(somewhere in the View code)
def users_to_list_view(users, list_view):
list_model = QStandardItemModel(list_view)
for user in users:
item = QStandardItem(user.username)
list_model.appendRow(item)
list_view.setModel(list_model)
def someSignal():
...
users_to_list_view(users, self.ui.listView)
def onSave():
...
AND HERE I WANT TO GET ALL THE ITEMS FROM THAT BLOODY LIST!

Should be something like
def users_to_list_view(users, list_view):
self.list_model = QStandardItemModel(list_view)
for user in users:
item = QStandardItem(user.username)
self.list_model.appendRow(item)
list_view.setModel(list_model)
def onSave():
self.list_model. # get items with some method

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!

Using pyviz in a jupyternotebook as a form, put input in nested list and show contents

im trying to use pyviz in a jupyter notebook to create some sort of form for others to populate with data.
this data then is to be saved to a nested list on the click of the save button. then you repeat it for every person.
then i need a button to show the current input of the nested list.
can someone point me in the right direction?so far ive got only the input fields, the list is always empty.
# companies at which people are working
company = ['wal', 'even', 'foot']
class Company(param.Parameterized):
# dropdown of company
company = param.ObjectSelector(objects=company)
# name of person
personname = param.String(doc="name")
# age of person
age = param.Number(0)
# save to list button
save_btn = param.Action(lambda self:self.param.trigger('save_btn'),doc="""Save""")
# show list
show_btn = param.Action(lambda self: self.param.trigger('show_btn'),doc="""Show dicitonary""")
# dict which collects all input
all_persons = []
# return content of dict
#param.depends('show_btn')
def show_list(self):
return self.all_persons
# save form content to dict
#param.depends('save_btn')
def save_to_list(self):
temp_list = []
temp_list.append[self.company]
temp_list.append[self.personname]
temp_list.append[self.age]
run = Company()
pn.Column(run.param.company, run.param.personname, run.param.age,run.param.save_btn,run.param.show_btn, run.show_list)
# desired nested list
# [['wal', "bob", "34"], ["foot", "anna", "56"]]
Your code contains a few typos and loose ends. From top to bottom:
all_persons is defined as empty list, but never connected to the temp list created in the method save_to_list()
#param.depends(...) is missing watch=True, e.g. #param.depends('show_btn', watch=True)
show_list() returns self.all_persons, but this variable is never used anywhere
E.g.:
company = ['A', 'B'] # simplified code
class Company(param.Parameterized):
company = param.ObjectSelector(default=company[0], objects=company)
person_name = param.String(doc="name")
age = param.Number(0)
save_btn = param.Action(lambda self: self.save_to_list() , doc="""Save""") # no need for param.trigger here...
print_btn = param.Action(lambda self: self.print_list() , doc="""Print""") # no need for param.trigger here...
all_persons = [] # starts with an empty list
# no need for param.depends, as the method directly is called via param.Action,
def save_to_list(self):
print('safe to list: {}, {}, {}'.format(self.company, self.person_name, self.age))
temp_list = []
temp_list.append(self.company) # use () for append, not []
temp_list.append(self.person_name)
temp_list.append(self.age)
print('temp_list: {}'.format(temp_list))
self.all_persons.append(temp_list)
# no need for param.depends, as the method is directly called via param.Action,
def print_list(self):
print('all_persons: {}'.format(self.all_persons))
run = Company()
layout = pn.Row(run.param)
layout.app() # to see print statements in a notebook, use the server variant via 'app()'

Replace text in textField with object name - Pymel

I've realized that there were similar questions located
here:
textfield query and prefix replacing
and
here:
Python - Change the textField after browsing - MAYA
However, these do not address the issue if you have two definitions and need the text in the textField to be queried (actually CHANGE the text in the textField).
I know from experience that doing what I have below in MelScript actually works, but for the sake of Python, and learning how to do it in Python, it seems to not work. Am I missing something? Do I need a lambda to get the name of the object selected and query the textField?
I have an example (a snip-bit of what needs to be fixed):
from pymel.core import *
def mainWindow():
window('myWin')
columnLayout(adj=1)
button('retopoplz', ann='Select a Mesh to Retopologize', bgc=[.15,.15,.15],
l='START RETOPOLOGY', c='Retopo(TextToMakeLive)')
TextToMakeLive = textField(ann='Mesh Selected', bgc=[.2,0,0],
edit=0, tx='NONE')
setParent('..')
showWindow('myWin')
def Retopo(TextToMakeLive):
#This tool selects the object to retopologize
MakeLiveField = textField(TextToMakeLive, q=1, tx=1)
MakeSelectionLive = (ls(sl=1))
if MakeSelectionLive is None:
warning('Please select an object to retopologize')
if MakeSelectionLive == 1:
TextToMakeLive = textField(TextToMakeLive, ed=1,
tx=MakeSelectionLive,
bgc=[0,.2,0])
shape = ls(s=MakeSelectionLive[0])
setAttr((shape + '.backfaceCulling'),3)
createDisplayLayer(n='RetopoLayer', num=1, nr=1)
makeLive(shape)
print('Retopology Activated!')
else:
warning('Select only ONE Object')
mainWindow()
GUI objects can always be edited -- including changing their commands -- as long as you store their names. So your mainWindow() could return the name(s) of gui controls you wanted to edit again and a second function could use those names to change the looks or behaviors of the created objects.
However, this is all much easier if you use a python class to 'remember' the names of the objects and any other state information: it's easy for the class to 'see' all the relevant info and state. Here's your original converted to classes:
from pymel.core import *
class RetopoWindow(object):
def __init__(self):
self.window = window('myWin')
columnLayout(adj=1)
button('retopoplz',ann='Select a Mesh to Retopologize', bgc=[.15,.15,.15],l='START RETOPOLOGY', c = self.do_retopo)
self.TextToMakeLive=textField(ann='Mesh Selected', bgc=[.2,0,0],edit=0,tx='NONE')
def show(self):
showWindow(self.window)
def do_retopo(self, *_):
#This tool selects the object to retopologize
MakeLiveField= textField(self.TextToMakeLive,q=1,tx=1)
MakeSelectionLive=(ls(sl=1))
if MakeSelectionLive is None:
warning('Please select an object to retopologize')
if len( MakeSelectionLive) == 1:
TextToMakeLive=textField(self.TextToMakeLive,ed=1,tx=MakeSelectionLive,bgc=[0,.2,0])
shape=ls(s=MakeSelectionLive[0])
setAttr((shape+'.backfaceCulling'),3)
createDisplayLayer(n='RetopoLayer',num=1,nr=1)
makeLive(shape)
print('Retopology Activated!')
else:
warning('Select only ONE Object')
RetopoWindow().show()
As for the callbacks: useful reference here
You need to assign the command flag AFTER you have created your textField to be queried.
So you would do:
my_button = button('retopoplz',ann='Select a Mesh to Retopologize', bgc=[.15,.15,.15],l='START RETOPOLOGY')
TextToMakeLive=textField(ann='Mesh Selected', bgc=[.2,0,0],edit=0,tx='NONE')
button(my_button, e=True, c=windows.Callback(Retopo, TextToMakeLive))
You were along the right thought chain when you suggested lambda. Pymel's Callback can be more advantageous over lambda here. Check out the docs: http://download.autodesk.com/global/docs/maya2014/zh_cn/PyMel/generated/classes/pymel.core.windows/pymel.core.windows.Callback.html

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

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())

Categories

Resources