Handling complex object trees in QML's model-view-delegate system? - python

I'm using QML with PyQt, and these two communicate with Qt's signal/slot system. I have Python objects which I've serialized to a JSON format, for example a list of stores and items sold in them:
[
{
"store": "Walmart",
"items": [
{
"name": "banana",
"price": 0.95
},
...
]
},
{
"store": "Target",
"items": [...]
},
...
]
This is then forwarded to QML with a signal, and then I store the whole JSON structure into a single variable, let's call it stores:
// main.qml
Window {
id: app
property var stores: []
// Slot function that's connected to Python
function onStoresInitialized(stores) {
app.stores = stores;
}
}
Now I have a ListView like so:
ListView {
model: app.stores
delegate: Column {
Repeater {
model: modelData["items"]
Text {
text: modelData["name"] + " $" + modelData["price"]
}
}
}
}
While the example is greatly simplified, it all seems to work fine up to this point. The problem arises when I want to update a single item, say we get a signal from Python that a particular item's price was updated. Our QML slot would look something like this:
function updatePrice(storeName, itemName, newPrice) {
for (let store : app.stores) {
if (store["name"] === storeName) {
for (let item : store["items"]) {
if (item["name"] === itemName) {
item["price"] = newPrice;
return;
}
}
}
}
}
This properly updates the price in my "model", but it doesn't signal the view of this change. I believe using raw data as a model is not the correct approach, but what is? All the QML guides seem to use simple data examples such as a 1D list of items with ListModel and ListElements, but what to do when I have a deep object tree such as here?

My QML days are long over, but one way to deal with this is to wrap the whole JSON into a custom object graph made with PyQt exposing the individual properties as pyqtProperty. And then massage the JSON into this object tree. We do this with a C++-model generated from a model specification in Python. It's a long story why it is that way, but it allows us to send JSON over the wire.
There might be a way to do all this in QML itself using QML types, but my personal preference for model creation would be in Python.

Related

Using Bad Json in Python

I am having a json in a file which i want to access in my Python Code. The Json file looks like :
{
"fc1" : {
region : "Delhi",
marketplace : "IN"
},
"fc2" : {
region : "Rajasthan",
marketplace : "IN"
}
}
The above json i want to use in my Python code. I want to access according to its keys("fc1", "fc2")
Since this is not like actual json, i am facing difficulty in accessing the values in json.
Is there any way in python language to access these type of json.
Thanks.
I agree with the comment that, if you generated that file, then you should put quotes around region and marketplace when generating it (or have the person who generated it do the same). However, if this absolutely isn't an option for whatever reason, the following approach might work:
import json
data_string = """
{
"fc1":{
region:"Delhi",
marketplace: "IN"
},
"fc2" : {
region:"Rajasthan",
marketplace: "IN"
}
}
"""
data = json.loads(data_string.replace('region', '"region"').replace('marketplace', '"marketplace"'))
data
>>>{'fc1': {'region': 'Delhi', 'marketplace': 'IN'},
'fc2': {'region': 'Rajasthan', 'marketplace': 'IN'}}
Note that you would have to do the same for any unquoted key.
There is module dirtyjson which reads this incorrect JSON.
import dirtyjson
data_string = """
{
"fc1":{
region:"Delhi",
marketplace: "IN"
},
"fc2" : {
region:"Rajasthan",
marketplace: "IN"
}
}
"""
data = dirtyjson.loads(data_string)
print(data)
print(data['fc1'])
print(data['fc2'])

Python: Pretty print json file with array ID's

I would like to pretty print a json file where i can see the array ID's. Im working on a Cisco Nexus Switch with NX-OS that runs Python (2.7.11). Looking at following code:
cmd = 'show interface Eth1/1 counters'
out = json.loads(clid(cmd))
print (json.dumps(out, sort_keys=True, indent=4))
This gives me:
{
"TABLE_rx_counters": {
"ROW_rx_counters": [
{
"eth_inbytes": "442370508663",
"eth_inucast": "76618907",
"interface_rx": "Ethernet1/1"
},
{
"eth_inbcast": "4269",
"eth_inmcast": "49144",
"interface_rx": "Ethernet1/1"
}
]
},
"TABLE_tx_counters": {
"ROW_tx_counters": [
{
"eth_outbytes": "217868085254",
"eth_outucast": "66635610",
"interface_tx": "Ethernet1/1"
},
{
"eth_outbcast": "1137",
"eth_outmcast": "557815",
"interface_tx": "Ethernet1/1"
}
]
}
}
But i need to access the field by:
rxuc = int(out['TABLE_rx_counters']['ROW_rx_counters'][0]['eth_inucast'])
rxmc = int(out['TABLE_rx_counters']['ROW_rx_counters'][1]['eth_inmcast'])
rxbc = int(out['TABLE_rx_counters']['ROW_rx_counters'][1]['eth_inbcast'])
txuc = int(out['TABLE_tx_counters']['ROW_tx_counters'][0]['eth_outucast'])
txmc = int(out['TABLE_tx_counters']['ROW_tx_counters'][1]['eth_outmcast'])
txbc = int(out['TABLE_tx_counters']['ROW_tx_counters'][1]['eth_outbcast'])
So i need to know the array ID (in this example zeros and ones) to access the information for this interface. It seems pretty easy with only 2 arrays, but imagine 500. Right now, i always copy the json code to jsoneditoronline.org where i can see the ID's:
Is there an easy way to make the IDs visible within python itself?
You posted is valid JSON.
The image is from a tool that takes the data from JSON and displays it. You can display it in any way you want, but the contents in the file will need to be valid JSON.
If you do not need to load the JSON later, you can do with it whatever you like, but json.dumps() will give you JSON only.

Change the font of an entire document without affecting formatting using Google Docs API

I am trying to change the font of an entire Google Doc using the API. The purpose is to let users of our application export documents with their company’s font.
This is what I am currently doing:
from googleapiclient.discovery import build
doc_service = build("docs", "v1")
document = self.doc_service.documents().get(documentId="[Document ID]").execute()
requests = []
for element in document["body"]["content"]:
if "sectionBreak" in element:
continue # Trying to change the font of a section break causes an error
requests.append(
{
"updateTextStyle": {
"range": {
"startIndex": element["startIndex"],
"endIndex": element["endIndex"],
},
"textStyle": {
"weightedFontFamily": {
"fontFamily": "[Font name]"
},
},
"fields": "weightedFontFamily",
}
}
)
doc_service.documents().batchUpdate(
documentId=self.copy_id, body={"requests": requests}
).execute()
The code above changes the font, but it also removes any bold text formatting because it overrides the entire style of an element. Some options I have looked into:
DocumentStyle
Documents have a DocumentStyle property, but it does not contain any font information.
NamedStyles
Documents also have a NamedStyles property. It contains styles like NORMAL_TEXT and HEADING_1. I could loop through all these and change their textStyle.weightedFontFamily. This would be the ideal solution, because it would keep style information where it belongs. But I have not found a way to change NamedStyles using the API.
Deeper loop
I could continue with my current approach, looping through the elements list on each element, keeping everything but the font from textStyle (which contains things like bold: true). However, our current approach already takes too long to execute, and such an approach would be both slower and more brittle, so I would like to avoid this.
Answer:
Extract the textStyle out of the current element and only change/add the weightedFontFamily/fontFamily object.
Code Example:
for element in document["body"]["content"]:
if "sectionBreak" in element:
continue # Trying to change the font of a section break causes an error
textStyle = element["paragraph"]["elements"][0]["textStyle"]
textStyle["weightedFontFamily"]["fontFamily"] = "[Font name]"
requests.append(
{
"updateTextStyle": {
"range": {
"startIndex": element["startIndex"],
"endIndex": element["endIndex"],
},
"textStyle": textStyle,
"fields": "weightedFontFamily",
}
}
)
doc_service.documents().batchUpdate(
documentId=self.copy_id, body={"requests": requests}
).execute()
This seems to work for me, even with section breaks in between, and at the end of the document. You might want to explore more corner cases..
This basically tries to mimic the SelectAll option
document = service.documents().get(documentId=doc_id).execute()
endIndex = sorted(document["body"]["content"], key=lambda x: x["endIndex"], reverse=True,)[0]["endIndex"]
service.documents().batchUpdate(
documentId=doc_id,
body={
"requests": {
"updateTextStyle": {
"range": {
"endIndex": endIndex,
"startIndex": 1,
},
"fields": "fontSize",
"textStyle": {"fontSize": {"magnitude": 100, "unit": "pt"}},
}
}
},
).execute()
Same should work for other fields too.
However, if you are going to just share a docx file to all the clients, you could keep a local copy of the PDF / DOCX and then modify those. It is fairly easy to work around the styles in DOCX (it is a bunch of xml files)
Use this to explore and update DOCX files OOXML Tools Chrome Extension
Similarly PDFs are key-values pairs stored as records. Check this: ReportLab

Access JSON element with parent name unknown

I've got the same extact problem with Access JSON item when parent key name is unknown, but I work in python.
{
"query": {
"pages": {
"ramdom_number_here": {
"pageid": ramdom_number_here,
"ns": 0,
"title": "Hello",
"extract": "Hello world! Enchanté to meet you"
}
}
}
}
How to get extract given the random_number changing every time?
p.s. It's quite a duplicate question and I'm not sure if the best thing is to open a new question or to answer under the other.
In this case, the key doesn't matter. You just want the (only) value stored in the pages object.
pages = d['query']['pages'].values()
x = list(pages)[0]['extract']

JSON object array parsing in django

I am new to python and django.
For my application in my django view, I am accepting array (and sub arrays) of JSON objects as request, by using json.loads I am trying to parse and iterate thru JSON objects but facing issues while parsing.
my javascript object sent from client is
var JSONObject = {
"employees_companyA":
[
{ "firstName":"John" , "lastName":"Doe" },
{ "firstName":"Anna" , "lastName":"Smith" },
{ "firstName":"Peter" , "lastName":"Jones" }
],
"employees_companyB":
[
{ "firstName":"John" , "lastName":"Doe" },
{ "firstName":"Anna" , "lastName":"Smith" },
{ "firstName":"Peter" , "lastName":"Jones" }
]
};
What is the best way to parse above two objects and read firstName, lastName for same.
I tried using o["firstName"], o.firstName etc (below is my code snippet)
json_obj = json.loads(request.POST['json_test'])
for o in json_obj:
temp_arr.append(o["firstName"])
I am sure this would be pretty straightforward but I couldn't find exact help here.
The top-level element of your JSON structure is not a list, but a mapping. It's keys are of the form "employees_companyA", "employees_companyB", etc.
You need to thus address that structure using the python mapping interface instead:
for value in json_obj.itervalues():
temp_arr.append(value[0]["firstName"])
or as a one-liner:
temp_arr = [value[0]['firstName'] for value in json_obj.itervalues()]
Both use the .itervalues() method on json_obj to loop over all the values in the structure.

Categories

Resources