QT核心編程之鼠標拖放 (4)
QT核心編程之鼠標拖放是本節介紹的內容。QT核心編程我們要分幾個部分來介紹,想參考更多內容,請看末尾的編輯推薦進行詳細閱讀,先來看本篇內容。
拖放提供了一種用戶在應用程序之間或之內傳遞信息的一種簡單可視機制。在術語中,這被稱為"直接操作模型"。拖放在功能上類似剪貼板的剪切和粘貼機制。拖放機制包括拖動、放下、剪貼板、拖放操作、添加新的拖放類型、高級拖放以及和其它應用程序之間的操作幾個方面。下面從這幾個方面分別進行說明:
(1)拖動
開始一個拖動,比如是在鼠標移動事件,創建一個適合你的媒體的QDragObject的子類的對象,例如:對于文本使用QTextDrag,對于圖片使用QImageDrag。然后調用drag()方法。例如,從一個窗口部件中開始拖動一些文本:
- void MyWidget::startDrag() {
- QDragObject *d = new QTextDrag( myHighlightedText(), this );
- d->dragCopy(); //拷貝選中文本 // 不要刪除d
- }
注意在拖動之后,QDragObject沒有被刪除。在拖放明顯完成后,這個QDragObject需要被保存。因為它還可能需要與其它進程通信。最后 Qt會刪除這個對象。如果擁有拖動對象的窗口部件在刪除拖動對象之前被刪除,那么任何沒有完成的放下操作將會被取消,并且拖動對象會被刪除。因為這個原因,你應該小心對待對象引用。
(2)放下
為了能在一個窗口部件中接收被放下的媒體,這個窗口部件調用setAcceptDrops(TRUE)(如:在它的構造函數中),并且重載事件處理方法dragEnterEvent()和dropEvent()。對于更復雜的應用程序,重載dragMoveEvent()和 dragLeaveEvent()也是必需的。
例如,當拖動后放下文本或圖片時,窗口部件接受并處理放下操作的代碼如下:
- MyWidget::MyWidget(...) : QWidget(...) {
- ... setAcceptDrops(TRUE); //接收被放下的媒體
- }//當一個拖動正在進行并且鼠標進入這個窗口部件,這個事件處理函數被調用
- void MyWidget::dragEnterEvent(QDragEnterEvent* event) {
- event->accept( QTextDrag::canDecode(event) || QImageDrag::canDecode(event) );
- }//當拖動在這個窗口部件上被放下,這個事件處理器被調用
- void MyWidget::dropEvent(QDropEvent* event) {
- QImage image;
- QString text;
- if ( QImageDrag::decode(event, image) ) {//解碼圖片
- insertImageAt(image, event->pos()); //在窗口部件中插入圖片
- } else if ( QTextDrag::decode(event, text) ) {
- insertTextAt(text, event->pos());
- }
- }
(3)剪貼板
QDragObject、QDragEnterEvent、QDragMoveEvent和QDropEvent類都是 QMimeSource(提供類型信息的類)的子類。如果你在QDragObject中基于你的數據進行傳遞,你不僅可使用拖放,而且還可以使用傳統的剪切和粘貼。QClipboard有兩個函數:
- setData(QMimeSource*)
- QMimeSource* data()const
使用這些函數,你可以把你的拖放初始信息放到剪貼板中:
- void MyWidget::copy(){
- QApplication::clipboard()->setData( new QTextDrag(myHighlightedText()) );
- }
- void MyWidget::paste(){
- QString text;
- if ( QTextDrag::decode(QApplication::clipboard()->data(), text) )
- insertText( text );
- }
你甚至能使用QDragObject的子類作為文件I/O部分。例如,如果你的程序有一個QDragObject的子類把CAD設計編碼成DXF格式,你可以象下面這樣存儲和裝載這個格式的文件:
- void MyWidget::save(){
- QFile out(current_file_name);
- out.open(IO_WriteOnly);
- MyCadDrag tmp(current_design); // MyCadDrag是QDragObject的子類
- out.writeBlock( tmp->encodedData( "image/x-dxf" ) );
- } void MyWidget::load(){
- QFile in(current_file_name);
- in.open(IO_ReadOnly);
- if ( !MyCadDrag::decode(in.readAll(), current_design) ) {
- QMessageBox::warning( this, "Format error", tr("The file \"%1\" is not in any supported format") .arg(current_file_name) );
- }
- }
(4)拖放操作
在一些簡單的情況下,拖放的目標接收一個被拖動的數據的拷貝,并且由源來決定是否刪除初始的拖動對象。這是QDropEvent中的"Copy"操作。目標也可以選擇理解其它操作,特別是"Move"和"Link"操作。如果目標理解了"Move"操作,目標負責拷貝和刪除操作,源不會嘗試刪除數據。如果目標理解為"Link"操作,它存儲它自己的引用到初始信息中,并且源不會刪除初始信息。最通用的拖放操作是在同一個窗口部件中執行一個"Move"操作。
拖動操作的另一個主要用途是當使用一個引用類型,比如text/uri-list,實際上被拖動的數據是文件或對象的引用。
(5)添加新的拖放類型
拖放不僅僅局限于文本和圖片,任何信息都可以被拖放。為了在應用程序之間拖放信息,兩個應用程序必須指明彼此都能接受和產生的數據格式。這個可以通過使用MIME類型來獲得。拖動的源提供一個它能產生的MIME類型列表(按從最合適的到最少合適的順序排列),并且放下的目標選擇一種它能接受的類型。例如,QTextDrag提供了"text/plain"MIME類型(普通的沒有格式的文本),還有"text/utf16"和"text /utf8"的Unicode格式的類型。QImageDrag提供了"image/*"類型,*是QImageIO支持的任何一種圖片格式,并且 QUriDrag子類提供了"text/uri-list"的支持,它是傳輸一個文件名列表(或URL)的標準格式。
為了實現一些還沒有可用QDragObject子類的信息類型的拖放,首先和最重要的步驟是查找合適的存在格式:IANA(Internet Assigned Numbers Authority)在ISI(Information Sciences Institute)提供了一個MIME媒體類型的分級列表。使用標準的MIME類型將會使你的應用程序現在及未來能更好地與其它軟件互相操作。
為了支持另外的媒體類型,從QDragObject或QStoredDrag派生類。當你需要提供多種媒體類型的支持時,從QDragObject派生類。當一個類型足夠時,就從更簡單的QStoredDrag派生類。
QDragObject的子類將會重載const char* format(int i) const和QByteArray encodedData(const char* mimetype) const成員,并且提供一套方法編碼媒體數據,提供靜態成員canDecode()和decode()解碼輸入的數據,QImageDrag的成員函數 bool canDecode(QMimeSource*) const和QByteArray decode(QMimeSource*) const在子類中需要類似的重載。
QStoredDrag的子類提供了提供一套方法編碼媒體數據,靜態成員canDecode()和decode()對進入的數據進行解碼。
(6)高級拖放
在剪貼板模式中,用戶可以剪切或復制資源信息,然后粘貼它。相似地,在拖放模式中,用戶可以拖動信息的拷貝或者拖動信息本身到一個新的位置(移動它)。拖放模式對于程序員來說都是更多的復雜性:程序直到放下(粘貼)完成才會知道用戶是想剪切還是復制。在應用程序之間拖動,這個沒有什么區別,但是在一個應用程序之內進行拖動,應用程序必須小心不要將拷貝粘貼到同一個地方。例如,在同上窗口部件中拖動文本,拖動的開始點和放下事件處理函數應象下面這樣重載:
- void MyEditor::startDrag(){
- QDragObject *d = new QTextDrag(myHighlightedText(), this);
- if ( d->drag() && d->target() != this ) cutMyHighlightedText(); //剪切選中的文本
- }
- void MyEditor::dropEvent(QDropEvent* event){
- QString text;
- if ( QTextDrag::decode(event, text) ) {
- if ( event->source() == this && event->action() == QDropEvent::Move ) { // 在同一個窗口部件時,不能使用粘貼拷貝,而應是移到到這個位置
- event->acceptAction();
- moveMyHighlightedTextTo(event->pos());
- }
- else {
- pasteTextAt(text, event->pos()); //粘貼拷貝
- }
- }
- }
一些窗口部件在數據被拖動到它們上面時需要指定"是"或"否"接收。例如,一個CAD程序也許只接收在視圖中的文本對象上放下的文本。在這種情況下,dragMoveEvent()被使用并且給定接受或者忽略拖動的區域。代碼列出如下:
- void MyWidget::dragMoveEvent(QDragMoveEvent* event){
- if ( QTextDrag::canDecode(event) ) {
- MyCadItem* item = findMyItemAt(event->pos());
- if ( item )
- event->accept();
- }
- }
(7)和其它應用程序之間的操作
在X11上,拖動使用公有的XDND協議,而Qt在Windows上使用OLE標準,Qt在Mac上使用Carbon拖動管理器。在X11 上,XDND使用MIME,所以不需要轉換。Qt的應用編程接口與平臺無關。在Windows上,識別MIME的應用程序可以通過使用MIME類型的剪貼板格式名字進行通信。一些Windows應用程序已經對它們的剪貼板格式使用MIME命名規范了。在內部,Qt有能力在專有的剪貼板格式和MIME類型之間轉換。在X11上,Qt也支持使用Motif拖放協議的拖動。
小結:QT核心編程之鼠標拖放的內容介紹完了,希望本文對你有所幫助,如果需要更多的內容,請參考編輯推薦。
【編輯推薦】