Converting string into an object instance name - python - python

I'm probably going about this all wrong but...
I am trying to populate a QTreeView from SQL data - using QAbstractItemModel (and having a great deal of trouble understanding it tbh). One of the tutorials I am following (the simplest) populates the Tree by simply calling new instances of the 'Node' and generating the model from the list. The Node has a name and a parentnode (as below). This is OK where you are generating the data within the program. This I can just about follow :)
However, I want to bring the data in from the table and use a string to identify the correct parentnode - mainly because if I am iterating over the records I won't be able to name each one using a separate variable(?). It will be for x in recs: node = Node("name", parentnode).
When I do this, I get the obvious error message that the string isnt the correct object and has no methods. Is there a way of using a string derived from my table to identify the correct 'parent' object (either that, or could somebody point me in the direction of a very basic Qtreeview model tutorial designed for very enthusiastic, but not necessary gifted learners).
rootNode = Node("Hips")
childNode0 = TransformNode("RightPirateLeg", rootNode)
childNode1 = Node("RightPirateLeg_END", childNode0)
childNode2 = CameraNode("LeftFemur", rootNode)
childNode3 = Node("LeftTibia", childNode2)
childNode4 = Node("LeftFoot", childNode3)
childNode5 = LightNode("LeftFoot_END", childNode4)
I realise that I am probably running before I can walk here and apologise in advance for my ignorance.

Are the strings the names of global variables? If so, you can access the value refenced by the global variable with globals()['name'], (replacing 'name' with the string name of the variable of course.)
Or, better yet, instead of littering variable names all over your global namespace
you could use a dict:
node={}
node['rootNode']=Node('Hips')
node['childNode0']=TransformNode('RightPirateLeg',node['rootNode'])
...
This makes it very easy to map between string names and values.

Related

XBRL label names differ between instance and calculation documents

I have what is, probably, a very stupid question, but I'm stumped by it and would appreciate any help.
I'm trying to gather xbrl data from SEC filings using Python and BeautifulSoup. One problem I'm having is that certain line items are referred to differently in the instance document and the calculation linkbase.
As a concrete example, take this recent 10-K from PHI Group Inc.:
https://www.sec.gov/Archives/edgar/data/704172/000149315221015100/0001493152-21-015100-index.htm
A line item with the xbrl tag 'WriteoffOfFinancingCosts' shows up as
<PHIL:WriteoffOfFinancingCosts ...> in the instance document (along with a value and contexts)
but shows up as 'loc_PHILWriteoffOfFinancingCosts' in the calculation linkbase.
But this relationship, 'PHIL:' = 'loc_PHIL', isn't standard across XBRL filings. How does one know what prefix will be added to a tag in the calculation linkbase so that (with the prefix removed) it can be reliably tied back to a tag in the instance document?
I can think of various workarounds, but it just seems silly; isn't there somewhere I can look in the calculation linkbase or elsewhere that will just TELL me exactly what prefix is added?
As some (possibly relevant) nuance: lots of tags in lots of filings, of course, have a prefix like 'us-gaap', indicating the us-gaap namespace, but that doesn't seem to guarantee that a tag in the calculation linkbase will therefore look like 'us-gaapAccountsPayableCurrent' and not 'loc_us-gaapAccountsPayableCurrent' or 'us-gaap:AccountsPayableCurrent' or some other variation of the basic pattern, all of which, of course, look different to BeautifulSoup.
Can anyone point me in the right direction?
PHIL:WriteoffOfFinancingCosts is the name of the XBRL concept, while loc_PHILWriteoffOfFinancingCosts is the (calculation linkbase) label of the locator pointing to the concept PHIL:WriteoffOfFinancingCosts. This mechanism is the way linkbases connect concepts together: each locator is a "proxy" to a concept.
loc_PHILWriteoffOfFinancingCosts is thus an internal detail of the calculation linkbase. The names of linkbase labels are in principle "free to choose", however there are conventions that established themselves (such as prefixing with loc_) but I would not rely on them. Rather, you can "follow the trail" by looking at the definition of the linkbase label:
<link:loc xlink:type="locator"
xlink:href="phil-20200630.xsd#PHIL_WriteoffOfFinancingCosts"
xlink:label="loc_PHILWriteoffOfFinancingCosts" />
Where you see, thanks to the xlink:href attribute, that this locator points to the concept with the ID PHIL_WriteoffOfFinancingCosts in file phil-20200630.xsd.
<element id="PHIL_WriteoffOfFinancingCosts"
name="WriteoffOfFinancingCosts" .../>
And you can see that the local name of this concept is WriteoffOfFinancingCosts. It is in the namespace commonly associated with prefix PHIL: but never appears in a concept definition as all concepts in that file are in the namespace commonly associated with PHIL:. Now, how do we know this? because at the top of the xsd file, it says targetNamespace="http://phiglobal.com/20200630" and the prefix PHIL: is also attached to this namespace in the instance file phil-20200630.xml with xmlns:PHIL="http://phiglobal.com/20200630"
It is common practice to choose concept IDs with the prefix followed by underscore followed by the local name. Some users rely on it, but following the levels of indirection, in spite of being more complex, is "safer": linkbase label loc_PHILWriteoffOfFinancingCosts -> concept ID PHIL_WriteoffOfFinancingCosts -> concept local name WriteoffOfFinancingCosts -> concept's fully qualified name PHIL:WriteoffOfFinancingCosts.
You probably notice how complex this is. In fact, this is the reason why it is worth using an XBRL processor, which will do all of this for you.
#Ghislain Fourny: Many thanks. I'm glad to know that I wasn't crazy for finding the situation complex. Knowing now that the linkbase labels are "free-to-choose", here is the specific BeautifulSoup workaround that I've come up with, in case anyone is interested:
labeldict = {}
resp = requests.get(calcurl, headers = headers)
ctext = resp.text
soup = BeautifulSoup(ctext, 'lxml')
tags = soup.find_all()
for tag in tags:
if tag.name == 'link:loc':
if tag.has_attr('xlink:href') and tag.has_attr('xlink:label'):
href = tag['xlink:href']
firstsplit = href.split('#')[1] ## gets the part of the link after the pound symbol
value = firstsplit.split('_')[1] ## gets the part after the underscore
key = tag['xlink:label']
labeldict[key] = value
Which results in a dictionary where keys are the 'loc_Phil'-type label names and the values are the plain concept names, e.g. labeldict['loc_PHILWriteoffOfFinancingCosts'] = 'WriteoffOfFinancingCosts'
This assumes that xsd links will always follow a format of '...#..._concept'. I haven't found any that don't follow that format, but that's not a guarantee.

Does "in" do the same thing as str.contains()?

I'm new to Python but am very confused as to how this code works:
Correct code I don't understand:
I don't understand how in the function, you can just write ".org' in domain to capture whether the referrer_domain is an organization. I thought you would have to filter via .str.contains() to be able to see if the domain includes .org or .com.
I originally coded:
dot_org = data[data['referrer_domain'].str.contains('.org')
dot_com = data[data['referrer_domain'].str.contains('.com')
def domain_type(type):
if type in dot_org['referrer_domain']:
return 'organization'
elif type in dot_com['referrer_domain']:
return 'company'
else:
return 'other'
data['new_column'] = data['referrer_domain'].apply(domain_type)
But this ended up labeling all of the rows in the new column I created as "other".
Is anyone able to explain why the code in the picture works, but why the code above doesn't?
First, you should not use type as a variable name, because it's a reserved word.
Aside from that, there is no str.contains method, at least not in plain Python. The official way of checking if a string contains another string is using the in operator.

Maya Python, Renaming joints: more than one object matches name

Ok, so I get two errors whenever I try to run this script: but before I get ahead of myself: lets get to my objective.
create two joint chains, names are unimportant: but essentially I know that you can use brackets to list and isolate joint chains with matching children names. Instead my script seems to be ignoring the brackets and giving me the error anyways. I've tried every different flag for list relatives: but all that seems to do is change the error to something else.
I know that if this script was properly working it would only work on one joint chain because of the hardcoded names: but the script I'm pulling it from has name prefexes tied to the GUI to avoid hardcoding and allow adaptive naming: I'm only using the hardcoded as an example for this script. My complaint is this script doesn't work on ANY joint chain because I keep getting the error "more than one object matches name."
To run the script,save the following code as a .py in your maya documents script folder, restart your copy of maya, then open a new python tab and run the first three lines of code above import maya.cmds
'''
import exampleScriptTemplate
reload (exampleScriptTemplate)
exampleScriptTemplate.gui()
'''
import maya.cmds as cmds
if cmds.window("buildWin", exists =True):
cmds.deleteUI("buildWin", window = True)
myWindow = cmds.window("buildWin",t='DS_pvFinder',rtf=1,w=100, h=100, toolbox=True)
column = cmds.columnLayout(adj=True)
def gui(*args):
cmds.columnLayout()
cmds.button(w=300,label='build placement curve',c=printMultiple)
cmds.showWindow(myWindow)
def printMultiple(*args):
root = cmds.ls(sl=True)[0]
child = cmds.listRelatives(root,ad=1,f=True,children=True,type='joint')
child.append(root)
child.reverse()
limbJnt = child
print (child)
armroot = []
for j in limbJnt:
wstJnt = cmds.rename(child[3], 'wrist_BIND')
elbJnt = cmds.rename(child[2], 'elbow_BIND')
sdrJnt = cmds.rename(child[1], 'shoulder_BIND')
clvJnt = cmds.rename(child[0], 'clavicle_BIND')
armroot.append(j)
return armroot
I know I'm in the right ballpark. I just need to know how to properly use the brackets to store the list of what i'm selecting instead of searching all of worldspace and breaking.
Thank you for your help
The code you provided is incomplete, no window is opening, so I tried only the printMultiple function which causes a Error: No object matches name in my case.
Your code cannot work like this since you mix hardcoded names with a loop which does nothing. I suppose your main problem is the order of your renamings. The child array contains absolute names like:
[u'joint1', u'|joint1|joint2', u'|joint1|joint2|joint3']
If you now rename child[0] to 'clavicle_BIND', all the remaining elements in the list become invalid because their real names in the scene now look like this:
[u'clavicle_BIND', u'|clavicle_BIND|joint2', u'|clavicle_BIND|joint2|joint3']
What results in an error at the second rename line. Inverting the order sovles this problem, first rename the leaf node, then the ones above.

Kivy Reference id by string

I'm building an app that has a grid of 20x20 labels which I've set as ids id: x1y1, id: x1y2, ... , id: x20y20 etc. I want to be able to reference the id by passing a string, and modify the text within, using something like;
self.ids.name("x1y1").text = "Anything"
I've had a look at dir(self.ids), dir(self.ids.items()) etc but can't seem to figure it out. Is it even possible?
I could create a list of if statements like;
if str(passedString) == "x1y1":
self.id.x1y1.text == "Anything"
if str(passedString) == "x1y2":
self.id.x1y2.text == "Anything"
Although I feel that this is incredibly bad practice, also considering I'd need 400 if statements! - Sure I could write a little helper-script to write all this code out for me, but again, it's not ideal.
EDIT:
I had a look at;
for key, val in self.ids.items():
print("key={0}, val={1}".format(key, val))
Which printed;
key=x1y2, val=<kivy.uix.label.Label object at 0x7f565a34e6d0>
key=x1y3, val=<kivy.uix.label.Label object at 0x7f565a2c1e88>
Might give you/someone an idea of where to go and/or what to do.
You just want to get the reference via a string of the id? You can use normal dictionary lookup (as ids is really a dict).
the_string = 'x1y1'
the_reference = self.ids[the_string]
the_reference.text = 'the_new_text'

Renaming fails for duplicated objects

I am making a renaming script, but I am getting in a bit of trouble with my
Seach and Replace function.
Actually, the function works as long as there are no duplication of the same
object in the hierarchy. For example, as depicted in the attachment, locator1
and locator2 are created from scratch, whereas locator3 is a duplication from
locator2
If I were to display them in their short name, it is as follows:
locator1
locator2
locator2|locator3
So as mentioned, when I tried to replace the word 'locator' to 'Point', the
renaming works for locator 1 and 2, but when it comes to locator3, I got the
error RuntimeError: No object matches name
As such, I was wondering if there is a better way for me to recode, cause in
cases like in Modelling, where artists duplicates the object over and over again
or using of instances..
I know that this fails is due to the short name in itself but is it possible to bypass it?
def searchReplace(self):
wordSearch = str(self.searchTxt.text())
wordReplace = str(self.replaceTxt.text())
objCnt = cmds.ls(sl=True, sn=True)
if len(objCnt) == 0:
self.searchTxt.clear()
self.replaceTxt.clear()
cmds.warning('Nothing is selected')
else:
for wordString in sorted(objCnt):
if wordSearch in wordString:
newWordString = wordString.replace(wordSearch, wordReplace)
cmds.rename(wordString, newWordString)
self.searchTxt.clear()
self.replaceTxt.clear()
print '%s' %wordString + " has changed to : " + "%s" %newWordString
This is a tricky problem, but the solution is actually really simple!
When you sort objCnt, you're doing it lexicographically:
for wordString in sorted(objCnt):
This means that locator2 comes before locator2|locator3. On its own that should be fine, but...
When locator2 is renamed, the path to locator3 has also changed, so accessing it fails.
The trick is to reverse the sort so longer objects come first. That way the children always get renamed before their parents
for wordString in sorted(objCnt, reverse=True):
For this to work, you also need to make sure your ls gives you long names, be adding the long=True argument

Categories

Resources