Найти - Пользователи
Полная версия: PyGtk. Drag_and_drop картинок.
Начало » GUI » PyGtk. Drag_and_drop картинок.
1
gmorgunov
Привет всем.
Пробовал рисовать картинки на 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()
В консоли печатаются вызовы всех callback-ов.По-моему очень простой и интересный механизм.
Попутный вопрос: кто-нибудь реализовывал метод rotate() для PyGtk ?
Если есть решение, отпишите пожалуйста, буду признателен.
The Gray Cardinal
По-моему качество изображения ничуть не хуже, чем в PyQt. Правда не знаю, как поведут себя картинки
при повороте. Если удастся решить вашу задачу на PyGtk, обязательно отпишу. :)

P.S. Для правильной работы примера см. посты ниже
The gray Cardinal
Я не пойму, где взять модуль gtkxpm.
gmorgunov
The gray Cardinal
Я не пойму, где взять модуль gtkxpm.
Забыл прикрепить этот модуль. Он взят из примера по ссылке http://www.pygtk.org/pygtk2tutorial/sec-DNDMethods.html . Вот он(должен лежать рядом с dndimage.py:

gtkxpm.py
# example gtkxpm.py

gtk_xpm = [
"32 39 5 1",
". c none",
"+ c black",
"@ c #3070E0",
"# c #F05050",
"$ c #35E035",
"................+...............",
"..............+++++.............",
"............+++++@@++...........",
"..........+++++@@@@@@++.........",
"........++++@@@@@@@@@@++........",
"......++++@@++++++++@@@++.......",
".....+++@@@+++++++++++@@@++.....",
"...+++@@@@+++@@@@@@++++@@@@+....",
"..+++@@@@+++@@@@@@@@+++@@@@@++..",
".++@@@@@@+++@@@@@@@@@@@@@@@@@@++",
".+#+@@@@@@++@@@@+++@@@@@@@@@@@@+",
".+##++@@@@+++@@@+++++@@@@@@@@$@.",
".+###++@@@@+++@@@+++@@@@@++$$$@.",
".+####+++@@@+++++++@@@@@+@$$$$@.",
".+#####+++@@@@+++@@@@++@$$$$$$+.",
".+######++++@@@@@@@++@$$$$$$$$+.",
".+#######+##+@@@@+++$$$$$$@@$$+.",
".+###+++##+##+@@++@$$$$$$++$$$+.",
".+###++++##+##+@@$$$$$$$@+@$$@+.",
".+###++++++#+++@$$@+@$$@++$$$@+.",
".+####+++++++#++$$@+@$$++$$$$+..",
".++####++++++#++$$@+@$++@$$$$+..",
".+#####+++++##++$$++@+++$$$$$+..",
".++####+++##+#++$$+++++@$$$$$+..",
".++####+++####++$$++++++@$$$@+..",
".+#####++#####++$$+++@++++@$@+..",
".+#####++#####++$$++@$$@+++$@@..",
".++####++#####++$$++$$$$$+@$@++.",
".++####++#####++$$++$$$$$$$$+++.",
".+++####+#####++$$++$$$$$$$@+++.",
"..+++#########+@$$+@$$$$$$+++...",
"...+++########+@$$$$$$$$@+++....",
".....+++######+@$$$$$$$+++......",
"......+++#####+@$$$$$@++........",
".......+++####+@$$$$+++.........",
".........++###+$$$@++...........",
"..........++##+$@+++............",
"...........+++++++..............",
".............++++..............."
]
Это образ кубика gtk. Пока не разобрался зачем он нужен, но на нем завязаны начальные установки.
The gray Cardinal
Спасибо, теперь запускается.
Но работает он неправильно. При перетаскивании, за какое бы место картинки я не “взялся”, при отпускании картинка перемещается так, что её верхний левый угол оказывается на месте курсора мыши. Это довольно увечное перетаскивание :). И совершенно непонятна роль gtkxpm.py. По идее, он не должен быть нужен вообще, его присутствие выглядит как-то нелепо.
gmorgunov
А по-моему неплохо. :) За какое бы место картинки не ухватился, появляется стрелочка и рука. Стрелочка показывает куда приземлить виджет, рука сигнализирует о том, что мы делаем( перетаскиваем).
Этот файл 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()
Запустите и сравните с PyQt. :)
P.S. Я занимаюсь GTK недавно, так что у самого полно вопросов. ( Сейчас разбираюсь с иерархией виджетов)
The gray Cardinal
gmorgunov
За какое бы место картинки не ухватился, появляется стрелочка и рука. Стрелочка показывает куда приземлить виджет, рука сигнализирует о том, что мы делаем( перетаскиваем).
Не, не согласен :). Вот, например, на рабочем столе можно перетаскивать окна при нажатом Alt. Представь, что это работало бы так же :). При отпускании перетаскиваемый объект делает конвульсивный прыжок. Я сначала даже не понял, что вообще происходит :). Это слишком неожиданно для пользователя, противоречит общепринятому.
gmorgunov
Запустите и сравните с PyQt.
Да, по первому впечатлению, качество картинки даже выше, чем у PyQt4 :).
Но на данном этапе я хочу всё же поковырять именно PyQt4, она вроде в целом мощнее.
Где бы нам взять 10 жизней, чтобы оценить все технологии? ;)
gmorgunov
У меня почему-то никаких прыжков не происходит. Да, бывало, когда эту прогу “раскачивал”. Но сейчас все нормально, логично. И с Alt - тоже.
Качество картинки в PyGTK действительно выше( спросил жену, она у меня - эксперт). :)
Вы почаще ставьте такие задачки( с картами). В деле и сравним достинства той и другой библиотеки.
А ведь на PyQt мы ее полностью-то не решили. Посмотрим как на GTK. :)
gmorgunov
Вот окончательный вариант 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()
The gray Cardinal
Спасибо, хороший пример.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB