0

I am trying to implement drag-and-drop in a Gtk TreeView using a JSON string to serialize (am I using that word right?) the information. But when I use the standard approach I get an error message. Also the string I send arrives as bytes for some reason.

The sending function is:

def drag_data_get(self, treeview, context, selection, info, time):
    treeselection = treeview.get_selection()
    model, itr = treeselection.get_selected()
    row = model[itr]
    data = json.dumps([row[0], row[1]])
    print(type(data), data)
    selection.set(selection.get_target(), 8, data)

The receiving function is:

def drag_data_received(self, treeview, context, x, y, selection, info, time):
    drop_info = self.view.get_dest_row_at_pos(x, y)
    data = selection.get_data()
    print(type(data), data)

The error occurs in the last ro of the drag_data_get() function:

Traceback (most recent call last):
  File "tv_dnd_json.py", line 63, in drag_data_get
    one = selection.set(selection.get_target(), 8, data)
TypeError: Item 0: Must be number, not str

I have no idea how to interpret this traceback.

The whole self-contained example:

from gi.repository import Gtk, Gdk
import json

class MainWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="TreeView Drag and Drop")
        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)
        self.add(vbox)

        self.store = Gtk.TreeStore(bool, str)
        self.view = Gtk.TreeView(model=self.store)
        vbox.add(self.view)

        renderer_toggle = Gtk.CellRendererToggle()
        column_toggle = Gtk.TreeViewColumn("", renderer_toggle, active=0)
        renderer_toggle.connect("toggled", self.on_toggled)
        self.view.append_column(column_toggle)

        renderer_name = Gtk.CellRendererText()
        column_name = Gtk.TreeViewColumn("Name", renderer_name, text=1)
        self.view.append_column(column_name)

        self.view.connect("drag-begin", self.drag_begin)
        self.view.connect("drag-data-get", self.drag_data_get)
        self.view.connect("drag-drop", self.drag_drop)
        self.view.connect("drag-data-delete", self.drag_data_delete)
        self.view.connect("drag-data-received", self.drag_data_received)
        self.view.connect("drag-end", self.drag_end)

        targets = [("text/plain", 0, 0)]
        self.view.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK,
            targets, Gdk.DragAction.DEFAULT|Gdk.DragAction.MOVE)
        self.view.enable_model_drag_dest(targets, Gdk.DragAction.DEFAULT)

        self.add_test_data()

    def add_test_data(self):
        parent = self.store.append(None, [True, "Item 1"])
        self.store.append(parent, [True, "Item 2"])
        self.store.append(None, [True, "Item 3"])
        self.store.append(None, [True, "Item 4"])

    def on_toggled(self, cellrenderer, path):
        self.store[path][0] = not self.store[path][0]

    def drag_begin(self, treeview, context):
        print("===================")
        print("Drag started")

    def drag_data_get(self, treeview, context, selection, info, time):
        print("===================")
        print("Drag data requested by destination")
        print("Selection", selection)
        treeselection = treeview.get_selection()
        model, itr = treeselection.get_selected()
        row = model[itr]
        data = json.dumps([row[0], row[1]])
        print(type(data), data)
        selection.set(selection.get_target(), 8, data)

    def drag_drop(self, treeview, context, selection, info, time):
        print("===================")
        print("Drag data droped")

    def drag_data_received(self, treeview, context, x, y, selection, info, time):
        print("===================")
        print("Drag data received")
        drop_info = self.view.get_dest_row_at_pos(x, y)
        data = selection.get_data()
        print(type(data), data)

    def drag_end(self, treeview, context):
        print("===================")
        print("Drag data end")

    def drag_data_delete(self, treeview, context):
        print("===================")
        print("Drag data delete")

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

Relevant links:

Community
  • 1
  • 1
tobias47n9e
  • 2,233
  • 3
  • 28
  • 54
  • According to the [documentation](https://lazka.github.io/pgi-docs/#Gtk-3.0/structs/SelectionData.html#Gtk.SelectionData.set), you have to set a pointer as an argument. That basically means that the C <-> Python API is not really working here since you do not have pointers in Python. You can probably file this as a bug. – elya5 Apr 19 '15 at 08:46
  • I also tried doing it this way. Maybe I could bypass the problem by encoding the json string into bytes()? http://stackoverflow.com/questions/29673495/treeview-drag-and-drop-in-pygobject-drag-data-delete-not-called – tobias47n9e Apr 19 '15 at 09:27
  • Ok. It seems to work with `bytes()`. But if you only have those two values, you do not have to use `json.dumps()`. – elya5 Apr 19 '15 at 10:32
  • I'm actually just trying to build a small working example, but after that I want to serialize a python object with about 25 values. – tobias47n9e Apr 19 '15 at 10:39

1 Answers1

1

Apparently, the 'set' is expecting a byte string.

Changing the line to:

    selection.set(selection.get_target(), 8, data.encode())

seems to work ok. I found this solution here:

Gtk.CssProvider.load_from_data TypeError: Item 0: Must be number, not str

Community
  • 1
  • 1
jcoppens
  • 5,306
  • 6
  • 27
  • 47
  • I also had to add `decode()` in order to get back the string. I changed line 73 to `data = selection.get_data().decode()`. Thanks! – tobias47n9e Apr 25 '15 at 15:54