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的窗口部件在图形用户界面中按用户的习惯的方式来处理键盘焦点。基本出发点是用户的击键能定向到屏幕上窗口中的任何一个,和在窗口中任何一个部件中。当用户按下一个键,他们期望键盘焦点能够到达正确的位置,并且软件必须尽量满足这种希望。系统必须确定击键定位在哪一个应用程序、应用程序中的哪一个窗口和窗口中的哪一个窗口部件。
焦点移动的方式
把焦点定位特殊的窗口部件的习惯方式有:
(1)用户按下Tab键(或者Shift键+Tab键)(或者有时是Enter键)。
(2)用户点击一个窗口部件。
(3)用户按下一个键盘快捷键。
(4)用户使用鼠标滚轮。
(5)用户移动焦点到一个窗口,并且应用程序必须决定窗口中的哪个窗口部件应该得到焦点。
这些移动机制的每个都是不同的,并且不同类型的窗口部件只能接收它们中的一些方式的焦点。下面我们将按次序介绍它们。
1. Tab或者Shift+Tab.
按Tab键是到目前为止用键盘移动焦点的最通用的方法。有时在输入数据的应用程序中Enter键和Tab键的作用是一样的。我们暂时忽略这一点。
所有窗口系统中的有关焦点的最通用使用方法是:按Tab键移动键盘焦点到每个窗口的窗口部件循环列表中的下一个窗口部件。Tab键按照循环列表的一个方向移动焦点,Shift键+Tab键按另一个方向移动焦点。按Tab键从一个窗口部件到下一个窗口部件移动焦点的次序叫做Tab键次序。
在Qt中,窗口部件循环列表存放在QFocusData类中。每个窗口有一个QFocusData对象,并且当选择合适的QWidget::FocusPolicy焦点策略的QWidget::setFocusPolicy()被调用的时候,窗口部件自动把它们自己追加到列表的末尾。你可以使用QWidget::setTabOrder()来自定义Tab键控制次序。如果你没有定义这个次序,那么Tab键会按照窗口部件构造的顺序移动焦点。Qt designer工具提供了一个可视化的改变Tab键控制次序的方法。
因为按Tab键是如此的常用,大多数含有焦点的窗口部件应该支持Tab焦点。主要例外情况是几乎没用到的窗口部件,并且在窗口部件上有一些移动焦点的键盘快捷键或者错误处理。
2. 用户点击一个窗口部件。
在使用鼠标或者其它指针设备的计算机中,用鼠标点击一个窗口部件是一种比按Tab键更常用的方法。
当鼠标点击把焦点移到一个窗口部件时,对于编辑器窗口部件,它也会移动文本光标(窗口部件的内部焦点)到鼠标被点击的地点。
鼠标点击移动焦点是大多数窗口部件必须支持的,有时窗口部件需要避免鼠标点击移动焦点。例如:在一个字处理程序中,当用户点击"B"(粗体)工具按钮,键盘焦点应该保留在原来的位置。在Qt中,只有QWidget::setFocusPolicy()函数影响点击焦点。
3. 用户按下一个键盘快捷键。
使用键盘快捷键来移动焦点不是很常用。这种情况可能会隐含地发生在打开的模式对话框中,但是也会显式地发生在使用焦点加速器中,例如在QLabel::setBuddy()、QGroupBox和QTabBar提供的加速器中。
用户想让焦点跳到的窗口部件都应支持快捷键焦点。例如:一个Tab对话框为它的每一个页提供键盘快捷键,所以用户可以按下比如Alt+P来跳到打印页面。但只能有少量的快捷键,并且为命令提供键盘快捷键也很重要,如:在标准快捷键列表中,Alt+P也可以用来粘贴、播放或打印。
4. 用户使用鼠标滚轮。
在Microsoft Windows上,鼠标滚轮的用法是一直由有键盘焦点的窗口部件处理。在Mac OS X和X11上,它由获得其它鼠标事件的窗口部件处理。
Qt处理这种平台差异的方法是当滚轮被使用时,让窗口部件移动键盘焦点。每个窗口部件上有合适的焦点策略,应用程序可以在Windows、Mac OS X和X11上按照习惯正确地处理焦点。
5. 用户移动焦点到这个窗口。
在这种情况下,应用程序必须决定窗口中的哪一个窗口部件接收焦点。Qt自动实现这样的做法:如果焦点以前在这个窗口中,那么窗口中有焦点的最后一个窗口部件应该重新获得焦点。如果以前焦点就从来没有来到过这个窗口,并且你知道焦点应该从哪里开始,就在你调用QWidget::show()显示它之前,在应该接收焦点的窗口部件上调用QWidget::setFocus()。如果你不知道,Qt会选择一个合适的窗口部件。