Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Serialize Gtk TreeStore / ListStore using JSON

I made a new example which shows much better what I am trying to do. The new example gives the following ouput. Is there a way that the data can go into the respective store key (the {} brackets)?

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

New example

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())

Old example

I am currently figuring out how to serialize a Gtk ListStore that is nested in a Gtk TreeStore. I got a small example to work, but am not sure if this approach will scale for programs that have more data attached (For example the layer object could hold a color or a date of creation). Is there maybe another way to to this?

My current approach is to gather the data in list and dictionary form myself and then just create the JSON-dump. I have the feeling that this would be rather difficult to maintain if I need to attach 25 values to each layer-object.

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()
like image 680
tobias47n9e Avatar asked Apr 26 '15 12:04

tobias47n9e


1 Answers

I have an improved version of your code with dict comprehension and @staticmethod that makes the signal callbacks more readable and shorter. Nevertheless, this does not really solve your problem as it still generates the json manually. If the ListStore gets more complex, it would probably be better to let the DataListStore class generate its own json with a corresponding method.

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()
like image 111
elya5 Avatar answered Nov 07 '22 09:11

elya5