390 likes | 630 Vues
Rozdział 9. Drag and drop. Seminarium 2008. Informatyka Prowadzący dr Grzegorz Wójcik. Plan. Omówienie działania drag & drop’u Implementacja drag & drop’u „File reader” „Choose project” Drag & drop + niestandardowe obiekty Drag & drop + schowek. Zasada Drag & Dropu.
E N D
Rozdział 9.Drag and drop. Seminarium 2008. Informatyka Prowadzący dr Grzegorz Wójcik
Plan • Omówienie działania drag & drop’u • Implementacja drag & drop’u • „File reader” • „Choose project” • Drag & drop + niestandardowe obiekty • Drag & drop + schowek
Zasada Drag & Dropu • Przeciągnięcie obiektu i upuszczenie go na Widget, bądź jego komponenty. • Źródło: • Komponent tej samej aplikacji • Zewnętrzna aplikacja
File reader Aplikacja umożliwia przeciągnięcie listy URI (czyli najprościej mówiąc zaznaczonego pliku/ów) na QTextBox’a, co skutkuje wczytaniem pliku i odpowiednio ustawieniem tytułu Widgeta.
class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(); protected: void dragEnterEvent(QDragEnterEvent *event); void dropEvent(QDropEvent *event); private: bool readFile(const QString &fileName); QTextEdit *textEdit; }; File reader Definicja głównego okna
MainWindow::MainWindow() { textEdit = new QTextEdit; setCentralWidget(textEdit); textEdit->setAcceptDrops(false); setAcceptDrops(true); setWindowTitle(tr("Text Editor")); } File reader Konstruktor. Ustawienie TextBoxa w centrum. Ustawienie (nie)akceptowanych akcji.
void MainWindow::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasFormat("text/uri-list")) event->acceptProposedAction(); } File reader dragEnterEvent. Sprawdzenie formatu przeciąganego obiektu. Pozwolenie na wykonanie akcji.
void MainWindow::dropEvent(QDropEvent *event) { QList<QUrl> urls = event->mimeData()->urls(); if (urls.isEmpty()) return; QString fileName = urls.first().toLocalFile(); if (fileName.isEmpty()) return; if (readFile(fileName)) setWindowTitle(tr("%1 - %2").arg(fileName) .arg(tr("Drag File"))); } File reader dropEvent.
Choose project Aplikacja umożliwia przeciągnięcie pracowników, aby pracowali przy odpowiednich projektach.
class ProjectListWidget : public QListWidget { Q_OBJECT public: ProjectListWidget(QWidget *parent = 0); protected: void mousePressEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); void dragEnterEvent(QDragEnterEvent *event); void dragMoveEvent(QDragMoveEvent *event); void dropEvent(QDropEvent *event); private: void startDrag(); QPoint startPos; }; Choose project Definicja klasy
ProjectListWidget::ProjectListWidget(QWidget *parent) : QListWidget(parent) { setAcceptDrops(true); } Choose project Konstruktor.
void ProjectListWidget::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) startPos = event->pos(); QListWidget::mousePressEvent(event); } Choose project Akcja wciśnięcia przycisku myszy na widget’ie.
void ProjectListWidget::mouseMoveEvent(QMouseEvent *event) { if (event->buttons() & Qt::LeftButton) { int distance = (event->pos() - startPos).manhattanLength(); if (distance >= QApplication::startDragDistance()) startDrag(); } QListWidget::mouseMoveEvent(event); } Choose project Akcja przesuwania myszy po widget’ie.
void ProjectListWidget::startDrag() { QListWidgetItem *item = currentItem(); if (item) { QMimeData *mimeData = new QMimeData; mimeData->setText(item->text()); QDrag *drag = new QDrag(this); drag->setMimeData(mimeData); drag->setPixmap(QPixmap(":/images/person.png")); if (drag->start(Qt::MoveAction) == Qt::MoveAction) delete item; } } Choose project Rozpoczęcie drag’a.
void ProjectListWidget::dragEnterEvent(QDragEnterEvent *event) { ProjectListWidget *source = qobject_cast<ProjectListWidget *>(event->source()); if (source && source != this) { event->setDropAction(Qt::MoveAction); event->accept(); } } Choose project Akcja rozpoczęcia drag’a na widget’ie.
void ProjectListWidget::dragMoveEvent(QDragMoveEvent *event) { ProjectListWidget *source = qobject_cast<ProjectListWidget *>(event->source()); if (source && source != this) { event->setDropAction(Qt::MoveAction); event->accept(); } } Choose project Akcja drag’a na widget’ie.
void ProjectListWidget::dropEvent(QDropEvent *event) { ProjectListWidget *source = qobject_cast<ProjectListWidget *>(event->source()); if (source && source != this) { addItem(event->mimeData()->text()); event->setDropAction(Qt::MoveAction); event->accept(); } } Choose project Akcja drop’u na widget’ie.
Drag & drop + niestandardowe obiekty Dlaczego? • QMimeData nie wystarcza • Typowe dane • Pliki • Zwykły tekst • Chcemy przeciągać niestandardowe dane
Drag & drop + niestandardowe obiekty Jak to zrobić? • Możemy kodować nasz dane np. do QByteArray, a następnie używać QMimeData::setData() i QMimeData::data() • Możemy stworzyć podklase QMimeData i nadpisać formats() i retrieveData() • W przypadku akcji w obrębie jednej aplikacji, możemy stworzyć podklase QMimeData i trzymać dane w jakiejkolwiek strukturze
QTableWidget Widget ma możliwość konwertowania przeciągania danych. W przypadku przeciągania danych zaznaczanych na widget’ie konwertuje je i wyświetla w odpowiednim formacie.
void MyTableWidget::mouseMoveEvent(QMouseEvent *event) { if (event->buttons() & Qt::LeftButton) { int distance = (event->pos() - startPos).manhattanLength(); if (distance >= QApplication::startDragDistance()) startDrag(); } QTableWidget::mouseMoveEvent(event); } QTableWidget – I wersja
void MyTableWidget::startDrag() { QString plainText = selectionAsPlainText(); //analogicznie jak w Rozdziale 4. copy() if (plainText.isEmpty()) return; QMimeData *mimeData = new QMimeData; mimeData->setText(plainText); mimeData->setHtml(toHtml(plainText)); mimeData->setData("text/csv", toCsv(plainText).toUtf8()); QDrag *drag = new QDrag(this); drag->setMimeData(mimeData); if (drag->start(Qt::CopyAction | Qt::MoveAction) == Qt::MoveAction) deleteSelection(); } QTableWidget – I wersja
QString MyTableWidget::toCsv(const QString &plainText) { QString result = plainText; result.replace("\\", "\\\\"); result.replace("\"", "\\\""); result.replace("\t", "\", \""); result.replace("\n", "\"\n\""); result.prepend("\""); result.append("\""); return result; } QTableWidget – I wersja
QString MyTableWidget::toHtml(const QString &plainText) { QString result = Qt::escape(plainText); result.replace("\t", "<td>"); result.replace("\n", "\n<tr><td>"); result.prepend("<table>\n<tr><td>"); result.append("\n</table>"); return result; } QTableWidget – I wersja
QTableWidget – I wersja RedGreenBlue //plainText CyanYellow Magenta "Red", "Green", "Blue„ //CSV "Cyan", "Yellow", "Magenta" <table> //HTML (prawie jak :-) <tr><td>Red<td>Green<td>Blue <tr><td>Cyan<td>Yellow<td>Magenta </table>
void MyTableWidget::dropEvent(QDropEvent *event) { if (event->mimeData()->hasFormat("text/csv")) { QByteArray csvData = event->mimeData()- >data("text/csv"); QString csvText = QString::fromUtf8(csvData); ... event->acceptProposedAction(); } else if (event->mimeData()->hasFormat("text/plain")) { QString plainText = event->mimeData()->text(); ... event->acceptProposedAction(); } } QTableWidget – I wersja
QTableWidget – I wersja Akceptujemy tylko 2 formaty. HTML – możemy przeciągnąć poza QTableWidget, ale na QTableWidget już nie przyjmujemy.
QTableWidget – II wersja Potencjalnie szybsza. W I wersji kosztowna może być konwersja na QByteArray. W II tworzymy podklasę QMimeData.
class TableMimeData : public QMimeData { Q_OBJECT public: TableMimeData(const QTableWidget *tableWidget, const QTableWidgetSelectionRange &range); const QTableWidget *tableWidget() const { return myTableWidget; } QTableWidgetSelectionRange range() const { return myRange; } QStringList formats() const; protected: QVariant retrieveData(const QString &format, QVariant::Type preferredType) const; private: static QString toHtml(const QString &plainText); static QString toCsv(const QString &plainText); QString text(int row, int column) const; QString rangeAsPlainText() const; const QTableWidget *myTableWidget; QTableWidgetSelectionRange myRange; QStringList myFormats; }; QTableWidget – II wersja
QTableWidget – II wersja Przechowujemy wskaźnik do naszej tabeli oraz zakres, czyli zbiór komórek. Ponadto, jak wspomniano powyżej, musimy ponownie zaimplementować funkcje formats() i retrieveData().
TableMimeData::TableMimeData(const QTableWidget *tableWidget, const QTableWidgetSelectionRange &range) { myTableWidget = tableWidget; myRange = range; myFormats << "text/csv" << "text/html" << "text/plain"; } QTableWidget – II wersja Przepisujemy i przypisujemy odpowiednie dane.
QStringList TableMimeData::formats() const { return myFormats; } QTableWidget – II wersja
QVariant TableMimeData::retrieveData(const QString &format, QVariant::Type preferredType) const { if (format == "text/plain") { return rangeAsPlainText(); } else if (format == "text/csv") { return toCsv(rangeAsPlainText()); } else if (format == "text/html") { return toHtml(rangeAsPlainText()); } else { return QMimeData::retrieveData(format, preferredType); } } QTableWidget – II wersja Zwracamy dane w odpowiednim formacie.
void MyTableWidget::dropEvent(QDropEvent *event) { const TableMimeData *tableData = qobject_cast<const TableMimeData *>(event->mimeData()); if (tableData) { const QTableWidget *otherTable = tableData->tableWidget(); QTableWidgetSelectionRange otherRange = tableData->range(); ... event->acceptProposedAction(); } else if (event->mimeData()->hasFormat("text/csv")) { QByteArray csvData = event->mimeData()->data("text/csv"); QString csvText = QString::fromUtf8(csvData); ... event->acceptProposedAction(); } else if (event->mimeData()->hasFormat("text/plain")) { QString plainText = event->mimeData()->text(); ... event->acceptProposedAction(); } QTableWidget::mouseMoveEvent(event); } QTableWidget – II wersja
Drag & drop + schowek • Znajdziemy pod QApplication::clipboard(). • QClipboard posiada metody: • setPixmap(), pixmap() • setText(), text() • setImage(), image() • QTextEdit posiada metody: • cut() • copy() • paste()
Drag & drop + schowek Czasem nie wystarcza nam zwykły schowek, gdy np. mamy dane nie będące tekstem, ani obrazem. Problem rozwiązujemy podobnie jak przed kilkoma slajdami. Tworzymy podklasę QClipboard i implementujemy odpowiednie metody, np. setMimedata(), mimedata()
Drag & drop + schowek • Selection • Supportowane tylko na X11 • Aby uzyskać dostęp do zaznaczonego tekstu należy: • sprawdzić czy Selection jest supportowane • przekazać w parametrze QClipboard::Selection
void MyTextEditor::mouseReleaseEvent(QMouseEvent *event) { QClipboard *clipboard = QApplication::clipboard(); if (event->button() == Qt::MidButton && clipboard->supportsSelection()) { QString text = clipboard->text(QClipboard::Selection); pasteText(text); } } Drag & drop + schowek
Rozdział 9. Drag and drop. Dziękuje