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/