Сериализовать Gtk TreeStore / ListStore с помощью JSON

Я сделал новый пример, который намного лучше показывает, что я пытаюсь сделать. Новый пример дает следующий результат. Есть ли способ, которым данные могут попасть в соответствующий ключ хранилища (скобки {})?

{
    "copy": [
        [
            [
                5.0,
                8.0,
                9.0
            ]
        ],
        [
            [
                4.0,
                0.0,
                1.0
            ]
        ]
    ],
    "name": "dataset1",
    "sets": [
        {
            "store": {},
            "type": "vector"
        },
        {
            "store": {},
            "type": "vector"
        }
    ]
}

Новый пример

from gi.repository import Gtk
import json
import random

class Vector(object):

    def __init__(self, data):
        self.store = Gtk.ListStore(float, float, float)
        self.store.append([data[0], data[1], data[2]])
        self.type = "vector"

    def return_data(self):
        store_data = []

        def iterate_over_data(model, path, itr):
            row = model[path]
            store_data.append([row[0], row[1], row[2]])

        self.store.foreach(iterate_over_data)

        return store_data

class DataSet(object):

    def __init__(self, name):
        self.name = name
        self.sets = []

    def add_vector(self):
        data = [random.randint(0,9) for x in range(3)]
        self.sets.append(Vector(data))

    def to_json(self):
        self.copy = []
        for s in self.sets:
            self.copy.append(s.return_data())

        return json.dumps(self, default=lambda o: o.__dict__, 
            sort_keys=True, indent=4)

obj1 = DataSet("dataset1")
for x in range(2):
    obj1.add_vector()

print(obj1.to_json())

Старый пример

В настоящее время я выясняю, как сериализовать Gtk ListStore, вложенный в Gtk TreeStore. У меня есть небольшой пример для работы, но я не уверен, будет ли этот подход масштабироваться для программ, к которым прикреплено больше данных (например, объект слоя может содержать цвет или дату создания). Может быть, есть другой способ сделать это?

Мой текущий подход состоит в том, чтобы самому собрать данные в форме списка и словаря, а затем просто создать дамп JSON. У меня такое чувство, что это будет довольно сложно поддерживать, если мне нужно привязать 25 значений к каждому объекту слоя.

from gi.repository import Gtk, Gdk
import json
import random


class LayerTreeView(Gtk.TreeView):

    def __init__(self, store):
        Gtk.TreeView.__init__(self, store)
        renderer = Gtk.CellRendererText()
        column = Gtk.TreeViewColumn("Name", renderer, text=0)
        self.append_column(column)


class DataTreeView(Gtk.TreeView):

    def __init__(self, store):
        Gtk.TreeView.__init__(self, store)
        self.store = store
        renderer = Gtk.CellRendererText()
        column = Gtk.TreeViewColumn("Data", renderer, text=0)
        self.append_column(column)


class MainWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="TreeView Serialize")
        self.connect("delete-event", Gtk.main_quit)
        self.set_border_width(10)
        self.set_default_size(400, 300)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6, expand=True)
        self.add(vbox)

        self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)

        hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
        button = Gtk.Button("Cut")
        button.connect("clicked", self.on_cut_clicked)
        hbox.pack_start(button, True, True, 0)

        button = Gtk.Button(stock=Gtk.STOCK_COPY)
        button.connect("clicked", self.on_copy_clicked)
        hbox.pack_start(button, True, True, 0)

        button = Gtk.Button(stock=Gtk.STOCK_PASTE)
        button.connect("clicked", self.on_paste_clicked)
        hbox.pack_start(button, True, True, 0)
        vbox.add(hbox)

        self.layer_store = Gtk.TreeStore(str, object, object)
        self.layer_view = LayerTreeView(self.layer_store)

        self.layer_sw = Gtk.ScrolledWindow()
        self.data_sw = Gtk.ScrolledWindow()
        self.layer_sw.add(self.layer_view)

        treebox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6, expand=True)
        treebox.pack_start(self.layer_sw, True, True, 0)
        treebox.pack_start(self.data_sw, True, True, 0)
        vbox.add(treebox)

        self.select = self.layer_view.get_selection()
        self.select.connect("changed", self.on_selection_changed)

        self.add_test_data()

    def add_test_data(self):
        for x in range(3):
            data_store = Gtk.ListStore(str)
            data_view = DataTreeView(data_store)
            for y in range(5):
                data_store.append([str(y+x)])
            self.layer_store.append(None, ["Data {}".format(x), data_store, data_view])

    def on_selection_changed(self, selection):
        """
        When layer is switched load respective data
        """
        model, treeiter = selection.get_selected()
        if treeiter != None:
            data_view = model[treeiter][2]
            child = self.data_sw.get_child()
            if child != None:
                self.data_sw.remove(self.data_sw.get_child())
            self.data_sw.add(data_view)
            self.show_all()

    def on_cut_clicked(self, button):
        pass

    def on_copy_clicked(self, button):
        copy_list = ["safe-to-paste"]
        data_dict = {}
        for row in self.layer_store:
            name = row[0]
            data_obj = row[1]
            value_list = []
            for datarow in data_obj:
                value = datarow[0]
                value_list.append(value)
            data_dict[name] = value_list

        copy_list.append(data_dict)
        data = json.dumps(copy_list)
        self.clipboard.set_text(data, -1)

    def on_paste_clicked(self, button):
        paste_str = self.clipboard.wait_for_text()
        try:
            parse = json.loads(paste_str)
            json_str = True
        except:
            json_str = False

        if json_str is False:
            return

        keyword = parse[0]
        if keyword != "safe-to-paste":
            return

        data_dict = parse[1]
        for x in data_dict:
            data_list = data_dict[x]
            data_store = Gtk.ListStore(str)
            data_view = DataTreeView(data_store)
            for y in data_list:
                data_store.append([str(y)])
            self.layer_store.append(None, [x, data_store, data_view])

win = MainWindow()
win.show_all()
Gtk.main()

person tobias47n9e    schedule 26.04.2015    source источник
comment
Вы используете буфер обмена и json для чего-нибудь еще? В противном случае, вероятно, есть более простые подходы.   -  person elya5    schedule 30.04.2015
comment
Я также хотел бы выполнять загрузку / сохранение и перетаскивание с помощью этого. Но я только начал работать над этим и не знаю лучших решений.   -  person tobias47n9e    schedule 30.04.2015


Ответы (1)


У меня есть улучшенная версия вашего кода с пониманием слов и @staticmethod, которая делает обратные вызовы сигналов более удобочитаемыми и короче. Тем не менее, это не решает вашу проблему, поскольку он по-прежнему генерирует json вручную. Если ListStore станет более сложным, вероятно, было бы лучше позволить классу DataListStore генерировать свой собственный json с соответствующим методом.

from gi.repository import Gtk, Gdk
import json


class LayerTreeView(Gtk.TreeView):

    def __init__(self, store):
        Gtk.TreeView.__init__(self, store)
        renderer = Gtk.CellRendererText()
        column = Gtk.TreeViewColumn("Name", renderer, text=0)
        self.append_column(column)


class DataTreeView(Gtk.TreeView):

    def __init__(self):
        Gtk.TreeView.__init__(self)
        renderer = Gtk.CellRendererText()
        column = Gtk.TreeViewColumn("Data", renderer, text=0)
        self.append_column(column)

class DataListStore(Gtk.ListStore):

    @staticmethod
    def from_json(*args, values=[]):
        store = DataListStore(*args)
        for value in values:
            store.append((value,))
        return store


class MainWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="TreeView Serialize")
        self.connect("delete-event", Gtk.main_quit)
        self.set_border_width(10)
        self.set_default_size(400, 300)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6, expand=True)
        self.add(vbox)

        self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)

        hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
        button = Gtk.Button("Cut")
        button.connect("clicked", self.on_cut_clicked)
        hbox.pack_start(button, True, True, 0)

        button = Gtk.Button(stock=Gtk.STOCK_COPY)
        button.connect("clicked", self.on_copy_clicked)
        hbox.pack_start(button, True, True, 0)

        button = Gtk.Button(stock=Gtk.STOCK_PASTE)
        button.connect("clicked", self.on_paste_clicked)
        hbox.pack_start(button, True, True, 0)
        vbox.add(hbox)

        self.layer_store = Gtk.TreeStore(str, object)
        self.layer_view = LayerTreeView(self.layer_store)
        self.data_view = DataTreeView()

        layer_sw = Gtk.ScrolledWindow()
        layer_sw.add(self.layer_view)
        data_sw = Gtk.ScrolledWindow()
        data_sw.add(self.data_view)

        treebox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6, expand=True)
        treebox.pack_start(layer_sw, True, True, 0)
        treebox.pack_start(data_sw, True, True, 0)
        vbox.add(treebox)

        select = self.layer_view.get_selection()
        select.connect("changed", self.on_selection_changed)

        self.add_test_data()

    def add_test_data(self):
        for x in range(3):
            data_list = [str(y+x) for y in range(5)]
            self.layer_store.append(None, ["Data {}".format(x), data_list])

    def on_selection_changed(self, selection):
        """
        When layer is switched load respective data
        """
        model, treeiter = selection.get_selected()
        if treeiter != None:
            self.data_view.set_model(
                DataListStore.from_json(str, values=model[treeiter][1])
            )

    def on_cut_clicked(self, button):
        pass

    def on_copy_clicked(self, button):
        copy_list = [
            'safe-to-paste',
            {row[0]: row[1] for row in self.layer_store},
        ]
        data = json.dumps(copy_list)
        self.clipboard.set_text(data, -1)

    def on_paste_clicked(self, button):
        paste_str = self.clipboard.wait_for_text()
        try:
            parse = json.loads(paste_str)
        except:
            return

        if parse[0] != "safe-to-paste":
            return

        data_dict = parse[1]
        for x in data_dict:
            self.layer_store.append(None, [x, data_dict[x]])

win = MainWindow()
win.show_all()
Gtk.main()
person elya5    schedule 06.05.2015
comment
Действительно хорошее улучшение. Как вы думаете, было бы лучше сохранить данные в словаре и заново заполнить хранилище списков из этого словаря? Я думал, что использовать собственные типы данных Python может быть проще, чем научить сериализации, как работать с Gtk Liststore? - person tobias47n9e; 06.05.2015
comment
JSON упростит задачу, если вы хотите сохранить данные в файл, но сейчас это не очень полезно. - person elya5; 07.05.2015