Seminarium 2008. Informatyka Prowadzący dr Grzegorz Wójcik 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.
File reader Definicja głównego okna 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 Konstruktor. Ustawienie TextBoxa w centrum. Ustawienie (nie)akceptowanych akcji. MainWindow::MainWindow() { textEdit = new QTextEdit; setCentralWidget(textEdit); textEdit->setAcceptDrops(false); setAcceptDrops(true); setWindowTitle(tr("Text Editor")); }
File reader dragEnterEvent. Sprawdzenie formatu przeciąganego obiektu. Pozwolenie na wykonanie akcji. void MainWindow::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasFormat("text/uri-list")) event->acceptProposedAction(); }
File reader dropEvent. void MainWindow::dropEvent(QDropEvent *event) { QList<QUrl> urls = event->mimeData()->urls(); if (urls.isEmpty()) return; QString fileName = urls.first().toLocalFile(); if (fileName.isEmpty()) if (readFile(fileName)) setWindowTitle(tr("%1 - %2").arg(fileName) .arg(tr("Drag File"))); }
Choose project Aplikacja umożliwia przeciągnięcie pracowników, aby pracowali przy odpowiednich projektach.
Choose project Definicja klasy 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 Konstruktor. ProjectListWidget::ProjectListWidget(QWidget *parent) : QListWidget(parent) { setAcceptDrops(true); }
Choose project Akcja wciśnięcia przycisku myszy na widget’ie. void ProjectListWidget::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) startPos = event->pos(); QListWidget::mousePressEvent(event); }
Choose project Akcja przesuwania myszy po 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 Rozpoczęcie drag’a. 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 Akcja rozpoczęcia drag’a na widget’ie. 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 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 drop’u 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(); }
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.
QTableWidget – I wersja 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 Red Green Blue //plainText Cyan Yellow 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>
QTableWidget – I wersja 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(); }
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.
QTableWidget – II wersja 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 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().
QTableWidget – II wersja Przepisujemy i przypisujemy odpowiednie dane. TableMimeData::TableMimeData(const QTableWidget *tableWidget, const QTableWidgetSelectionRange &range) { myTableWidget = tableWidget; myRange = range; myFormats << "text/csv" << "text/html" << "text/plain"; }
QTableWidget – II wersja QStringList TableMimeData::formats() const { return myFormats; }
QTableWidget – II wersja Zwracamy dane w odpowiednim formacie. 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 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); } else if (event->mimeData()->hasFormat("text/plain")) { QString plainText = event->mimeData()->text(); } QTableWidget::mouseMoveEvent(event);
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
Drag & drop + schowek void MyTextEditor::mouseReleaseEvent(QMouseEvent *event) { QClipboard *clipboard = QApplication::clipboard(); if (event->button() == Qt::MidButton && clipboard->supportsSelection()) { QString text = clipboard->text(QClipboard::Selection); pasteText(text); }
Rozdział 9. Drag and drop. Dziękuje