2012-12-14 17 views
11

Sto effettuando il porting liblarch, una libreria per la gestione di grafici aciclici diretti, da PyGTK (GTK2) a PyGObject introspection (GTK3). Ho incontrato il problema con GtkTreeView.Come trascinare e rilasciare GtkTreeView in GTK3?

L'app che utilizza liblarch deve ordinare GtkTreeView in base a una colonna, ma nello stesso tempo, l'utente può trascinare e rilasciare righe, spostare una riga sotto un'altra riga. Per questo ho dovuto elaborare manualmente dnd_data_get() e dnd_data_receive() che è perfettamente a posto.

C'è la configurazione minima per GtkTreeView che funziona con PyGTK. Le righe sono ordinate e l'utente può spostare le righe.

#!/usr/bin/python 
# -*- coding: utf-8 -*- 

import gtk 

window = gtk.Window() 
window.set_size_request(300, 200) 
window.connect('delete_event', lambda w,e: gtk.main_quit()) 

# Define Liblarch Tree 

store = gtk.TreeStore(str, str) 
store.insert(None, -1, ["A", "Task A"]) 
store.insert(None, -1, ["B", "Task B"]) 
store.insert(None, -1, ["C", "Task C"]) 
d_parent = store.insert(None, -1, ["D", "Task D"]) 
store.insert(d_parent, -1, ["E", "Task E"]) 

# Define TreeView in similar way as it happens in GTG/Liblarch_gtk 
tv = gtk.TreeView() 

col = gtk.TreeViewColumn() 
col.set_title("Title") 
render_text = gtk.CellRendererText() 
col.pack_start(render_text, expand=True) 
col.add_attribute(render_text, 'markup', 1) 
col.set_resizable(True) 
col.set_expand(True) 
col.set_sort_column_id(0) 
tv.append_column(col) 
tv.set_property("expander-column", col) 

treemodel = store 

def _sort_func(model, iter1, iter2): 
    """ Sort two iterators by function which gets node objects. 
    This is a simple wrapper which prepares node objects and then 
    call comparing function. In other case return default value -1 
    """ 
    node_a = model.get_value(iter1, 0) 
    node_b = model.get_value(iter2, 0) 
    if node_a and node_b: 
     sort = cmp(node_a, node_b) 
    else: 
     sort = -1 
    return sort 

treemodel.set_sort_func(1, _sort_func) 
tv.set_model(treemodel) 

def on_child_toggled(treemodel2, path, iter, param=None): 
    """ Expand row """ 
    if not tv.row_expanded(path): 
     tv.expand_row(path, True) 

treemodel.connect('row-has-child-toggled', on_child_toggled) 

tv.set_search_column(1) 
tv.set_property("enable-tree-lines", False) 
tv.set_rules_hint(False) 


#### Drag and drop stuff 

dnd_internal_target = '' 
dnd_external_targets = {} 

def on_drag_fail(widget, dc, result): 
    print "Failed dragging", widget, dc, result 

def __init_dnd(): 
    """ Initialize Drag'n'Drop support 

    Firstly build list of DND targets: 
     * name 
     * scope - just the same widget/same application 
     * id 

    Enable DND by calling enable_model_drag_dest(), 
    enable_model-drag_source() 

    It didnt use support from gtk.Widget(drag_source_set(), 
    drag_dest_set()). To know difference, look in PyGTK FAQ: 
    http://faq.pygtk.org/index.py?file=faq13.033.htp&req=show 
    """ 
    #defer_select = False 

    if dnd_internal_target == '': 
     error = 'Cannot initialize DND without a valid name\n' 
     error += 'Use set_dnd_name() first' 
     raise Exception(error) 

    dnd_targets = [(dnd_internal_target, gtk.TARGET_SAME_WIDGET, 0)] 
    for target in dnd_external_targets: 
     name = dnd_external_targets[target][0] 
     dnd_targets.append((name, gtk.TARGET_SAME_APP, target)) 

    tv.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, 
     dnd_targets, gtk.gdk.ACTION_DEFAULT | gtk.gdk.ACTION_MOVE) 

    tv.enable_model_drag_dest(\ 
     dnd_targets, gtk.gdk.ACTION_DEFAULT | gtk.gdk.ACTION_MOVE) 


def on_drag_data_get(treeview, context, selection, info, timestamp): 
    """ Extract data from the source of the DnD operation. 

    Serialize iterators of selected tasks in format 
    <iter>,<iter>,...,<iter> and set it as parameter of DND """ 
    print "on_drag_data_get(", treeview, context, selection, info, timestamp 

    treeselection = treeview.get_selection() 
    model, paths = treeselection.get_selected_rows() 
    iters = [model.get_iter(path) for path in paths] 
    iter_str = ','.join([model.get_string_from_iter(iter) for iter in iters]) 
    selection.set(dnd_internal_target, 0, iter_str) 
    print "Sending", iter_str 

def on_drag_data_received(treeview, context, x, y, selection, info,\ 
          timestamp): 
    """ Handle a drop situation. 

    First of all, we need to get id of node which should accept 
    all draged nodes as their new children. If there is no node, 
    drop to root node. 

    Deserialize iterators of dragged nodes (see self.on_drag_data_get()) 
    Info parameter determines which target was used: 
     * info == 0 => internal DND within this TreeView 
     * info > 0 => external DND 

    In case of internal DND we just use Tree.move_node(). 
    In case of external DND we call function associated with that DND 
    set by self.set_dnd_external() 
    """ 
    print "on_drag_data_received", treeview, context, x, y, selection, info, timestamp 

    model = treeview.get_model() 
    destination_iter = None 
    destination_tid = None 
    drop_info = treeview.get_dest_row_at_pos(x, y) 
    if drop_info: 
     path, position = drop_info 
     destination_iter = model.get_iter(path) 
     if destination_iter: 
      destination_tid = model.get_value(destination_iter, 0) 

    # Get dragged iter as a TaskTreeModel iter 
    # If there is no selected task (empty selection.data), 
    # explictly skip handling it (set to empty list) 
    if selection.data == '': 
     iters = [] 
    else: 
     iters = selection.data.split(',') 

    dragged_iters = [] 
    for iter in iters: 
     print "Info", info 
     if info == 0: 
      try: 
       dragged_iters.append(model.get_iter_from_string(iter)) 
      except ValueError: 
       #I hate to silently fail but we have no choice. 
       #It means that the iter is not good. 
       #Thanks shitty gtk API for not allowing us to test the string 
       print "Shitty iter", iter 
       dragged_iter = None 

     elif info in dnd_external_targets and destination_tid: 
      f = dnd_external_targets[info][1] 

      src_model = context.get_source_widget().get_model() 
      dragged_iters.append(src_model.get_iter_from_string(iter)) 


    for dragged_iter in dragged_iters: 
     if info == 0: 
      if dragged_iter and model.iter_is_valid(dragged_iter): 
       dragged_tid = model.get_value(dragged_iter, 0) 
       try: 
        row = [] 
        for i in range(model.get_n_columns()): 
         row.append(model.get_value(dragged_iter, i)) 
        #tree.move_node(dragged_tid, new_parent_id=destination_tid) 
        print "move_after(%s, %s) ~ (%s, %s)" % (dragged_iter, destination_iter, dragged_tid, destination_tid) 
        #model.move_after(dragged_iter, destination_iter) 
        model.insert(destination_iter, -1, row) 
        model.remove(dragged_iter) 
       except Exception, e: 
        print 'Problem with dragging: %s' % e 
     elif info in dnd_external_targets and destination_tid:  
      source = src_model.get_value(dragged_iter,0) 
      # Handle external Drag'n'Drop 
      f(source, destination_tid) 


dnd_internal_target = 'gtg/task-iter-str' 
__init_dnd() 
tv.connect('drag_data_get', on_drag_data_get) 
tv.connect('drag_data_received', on_drag_data_received) 
tv.connect('drag_failed', on_drag_fail) 

window.add(tv) 
window.show_all() 

tv.expand_all() 
gtk.main() 

# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 

Ho portato questo script in PyGObject (GTK3). Il mio codice:

#!/usr/bin/python 
# -*- coding: utf-8 -*- 

from gi.repository import Gtk, Gdk 

window = Gtk.Window() 
window.set_size_request(300, 200) 
window.connect('delete_event', lambda w,e: Gtk.main_quit()) 

# Define Liblarch Tree 

store = Gtk.TreeStore(str, str) 
store.insert(None, -1, ["A", "Task A"]) 
store.insert(None, -1, ["B", "Task B"]) 
store.insert(None, -1, ["C", "Task C"]) 
d_parent = store.insert(None, -1, ["D", "Task D"]) 
store.insert(d_parent, -1, ["E", "Task E"]) 

# Define TreeView in similar way as it happens in GTG/Liblarch_gtk 
tv = Gtk.TreeView() 

col = Gtk.TreeViewColumn() 
col.set_title("Title") 
render_text = Gtk.CellRendererText() 
col.pack_start(render_text, expand=True) 
col.add_attribute(render_text, 'markup', 1) 
col.set_resizable(True) 
col.set_expand(True) 
col.set_sort_column_id(0) 
tv.append_column(col) 
tv.set_property("expander-column", col) 

treemodel = store 

def _sort_func(model, iter1, iter2): 
    """ Sort two iterators by function which gets node objects. 
    This is a simple wrapper which prepares node objects and then 
    call comparing function. In other case return default value -1 
    """ 
    node_a = model.get_value(iter1, 0) 
    node_b = model.get_value(iter2, 0) 
    if node_a and node_b: 
     sort = cmp(node_a, node_b) 
    else: 
     sort = -1 
    return sort 

treemodel.set_sort_func(1, _sort_func) 
tv.set_model(treemodel) 

def on_child_toggled(treemodel2, path, iter, param=None): 
    """ Expand row """ 
    if not tv.row_expanded(path): 
     tv.expand_row(path, True) 

treemodel.connect('row-has-child-toggled', on_child_toggled) 

tv.set_search_column(1) 
tv.set_property("enable-tree-lines", False) 
tv.set_rules_hint(False) 


#### Drag and drop stuff 

dnd_internal_target = '' 
dnd_external_targets = {} 

def on_drag_fail(widget, dc, result): 
    print "Failed dragging", widget, dc, result 

def __init_dnd(): 
    """ Initialize Drag'n'Drop support 

    Firstly build list of DND targets: 
     * name 
     * scope - just the same widget/same application 
     * id 

    Enable DND by calling enable_model_drag_dest(), 
    enable_model-drag_source() 

    It didnt use support from Gtk.Widget(drag_source_set(), 
    drag_dest_set()). To know difference, look in PyGTK FAQ: 
    http://faq.pygtk.org/index.py?file=faq13.033.htp&req=show 
    """ 
    #defer_select = False 

    if dnd_internal_target == '': 
     error = 'Cannot initialize DND without a valid name\n' 
     error += 'Use set_dnd_name() first' 
     raise Exception(error) 

    dnd_targets = [(dnd_internal_target, Gtk.TargetFlags.SAME_WIDGET, 0)] 
    for target in dnd_external_targets: 
     name = dnd_external_targets[target][0] 
     dnd_targets.append((name, Gtk.TARGET_SAME_APP, target)) 

    tv.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, 
     dnd_targets, Gdk.DragAction.DEFAULT | Gdk.DragAction.MOVE) 

    tv.enable_model_drag_dest(\ 
     dnd_targets, Gdk.DragAction.DEFAULT | Gdk.DragAction.MOVE) 


def on_drag_data_get(treeview, context, selection, info, timestamp): 
    """ Extract data from the source of the DnD operation. 

    Serialize iterators of selected tasks in format 
    <iter>,<iter>,...,<iter> and set it as parameter of DND """ 
    print "on_drag_data_get(", treeview, context, selection, info, timestamp 

    treeselection = treeview.get_selection() 
    model, paths = treeselection.get_selected_rows() 
    iters = [model.get_iter(path) for path in paths] 
    iter_str = ','.join([model.get_string_from_iter(iter) for iter in iters]) 
    selection.set(dnd_internal_target, 0, iter_str) 
    print "Sending", iter_str 

def on_drag_data_received(treeview, context, x, y, selection, info,\ 
          timestamp): 
    """ Handle a drop situation. 

    First of all, we need to get id of node which should accept 
    all draged nodes as their new children. If there is no node, 
    drop to root node. 

    Deserialize iterators of dragged nodes (see self.on_drag_data_get()) 
    Info parameter determines which target was used: 
     * info == 0 => internal DND within this TreeView 
     * info > 0 => external DND 

    In case of internal DND we just use Tree.move_node(). 
    In case of external DND we call function associated with that DND 
    set by self.set_dnd_external() 
    """ 
    print "on_drag_data_received", treeview, context, x, y, selection, info, timestamp 

    model = treeview.get_model() 
    destination_iter = None 
    destination_tid = None 
    drop_info = treeview.get_dest_row_at_pos(x, y) 
    if drop_info: 
     path, position = drop_info 
     destination_iter = model.get_iter(path) 
     if destination_iter: 
      destination_tid = model.get_value(destination_iter, 0) 

    # Get dragged iter as a TaskTreeModel iter 
    # If there is no selected task (empty selection.data), 
    # explictly skip handling it (set to empty list) 
    if selection.data == '': 
     iters = [] 
    else: 
     iters = selection.data.split(',') 

    dragged_iters = [] 
    for iter in iters: 
     print "Info", info 
     if info == 0: 
      try: 
       dragged_iters.append(model.get_iter_from_string(iter)) 
      except ValueError: 
       #I hate to silently fail but we have no choice. 
       #It means that the iter is not good. 
       #Thanks shitty Gtk API for not allowing us to test the string 
       print "Shitty iter", iter 
       dragged_iter = None 

     elif info in dnd_external_targets and destination_tid: 
      f = dnd_external_targets[info][1] 

      src_model = context.get_source_widget().get_model() 
      dragged_iters.append(src_model.get_iter_from_string(iter)) 


    for dragged_iter in dragged_iters: 
     if info == 0: 
      if dragged_iter and model.iter_is_valid(dragged_iter): 
       dragged_tid = model.get_value(dragged_iter, 0) 
       try: 
        row = [] 
        for i in range(model.get_n_columns()): 
         row.append(model.get_value(dragged_iter, i)) 
        #tree.move_node(dragged_tid, new_parent_id=destination_tid) 
        print "move_after(%s, %s) ~ (%s, %s)" % (dragged_iter, destination_iter, dragged_tid, destination_tid) 
        #model.move_after(dragged_iter, destination_iter) 
        model.insert(destination_iter, -1, row) 
        model.remove(dragged_iter) 
       except Exception, e: 
        print 'Problem with dragging: %s' % e 
     elif info in dnd_external_targets and destination_tid:  
      source = src_model.get_value(dragged_iter,0) 
      # Handle external Drag'n'Drop 
      f(source, destination_tid) 


dnd_internal_target = 'gtg/task-iter-str' 
__init_dnd() 
tv.connect('drag_data_get', on_drag_data_get) 
tv.connect('drag_data_received', on_drag_data_received) 
tv.connect('drag_failed', on_drag_fail) 

window.add(tv) 
window.show_all() 

tv.expand_all() 
Gtk.main() 

# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 

io non sono in grado di gestire correttamente dnd_data_receive() in cui o non viene evocato o non ci sono dati vengono ricevuti. Viene a mancare sempre con il seguente callback + i suoi parametri:

Failed dragging <TreeView object at 0xeb4370 (GtkTreeView at 0xf742a0)> <gtk.gdk.X11DragContext object at 0xf351e0 (GdkX11DragContext at 0xf96ca0)> <enum GTK_DRAG_RESULT_NO_TARGET of type GtkDragResult> 

La mia domanda: Come porto il primo script di PyGObject (GTK3) in modo GtkTreeView possono essere ordinati e nelle stesse righe di tempo può essere drag-and- caduto? Come modificare la gestione dei callback drag-and-drop per elaborare correttamente il trascinamento della selezione?

+0

Il collegamento GitHub a liblarch è danneggiato. – rvighne

risposta

5

In primo luogo, l'errore che si ottiene sembra correlato alla versione di PyGObject. Riproduco le informazioni di errore simili prima di reinstallare il mio portatile con l'ultima versione di Ubuntu 13.04 beta. Ma dopo l'aggiornamento, la richiamata errore cambia in qualcosa di simile a

on_drag_data_get(<TreeView object at 0x1765870 (GtkTreeView at 0x19120a0)> <gtk.gdk.X11DragContext object at 0x1765aa0 (GdkX11DragContext at 0x1988820)> <GtkSelectionData at 0x7fffb106b760> 0 21962912 
Traceback (most recent call last): 
    File "dnd_gtk3_org.py", line 116, in on_drag_data_get 
    selection.set(dnd_internal_target, 0, iter_str) 
    File "/usr/lib/python2.7/dist-packages/gi/types.py", line 113, in function 
    return info.invoke(*args, **kwargs) 
TypeError: argument type: Expected Gdk.Atom, but got str 
on_drag_data_received <TreeView object at 0x1765870 (GtkTreeView at 0x19120a0)> <gtk.gdk.X11DragContext object at 0x1765be0 (GdkX11DragContext at 0x1988940)> 45 77 <GtkSelectionData at 0x7fffb106b6e0> 0 21962912 
Traceback (most recent call last): 
    File "dnd_gtk3_org.py", line 151, in on_drag_data_received 
    if selection.data == '': 
AttributeError: 'SelectionData' object has no attribute 'data' 

Ci sono solo due piccoli problemi:

  • il primo parametro di SelectionData.set() sembra solo può essere Gtk.gdk.Atom ma non una stringa che lo specifica come in pygtk.
  • SelectionData non ha attributi 'dati' ma ha invece un metodo get_data().

Un frammento di codice di lavoro elencato di seguito

#!/usr/bin/python 
# -*- coding: utf-8 -*- 

from gi.repository import Gtk, Gdk 

window = Gtk.Window() 
window.set_size_request(300, 200) 
window.connect('delete_event', Gtk.main_quit) 

# Define Liblarch Tree 

store = Gtk.TreeStore(str, str) 
store.insert(None, -1, ["A", "Task A"]) 
store.insert(None, -1, ["B", "Task B"]) 
store.insert(None, -1, ["C", "Task C"]) 
d_parent = store.insert(None, -1, ["D", "Task D"]) 
store.insert(d_parent, -1, ["E", "Task E"]) 

# Define TreeView in similar way as it happens in GTG/Liblarch_gtk 
tv = Gtk.TreeView() 

col = Gtk.TreeViewColumn() 
col.set_title("Title") 
render_text = Gtk.CellRendererText() 
col.pack_start(render_text, expand=True) 
col.add_attribute(render_text, 'markup', 1) 
col.set_resizable(True) 
col.set_expand(True) 
col.set_sort_column_id(0) 
tv.append_column(col) 
tv.set_property("expander-column", col) 

treemodel = store 

def _sort_func(model, iter1, iter2): 
    """ Sort two iterators by function which gets node objects. 
    This is a simple wrapper which prepares node objects and then 
    call comparing function. In other case return default value -1 
    """ 
    node_a = model.get_value(iter1, 0) 
    node_b = model.get_value(iter2, 0) 
    if node_a and node_b: 
     sort = cmp(node_a, node_b) 
    else: 
     sort = -1 
    return sort 

treemodel.set_sort_func(1, _sort_func) 
tv.set_model(treemodel) 

def on_child_toggled(treemodel2, path, iter, param=None): 
    """ Expand row """ 
    if not tv.row_expanded(path): 
     tv.expand_row(path, True) 

treemodel.connect('row-has-child-toggled', on_child_toggled) 

tv.set_search_column(1) 
tv.set_property("enable-tree-lines", False) 
tv.set_rules_hint(False) 


#### Drag and drop stuff 

dnd_internal_target = '' 
dnd_external_targets = {} 

def on_drag_fail(widget, dc, result): 
    print "Failed dragging", widget, dc, result 

def __init_dnd(): 
    """ Initialize Drag'n'Drop support 

    Firstly build list of DND targets: 
     * name 
     * scope - just the same widget/same application 
     * id 

    Enable DND by calling enable_model_drag_dest(), 
    enable_model-drag_source() 

    It didnt use support from Gtk.Widget(drag_source_set(), 
    drag_dest_set()). To know difference, look in PyGTK FAQ: 
    http://faq.pygtk.org/index.py?file=faq13.033.htp&req=show 
    """ 
    #defer_select = False 

    if dnd_internal_target == '': 
     error = 'Cannot initialize DND without a valid name\n' 
     error += 'Use set_dnd_name() first' 
     raise Exception(error) 

    dnd_targets = [(dnd_internal_target, Gtk.TargetFlags.SAME_WIDGET, 0)] 
    for target in dnd_external_targets: 
     name = dnd_external_targets[target][0] 
     dnd_targets.append((name, Gtk.TARGET_SAME_APP, target)) 

    tv.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, 
     dnd_targets, Gdk.DragAction.DEFAULT | Gdk.DragAction.MOVE) 

    tv.enable_model_drag_dest(\ 
     dnd_targets, Gdk.DragAction.DEFAULT | Gdk.DragAction.MOVE) 


def on_drag_data_get(treeview, context, selection, info, timestamp): 
    """ Extract data from the source of the DnD operation. 

    Serialize iterators of selected tasks in format 
    <iter>,<iter>,...,<iter> and set it as parameter of DND """ 
    print "on_drag_data_get(", treeview, context, selection, info, timestamp 

    treeselection = treeview.get_selection() 
    model, paths = treeselection.get_selected_rows() 
    iters = [model.get_iter(path) for path in paths] 
    iter_str = ','.join([model.get_string_from_iter(iter) for iter in iters]) 
    selection.set(selection.get_target(), 0, iter_str) 
    print "Sending", iter_str 

def on_drag_data_received(treeview, context, x, y, selection, info,\ 
          timestamp): 
    """ Handle a drop situation. 

    First of all, we need to get id of node which should accept 
    all draged nodes as their new children. If there is no node, 
    drop to root node. 

    Deserialize iterators of dragged nodes (see self.on_drag_data_get()) 
    Info parameter determines which target was used: 
     * info == 0 => internal DND within this TreeView 
     * info > 0 => external DND 

    In case of internal DND we just use Tree.move_node(). 
    In case of external DND we call function associated with that DND 
    set by self.set_dnd_external() 
    """ 
    print "on_drag_data_received", treeview, context, x, y, selection, info, timestamp 

    model = treeview.get_model() 
    destination_iter = None 
    destination_tid = None 
    drop_info = treeview.get_dest_row_at_pos(x, y) 
    if drop_info: 
     path, position = drop_info 
     destination_iter = model.get_iter(path) 
     if destination_iter: 
      destination_tid = model.get_value(destination_iter, 0) 

    # Get dragged iter as a TaskTreeModel iter 
    # If there is no selected task (empty selection.data), 
    # explictly skip handling it (set to empty list) 
    data = selection.get_data() 
    if data == '': 
     iters = [] 
    else: 
     iters = data.split(',') 

    dragged_iters = [] 
    for iter in iters: 
     print "Info", info 
     if info == 0: 
      try: 
       dragged_iters.append(model.get_iter_from_string(iter)) 
      except ValueError: 
       #I hate to silently fail but we have no choice. 
       #It means that the iter is not good. 
       #Thanks shitty Gtk API for not allowing us to test the string 
       print "Shitty iter", iter 
       dragged_iter = None 

     elif info in dnd_external_targets and destination_tid: 
      f = dnd_external_targets[info][1] 

      src_model = context.get_source_widget().get_model() 
      dragged_iters.append(src_model.get_iter_from_string(iter)) 


    for dragged_iter in dragged_iters: 
     if info == 0: 
      if dragged_iter and model.iter_is_valid(dragged_iter): 
       dragged_tid = model.get_value(dragged_iter, 0) 
       try: 
        row = [] 
        for i in range(model.get_n_columns()): 
         row.append(model.get_value(dragged_iter, i)) 
        #tree.move_node(dragged_tid, new_parent_id=destination_tid) 
        print "move_after(%s, %s) ~ (%s, %s)" % (dragged_iter, destination_iter, dragged_tid, destination_tid) 
        #model.move_after(dragged_iter, destination_iter) 
        model.insert(destination_iter, -1, row) 
        model.remove(dragged_iter) 
       except Exception, e: 
        print 'Problem with dragging: %s' % e 
     elif info in dnd_external_targets and destination_tid:  
      source = src_model.get_value(dragged_iter,0) 
      # Handle external Drag'n'Drop 
      f(source, destination_tid) 


dnd_internal_target = 'gtg/task-iter-str' 
__init_dnd() 
tv.connect('drag_data_get', on_drag_data_get) 
tv.connect('drag_data_received', on_drag_data_received) 
tv.connect('drag_failed', on_drag_fail) 

window.add(tv) 
window.show_all() 

tv.expand_all() 
Gtk.main() 

# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 

Il diff tra il frammento di codice con quello di tua domanda è

116c116 
<  selection.set(selection.get_target(), 0, iter_str) 
--- 
>  selection.set(dnd_internal_target, 0, iter_str) 
151,152c151 
<  data = selection.get_data() 
<  if data == '': 
--- 
>  if selection.data == '': 
155c154 
<   iters = data.split(',') 
--- 
>   iters = selection.data.split(',') 

Inoltre, v'è un altro esempio per GTK + 3 versione Trascinare e rilasciare TreeView in un'altra discussione: unresponsive drag and drop in pygobject

1

GTG è un ottimo software! Ma è troppo lento, almeno sul mio computer. Così ho scritto una libreria C++ che visualizza un grafo aciclico diretto usando un Gtk :: TreeView, e ho guardato molto al codice sorgente di LibLarch.

Per quanto ne so, i binding Python e C++ di GTK condividono lo stesso limite, proveniente da GTK stesso (una volta ho guardato il codice sorgente GTK per trovare esattamente il motivo per cui funziona così): se si attiva il trascinamento e -drop e ordinamento, drag-and-drop non funzionerà. Offro tre cose che potete fare a questo proposito:

  1. fare una patch per GTK che limita DND durante l'ordinamento è abilitato, invece di bloccare completamente lo

  2. Implementare l'ordinamento da soli. È facile: inizia caricando i tuoi dati su una vista ad albero ordinata. Ora, ogni volta che l'utente trascina e rilascia, sposta la riga trascinata nella nuova posizione usando la funzione di ordinamento. Ma lascia che GTK si stacchi.

  3. Questo può essere fatto in aggiunta a 2, è un problema di progettazione della GUI: In GtkTreeView è possibile inserire un elemento tra elementi di pari livello, che non ha molto senso negli alberi ordinati. In termini di interfaccia utente, è meglio consentire di rilasciare solo le righe ON, non TRA quelle. Esempio: la vista elenco Nautilus funziona in questo modo. La soluzione è sovrascrivere il gestore predefinito drag_data_received() TreeView, o meglio di quello in termini di manutenibilità: invia al modello un suggerimento dalla vista che indica al modello se la posizione di rilascio è ON o PRIMA. Se la posizione è davanti, fare drop_possible() Azionamento virtuale del albero restituire false, e quindi non si vede "si può cadere qui" didplay del controllo TreeView", in tal modo si ottiene un'interfaccia grafica più pulita.

2 e 3 è quello che faccio in C++, dovresti essere in grado di farlo facilmente in Python :)

Inoltre, una nota riguardante l'opzione 1: GtktreeView (o era GtkTreeStore? Ho dimenticato) semplicemente blocca qualsiasi calo se l'ordinamento è abilitato.Se qualcuno lo aggiusta (tu ... o io ...), o almeno scrive una classe di visualizzazione derivata, avremo una GUI pulita di default per gli alberi ordinati con supporto dnd.

Problemi correlati