Hi all,
I'm using pygtk-1.99.16 (packaged for Debian Unstable).
I'm trying to extend the row reordering using drag and drop, but I'm
stuck with some odd behaviours.
First: I've tried to use the GtkTreeView::enable_model_drag_source, but,
although the ChangeLog says that it has been included inside gtk.defs
sometime in 2001, here's the error that I get:
AttributeError: 'ScheduleView' object has no attribute 'enable_model_drag_source'
[ScheduleView is a subclassed gtk.TreeView, but this error also appears
when I use a plain gtk.TreeView].
Since I do not know how to wrap the gtk_tree_view_enable_model_drag_*
functions, I've tryed to work them around, and so I wrote a working
example, using the list_store.py example inside pygtk-demo as a
template. I cannot stress this too much: the dnd_treeview.py program
works correctly.
Unfortunately, when I try to use the same workaround inside my code,
this is what happens:
1. dragging every row *after* the fourth from the end to a
position *before* the fourth from the end results in the row
being set to the fourth from the end; e.g.
row 1 row 1 row 1
row 2 row 2 <- drop row 2
row 3 row 3 row 3
row 4 row 4 row 7 <- result
row 5 row 5 row 4
row 6 row 6 row 5
row 7 <- drag row 7 row 6
2. dragging every row *before* the fourth from the end produces
no result whatsoever.
I know that it sounds strange, and believe me: I've tried every
concievable option and method, before giving up and asking here.
I've attached the working dnd example, and the two callbacks I use for
drag_data_get and drag_data_received.
Thanks in advance,
Emmanuele.
--
Emmanuele Bassi (Zefram) [ http://digilander.libero.it/ebassi/ ]
GnuPG Key fingerprint = 4DD0 C90D 4070 F071 5738 08BD 8ECC DB8F A432 0FF4
#!/usr/bin/env python
'''Tree View/List Store
The GtkListStore is used to store data in list form, to be used
later on by a GtkTreeView to display it. This demo builds a
simple GtkListStore and displays it. See the Stock Browser
demo for a more advanced example.''' # "
description = 'List Store'
import pygtk
pygtk.require('2.0')
import gobject
import gtk
from gtk import TRUE, FALSE
COLUMN_FIXED = 0
COLUMN_NUMBER = 1
COLUMN_SEVERITY = 2
COLUMN_DESCRIPTION = 3
data = \
[[FALSE, 60482, 'Normal', 'scrollable notebooks and hidden tabs'],
[FALSE, 60620, 'Critical',
'gdk_window_clear_area (gdkwindow-win32.c) is not thread-safe'],
[FALSE, 50214, 'Major', 'Xft support does not clean up correctly'],
[TRUE, 52877, 'Major', 'GtkFileSelection needs a refresh method. '],
[FALSE, 56070, 'Normal', "Can't click button after setting in sensitive"],
[TRUE, 56355, 'Normal', 'GtkLabel - Not all changes propagate correctly'],
[FALSE, 50055, 'Normal', 'Rework width/height computations for TreeView'],
[FALSE, 58278, 'Normal', "gtk_dialog_set_response_sensitive () doesn't work"],
[FALSE, 55767, 'Normal', 'Getters for all setters'],
[FALSE, 56925, 'Normal', 'Gtkcalender size'],
[FALSE, 56221, 'Normal', 'Selectable label needs right-click copy menu'],
[TRUE, 50939, 'Normal', 'Add shift clicking to GtkTextView'],
[FALSE, 6112, 'Enhancement', 'netscape-like collapsable toolbars'],
[FALSE, 1, 'Normal', 'First bug :=)']]
def serialize(model, iter):
state = model.get_value(iter, COLUMN_FIXED)
number = model.get_value(iter, COLUMN_NUMBER)
severity = model.get_value(iter, COLUMN_SEVERITY)
description = model.get_value(iter, COLUMN_DESCRIPTION)
s = ('%d�%d�%s�%s' % (state, number, severity, description))
return s
def unserialize(s, model, iter):
state, number, severity, description = s.split('�', 4)
model.set(iter,
COLUMN_FIXED, int(state),
COLUMN_NUMBER, int(number),
COLUMN_SEVERITY, severity,
COLUMN_DESCRIPTION, description)
def on_drag_begin(treeview, context):
print 'on_drag_begin'
def on_drag_motion(treeview, context, x, y, time):
#print 'on_drag_motion'
pass
def on_drag_data_get(treeview, context, selection, info, time):
print 'on_drag_data_get'
s = treeview.get_selection()
model, s_iter = s.get_selected()
payload = serialize(model, s_iter)
print 'Source: %s' % payload
selection.set(selection.target, 8, payload)
model.remove(s_iter)
def on_drag_data_received(treeview, context, x, y, selection, info, time):
print 'on_drag_data_received'
model = treeview.get_model()
try:
path, column, cell_x, cell_y = treeview.get_path_at_pos(x, y)
except TypeError, e:
print 'out of boundaries'
context.finish(gtk.FALSE, gtk.FALSE, time)
return
print 'path := %s' % path
new_path = (path[0] - 1,)
print 'new_path := %s' % new_path
target_iter = model.get_iter(new_path)
if selection and selection.format == 8:
print 'Target: %s' % selection.data
iter = model.insert_before(target_iter)
unserialize(selection.data, model, iter)
context.finish(gtk.TRUE, gtk.FALSE, time)
else:
context.finish(gtk.FALSE, gtk.FALSE, time)
def on_drag_drop(treeview, context, x, y, time):
return gtk.FALSE
def create_model():
store = gtk.ListStore(gobject.TYPE_BOOLEAN,
gobject.TYPE_UINT,
gobject.TYPE_STRING,
gobject.TYPE_STRING)
for item in data:
iter = store.append()
store.set(iter, COLUMN_FIXED, item[0],
COLUMN_NUMBER, item[1],
COLUMN_SEVERITY, item[2],
COLUMN_DESCRIPTION, item[3])
return store
def fixed_toggled(cell, path, model):
# get toggled iter
iter = model.get_iter((int(path),))
fixed = model.get_value(iter, COLUMN_FIXED)
# do something with the value
fixed = not fixed
# set new value
model.set(iter, COLUMN_FIXED, fixed)
def add_columns(treeview):
model = treeview.get_model()
# column for fixed toggles
renderer = gtk.CellRendererToggle()
renderer.connect('toggled', fixed_toggled, model)
column = gtk.TreeViewColumn('Fixed?', renderer, active=COLUMN_FIXED)
column.set_clickable(TRUE)
# set this column to a fixed sizing(of 50 pixels)
column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
column.set_fixed_width(50)
column.set_clickable(TRUE)
treeview.append_column(column)
# column for bug numbers
column = gtk.TreeViewColumn('Bug Number', gtk.CellRendererText(),
text=COLUMN_NUMBER)
treeview.append_column(column)
# columns for severities
column = gtk.TreeViewColumn('Severity', gtk.CellRendererText(),
text=COLUMN_SEVERITY)
treeview.append_column(column)
# column for description
column = gtk.TreeViewColumn('Description', gtk.CellRendererText(),
text=COLUMN_DESCRIPTION)
treeview.append_column(column)
def main():
TARGET_STRING = 0
TARGET_ROOTWIN = 1
DND_TARGETS = [
('STRING', 0, TARGET_STRING),
('text/plain', 0, TARGET_STRING),
]
win = gtk.Window()
win.connect('destroy', lambda win: gtk.main_quit())
win.set_title('GtkListStore demo')
win.set_border_width(8)
vbox = gtk.VBox(FALSE, 8)
win.add(vbox)
label = gtk.Label('This is the bug list (note: not based on real data, '+\
'it would be nice to have a nice ODBC interface to bugzilla or so, though).')
vbox.pack_start(label, FALSE, FALSE)
sw = gtk.ScrolledWindow()
sw.set_shadow_type(gtk.SHADOW_ETCHED_IN)
sw.set_policy(gtk.POLICY_NEVER,
gtk.POLICY_AUTOMATIC)
vbox.pack_start(sw)
model = create_model()
treeview = gtk.TreeView(model)
treeview.set_rules_hint(TRUE)
treeview.set_search_column(COLUMN_DESCRIPTION)
treeview.drag_source_set(gtk.gdk.BUTTON1_MASK, DND_TARGETS,
gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_MOVE)
treeview.drag_dest_set(gtk.DEST_DEFAULT_ALL, DND_TARGETS[:-1],
gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_MOVE)
treeview.connect('drag_begin', on_drag_begin)
treeview.connect('drag_data_get', on_drag_data_get)
treeview.connect('drag_data_received', on_drag_data_received)
treeview.connect('drag_motion', on_drag_motion)
treeview.connect('drag_drop', on_drag_drop)
sw.add(treeview)
add_columns(treeview)
win.set_default_size(280, 250)
win.show_all()
gtk.main()
if __name__ == '__main__':
main()
def on_drag_data_get(sview, context, selection, info, time):
print 'on_drag_data_get'
s = sview.get_selection()
model, s_iter = s.get_selected()
# retrieve the selected task
task = sview.get_task(s_iter)
# insert path before the task.
s_path = int(model.get_path(s_iter)[0])
# create the payload string.
payload = '%d�' % path
payload += '<?xml version="1.0" encoding="UTF-8"?>'
payload += '<gnome-schedule>'
payload += task.serialize()
payload += '</gnome-schedule>'
print 'payload := %s' % payload
selection.set(selection.target, 8, payload)
def on_drag_data_received(sview, context, x, y, selection, info, time):
print 'on_drag_data_received'
if selection and selection.format == 8:
# retrieve both the path and the payload
path, payload = selection.data.split('�', 2)
start_path = (int(path),)
parser = ScheduleParser()
parser.parseString(payload)
tasks = parser.get_entries()
assert len(tasks) == 1
model = sview.get_model()
try:
path, column, cell_x, cell_y = sview.get_path_at_pos(x, y)
except TypeError, e:
print 'out of boundaries... aborting.'
context.finish(gtk.FALSE, gtk.FALSE, time)
return
# the path returned from the get_path_at_pos method is always shifted by
# one. Is this wanted or is it a bug?
drop_path = (path[0] - 1,)
print 'start path := %s (type: %s)' % (start_path, type(start_path))
print 'drop path := %s (type: %s)' % (drop_path, type(drop_path))
# used for future multiple drag'n'drop.
for t in tasks:
drop_iter = model.get_iter(drop_path)
iter = model.insert_before(drop_iter)
# fill the model.
model.set(iter,
COLUMN_PRIORITY, t.get_priority_string(),
COLUMN_DATE_START, t.get_date_start(),
COLUMN_DATE_END, t.get_date_end(),
COLUMN_COMPLETED, t.is_complete(),
COLUMN_DESCRIPTION, t.get_description(),
COLUMN_NOTES, t.get_notes(),
COLUMN_ACTIVE, not t.is_complete())
start_iter = model.get_iter(start_path)
model.remove(start_iter)
context.finish(gtk.TRUE, gtk.FALSE, time)
else:
context.finish(gtk.FALSE, gtk.FALSE, time)
_______________________________________________
pygtk mailing list [EMAIL PROTECTED]
http://www.daa.com.au/mailman/listinfo/pygtk
Read the PyGTK FAQ: http://www.async.com.br/faq/pygtk/