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