简单工具/库用于可视化大型Python字典
我有一个很大的字典结构,像这样:
my_data = {
'key1': {
'_': 'value1': 'aaa'
},
'key2': {
'_': 'value2': 'bbb',
'key2.1': {
'_': 'ccc',
'key2.1.1': {
'_': 'ddd'
}
}
'key2.2': {
'_': 'eee',
'key2.2.1': {
'_': 'fff'
}
'key2.2.2': {
'_': 'ggg'
}
}
}
}
还有很多类似的内容。
我想把它展示给用户,做成一种树形结构的样子,使用GTK、TK或者其他工具,这样用户就可以浏览、折叠和展开各个分支,可能还可以搜索键和值。
也许我不需要自己手动开发这样的工具,市面上已经有现成的东西可以直接用来可视化这种数据了?
相关问题:
8 个回答
9
这里已经有一些很不错的回答了,但我觉得这个回答算是“简单”的(它只用了Python自带的库tkinter和uuid)。
这个方法是基于John Gaines Jr.在另一个问题中的回答,经过Will Ware的修改以支持列表,然后我又做了一些改动,让它也能支持元组(可以在Python 3上运行)。
我还重新整理了一下,这样你可以用简单的方式调用查看器,比如用tk_tree_view(data),只需要传入一个字典(就像最后的例子那样)。
import uuid
import tkinter as tk
from tkinter import ttk
def j_tree(tree, parent, dic):
for key in sorted(dic.keys()):
uid = uuid.uuid4()
if isinstance(dic[key], dict):
tree.insert(parent, 'end', uid, text=key)
j_tree(tree, uid, dic[key])
elif isinstance(dic[key], tuple):
tree.insert(parent, 'end', uid, text=str(key) + '()')
j_tree(tree, uid,
dict([(i, x) for i, x in enumerate(dic[key])]))
elif isinstance(dic[key], list):
tree.insert(parent, 'end', uid, text=str(key) + '[]')
j_tree(tree, uid,
dict([(i, x) for i, x in enumerate(dic[key])]))
else:
value = dic[key]
if isinstance(value, str):
value = value.replace(' ', '_')
tree.insert(parent, 'end', uid, text=key, value=value)
def tk_tree_view(data):
# Setup the root UI
root = tk.Tk()
root.title("tk_tree_view")
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)
# Setup the Frames
tree_frame = ttk.Frame(root, padding="3")
tree_frame.grid(row=0, column=0, sticky=tk.NSEW)
# Setup the Tree
tree = ttk.Treeview(tree_frame, columns=('Values'))
tree.column('Values', width=100, anchor='center')
tree.heading('Values', text='Values')
j_tree(tree, '', data)
tree.pack(fill=tk.BOTH, expand=1)
# Limit windows minimum dimensions
root.update_idletasks()
root.minsize(root.winfo_reqwidth(), root.winfo_reqheight())
root.mainloop()
if __name__ == "__main__":
# Setup some test data
data = {
"firstName": "John",
"lastName": "Smith",
"gender": "male",
"age": 32,
"address": {
"streetAddress": "21 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": "10021"},
"phoneNumbers": [
{"type": "home", "number": "212 555-1234"},
{"type": "fax",
"number": "646 555-4567",
"alphabet": [
"abc",
"def",
"ghi"]
}
]}
# call it with
tk_tree_view(data)
它的效果是这样的:
11
我不知道有没有现成的工具,但你可以使用Traits UI来快速开发自己的工具。
from enthought.traits.api \
import HasTraits, Instance
from enthought.traits.ui.api \
import View, VGroup, Item, ValueEditor
class DictEditor(HasTraits):
Object = Instance( object )
def __init__(self, obj, **traits):
super(DictEditor, self).__init__(**traits)
self.Object = obj
def trait_view(self, name=None, view_elements=None):
return View(
VGroup(
Item( 'Object',
label = 'Debug',
id = 'debug',
editor = ValueEditor(),
style = 'custom',
dock = 'horizontal',
show_label = False
),
),
title = 'Dictionary Editor',
width = 800,
height = 600,
resizable = True,
)
def build_sample_data():
my_data = dict(zip(range(10),range(10,20)))
my_data[11] = dict(zip(range(10),range(10,20)))
my_data[11][11] = dict(zip(range(10),range(10,20)))
return my_data
# Test
if __name__ == '__main__':
my_data = build_sample_data()
b = DictEditor(my_data)
b.configure_traits()
就这样。你将会得到一个像这样的图形界面:
Traits UI采用了模型-视图-控制器(MVC)的方法来创建图形界面,这样你就不需要手动编写每一个小部件的代码。在这里,我使用了预定义的ValueEditor来显示各种类型的数据。你现在可以扩展它,支持搜索、过滤等功能……
编辑
简单的扩展来支持过滤功能:
# -*- coding: utf-8 -*-
"""
Created on Fri Feb 22 12:52:28 2013
@author: kranzth
"""
from enthought.traits.api \
import HasTraits, Instance, Str, on_trait_change
from enthought.traits.ui.api \
import View, VGroup, Item, ValueEditor, TextEditor
from copy import deepcopy
class DictEditor(HasTraits):
SearchTerm = Str()
Object = Instance( object )
def __init__(self, obj, **traits):
super(DictEditor, self).__init__(**traits)
self._original_object = obj
self.Object = self._filter(obj)
def trait_view(self, name=None, view_elements=None):
return View(
VGroup(
Item( 'SearchTerm',
label = 'Search:',
id = 'search',
editor = TextEditor(),
#style = 'custom',
dock = 'horizontal',
show_label = True
),
Item( 'Object',
label = 'Debug',
id = 'debug',
editor = ValueEditor(),
style = 'custom',
dock = 'horizontal',
show_label = False
),
),
title = 'Dictionary Editor',
width = 800,
height = 600,
resizable = True,
)
@on_trait_change("SearchTerm")
def search(self):
self.Object = self._filter(self._original_object, self.SearchTerm)
def _filter(self, object_, search_term=None):
def has_matching_leaf(obj):
if isinstance(obj, list):
return any(
map(has_matching_leaf, obj))
if isinstance(obj, dict):
return any(
map(has_matching_leaf, obj.values()))
else:
try:
if not str(obj) == search_term:
return False
return True
except ValueError:
False
obj = deepcopy(object_)
if search_term is None:
return obj
if isinstance(obj, dict):
for k in obj.keys():
if not has_matching_leaf(obj[k]):
del obj[k]
for k in obj.keys():
if isinstance(obj, dict):
obj[k] = self._filter(obj[k], search_term)
elif isinstance(obj, list):
filter(has_matching_leaf,obj[k])
return obj
def build_sample_data():
def make_one_level_dict():
return dict(zip(range(100),
range(100,150) + map(str,range(150,200))))
my_data = make_one_level_dict()
my_data[11] = make_one_level_dict()
my_data[11][11] = make_one_level_dict()
return my_data
# Test
if __name__ == '__main__':
my_data = build_sample_data()
b = DictEditor(my_data)
b.configure_traits()
这会给你一个可以“边输入边过滤”的文本框。虽然搜索在某些情况下不是完全准确,但你可以理解这个思路。
请注意,在这个示例中,字典里的数据有的是整数,有的是字符串,两种类型的数据都会被找到。

9
我最终按照@PavelAnossov的建议,把我的数据转换成了json格式,并使用了d3树形布局。

