Hi there people,

I'm new in this list (this is my first post) and I'm also new at pygtk,
so it was clear someday I was going to come to you guys :)

The thing is I want to do a 2-level tree with a TreeView, each level
having different node types:

- The 1st level will contain "folders". They should be able to contain
entries (leaf nodes), but they may not be moved/drag'n'dropped anywhere.

- The 2nd level will contain "leafs". They may not have any child (other
leafs). They must be always contained inside a 1st level node, but can
be moved/drag'n'dropped to any 1st level node.

The problem is I don't know how to avoid drag'n'dropping leafs to the
top level. This would not be a problem if the tree maintained it's
consistency, but it does not. For example, if I move a leaf at the 1st
level, the leaf gets copied instead of moved (even if I have defined
drag'n'drops as MOVES!).

I'm attaching a functional PoC to let you test what I'm trying to
explain. Just run it, drag'n'drop a leaf node to put it in the 1st level
(the same level as "folders") and see that it creates a copy. Even when
you drag'n'drop this last copy inside a 1st level node, it will create
another copy.

PS. JFYI, this won't be a filesystem mapping, but a hierarchy of
categories (root nodes) containing rss/feeds (leaf nodes). Hope I have
been clear enough.

Hope hearing from you soon,
-- 
jors <worbynet _ at _ gmail.com>

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

import pygtk
pygtk.require('2.0')
import gtk

category_list = []

class Naufrago:

 # CALLBACKS #
 #############
 def quit(self):
  """Quits the app"""
  self.hide()
  self.destroy()

 def delete_event(self, event, data=None):
  """Closes the app through window manager signal"""
  gtk.main_quit()
  return False

# FUNCTIONS #
#############
 def row_selection(self, event):
  """Row change detector"""
  (model, iter) = self.treeselection.get_selected()
  if(iter is not None): # Hay algĂșn nodo seleccionado
   row_name = self.treestore.get_value(iter, 0)
   print 'Selected row/node: ' + row_name

 def create_main_window(self):
  """Creates the main window with all it's widgets"""
  self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
  self.window.set_title("2 level tree example")
  self.window.set_size_request(250, 200)
  self.window.connect("delete_event", self.delete_event)
  self.treestore = self.populate_feeds() # Propaga los feeds del usuario
  self.treeview = gtk.TreeView(self.treestore)
  self.treeselection = self.treeview.get_selection()
  self.treeselection.connect("changed", self.row_selection)
  self.tvcolumn = gtk.TreeViewColumn('Column')
  self.treeview.append_column(self.tvcolumn)

  self.cellpb = gtk.CellRendererPixbuf()
  self.cell = gtk.CellRendererText()
  self.tvcolumn.pack_start(self.cellpb, False)
  self.tvcolumn.pack_start(self.cell, True)
  if gtk.gtk_version[1] < 2:
     self.tvcolumn.set_cell_data_func(self.cellpb, self.make_pb)
  else:
     self.tvcolumn.set_attributes(self.cellpb, stock_id=1)
  self.tvcolumn.set_attributes(self.cell, text=0)

  self.tvcolumn.set_sort_column_id(0)
  # Allow drag and drop reordering of rows
  self.treeview.set_reorderable(True)
  self.treeview_setup_dnd(self.treeview)
 
  self.window.add(self.treeview)
  self.window.show_all()

 def populate_feeds(self):
  """Create the tree node structure"""
  rootnodes = ['root1', 'root2']
  leafnodes = ['leaf1', 'leaf2']
  
  self.treestore = gtk.TreeStore(str, str, 'gboolean')
  for root in rootnodes:
   category_list.append(root)
   dad_iter = self.treestore.append(None, [root, gtk.STOCK_OPEN, True])
   for leaf in leafnodes:
    son_iter = self.treestore.append(dad_iter, [leaf, gtk.STOCK_NEW, True])
  return self.treestore

 def treeview_expand_to_path(self, treeview, path):
  """Expand row at path, expanding any ancestors as needed."""
  for i in range(len(path)):
   treeview.expand_row(path[:i+1], open_all=False)

 def treeview_copy_row(self, treeview, model, source, target, drop_position):
  """Copy tree model rows from treeiter source into, before or after treeiter target.
  The expanded/collapsed status of each row is maintained."""
  source_row = model[source]
  if drop_position == gtk.TREE_VIEW_DROP_INTO_OR_BEFORE:
   new = model.prepend(parent=target, row=source_row)
  elif drop_position == gtk.TREE_VIEW_DROP_INTO_OR_AFTER:
   new = model.append(parent=target, row=source_row)
  elif drop_position == gtk.TREE_VIEW_DROP_BEFORE:
   new = model.insert_before(parent=None, sibling=target, row=source_row)
  elif drop_position == gtk.TREE_VIEW_DROP_AFTER:
   new = model.insert_after(parent=None, sibling=target, row=source_row)

  # If the source row is expanded, expand the newly copied row
  # also.  We must add at least one child before we can expand,
  # so we expand here after the children have been copied.
  source_is_expanded = treeview.row_expanded(model.get_path(source))
  if source_is_expanded:
   self.treeview_expand_to_path(treeview, model.get_path(new))

 def checkSanity(self, model, iter_to_copy, target_iter):
  """Prevents that a node can be copied inside himself (anti-recursivity!),
  and also checks for application node logic (only leaf nodes can be moved
  to root nodes, and these leaf nodes SHOULD NOT become root nodes!)."""
  iter_to_copy_value = model.get_value(iter_to_copy, 0)
  target_iter_value = model.get_value(target_iter, 0)

  path_of_iter_to_copy = model.get_path(iter_to_copy)
  print 'iter_to_copy_value: ' + iter_to_copy_value + ', path_of_iter_to_copy: ' + str(path_of_iter_to_copy)
  path_of_target_iter = model.get_path(target_iter)
  print 'target_iter_value: ' + target_iter_value + 'Path of target_iter: ' + str(path_of_target_iter) + '\n'

  if((path_of_target_iter[0:len(path_of_iter_to_copy)] == path_of_iter_to_copy) or # anti-recursive
    (iter_to_copy_value in category_list) or # source is parent
    (target_iter_value not in category_list)): # dest is child
     return False
  else:
   return True

 def treeview_on_drag_data_received(self, treeview, drag_context, x, y, selection_data, info, eventtime):
  """Handler for 'drag-data-received' that moves dropped TreeView rows."""
  target_path, drop_position = treeview.get_dest_row_at_pos(x, y)
  model, source = treeview.get_selection().get_selected()
  target = model.get_iter(target_path)
  if self.checkSanity(model, source, target):
   self.treeview_copy_row(treeview, model, source, target, drop_position)
   # If the drop has created a new child (drop into), expand
   # the parent so the user can see the newly copied row.
   if(drop_position == gtk.TREE_VIEW_DROP_INTO_OR_BEFORE or drop_position == gtk.TREE_VIEW_DROP_INTO_OR_AFTER):
    treeview.expand_row(target_path, open_all=False)
    # Finish the drag and have gtk+ delete the drag source rows.
    drag_context.finish(success=True, del_=True, time=eventtime)
   else:
    drag_context.finish(success=False, del_=False, time=eventtime)

 def treeview_setup_dnd(self, treeview):
  """Setup a treeview to move rows using drag and drop."""
  target_entries = [('example', gtk.TARGET_SAME_WIDGET, 0)]
  # Drag start operation (origin)
  treeview.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, target_entries, gtk.gdk.ACTION_MOVE)
  # Drag end operation (destination)
  treeview.enable_model_drag_dest(target_entries, gtk.gdk.ACTION_MOVE)
  treeview.connect('drag-data-received', self.treeview_on_drag_data_received)

 # INIT #
 ########
 def __init__(self):

  # Creamos la ventana principal
  self.create_main_window()

def main():
 gtk.main()
 
if __name__ == "__main__":
 hello = Naufrago()
 main()

_______________________________________________
pygtk mailing list   [email protected]
http://www.daa.com.au/mailman/listinfo/pygtk
Read the PyGTK FAQ: http://faq.pygtk.org/

Reply via email to