Форум сайта python.su
Привет всем.
Пробовал рисовать картинки на PyGtk и столкнулся с тем, что в примерах по drap_and_drop картинки размножаются :). Смотрел здесь:http://www.pygtk.org/pygtk2tutorial/sec-DNDMethods.html.
И в примерах, поставляемых с PyGtk - только схематичная реализация drag_and_drop.(./usr/lib/pygtk/2.0/demos/dnd.py)Поискал на форуме, в интернете и ответа не нашел. Пришлось разбираться самому.Вот что получилось. На первый взгляд программа кажется сложной, но если приглядеться, то многое становится интуитивно понятно. В инете встречал вопросы по drag_and_drop, так что думаю кому-нибудь это пригодится.Картинка упакована в gtk.Button. В дальнейшем попробую реализовать для gtk.DrawingArea.
dndimage.py
#!/usr/bin/python
# coding: utf-8
import pygtk
import gtk
import string, time
import gtkxpm
class DNDImageButton:
TARGET_TYPE_TEXT = 80
TARGET_TYPE_PIXMAP = 81
fromImage = [ ( "text/plain", 0, TARGET_TYPE_TEXT ),
( "", 0, TARGET_TYPE_PIXMAP ) ]
toButton = [ ( "text/plain", 0, TARGET_TYPE_TEXT ) ]
toCanvas = [ ( "", 0, TARGET_TYPE_PIXMAP ) ]
################### установки layout-a ###########################
def layout_resize(self, widget, event):
x, y, width, height = widget.get_allocation()
if width > self.lwidth or height > self.lheight:
self.lwidth = max(width, self.lwidth)
self.lheight = max(height, self.lheight)
widget.set_size(self.lwidth, self.lheight)
def makeLayout(self):
self.lwidth = 0
self.lheight = 0
box = gtk.VBox(False,0)
box.show()
table = gtk.Table(2, 2, False)
table.show()
box.pack_start(table, True, True, 0)
layout = gtk.Layout()
self.layout = layout
layout.set_size(self.lwidth, self.lheight)
layout.connect("size-allocate", self.layout_resize)
layout.show()
table.attach(layout, 0, 1, 0, 1, gtk.FILL|gtk.EXPAND,
gtk.FILL|gtk.EXPAND, 0, 0)
################### испускаем сигналы при перетаскивании ##########################
layout.connect('drag_leave', self.target_drag_leave)
layout.connect('drag_motion', self.target_drag_motion)
layout.connect('drag_drop', self.target_drag_drop)
layout.connect("drag_data_received", self.receiveCallback)
################### установки цели ##########################
layout.drag_dest_set(gtk.DEST_DEFAULT_MOTION |
gtk.DEST_DEFAULT_HIGHLIGHT |
gtk.DEST_DEFAULT_DROP,
self.toCanvas, gtk.gdk.ACTION_MOVE )
self.addImage(gtkxpm.gtk_xpm, 0, 0)
return box
####################### устанавл. виджет в коорд. xd, yd ##########################
def addImage(self, xpm, xd, yd):
hadj = self.layout.get_hadjustment()
vadj = self.layout.get_vadjustment()
style = self.window.get_style()
pixmap, mask = gtk.gdk.pixmap_create_from_xpm(
self.window.window, style.bg[gtk.STATE_NORMAL], "/home/mike/Desktop/lena.jpg") #<-----------
image = gtk.Image()
image.set_from_pixmap(pixmap, mask)
button = gtk.Button()
button.add(image)
####################### соединяем виджет ##########################
button.connect("drag_data_get", self.sendCallback)
button.connect('drag_data_delete', self.delete_cb)
###################### установки источника ##########################
button.drag_source_set(gtk.gdk.BUTTON1_MASK, self.fromImage,
gtk.gdk.ACTION_MOVE )
button.show_all()
self.layout.put(button, int(xd+hadj.value), int(yd+vadj.value))
return
####################### функции CALLBACK ##########################
def sendCallback(self, widget, context, selection, targetType, eventTime):
print "send_cb "
if targetType == self.TARGET_TYPE_PIXMAP:
selection.set(selection.target, 8,
string.join(gtkxpm.gtk_xpm, '\n'))
def receiveCallback(self, widget, context, x, y, selection, targetType,
time):
print "receive_cb "
if targetType == self.TARGET_TYPE_PIXMAP:
self.addImage(gtkxpm.gtk_xpm, x, y)
###################### функция удаления прежнего виджета ##########################
def delete_cb(self, widget, context):
print "delete_cb "
widget.destroy()
def target_drag_leave(self, widget, context, time):
print 'leave'
def target_drag_motion(self, widget, context, x, y, time):
print 'motion'
def target_drag_drop(self, widget, context, x, y, time):
print 'drop'
########################################################################################
def __init__(self):
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_default_size(400, 400)
self.window.connect("destroy", lambda w: gtk.main_quit())
self.window.show()
layout = self.makeLayout()
self.window.add(layout)
########################################################################################
DNDImageButton()
gtk.main()
Отредактировано (Дек. 6, 2008 05:26:41)
Офлайн
Я не пойму, где взять модуль gtkxpm.
Офлайн
The gray CardinalЗабыл прикрепить этот модуль. Он взят из примера по ссылке http://www.pygtk.org/pygtk2tutorial/sec-DNDMethods.html . Вот он(должен лежать рядом с dndimage.py:
Я не пойму, где взять модуль gtkxpm.
# example gtkxpm.py
gtk_xpm = [
"32 39 5 1",
". c none",
"+ c black",
"@ c #3070E0",
"# c #F05050",
"$ c #35E035",
"................+...............",
"..............+++++.............",
"............+++++@@++...........",
"..........+++++@@@@@@++.........",
"........++++@@@@@@@@@@++........",
"......++++@@++++++++@@@++.......",
".....+++@@@+++++++++++@@@++.....",
"...+++@@@@+++@@@@@@++++@@@@+....",
"..+++@@@@+++@@@@@@@@+++@@@@@++..",
".++@@@@@@+++@@@@@@@@@@@@@@@@@@++",
".+#+@@@@@@++@@@@+++@@@@@@@@@@@@+",
".+##++@@@@+++@@@+++++@@@@@@@@$@.",
".+###++@@@@+++@@@+++@@@@@++$$$@.",
".+####+++@@@+++++++@@@@@+@$$$$@.",
".+#####+++@@@@+++@@@@++@$$$$$$+.",
".+######++++@@@@@@@++@$$$$$$$$+.",
".+#######+##+@@@@+++$$$$$$@@$$+.",
".+###+++##+##+@@++@$$$$$$++$$$+.",
".+###++++##+##+@@$$$$$$$@+@$$@+.",
".+###++++++#+++@$$@+@$$@++$$$@+.",
".+####+++++++#++$$@+@$$++$$$$+..",
".++####++++++#++$$@+@$++@$$$$+..",
".+#####+++++##++$$++@+++$$$$$+..",
".++####+++##+#++$$+++++@$$$$$+..",
".++####+++####++$$++++++@$$$@+..",
".+#####++#####++$$+++@++++@$@+..",
".+#####++#####++$$++@$$@+++$@@..",
".++####++#####++$$++$$$$$+@$@++.",
".++####++#####++$$++$$$$$$$$+++.",
".+++####+#####++$$++$$$$$$$@+++.",
"..+++#########+@$$+@$$$$$$+++...",
"...+++########+@$$$$$$$$@+++....",
".....+++######+@$$$$$$$+++......",
"......+++#####+@$$$$$@++........",
".......+++####+@$$$$+++.........",
".........++###+$$$@++...........",
"..........++##+$@+++............",
"...........+++++++..............",
".............++++..............."
]
Офлайн
Спасибо, теперь запускается.
Но работает он неправильно. При перетаскивании, за какое бы место картинки я не “взялся”, при отпускании картинка перемещается так, что её верхний левый угол оказывается на месте курсора мыши. Это довольно увечное перетаскивание :). И совершенно непонятна роль gtkxpm.py. По идее, он не должен быть нужен вообще, его присутствие выглядит как-то нелепо.
Отредактировано (Дек. 6, 2008 11:21:51)
Офлайн
А по-моему неплохо. :) За какое бы место картинки не ухватился, появляется стрелочка и рука. Стрелочка показывает куда приземлить виджет, рука сигнализирует о том, что мы делаем( перетаскиваем).
Этот файл gtkxpm.py отложил до лучших времен( со временем надеюсь пойму, что и как).Это первый шаг. Ближайшая цель - приспособить drag_and_drop к этому:
#!/usr/bin/env python
# coding: utf-8
import pygtk
import gtk
class DrawingAreaExample:
def __init__(self):
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.connect("destroy", lambda w: gtk.main_quit())
self.area = gtk.DrawingArea()
self.area.set_size_request(400, 300)
self.sw = gtk.ScrolledWindow()
self.sw.add_with_viewport(self.area)
self.table = gtk.Table(2,2)
self.table.attach(self.sw, 1, 2, 1, 2)
window.add(self.table)
self.area.connect("expose-event", self.area_expose_cb)
self.area.show()
self.sw.show()
self.table.show()
window.show()
def area_expose_cb(self, area, event):
self.style = self.area.get_style()
self.gc = self.style.fg_gc[gtk.STATE_NORMAL]
self.mydraw_pixmap(0, 0, "/home/mike/Desktop/lena.jpg")
return True
def mydraw_pixmap(self, x, y, file):
pix, mask = gtk.gdk.pixmap_create_from_xpm(
self.area.window, self.style.bg[gtk.STATE_NORMAL], file)
self.area.window.draw_drawable(self.gc, pix, 0, 0, x, y,
-1, -1)
return
if __name__ == "__main__":
DrawingAreaExample()
gtk.main()
Офлайн
gmorgunovНе, не согласен :). Вот, например, на рабочем столе можно перетаскивать окна при нажатом Alt. Представь, что это работало бы так же :). При отпускании перетаскиваемый объект делает конвульсивный прыжок. Я сначала даже не понял, что вообще происходит :). Это слишком неожиданно для пользователя, противоречит общепринятому.
За какое бы место картинки не ухватился, появляется стрелочка и рука. Стрелочка показывает куда приземлить виджет, рука сигнализирует о том, что мы делаем( перетаскиваем).
gmorgunovДа, по первому впечатлению, качество картинки даже выше, чем у PyQt4 :).
Запустите и сравните с PyQt.
Офлайн
У меня почему-то никаких прыжков не происходит. Да, бывало, когда эту прогу “раскачивал”. Но сейчас все нормально, логично. И с Alt - тоже.
Качество картинки в PyGTK действительно выше( спросил жену, она у меня - эксперт). :)
Вы почаще ставьте такие задачки( с картами). В деле и сравним достинства той и другой библиотеки.
А ведь на PyQt мы ее полностью-то не решили. Посмотрим как на GTK. :)
Отредактировано (Дек. 6, 2008 16:17:52)
Офлайн
Вот окончательный вариант drag_and_drop картинок, размещенных в кнопке.
Просто вставьте свою картинку(file=/home/mike/Desktop/lena.jpg) и запустите программу.
В консоли будут печататся ваши действия при drag_and_drop.
#!/usr/bin/python
# coding: utf-8
import pygtk
from gtk import *
import time
class DNDImageButton:
file = "/home/mike/Desktop/lena.jpg"
fromImage=[("",0,0),("",0,0)]
toCanvas=[("",0,0)]
################### установки layout-a ###########################
def layout_resize(self, widget, event):
x, y, width, height = widget.get_allocation()
if width > self.lwidth or height > self.lheight:
self.lwidth = max(width, self.lwidth)
self.lheight = max(height, self.lheight)
widget.set_size(self.lwidth, self.lheight)
def makeLayout(self):
self.lwidth = 0
self.lheight = 0
box = VBox()
box.show()
table = Table()
table.show()
box.pack_start(table)
layout = Layout()
self.layout = layout
layout.set_size(self.lwidth, self.lheight)
layout.connect("size-allocate", self.layout_resize)
layout.show()
table.attach(layout, 0, 1, 0, 1, FILL|EXPAND, FILL|EXPAND, 0, 0)
################### испускаем сигналы при перетаскивании ##########################
layout.connect('drag_leave', self.target_drag_leave)
layout.connect('drag_motion', self.target_drag_motion)
layout.connect('drag_drop', self.target_drag_drop)
layout.connect("drag_data_received", self.receiveCallback)
################### установки цели ##########################
layout.drag_dest_set(DEST_DEFAULT_MOTION |
DEST_DEFAULT_HIGHLIGHT |
DEST_DEFAULT_DROP,
self.toCanvas, gdk.ACTION_MOVE )
self.myaddImage(0, 0)
return box
####################### устаеавл. виджет в коорд. xd, yd ##########################
def myaddImage(self, xd, yd):
hadj = self.layout.get_hadjustment()
vadj = self.layout.get_vadjustment()
image = Image()
image.set_from_file(self.file)
button = Button()
button.add(image)
####################### соединяем виджет ##########################
button.connect("drag_data_get", self.sendCallback)
button.connect('drag_data_delete', self.delete_cb)
###################### установки источника ##########################
button.drag_source_set(gdk.BUTTON1_MASK, self.fromImage, gdk.ACTION_MOVE )
button.show_all()
self.layout.put(button, int(xd+hadj.value), int(yd+vadj.value))
return
####################### функции CALLBACK ##########################
def sendCallback(self, widget, context, selection, targetType, eventTime):
print "send_cb "
selection.set(selection.target, 8, "")
def receiveCallback(self, widget, context, x, y, selection, targetType, time):
print "receive_cb "
self.myaddImage(x, y)
def delete_cb(self, widget, context):
print "delete_cb "
widget.destroy()
widget.hide_on_delete()
def target_drag_leave(self, widget, context, time):
print 'leave'
def target_drag_motion(self, widget, context, x, y, time):
print 'motion'
def target_drag_drop(self, widget, context, x, y, time):
print 'drop'
########################################################################################
def __init__(self):
self.window = Window(WINDOW_TOPLEVEL)
self.window.set_default_size(400, 400)
self.window.connect("destroy", lambda w: main_quit())
self.window.show()
layout = self.makeLayout()
self.window.add(layout)
########################################################################################
DNDImageButton()
main()
Офлайн
Спасибо, хороший пример.
Офлайн