python - instantiate classes by looping through a dictionary - python-3.x

I'm quite new to Python, but come from a Lua background. I know exactly how I would achieve this in lua. I'm sure the answer already exists but I don't know how to ask the question - what terminology to search for? Things like 'dynamically define variables' return a lot of arguments, along with advice to use dictionaries as that is what they are for. But in my case dictionaries don't seem to work.
A condensed example, where Button(ID) is creating an instance of a button class:
Button1 = Button(8)
Button2 = Button(3)
Button3 = Button(432)
ButtonClose = Button(5004)
As there are more than 4 buttons in my actual UI, and I do more with them than just instantiate the class objects, I wish to use a loop structure of some sort to condense the code.
BtnList = {'Button1' : 8, 'Button2' : 3, 'Button3' : 432, 'ButtonClose' : 5004,}
for btn,ID in BtnList:
# some code here
I've tried using the following outside of any loops/functions, to avoid scoping issues while testing:
btns = {}
btns.Button1 = Button(8)
But this doesn't seem to be possible as I get an error:
AttributeError: 'dict' object has no attribute 'Button1'
Help please?

You have a problem with dict mapping. It should be changed to
btns = {}
btns["Button1"] = Button(8)
Then the string "Button1" inside the btns dictionary will contain the Button Object.

Related

PyQt5 repeat code for a number of widget of same type

really new to programming, python and OOP.
In Python3 and PyQt5, I have a number of objects: QLineEdit(s) defined in Designer and loaded
with uicload, I use to get inputs, I found a way to validate them, my list looks like:
self.validatorint = QtGui.QIntValidator()
self.inputguiwin.annualsalaryinput.setValidator(self.validatorint)
self.inputguiwin.annualsalaryinput.textChanged.connect(self.check_state)
self.inputguiwin.annualsalaryinput.textChanged.emit(self.inputguiwin.annualsalaryinput.text())
self.inputguiwin.annualsalaryinput.textChanged.connect(self.disablepushButtonOK)
self.validatordouble = QtGui.QDoubleValidator(0.100, 1.00, 2)
self.inputguiwin.tosaveinput.setValidator(self.validatordouble)
self.inputguiwin.tosaveinput.textChanged.connect(self.check_state)
self.inputguiwin.tosaveinput.textChanged.emit(self.inputguiwin.houseinput.text())
self.inputguiwin.tosaveinput.textChanged.connect(self.disablepushButtonOK)
self.validatorint = QtGui.QIntValidator()
self.inputguiwin.houseinput.setValidator(self.validatorint)
self.inputguiwin.houseinput.textChanged.connect(self.check_state)
self.inputguiwin.houseinput.textChanged.emit(self.inputguiwin.houseinput.text())
self.inputguiwin.houseinput.textChanged.connect(self.disablepushButtonOK)
self.inputguiwin.pushButtonOK.setEnabled(False)
self.inputguiwin.annualsalaryinput is a QLineEdit Widget in may main GUI inputguiwin,
then I have tosaveinput and houseinput. The lines above connect the inputs to a function
that changes the background color of my input when it is validated and re-able my OK PushButton
Thinking about extending the number of inputs I was wondering about a way of iterating the
four lines of code for all my QLinesEdit widgets.
Now, I dont understand a lot about Python, classes, object and so on. Which is the best or a way to iterate over the 'list' of my widgets ? I mean is there a way to have a list of objects in Python,
what is the best way to iterate over the widgets in my example? at least the ones that will share the same identical QtGui.QIntValidator() type ?
If you want to reduce your code and make it more readable then you will have to use lists and iterate over them:
for lineedit, validator in (
(self.inputguiwin.annualsalaryinput, QtGui.QIntValidator(),),
(self.inputguiwin.tosaveinput, QtGui.QDoubleValidator(0.100, 1.00, 2),),
(self.inputguiwin.houseinput, QtGui.QIntValidator(),),
):
lineedit.setValidator(validator)
lineedit.textChanged.connect(self.check_state)
lineedit.textChanged.emit(lineedit.text())
lineedit.textChanged.connect(self.disablepushButtonOK)
thanks again I ended up with:
input_list_int=[self.inputguiwin.annualsalaryinput, self.inputguiwin.houseinput]
for i in input_list_int:
i_inputguiwin=i
self.validatorint = QtGui.QIntValidator()
i_inputguiwin.setValidator(self.validatorint)
i_inputguiwin.textChanged.connect(self.check_state)
i_inputguiwin.textChanged.emit(i_inputguiwin.text())
i_inputguiwin.textChanged.connect(self.disablepushButtonOK)
input_list_double=[self.inputguiwin.tosaveinput]
for i in input_list_double:
i_inputguiwin=i
self.validatordouble = QtGui.QDoubleValidator(0.100, 1.00, 2)
i_inputguiwin.setValidator(self.validatordouble)
i_inputguiwin.textChanged.connect(self.check_state)
i_inputguiwin.textChanged.emit(i_inputguiwin.text())
i_inputguiwin.textChanged.connect(self.disablepushButtonOK)
obviously your method is better, I'll try to dig more into lists

How to reference a class from a string which is part of a variable 3.7

I have just read a text file and extracted a string and stored it as a variable. This string also happens to be the name of a class I want to reference in order to use in a function for example. The "which_class" variable is the whichever class was stored in the file
I tried passing the which_class variable in as a parameter to the function. Removing the quotations seems to make it work but I am unsure how to do this.
class needed_for_func_one():
multiplier = 1.23
class needed_for_func_two():
multiplier = 1.15
def random_function(which_class):
print(123 * which_class.multiplier)
PSEUDO CODE
READ FROM FILE STORE STRING AS "which_class"
which_class = "needed_for_func_two"
random_function(which_class)
But this didn't work it just gave me an attribute error
the eval function could help you here.
random_function(eval(whichClass))
However, you should probably rethink whether you really want to it that way or if there is a much cleaner solution.
I think your question is related to this one
How you call the function depends if it is a global function or if it is inside an object.
globals()['call_this_function']() # this is probably what you need
or
getattr(from_this_object, 'call_this_function')()
first, to use a class you need an object of a class.
so if what you read is a name of the class or any other thing it does not matter, just use an if statement to decide what is inside that variable so-called "which_class".
then create an object like :
if which_class=="needed_for_func_one":
newObject = needed_for_func_one()
elseif which_class=="needed_for_func_two":
newObject = needed_for_func_two()
then use the print like :
print(123 * newObject.multiplier )

Modify selection to first element by Selection.SetElementIds

I'm having trouble writing a script that lets med select the first element in my selection. This is useful for me because i select my correct Air Terminal from a schedule (where I can see the similar air-flow which I want to use) and use the command Create Similar from the selection. The trouble is that this command does not work when multiple elements are selected. Therefore, I want the first object from the list.
This is the code which I'm trying:
from Autodesk.Revit.UI.Selection.Selection import SetElementIds
from System.Collections.Generic import List
uidoc = __revit__.ActiveUIDocument
doc = __revit__.ActiveUIDocument.Document
selection = [ doc.GetElement( elId ) for elId in __revit__.ActiveUIDocument.Selection.GetElementIds() ]
sel=[]
for i in selection:
sel.append(i.Id)
uidoc.Selection.SetElementIds(List[ElementId](sel[0]))
That will return the following error message:
Exception : Microsoft.Scripting.ArgumentTypeException: expected int, got ElementId
OK, then I'll try to replace
uidoc.Selection.SetElementIds(List[ElementId](sel[0]))
with
uidoc.Selection.SetElementIds(List[ElementId](sel[0].IntegerValue))
This seems to work, but selection is not modified
I am just starting to write RPS-scripts, but I'm hoping someone will show me what am I doing wrong here even if it is very obvious.
Thank you.
Kyrre
EDIT:
Thank you Jeremy, for solving this for me! The trick was to generate a List, not a python list. .Add method is what I did not get.
Final code if someone is interested:
from Autodesk.Revit.UI.Selection.Selection import SetElementIds
from System.Collections.Generic import List
from Autodesk.Revit.DB import ElementId
uidoc = __revit__.ActiveUIDocument
doc = __revit__.ActiveUIDocument.Document
selection = [ doc.GetElement( elId ) for elId in __revit__.ActiveUIDocument.Selection.GetElementIds() ]
sel=[]
for i in selection:
sel.append(i.Id)
ids=List[ElementId](1)
ids.Add(sel[0])
uidoc.Selection.SetElementIds(ids)
SetElementIds takes a .NET ICollection<ElementId> argument, as you can see from the Revit API documentation.
Your statement calls the .NET List constructor that expects an integrer argument specifying the number N of elements to allovate space for: List[ElementId](N).
sel[0] is an ElementId, not an integer, which causes the first error.
sel[0].IntegerValue is a (very large and semi-arbitrary) integer number, so that causes no error. However, you are still leaving the List empty, unpopulated.
You should initialise the List for one single element and add that:
ids = List[ElementId](1)
ids.Add(sel[0])

Generate Wagtail Field's using a FOR loop

I am new to Wagtail and want to create several Field's in my models.py by iterating over a list of names like this ...
class HomePage(Page):
myFields = [ ('paragraph', blocks.RichTextBlock(classname="oneA")),('image', ImageChooserBlock()) ]
mySections = ['sectionOne', 'sectionTwo', 'sectionThree']
for mySection in mySections:
mySection = StreamField(myFields,null=True,blank=True)
content_panels = Page.content_panels + [
StreamFieldPanel('sectionOne'), StreamFieldPanel('sectionTwo'), StreamFieldPanel('sectionThree'),
]
This produces an error message ...
django.core.exceptions.FieldDoesNotExist: HomePage has no field named 'sectionOne'
Is there a way of doing this, or do I have to declare each one individually like so:
sectionOne = StreamField(myFields,null=True,blank=True)
This doesn't work because mySection = StreamField(...) is just repeatedly defining a field called mySection - there's no way for Python to know that you want to define a field with the name currently given in mySection.
I think the only way to achieve this is to set the fields up in a metaclass, which will almost certainly be more complex and less readable than just repeating the line. See How to dynamically set attributes to a class in models in Django?, Dynamically create class attributes

Create a collection of item ids Revit API in Python

So I am trying to use a list of input strings to isolate them in a view using Revit API. I got this far, but I am getting stuck where I am trying to create a set that takes all elements in a view and removes ones that are created from input IDs. I am doing this to end up with a set of all elements except ones that i want to isolate.
dataEnteringNode = IN0
view = IN0
str_ids = IN1
doc = __doc__
collector = FilteredElementCollector(doc, view.Id)
for i in str_ids:
int_id = int(i)
id = ElementId(int_id)
element = doc.GetElement(id)
element_set = ElementSet()
element_set.Insert(element)
elements_to_hide = collector.WhereElementIsNotElementType().Excluding(element_set).ToElements()
#Assign your output to the OUT variable
OUT = elements_to_hide
I would greatly appreciate a help in solving this error. I am getting that "expected ICollection[ElementId], got set". I am guessing the problem lies in a Excluding filter where i need to create a collection of Ids to exclude but I dont know how. Thank you in advance. Thank you for help in advance!
The reason your code doesn't work is that ElementSet in the Revit API does not implement the ICollection<T> interface - just the IEnumerable<T>. So, to get your code working, you will need to create an ICollection<T> object from your set.
Try something like this:
# ...
from System.Collections.Generic import List
element_collection = List[ElementId](element_set)
elements_to_hide = collector.WhereElementIsNotElementType().Excluding(element_collection).ToElements()

Resources