您的位置:首页 > 编程语言 > Qt开发

Qt触摸屏原理及实现

2015-06-04 10:56 471 查看
Qt中触摸屏实现鼠标原理:
Pointer Handling Layer

The mouse driver (represented by an instance of the
QWSMouseHandlerclass) is loaded by the server application when it startsrunning(当鼠标驱动需要运行时), using Qt's
pluginsystem.

A mouse driver(即图中的Mouse Handler, 由QWSMouseHandler类或其派生类的实例描述)receives mouse events from the device and

encapsulates each event with an instance ofthe
QWSEvent classwhich it then passes to the server.

Qt forEmbedded Linux provides ready-made(现成的) drivers for severalmouse protocols, see the

pointerhandling documentation for details. Custom mouse drivers can beimplemented by subclassing the
QWSMouseHandlerclass and creating a mouse driver plugin. The defaultimplementation of the

QMouseDriverFactoryclass will automatically detect the plugin, loading the driver intothe server application at runtime.

In addition to the generic mouse handler,
Qt forEmbedded Linux provides a calibrated mouse handler. Use theQWSCalibratedMouseHandler (嵌入式Linux Qt中的一种MouseHandler,下面的QWSTslibMouseHandler为其派生类 ) classas
the base class when the system device does not have a fixedmapping between device and screen coordinates and/or produces noisyevents, e.g. a touchscreen.

See also:
Qt forEmbedded Linux Pointer Handling and
How to CreateQt Plugins.

2. 在Qt源代码中search : tslib对上层的接口函数ts_read() /ts_read_raw()=> 仅有src/gui/embedded/qmousetslib_qws.cpp中调用, 可见,类QWSTslibMouseHandlerPrivate 采样tslib提供的触摸屏事件,并作为鼠标事件提交给上层(即图中的MouseHandler).

其代码注释:

The QWSTslibMouseHandlerclass implements a mouse driver for the UniversalTouch Screen Library, tslib.

QWSTslibMouseHandler inherits theQWSCalibratedMouseHandlerclass, providingcalibration and noise reduction functionalityin additionto
generating mouse events, for devices usingthe Universal Touch ScreenLibrary.(嵌入式LinuxQt中的一种可校验并能降噪的MouseHandler: QWSCalibratedMouseHandler, QWSTslibMouseHandler为其派生类,其对tslib的触控事件封装为鼠标事件)

To be able to compile this mouse handler, Qt forEmbedded Linux must be configured withthe -qt-mouse-tslib option, seethe

Pointer Handling documentation fordetails. In addition, the tslib headers andlibrary must be present in the
build environment. The tslib sources can be downloadedfrom http://tslib.berlios.de. Use the -Land -I options with configure toexplicitly specify the locationof the library and
itsheaders: 如 doc/src/snippets/code/src_gui_embedded_qmousetslib_qws.cpp第0段所述:

configure -L<path to tslib library> -I<path to tslib headers>

In order to use this mouse handler, tslib mustalso be correctly installed on the targetmachine. This includes providing a ts.conf configuration file andsetting the necessary environment variables,
seethe README file provided with tslib fordetails. The ts.conf file will usually contain thefollowing two lines: 如 doc/src/snippets/code/src_gui_embedded_qmousetslib_qws.cpp 第1段所述:

module_raw input

module linear

To make Qt for EmbeddedLinux explicitly choose the tslibmouse handler, set the QWS_MOUSE_PROTO environmentvariable.

3.
阅读tslib源代码中的README,了解ts.conf的配置

结合以上内容可以得到以下信息:

1.要能正确使用触摸屏鼠标必须要做到:

1.kernel正确的触摸屏驱动,测试并确认/dev/input中的输入设备,假设测试event1设备文件是否为触摸屏设备文件:

cat/dev/input/mice |hexdump 触控触摸屏将有数据在终端输出

2.目标系统上移植,配置tslib

.运行ts_calibrate产生校正文件 pointercal

.运行ts_test, 以调整 ts的etc/ts.conf

.配置Tslib的环境变量: 见 tslib的编译及QT中的使用

3.Qt使用tslib

.编译Qt时使用-qt-mouse-tslib(指定带tslib驱动的鼠标)

.目标环境中必须包含tslib的库和头文件(ts_source),同时编译Qt的configure脚本中必须指定:

configure -L<path to tslib library> -I<path to tslib headers>

.正确地配置tslib的ts.conf

.设置QT环境变量QWS_MOUSE_PROTO显示地指定鼠标事件设备

2.结合源代码Tslib鼠标的结构图:

Mouse Driver Factory : QMouseDriverFactory

QMouseDriverFactory is used to detect and instantiate the availablemouse drivers, allowing
Qt forEmbedded Linux to loadthe

preferreddriver into the server application at runtime. The

create()function returns a
QWSMouseHandlerobject representing the

mousedriver identified by a given key. The valid keys (i.e. thesupported drivers) can be retrieved using the

keys()function.

Factory的create()方法new一个对应的MouseHandler对象,即加载驱动.keys()方法返回该插件能加载的所有驱动的对应关键字字符串

Mouse DriverPlugin : TslibMouseDriver 继承自QMouseDriverPlugin (src/plugins/mousedrivers/tslib/main.cpp)

A mousedriver plugin can be created by subclassing QMouseDriverPlugin andreimplementing the pure virtual

keys()and
create()

functions. By exporting the derived class using the
Q_EXPORT_PLUGIN2()macro, The default implementation of the

QMouseDriverFactoryclass will automatically detect the plugin and loadthe driver into the server application atrun-time. 

Writing aplugin that extends Qt itself is achieved by subclassing theappropriate plugin base class, implementing a few functions,and

adding amacro. Derived plugins are stored by default insub-directories of the standard plugin directory. Qt will not findplugins if they

are notstored in the rightdirectory.(对鼠标而言,该目录为src/plugins/mousedrivers/)

插件的create()方法和keys()方法一样

Mouse Handler : QWSTslibMouseHandler(封装QWSTslibMouseHandlerPrivate.src/gui/embedded/qmousetslib_qws.cpp)

现象:点击触摸屏有反映,但是触点与鼠标图标之间总是有距离,而且距离随着触控操作变化:如,点击屏角,使触控点和鼠标位置很接近,

随后的触控操作能保持这种近距离, 但随着后继触控操作距离会变化.

问题分析:触摸屏有反映说明tslib有触控事件送往QWSTslibMouseHandler,怀疑只是送往QWSTslibMouseHandler的坐标信息未矫正

=> 直接运行tslib的配套工具 ts_calibrate,并用ts_test验证,坐标校验成功,但是Qt应用程序仍然存在触点偏移。

=> google类似问题,多为校验问题,有些文章提到QWS_MOUSE_PROTO显示指定鼠标事件来源,结合以上总结,怀疑是否使Qt默认没有使用正确的鼠标事件. Qt for Embedded Linux PointerHandling 证明了这点:
Note that
Qt forEmbedded Linux does not support auto-detection of touchpanels in which case the driver must be specified explicitly todetermine which device to use.


=> QWS_MOUSE_PROTO

Specifies the driverfor pointer handling. For example, if the current shell is bash,ksh, zsh or sh:

exportQWS_MOUSE_PROTO=<driver>[:<driverspecific options>]
<driver> areMouseMan,IntelliMouse,Microsoft,VR41xx,LinuxTP,Yopy,Tslib

andkeys identifying
custom drivers.(即此参数同对应QMouseDriverFactory或QMouseDriverPlugin的keys()返回的字符串)

<driver specificoptions> are typically a device,e.g.,/dev/mouse
for mouse devicesand/dev/tsfor touch panels.确认设备的方法: 如,测试/dev/input/mice 是否是输入设备: cat /dev/input/mice |hexdump,
测试移动鼠标应该有数据输出


Multiple mousedrivers can be specified in one go:

exportQWS_MOUSE_PROTO="<driver>[:<driverspecific options>]<driver>[:<driverspecific options>]<driver>[:<driverspecific options>]"
=> 使用cat发现目标设备的触摸屏设备文件为/etc/input/event1,所以设置exportQWS_MOUSE_PROTO=tslib:/etc/input/event1, 累计偏移故障消失

=>为同时使用USB鼠标,用cat发现/etc/input/mice为USB鼠标输入,所以设置:exportQWS_MOUSE_PROTO="tslib:/etc/input/event1interlliMouse:/etc/input/mice". 此时USB鼠标可用,但是触摸屏鼠标又出现累计偏差,怀疑和mice(mouse的复数)有关,是否mice是一个叠加输入?

应该有单独输入的设备文件.

=>用cat测试/dev/input下的设备文件,发现USB鼠标的输入文件还有/dev/input/mouse1,反过来触控触摸屏/dev/input/mice也有输出,但是比较event1与mice的数据发现不同

=>QWS_MOUSE_PROTO中针对USB鼠标和触摸屏分别使用各自独立的设备文件exportQWS_MOUSE_PROTO="tslib:/etc/input/event1interlliMouse:/etc/input/mouse1", 问题解决.

=> 结论:mice是所有鼠标设备的设备文件(USB/PS2鼠标 或 触摸屏鼠标等),其可能源自mouse1,event1, 并且其数据做了处理, mice作为默认鼠标设备文件优先使用,而tslib需要的是提供原生触摸屏数据的event1,导致触摸屏定位总有偏差


Mouse Calibration Example

Files:

qws/mousecalibration/calibration.cpp

qws/mousecalibration/calibration.h

qws/mousecalibration/scribblewidget.cpp

qws/mousecalibration/scribblewidget.h

qws/mousecalibration/main.cpp

qws/mousecalibration/mousecalibration.pro

The Mouse Calibration example demonstrates how to write a simple program using the mechanisms provided by the QWSMouseHandler class
to calibrate the mouse handler in Qt for Embedded Linux.

Calibration is the process of mapping between physical (i.e. device) coordinates and logical coordinates.

The example consists of two classes in addition to the main program:

Calibration is a dialog widget that retrieves the device coordinates.

ScribbleWidget is a minimal drawing program used to let the user test the new mouse settings.

First we will review the main program, then we will take a look at the Calibration class. The ScribbleWidget class is only a help tool in
this context, and will not be covered here.


The Main Program

The program starts by presenting a message box informing the user of what is going to happen:
int main(int argc, char **argv)
{
QApplication app(argc, argv, QApplication::GuiServer);

if (!QWSServer::mouseHandler())
qFatal("No mouse handler installed");

{
QMessageBox message;
message.setText("<p>Please press once at each of the marks "
"shown in the next screen.</p>"
"<p>This messagebox will timout after 10 seconds "
"if you are unable to close it.</p>");
QTimer::singleShot(10 * 1000, &message, SLOT(accept()));
message.exec();
}


The QMessageBox class provides a modal dialog with a range of different messages, roughly arranged along two
axes: severity and complexity. The message box has a different icon for each of the severity levels, but the icon must be specified explicitly. In our case we use the defaultQMessageBox::NoIcon value.
In addition we use the default complexity, i.e. a message box showing the given text and an OK button.

At this stage in the program, the mouse could be completely uncalibrated, making the user unable to press the OK button. For that reason we use the static QTimer::singleShot()
function to make the message box disappear after 10 seconds. The QTimer class provides repetitive and single-shot
timers: The single shot function calls the given slot after the specified interval.
Calibration cal;
cal.exec();


Next, we create an instance of the Calibration class which is a dialog widget retrieving the required sample coordinates: The dialog sequentially presents five marks for the user to press, storing
the device coordinates for the mouse press events.
{
QMessageBox message;
message.setText("<p>The next screen will let you test the calibration "
"by drawing into a widget.</p><p>This program will "
"automatically close after 20 seconds.<p>");
QTimer::singleShot(10 * 1000, &message, SLOT(accept()));
message.exec();
}

ScribbleWidget scribble;
scribble.showMaximized();
scribble.show();

app.setActiveWindow(&scribble);
QTimer::singleShot(20 * 1000, &app, SLOT(quit()));

return app.exec();
}


When the calibration dialog returns, we let the user test the new mouse settings by drawing onto a ScribbleWidget object. Since the mouse still can be uncalibrated, we continue to use the QMessageBox and QTimer classes
to inform the user about the program's progress.

An improved calibration tool would let the user choose between accepting the new calibration, reverting to the old one, and restarting the calibration.


Calibration Class Definition

The Calibration class inherits from QDialog and is
responsible for retrieving the device coordinates from the user.
class Calibration : public QDialog
{
public:
Calibration();
~Calibration();
int exec();

protected:
void paintEvent(QPaintEvent*);
void mouseReleaseEvent(QMouseEvent*);
void accept();

private:
QWSPointerCalibrationData data;
int pressCount;
};


We reimplement QDialog's exec() and accept() slots,
and QWidget's paintEvent() and mouseReleaseEvent() functions.

In addition, we declare a couple of private variables, data and pressCount, holding the Calibration object's
number of mouse press events and current calibration data. The pressCount variable is a convenience variable, while the data is a QWSPointerCalibrationData object
(storing the physical and logical coordinates) that is passed to the mouse handler. The QWSPointerCalibrationData class
is simply a container for calibration data.


Calibration Class Implementation

In the constructor we first ensure that the Calibration dialog fills up the entire screen, has focus and will receive mouse events (the latter by making the dialog modal):
Calibration::Calibration()
{
QRect desktop = QApplication::desktop()->geometry();
desktop.moveTo(QPoint(0, 0));
setGeometry(desktop);

setFocusPolicy(Qt::StrongFocus);
setFocus();
setModal(true);


Then we initialize the screenPoints array:
int width = qt_screen->deviceWidth();
int height = qt_screen->deviceHeight();

int dx = width / 10;
int dy = height / 10;

QPoint *points = data.screenPoints;
points[QWSPointerCalibrationData::TopLeft] = QPoint(dx, dy);
points[QWSPointerCalibrationData::BottomLeft] = QPoint(dx, height - dy);
points[QWSPointerCalibrationData::BottomRight] = QPoint(width - dx, height - dy);
points[QWSPointerCalibrationData::TopRight] = QPoint(width - dx, dy);
points[QWSPointerCalibrationData::Center] = QPoint(width / 2, height / 2);


In order to specify the calibration, the screenPoints array must contain the
screen coordinates for the logical positions represented by theQWSPointerCalibrationData::Location enum
(e.g. QWSPointerCalibrationData::TopLeft). Since non-linearity is expected to increase on the
edge of the screen, all points are kept 10 percent within the screen. The qt_screen pointer is a reference to the screen device. There can only be one screen device per application.
pressCount = 0;
}


Finally, we initialize the variable which keeps track of the number of mouse press events we have received.
Calibration::~Calibration()
{
}


The destructor is trivial.
int Calibration::exec()
{
QWSServer::mouseHandler()->clearCalibration();
grabMouse();
activateWindow();
int ret = QDialog::exec();
releaseMouse();
return ret;
}


The reimplementation of the QDialog::exec() slot is called from the main program.

First we clear the current calibration making the following mouse event delivered in raw device coordinates. Then we call the QWidget::grabMouse()
function to make sure no mouse events are lost, and the QWidget::activateWindow() function to make the top-level
widget containing this dialog, the active window. When the call to the QDialog::exec() base function returns, we call QWidget::releaseMouse()
to release the mouse grab before the function returns.
void Calibration::paintEvent(QPaintEvent*)
{
QPainter p(this);
p.fillRect(rect(), Qt::white);

QPoint point = data.screenPoints[pressCount];

<span class="comment" style="color:#8b00;font-style:italic">// Map to logical coordinates in case the screen is transformed</span>
QSize screenSize(qt_screen->deviceWidth(), qt_screen->deviceHeight());
point = qt_screen->mapFromDevice(point, screenSize);

p.fillRect(point.x() - 6, point.y() - 1, 13, 3, Qt::black);
p.fillRect(point.x() - 1, point.y() - 6, 3, 13, Qt::black);
}


The QWidget::paintEvent() function is reimplemented to receive the widget's paint events. A paint event is a request
to repaint all or parts of the widget. It can happen as a result of QWidget::repaint() or QWidget::update(),
or because the widget was obscured and has now been uncovered, or for many other reasons. In our reimplementation of the function we simply draw a cross at the next point the user should press.
void Calibration::mouseReleaseEvent(QMouseEvent *event)
{
<span class="comment" style="color:#8b00;font-style:italic">// Map from device coordinates in case the screen is transformed</span>
QSize screenSize(qt_screen->width(), qt_screen->height());
QPoint p = qt_screen->mapToDevice(event->pos(), screenSize);

data.devPoints[pressCount] = p;

if (++pressCount < 5)
repaint();
else
accept();
}


We then reimplement the QWidget::mouseReleaseEvent() function to receive the widget's move events, using the QMouseEvent object
passed as parameter to find the coordinates the user pressed, and update the QWSPointerCalibrationData::devPoints array.

In order to complete the mapping between logical and physical coordinates, the devPoints array
must contain the raw device coordinates for the logical positions represented by the QWSPointerCalibrationData::Location enum
(e.g. QWSPointerCalibrationData::TopLeft)

We continue by drawing the next cross, or close the dialog by calling the QDialog::accept() slot if we have collected
all the required coordinate samples.
void Calibration::accept()
{
Q_ASSERT(pressCount == 5);
QWSServer::mouseHandler()->calibrate(&data);
QDialog::accept();
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: