Reputation: 1
I have a dictionary listing of a file structure where the last five keys of each 'file' represent file metadata that I would like to show on various columns.
tree = {'C:': {'Windows': {'System32': {'drivers': {'etc': {'hosts': {'Attrs:Archive': {'Crtd:2013-08-22T13:25:43.000Z': {'Mdfd:2013-08-22T13:25:41.000Z': {'Accd:2013-08-22T13:25:41.000Z': {'Chngd:2016-10-13T20:40:04.000Z': {}}}}}}, 'lmhosts.sam': {'Attrs:Archive': {'Crtd:2013-08-22T15:39:39.000Z': {'Mdfd:2013-08-22T15:38:13.000Z': {'Accd:2013-08-22T15:38:13.000Z': {'Chngd:2016-10-13T20:40:04.000Z': {}}}}}}, 'services': {'Attrs:Archive': {'Crtd:2013-08-22T13:25:43.000Z': {'Mdfd:2018-08-13T23:44:10.000Z': {'Accd:2013-08-22T13:25:41.000Z': {'Chngd:2018-08-13T23:44:10.000Z': {}}}}}}, 'networks': {'Attrs:Archive': {'Crtd:2013-08-22T13:25:43.000Z': {'Mdfd:2013-08-22T13:25:41.000Z': {'Accd:2013-08-22T13:25:41.000Z': {'Chngd:2016-10-13T20:40:04.000Z': {}}}}}}, 'protocol': {'Attrs:Archive': {'Crtd:2013-08-22T13:25:43.000Z': {'Mdfd:2013-08-22T13:25:41.000Z': {'Accd:2013-08-22T13:25:41.000Z': {'Chngd:2016-10-13T20:40:04.000Z': {}}}}}}}}}}}}
My popluate tree function checks each key to see if it contains a keyword for each metadata attribute, strips it out if found and sets it to one of 6 variables depending on which attribute. If no attribute string is found, it treats it as a file and appends it as a row/new file with the the attribute data added.
def fill_model_from_json(self, parent, d):
if isinstance(d, dict):
for key, value in d.items():
it = QtGui.QStandardItem(str())
it2 = QtGui.QStandardItem(str())
if 'Attrs:' in key: #if metadata-specific string in the dict key
attrs_value = re.sub("^(.*Attrs:)" ,'', key) #strip out some the tag leaving just the data
it2 = QtGui.QStandardItem(str(attrs_value)) #assign it to it2, should go on row2 of parent item
else :
it = QtGui.QStandardItem(str(key))
parent.appendRow([it, it2])
self.fill_model_from_json(it, value)
Unfortunately this doesnt quite work and each attribute qstandarditem generates its own row/child instead of populating the parent row. This is my first PyQT project and I'm a bit stuck on how to fix to the population function to generate the correct qtree structure.
How it currently renders:
How it should render:
import os
import sys
from PyQt5 import QtGui,QtCore,QtWidgets
from PyQt5.QtGui import QIcon
import re
tree = {'C:': {'Windows': {'System32': {'drivers': {'etc': {'hosts': {'Attrs:Archive': {'Crtd:2013-08-22T13:25:43.000Z': {'Mdfd:2013-08-22T13:25:41.000Z': {'Accd:2013-08-22T13:25:41.000Z': {'Chngd:2016-10-13T20:40:04.000Z': {}}}}}}, 'lmhosts.sam': {'Attrs:Archive': {'Crtd:2013-08-22T15:39:39.000Z': {'Mdfd:2013-08-22T15:38:13.000Z': {'Accd:2013-08-22T15:38:13.000Z': {'Chngd:2015-10-13T20:48:04.000Z': {}}}}}}, 'services': {'Attrs:Archive': {'Crtd:2013-08-22T13:25:43.000Z': {'Mdfd:2018-08-13T23:44:10.000Z': {'Accd:2013-08-22T13:25:41.000Z': {'Chngd:2018-08-13T23:44:10.000Z': {}}}}}}, 'networks': {'Attrs:Archive': {'Crtd:2017-05-21T14:23:21.000Z': {'Mdfd:2013-08-22T13:25:41.000Z': {'Accd:2013-08-22T13:25:41.000Z': {'Chngd:2016-10-13T20:40:04.000Z': {}}}}}}, 'protocol': {'Attrs:Archive': {'Crtd:2012-08-22T07:25:31.000Z': {'Mdfd:2012-08-22T01:34:41.000Z': {'Accd:2012-08-22T14:17:41.000Z': {'Chngd:2016-10-13T20:40:04.000Z': {}}}}}}}}}}}}
class MainFrame(QtWidgets.QWidget):
def __init__(self):
QtWidgets.QWidget.__init__(self)
self.tree = QtWidgets.QTreeView(self)
layout = QtWidgets.QHBoxLayout(self)
layout.addWidget(self.tree)
root_model = QtGui.QStandardItemModel()
root_model.setColumnCount(6)
self.tree.setModel(root_model)
self.tree.model().setHorizontalHeaderLabels(["File", "Attribute", "Created", "Modified", "Accessed", "Changed"]) #labels columns
self.fill_model_from_json(root_model.invisibleRootItem(), tree)
self.tree.expandAll()
def fill_model_from_json(self, parent, d):
if isinstance(d, dict):
for key, value in d.items():
it = QtGui.QStandardItem(str(""))
it2 = QtGui.QStandardItem(str(""))
it3 = QtGui.QStandardItem(str(""))
it4 = QtGui.QStandardItem(str(""))
it5 = QtGui.QStandardItem(str(""))
it6 = QtGui.QStandardItem(str(""))
if 'Attrs:' in key:
attrs_value = re.sub("^(.*Attrs:)" ,'', key)
it2 = QtGui.QStandardItem(str(attrs_value))
if 'Crtd:' in key:
crtd_value = re.sub("^(.*Crtd:)" ,'', key)
it3 = QtGui.QStandardItem(str(crtd_value))
if 'Mdfd:' in key:
mdfd_value = re.sub("^(.*Mdfd:)" ,'', key)
it4 = QtGui.QStandardItem(str(mdfd_value))
if 'Accd:' in key:
accd_value = re.sub("^(.*Accd:)" ,'', key)
it5 = QtGui.QStandardItem(str(accd_value))
if 'Chngd:' in key:
chngd_value = re.sub("^(.*Chngd:)" ,'', key)
it6 = QtGui.QStandardItem(str(chngd_value))
else :
it = QtGui.QStandardItem(str(key))
parent.appendRow([it, it2, it3, it4, it5, it6])
self.fill_model_from_json(it, value)
app = QtWidgets.QApplication(sys.argv)
main = MainFrame()
main.show()
sys.exit(app.exec_())
Upvotes: 0
Views: 43
Reputation: 243965
The structure of your json fulfills a strange pattern so the method of creating the model will have similar characteristics. In this case I assume that if I find a key that meets the condition that they start with any of the elements of "Attrs:", "Crtd:", "Mdfd:", "Accd:", "Chngd:" then its children will comply the same. I also assume that they will only have one child.
Considering the above, the solution is:
def fill_model_from_json(self, parent, d):
if isinstance(d, dict):
for key, value in d.items():
if self.find_value(key) is not None:
items = []
for i, value in enumerate(self.get_values(d), start=1):
it = QtGui.QStandardItem(value)
parent.parent().setChild(parent.row(), i, it)
else:
it = QtGui.QStandardItem(key)
parent.appendRow(it)
self.fill_model_from_json(it, value)
def get_values(self, d):
values = [""] * 5
while d:
key = list(d.keys())[0]
value = d[key]
d = value
t = self.find_value(key)
if t is not None:
i, value = t
values[i] = value
return values
def find_value(self, key):
COLUMNS = ("Attrs:", "Crtd:", "Mdfd:", "Accd:", "Chngd:")
for i, column in enumerate(COLUMNS):
if column in key:
value = re.sub(r"^(.*{})".format(column), "", key)
return i, value
Upvotes: 1