程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> Qt學習之路(54): 自定義拖放數據對象

Qt學習之路(54): 自定義拖放數據對象

編輯:關於C語言

前面的例子都是使用的系統提供的拖放對象 QMimeData 進行拖放數據的存儲,比如使用 QMimeData::setText() 創建文本,使用 QMimeData::urls() 創建 URL 對象。但是,如果你希望使用一些自定義的對象作為拖放數據,比如自定義類等等,單純使用 QMimeData 可能就沒有那麼容易了。為了實現這種操作,我們可以從下面三種實現方式中選擇一個: 第一種方法不需要繼承任何類,但是有一些局限:即是拖放不會發生,我們也必須將自定義的數據對象轉換成 QByteArray 對象;如果你希望支持很多種拖放的數據,那麼每種類型的數據都必須使用一個 QMimeData 類,這可能會導致類爆炸;如果數據很大的話,這種方式可能會降低系統的可維護性。然而,後兩種實現方式就不會有這些問題,或者說是能夠減小這種問題,並且能夠讓我們有完全控制權。 我們先來看一個應用,使用 QTableWidget 來進行拖放操作,拖放的類型包括 plain/text,plain/html 和 plain/csv。如果使用第一種實現方法,我們的代碼將會如下所示:

  1. void MyTableWidget::mouseMoveEvent(QMouseEvent *event)  
  2. {  
  3.     if (event->buttons() & Qt::LeftButton) {  
  4.         int distance = (event->pos() - startPos).manhattanLength();  
  5.         if (distance >= QApplication::startDragDistance())  
  6.             performDrag();  
  7.     }  
  8.     QTableWidget::mouseMoveEvent(event);  
  9. }  
  10.  
  11. void MyTableWidget::performDrag()  
  12. {  
  13.     QString plainText = selectionAsPlainText();  
  14.     if (plainText.isEmpty())  
  15.         return;  
  16.  
  17.     QMimeData *mimeData = new QMimeData;  
  18.     mimeData->setText(plainText);  
  19.     mimeData->setHtml(toHtml(plainText));  
  20.     mimeData->setData("text/csv", toCsv(plainText).toUtf8());  
  21.  
  22.     QDrag *drag = new QDrag(this);  
  23.     drag->setMimeData(mimeData);  
  24.     if (drag->exec(Qt::CopyAction | Qt::MoveAction) == Qt::MoveAction)  
  25.         deleteSelection();  
  26. }  
對於這段代碼,我們應該已經很容易的理解:在 performDrag() 函數中,我們調用 QMimeData 的 setText() 和 setHTML() 函數存儲 plain/text 和 plain/html 數據,使用 setData() 將 text/csv 類型的數據作為二進制 QByteArray 類型存儲。
  1. QString MyTableWidget::toCsv(const QString &plainText)  
  2. {  
  3.     QString result = plainText;  
  4.     result.replace("\\", "\\\\");  
  5.     result.replace("\"", "\\\"");  
  6.     result.replace("\t", "\", \"");  
  7.     result.replace("\n", "\"\n\"");  
  8.     result.prepend("\"");  
  9.     result.append("\"");  
  10.     return result;  
  11. }  
  12.  
  13. QString MyTableWidget::toHtml(const QString &plainText)  
  14. {  
  15.     QString result = Qt::escape(plainText);  
  16.     result.replace("\t", "<td>");  
  17.     result.replace("\n", "\n<tr><td>");  
  18.     result.prepend("<table>\n<tr><td>");  
  19.     result.append("\n</table>");  
  20.     return result;  
  21. }  
toCsv() 和 toHtml() 函數將數據取出並轉換成我們需要的 csv 和 html類型的數據。例如,下面的數據
Red   Green   Blue
Cyan  Yellow  Magenta
轉換成 csv 格式為:
"Red", "Green", "Blue"
"Cyan", "Yellow", "Magenta"
轉換成 html 格式為: <table>
<tr><td>Red<td>Green<td>Blue
<tr><td>Cyan<td>Yellow<td>Magenta
</table> 在放置的函數中我們像以前一樣使用:
  1. void MyTableWidget::dropEvent(QDropEvent *event)  
  2. {  
  3.     if (event->mimeData()->hasFormat("text/csv")) {  
  4.         QByteArray csvData = event->mimeData()->data("text/csv");  
  5.         QString csvText = QString::fromUtf8(csvData);  
  6.         // ...  
  7.         event->acceptProposedAction();  
  8.     } else if (event->mimeData()->hasFormat("text/plain")) {  
  9.         QString plainText = event->mimeData()->text();  
  10.         // ...  
  11.         event->acceptProposedAction();  
  12.     }  
  13. }  
雖然我們接受三種數據類型,但是在這個函數中我們只接受兩種類型。至於 html 類型,我們希望如果用戶將 QTableWidget 的數據拖到一個 HTML 編輯器,那麼它就會自動轉換成 html 代碼,但是我們不計劃支持將外部的 html 代碼拖放到 QTableWidget 上。為了讓這段代碼能夠工作,我們需要在構造函數中設置 setAcceptDrops(true) 和 setSelectionMode(ContiguousSelection)。 好了,上面就是我們所說的第一種方式的實現。這裡並沒有給出完整的實現代碼,大家可以根據需要自己實現一下試試。下面我們將按照第二種方法重新實現這個需求。
  1. class TableMimeData : public QMimeData  
  2. {  
  3.     Q_OBJECT  
  4.  
  5. public:  
  6.     TableMimeData(const QTableWidget *tableWidget,  
  7.                   const QTableWidgetSelectionRange &range);  
  8.  
  9.     const QTableWidget *tableWidget() const { return myTableWidget; }  
  10.     QTableWidgetSelectionRange range() const { return myRange; }  
  11.     QStringList formats() const;  
  12.  
  13. protected:  
  14.     QVariant retrieveData(const QString &format,  
  15.                           QVariant::Type preferredType) const;  
  16.  
  17. private:  
  18.     static QString toHtml(const QString &plainText);  
  19.     static QString toCsv(const QString &plainText);  
  20.  
  21.     QString text(int row, int column) const;  
  22.     QString rangeAsPlainText() const;  
  23.  
  24.     const QTableWidget *myTableWidget;  
  25.     QTableWidgetSelectionRange myRange;  
  26.     QStringList myFormats;  
  27. };  
為了避免存儲具體的數據,我們存儲 table 和選擇區域的坐標的指針。
  1. TableMimeData::TableMimeData(const QTableWidget *tableWidget,  
  2.                              const QTableWidgetSelectionRange &range)  
  3. {  
  4.     myTableWidget = tableWidget;  
  5.     myRange = range;  
  6.     myFormats << "text/csv" << "text/html" << "text/plain";  
  7. }  
  8.  
  9. QStringList TableMimeData::formats() const 
  10. {  
  11.     return myFormats;  
構造函數中,我們對私有變量進行初始化。formats() 函數返回的是被 MIME 數據對象支持的數據類型列表。這個列表是沒有先後順序的,但是最佳實踐是將“最適合”的類型放在第一位。對於支持多種類型的應用程序而言,有時候會直接選用第一個符合的類型存儲。
  1. QVariant TableMimeData::retrieveData(const QString &format,  
  2.                                      QVariant::Type preferredType) const 
  3. {  
  4.     if (format == "text/plain") {  
  5.         return rangeAsPlainText();  
  6.     } else if (format == "text/csv") {  
  7.         return toCsv(rangeAsPlainText());  
  8.     } else if (format == "text/html") {  
  9.         return toHtml(rangeAsPlainText());  
  10.     } else {  
  11.         return QMimeData::retrieveData(format, preferredType);  
  12.     }  
  函數 retrieveData() 將給出的 MIME 類型作為 QVariant 返回。參數 format 的值通常是 formats() 函數返回值之一,但是我們並不能假定一定是這個值之一,因為並不是所有的應用程序都會通過 formats() 函數檢查 MIME 類型。一些返回函數,比如 text(), html(), urls(), imageData(), colorData() 和 data() 實際上都是在 QMimeData 的 retrieveData() 函數中實現的。第二個參數 preferredType 給出我們應該在 QVariant 中存儲哪種類型的數據。在這裡,我們簡單的將其忽略了,並且在 else 語句中,我們假定 QMimeData 會自動將其轉換成所需要的類型。
  1. void MyTableWidget::dropEvent(QDropEvent *event)  
  2. {  
  3.     const TableMimeData *tableData =  
  4.             qobject_cast<const TableMimeData *>(event->mimeData());  
  5.  
  6.     if (tableData) {  
  7.         const QTableWidget *otherTable = tableData->tableWidget();  
  8.         QTableWidgetSelectionRange otherRange = tableData->range();  
  9.         // ...  
  10.         event->acceptProposedAction();  
  11.     } else if (event->mimeData()->hasFormat("text/csv")) {  
  12.         QByteArray csvData = event->mimeData()->data("text/csv");  
  13.         QString csvText = QString::fromUtf8(csvData);  
  14.         // ...  
  15.         event->acceptProposedAction();  
  16.     } else if (event->mimeData()->hasFormat("text/plain")) {  
  17.         QString plainText = event->mimeData()->text();  
  18.         // ...  
  19.         event->acceptProposedAction();  
  20.     }  
  21.     QTableWidget::mouseMoveEvent(event);  
在放置的函數中,我們需要按照我們自己定義的數據類型進行選擇。我們使用 qobject_cast 宏進行類型轉換。如果成功,說明數據來自同一應用程序,因此我們直接設置 QTableWidget 相關 數據,如果轉換失敗,我們則使用一般的處理方式。

本文出自 “豆子空間” 博客,請務必保留此出處http://devbean.blog.51cto.com/448512/288742

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved