Qt 是目前最先进、最完整的跨平台C++开发工具。它不仅完全实现了一次编写,所有平台无差别运行,更提供了几乎所有开发过程中需要用到的工具。如今,Qt已被运用于超过70个行业、数千家企业,支持数百万设备及应用。
当您在平板电脑上使用Qt应用程序时, QTabletEvents就会生成。如果您想处理tablet事件,需要重新实现tabletEvent()事件处理程序。当用于绘图的工具(触控笔)进入并离开写字板附近时(即,当它关闭但未按下时),当工具被按下并从中释放时,当工具在写字板上移动时,以及当工具上的一个按钮被按下或释放时,都会产生事件。
QTabletEvent中可用的信息取决于所使用的设备,本实例可以处理多达三种不同绘图工具的平板电脑:触控笔、喷枪和艺术笔。对于这些事件,将包含工具的位置,平板电脑上的压力、按钮状态、垂直倾斜和水平倾斜(即设备与平板电脑垂直方向之间的角度,如果平板电脑硬件可以提供)。喷枪有指轮,这个位置也可以在平板电脑事件中找到;艺术笔提供围绕垂直于平板表面的轴旋转,因此它可以用于书法。
在这个例子中,我们实现了一个绘图程序。您可以用触控笔在平板电脑上画画,就像在纸上用铅笔一样。当用喷枪画画时,会得到一种虚拟的油漆喷雾,手指轮用来改变喷雾的密度。当您用美术笔绘制时,会得到一条线,它的宽度和端点角度取决于笔的旋转,压力和倾斜也可以被分配来改变颜色的alpha和饱和度值以及笔画的宽度。
本示例包括以下内容:
- MainWindow类继承QMainWindow,创建菜单,并连接它们的槽和信号。
- TabletCanvas类继承了QWidget并接收tablet事件,它使用事件将其绘制到屏幕外的像素图上,然后渲染它。
- TabletApplication类继承了QApplication,这个类处理平板电脑接近事件。
- main()函数创建一个主窗口,并将其显示为顶层窗口。
MainWindow类定义
MainWindow创建一个TabletCanvas,并将其设置为中心小部件。
cpp
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(TabletCanvas *canvas);
private slots:
void setBrushColor();
void setAlphaValuator(QAction *action);
void setLineWidthValuator(QAction *action);
void setSaturationValuator(QAction *action);
void setEventCompression(bool compress);
bool save();
void load();
void clear();
void about();
private:
void createMenus();
TabletCanvas *m_canvas;
QColorDialog *m_colorDialog = nullptr;
};
createMenus()用操作设置菜单,我们有一个QActionGroup用于分别改变alpha通道、颜色饱和度和线宽的操作。操作组连接到setAlphaValuator()、setSaturationValuator()和setLineWidthValuator()插槽,它们调用TabletCanvas中的函数。
MainWindow类实现
我们从构造函数MainWindow()开始:
cpp
MainWindow::MainWindow(TabletCanvas *canvas)
: m_canvas(canvas)
{
createMenus();
setWindowTitle(tr("Tablet Example"));
setCentralWidget(m_canvas);
QCoreApplication::setAttribute(Qt::AA_CompressHighFrequencyEvents);
}
在构造函数中,我们调用createMenus()来创建所有的操作和菜单,并将画布设置为中心小部件。
cpp
MainWindow::MainWindow(TabletCanvas *canvas)
: m_canvas(canvas)
{
createMenus();
setWindowTitle(tr("Tablet Example"));
setCentralWidget(m_canvas);
QCoreApplication::setAttribute(Qt::AA_CompressHighFrequencyEvents);
}
在createMenus()的开头,我们填充了File菜单。使用在Qt 5.6中引入的addAction()的重载来创建带有快捷方式(也可以是图标)的菜单项,将其添加到菜单中,并将其连接到插槽,所有这些都只需要一行代码,使用QKeySequence为这些常用菜单项获取特定于平台的标准快捷键。
我们还填充了画笔菜单,更改笔刷的命令通常没有标准的快捷方式,因此使用tr()来翻译快捷方式以及应用程序的语言翻译。
现在,我们将在Tablet菜单的子菜单中创建一组互斥操作,用于选择每个QTabletEvent QMenu *alphaChannelMenu = tabletMenu->addMenu(tr("&Alpha Channel"));
cpp
QAction *alphaChannelPressureAction = alphaChannelMenu->addAction(tr("&Pressure"));
alphaChannelPressureAction->setData(TabletCanvas::PressureValuator);
alphaChannelPressureAction->setCheckable(true);
QAction *alphaChannelTangentialPressureAction = alphaChannelMenu->addAction(tr("T&angential Pressure"));
alphaChannelTangentialPressureAction->setData(TabletCanvas::TangentialPressureValuator);
alphaChannelTangentialPressureAction->setCheckable(true);
alphaChannelTangentialPressureAction->setChecked(true);
QAction *alphaChannelTiltAction = alphaChannelMenu->addAction(tr("&Tilt"));
alphaChannelTiltAction->setData(TabletCanvas::TiltValuator);
alphaChannelTiltAction->setCheckable(true);
QAction *noAlphaChannelAction = alphaChannelMenu->addAction(tr("No Alpha Channel"));
noAlphaChannelAction->setData(TabletCanvas::NoValuator);
noAlphaChannelAction->setCheckable(true);
QActionGroup *alphaChannelGroup = new QActionGroup(this);
alphaChannelGroup->addAction(alphaChannelPressureAction);
alphaChannelGroup->addAction(alphaChannelTangentialPressureAction);
alphaChannelGroup->addAction(alphaChannelTiltAction);
alphaChannelGroup->addAction(noAlphaChannelAction);
connect(alphaChannelGroup, &QActionGroup::triggered,
this, &MainWindow::setAlphaValuator);
我们希望用户能够选择绘画颜色的alpha分量是否应该通过平板压力、倾斜或喷枪工具上拇指轮的位置来调节。对每个选择都有一个操作,另外一个操作是选择不改变alpha,也就是说,保持颜色不透明。我们让操作可检查,然后alphaChannelGroup将确保在任何时候只检查一个操作。当一个操作被检查时,triggered()信号从组中发出,因此我们将其连接到MainWindow::setAlphaValuator(),它将需要知道从现在开始关注QTabletEvent的哪个属性(估值器),因此使用QAction::data属性来传递此信息。(为了实现这一点,枚举Valuator必须是一个注册元类型,以便它可以插入到QVariant中。这是通过tabletcanvas.h中的Q_ENUM声明完成的。)
下面是setAlphaValuator()的实现:
cpp
void MainWindow::setAlphaValuator(QAction *action)
{
m_canvas->setAlphaChannelValuator(qvariant_cast<TabletCanvas::Valuator>(action->data()));
}
它只需要从QAction::data()中检索Valuator枚举,并将其传递给tablecanvas::setAlphaChannelValuator()。如果不使用data属性,则需要比较QAction指针本身,例如在switch语句中。但这需要在类变量中保留指向每个QAction的指针,以便进行比较。
下面是setBrushColor()的实现:
cpp
void MainWindow::setBrushColor()
{
if (!m_colorDialog) {
m_colorDialog = new QColorDialog(this);
m_colorDialog->setModal(false);
m_colorDialog->setCurrentColor(m_canvas->color());
connect(m_colorDialog, &QColorDialog::colorSelected, m_canvas, &TabletCanvas::setColor);
}
m_colorDialog->setVisible(true);
}
我们在用户第一次选择画笔颜色时延迟初始化QColorDialog,从菜单或通过操作快捷方式。当对话框打开时,每次用户选择不同的颜色时,TabletCanvas::setColor()将被调用来更改绘图颜色。因为它是一个非模态对话框,所以用户可以自由地让颜色对话框保持打开状态,以便能够方便而频繁地更改颜色,或者关闭它并稍后重新打开它。
下面是save()的实现:
cpp
bool MainWindow::save()
{
QString path = QDir::currentPath() + "/untitled.png";
QString fileName = QFileDialog::getSaveFileName(this, tr("Save Picture"),
path);
bool success = m_canvas->saveImage(fileName);
if (!success)
QMessageBox::information(this, "Error Saving Picture",
"Could not save the image");
return success;
}
我们使用QFileDialog让用户选择一个文件来保存绘图,然后调用TabletCanvas::saveImage()将其实际写入文件。
下面是load()的实现:
cpp
void MainWindow::load()
{
QString fileName = QFileDialog::getOpenFileName(this, tr("Open Picture"),
QDir::currentPath());
if (!m_canvas->loadImage(fileName))
QMessageBox::information(this, "Error Opening Picture",
"Could not open picture");
}
我们让用户选择要用QFileDialog打开的图像文件,然后要求画布使用loadImage()加载图像。
下面是about()的实现:
cpp
void MainWindow::about()
{
QMessageBox::about(this, tr("About Tablet Example"),
tr("This example shows how to use a graphics drawing tablet in Qt."));
}
我们将显示一个消息框,其中包含对示例的简短描述。
Qt Widget组件推荐
- QtitanRibbon - Ribbon UI组件:是一款遵循Microsoft Ribbon UI Paradigm for Qt技术的Ribbon UI组件,QtitanRibbon致力于为Windows、Linux和Mac OS X提供功能完整的Ribbon组件。
- QtitanChart - Qt类图表组件:是一个C ++库,代表一组控件,这些控件使您可以快速地为应用程序提供漂亮而丰富的图表。
- QtitanDataGrid - Qt网格组件:提供了一套完整的标准 QTableView 函数和传统组件无法实现的独特功能。使您能够将不同来源的各类数据加载到一个快速、灵活且功能强大的可编辑网格中,支持排序、分组、报告、创建带状列、拖放按钮和许多其他方便的功能。
- QtitanDocking:允许您像 Visual Studio 一样为您的伟大应用程序配备可停靠面板和可停靠工具栏。黑色、白色、蓝色调色板完全支持 Visual Studio 2019 主题!