Уведомления

Группа в Telegram: @pythonsu
  • Начало
  • » GUI
  • » PyQt4: поворот и drag-and-drop картинок [RSS Feed]

#1 Дек. 7, 2008 23:37:53

poltergeist
От:
Зарегистрирован: 2007-02-28
Сообщения: 522
Репутация: +  0  -
Профиль   Отправить e-mail  

PyQt4: поворот и drag-and-drop картинок

Посмотрите примеры реализации dragEnterEvent в примерах, там надо проверить наличие в MimeData нужных данных (изображение или что-то другое, в зависимости от того как реализуете) и подтвердить это (event.setAccepted(True)). Иначе, dropEvent не будет вызываться.
Добираться до объекта QDrag не нужно, всё что переносите, храниться в mimeData, так что тут стоит задуматься, как переносить информацию… Можно запихнуть изображение целиком (поток данных), можно просто путь к файлу изображения, сами решайте как вам удобнее…

кстати, правильно ли это?
Да, вроде правильно делаете.



Офлайн

#2 Дек. 8, 2008 13:51:04

The gray Cardinal
От:
Зарегистрирован: 2007-03-07
Сообщения: 422
Репутация: +  0  -
Профиль   Отправить e-mail  

PyQt4: поворот и drag-and-drop картинок

Сделал, но коряво до ужаса.
Картинки, которые необходимы для примера (должны лежать рядом с примером): http://stream.ifolder.ru/9445057 (архив 30 Кб).
Код примера (пригоден для запуска):

#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtGui, QtCore

class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setGeometry(300, 300, 600, 500)

# создание сцены для отображения элементов-рисунков:
self.scene = Scene()
# создание виджета представления для отображения сцены:
view = QtGui.QGraphicsView(self.scene, self)
# параметры качества прорисовки для виджета представления:
view.setRenderHints(QtGui.QPainter.Antialiasing | QtGui.QPainter.SmoothPixmapTransform)
view.setBackgroundBrush(QtGui.QColor(0, 128, 64)) # цвет фона представления
self.setCentralWidget(view) # размещение виджета представления в главном окне

# первый элемент:
item = Element(QtGui.QPixmap(u'011.jpg'), None, self.scene)
item.setZValue(1)

# второй элемент (с поворотом)
item2 = Element(QtGui.QPixmap(u'113.jpg'), None, self.scene)
item2.rotate(25) # поворот
item2.setOffset(100, -30) # смещение
item2.setZValue(2)

class Scene(QtGui.QGraphicsScene):
def __init__(self, parent = None):
QtGui.QGraphicsScene.__init__(self, parent)
self.dndElement = None
self.dndPixmap = None

# операция drag and drop входит в область сцены
def dragEnterEvent(self, event):
pass

# операция drag and drop покидает область сцены
def dragLeaveEvent(self, event):
pass

# в процессе выполнения операции drag and drop
def dragMoveEvent(self, event):
pass

# завершение операции drag and drop
def dropEvent(self, event):
# создание копии перенесённого элемента на новом месте:
item = Element(self.dndPixmap, None, self)
item.setOffset(event.scenePos().x() - 80, event.scenePos().y() - 110)
# удаление перенесённого элемента:
self.removeItem(self.dndElement)
self.dndElement = None
self.dndPixmap = None

class Element(QtGui.QGraphicsPixmapItem):
def __init__(self, pixmap, parent = None, scene = None):
QtGui.QGraphicsPixmapItem.__init__(self, pixmap, parent, scene)
self.setTransformationMode(QtCore.Qt.SmoothTransformation) # качество прорисовки
self.setCursor(QtCore.Qt.OpenHandCursor) # вид курсора мыши над элементом
#self.setAcceptDrops(True)

def mousePressEvent(self, event):
if event.button() != QtCore.Qt.LeftButton: # только левая клавиша мыши
event.ignore()
return
drag = QtGui.QDrag(event.widget()) # объект Drag
mime = QtCore.QMimeData()
drag.setMimeData(mime)
self.scene().dndElement = self # запоминаем элемент, который переносится
self.scene().dndPixmap = self.pixmap()
drag.setPixmap(self.pixmap()) # рисунок, отображающийся в процессе переноса

drag.setHotSpot(QtCore.QPoint(80, 110)) # позиция "ухватки"
# x = int(self.mapToScene(event.scenePos()).x())
# y = int(self.mapToScene(event.scenePos()).y())
# drag.setHotSpot(QtCore.QPoint(x, y))

# временный "затемнённый" рисунок перетаскиваемой картинки
tempPixmap = QtGui.QPixmap(self.pixmap())
painter = QtGui.QPainter()
painter.begin(tempPixmap)
painter.fillRect(self.pixmap().rect(), QtGui.QColor(127, 127, 127, 127))
painter.end()
self.setPixmap(tempPixmap)

drag.start() # запуск (начало) перетаскивания

if __name__=="__main__":
app = QtGui.QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec_())
Проблемы и вопросы:

1. Я так и не понял, как правильно воспользоваться QMimeData для передачи данных в момент перетаскивания. Картинку таким способом передать вроде можно, а вот как передать сам перетаскиваемый элемент, я не понял. Мне сам этот элемент нужен для того, чтобы его удалить при окончании перетаскивания. В результате я сделал свойство в классе сцены, чтобы в нём хранить перетаскиваемый в данный момент элемент. Это сильный моветон? Кстати, как устанавливать (хранить?) Z-порядок при перетаскивании (это нужно, т.к. перетаскиваемый рисунок запросто может отрисоваться сзади)?

2. В строчке “drag = QtGui.QDrag(event.widget())” я сам не до конца понимаю, что делаю. Что такое event.widget()? Это объект представления QGraphicsView?

3. Насколько я понял, сцена меняет свои размеры автоматически, при необходимости. И она вначале меньше по площади, чем QGraphicsView, который к ней подключён. В момент перетаскивания картинок в примере всё начинает некрасиво дёргаться, если перетаскивание делается таким образом, что оно приводит к расширению сцены. Правильно ли я это понял? Как избежать дёрганий?

4. При начале перетаскивания можно задать для объекта QDrag “позицию ухватки мыши” методом setHotSpot, что в коде и сделано. Там же ты можешь наблюдать неуклюжую попытку пересчитать координаты, чтобы ухватываться за ту часть картинки, над которой визуально был курсор мыши в момент начала действия (эти строки в коде закомментированы). Попытка эта работает только для случая, когда элемент не трансформирован (в данном случае - не повёрнут). Как пересчитать универсально, с учётом поворота и масштабирования, если численные характеристики этих двух операций заранее известны?



Офлайн

#3 Дек. 9, 2008 03:00:46

ZZZ
От: Москва
Зарегистрирован: 2008-04-03
Сообщения: 2161
Репутация: +  26  -
Профиль   Адрес электронной почты  

PyQt4: поворот и drag-and-drop картинок

:-)
Да… Это проблема, что люди, изучающие PyQt забывают, что пишут на питоне. Сам проходил. Слишком уж много Сишности в этом Qt… :-)

    def mousePressEvent(self, event):
drag = QtGui.QDrag(event.widget()) # объект Drag
mime = QtCore.QMimeData()
mime.Element = self
drag.setMimeData(mime)
    def dropEvent(self, event):
print event.mimeData().Element
Думаю, что первый вопрос это решит.
Но, обрати вримание, что в model-view это не всегда работает – как-то долго не мог понять, почему с QModelIndex такой фокус не проходит. :-)

Ещё обрати внимание на QDropEvent::source(), это разрешит второй вопрос и более Qt'шно решит первый.

Думаю, что четвёртый вопрос тебе поможет решить QMatrix, но как конкретно, я тебе не скажу, потому что сам уже не помню этих тонкостей. Ну или вспомнить школьную математику и пересчитать с учётом угла поворота. Я вообще, не занимался отрисовкой в Qt4 и QGraphicsView вместе с QGraphicsScene для меня пока магия… Хотя сейчас глянул и идея мне понравилась.

Мыслей по третьему вопросу пока нет.



Офлайн

#4 Дек. 9, 2008 03:32:13

ZZZ
От: Москва
Зарегистрирован: 2008-04-03
Сообщения: 2161
Репутация: +  26  -
Профиль   Адрес электронной почты  

PyQt4: поворот и drag-and-drop картинок

Блин, ты мемя совсем запутал! Ну на кой, простите, хрен ты использовал QGraphicsItem::setOffset()? Используй QGraphicsItem::setPos()! Тогда QGraphicsSceneMouseEvent::pos() будет точно возвращать тебе нужные координаты: drag.setHotSpot(event.pos().toPoint()). И никаких матриц…
Дальше, я думаю, справишься.



Офлайн

#5 Дек. 9, 2008 04:26:45

poltergeist
От:
Зарегистрирован: 2007-02-28
Сообщения: 522
Репутация: +  0  -
Профиль   Отправить e-mail  

PyQt4: поворот и drag-and-drop картинок

ZZZ я бы всё же посоветовал не хакать по-питонски (mime.Element = self) и усложнять жизнь разработчику… можно ведь обойтись “правильным” способом переноса данных (mime.setData(mtype, data)) драг&дропом, который уже будет в себе иметь понятный интерфейс (mtype). Такой подход будет универсальным, т.е. можно будет передавать данные не только в пределах одного приложения… Этот человек пишет пример, в том числе и для того чтобы других учить, а твой пример я считаю не хорошим в этом плане:(



Отредактировано (Дек. 9, 2008 04:28:41)

Офлайн

#6 Дек. 9, 2008 13:29:22

The gray Cardinal
От:
Зарегистрирован: 2007-03-07
Сообщения: 422
Репутация: +  0  -
Профиль   Отправить e-mail  

PyQt4: поворот и drag-and-drop картинок

ZZZ
Ещё обрати внимание на QDropEvent::source(), это разрешит второй вопрос и более Qt'шно решит первый.
Не понял. Вопрос в том, что я не пойму, что именно там за виджет сидит, в моём случае. Это не мой элемент-наследник QGraphicsPixmapItem, это не сцена, это не её QGraphicsView, и это не главное окно. Что это тогда? У меня больше никаких объектов-то нет :).



Офлайн

#7 Дек. 9, 2008 13:34:06

The gray Cardinal
От:
Зарегистрирован: 2007-03-07
Сообщения: 422
Репутация: +  0  -
Профиль   Отправить e-mail  

PyQt4: поворот и drag-and-drop картинок

Да, кстати: ZZZ, огромное спасибо за участие :) poltergeist - тоже :).



Офлайн

#8 Дек. 9, 2008 13:42:53

The gray Cardinal
От:
Зарегистрирован: 2007-03-07
Сообщения: 422
Репутация: +  0  -
Профиль   Отправить e-mail  

PyQt4: поворот и drag-and-drop картинок

ZZZ
print event.mimeData().Element
Спасибо, шикарненько :). Никогда не отдавал себе отчёта в этом…
class MyClass():
pass

obj = MyClass()
obj.bla_bla = 'Python is debauched!'
print obj.bla_bla
Как эта возможность по-научному называется? “Разнузданный Python”? :)



Отредактировано (Дек. 9, 2008 13:44:41)

Офлайн

#9 Дек. 9, 2008 16:36:56

The gray Cardinal
От:
Зарегистрирован: 2007-03-07
Сообщения: 422
Репутация: +  0  -
Профиль   Отправить e-mail  

PyQt4: поворот и drag-and-drop картинок

Кажется, осилил.



Офлайн

#10 Дек. 9, 2008 20:08:59

gmorgunov
От:
Зарегистрирован: 2008-10-15
Сообщения: 137
Репутация: +  0  -
Профиль   Отправить e-mail  

PyQt4: поворот и drag-and-drop картинок

Привет. Поздравляю. :)
А вот вариант решения на PyGTK. По-моему проще. Ухваченная картинка ложится всегда сверху ( по идее, так и должно быть). Движение на второй картинке(av3435.gif - движущийся пингвин в моем профиле) сохраняется.

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

import pygtk
from gtk import *
import time

class DNDImageButton:
file1 ="/home/mike/Desktop/lena.jpg"
file2 ="/home/mike/Desktop/av3435.gif"
fromImage=[("",0,0),("",0,0)]
toCanvas=[("",0,0)]
def __init__(self):
################### хэши - для распознавания кнопок ############################
global HASH,HASH1,HASH2
self.window = Window(WINDOW_TOPLEVEL)
self.window.set_default_size(500, 500)
self.window.connect("destroy", lambda w: main_quit())
self.window.show()
layout = self.makeLayout()
self.window.add(layout)
self.myaddImage(100, 100, self.file1)
self.myaddImage(200, 200, self.file2)
################### установки 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 )
return box
####################### устаеавл. виджет в коорд. xd, yd ##########################
def myaddImage(self, xd, yd, f):
global HASH,HASH1,HASH2
hadj = self.layout.get_hadjustment()
vadj = self.layout.get_vadjustment()
image = Image()
image.set_from_file(f)
button = Button()
button.add(image)
if f == self.file1:
HASH1 = button.__hash__()
else:
HASH2 = button.__hash__()
####################### соединяем виджет ##########################
button.connect('button_press_event', self.button_press)
button.connect("drag_data_get", self.sendCallback)
button.connect('drag_data_delete', self.delete_cb)
button.connect("drag_data_received", self.receiveCallback)
###################### установки источника ##########################
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))
print 'create button'
return

####################### функции CALLBACK ##########################
def button_press(self,button,event):
global HASH,HASH1,HASH2
print 'button_press'
HASH = button.__hash__()

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):
global HASH,HASH1,HASH2
print "receive_cb "
if HASH == HASH1:
self.myaddImage(x,y,self.file1)
else:
self.myaddImage(x,y,self.file2)

def delete_cb(self, widget, context):
print "delete_cb "
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'
print x,y
def target_drag_drop(self, widget, context, x, y, time):
print 'drop'
####################################################################
####################################################################
DNDImageButton()
main()



Офлайн

  • Начало
  • » GUI
  • » PyQt4: поворот и drag-and-drop картинок[RSS Feed]

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version