#include "mainwindow.h" #include "ui_mainwindow.h" #include #include #include #include #ifdef Q_OS_WIN32 #include #include #include #include "wintab/WINTAB.H" #include "wintab/Utils.h" #include "Dbt.h" #include "devguid.h" #endif #include "signpenpainter.h" //#include using namespace std; //inline long long vk_timestamp() //{ // return chrono::duration_cast(chrono::high_resolution_clock::now().time_since_epoch()).count(); //} MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); m_pImage = nullptr; m_pPainter = nullptr;//后台绘图器 m_wintab_handle = 0; m_mouse_pressed = false; isUsedMouse = false; #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) isUsedInk = false; ui->checkBox_ink->setVisible(false); #else ui->checkBox_ink->setVisible(false); #endif ui->plainTextEdit->setVisible(true); ResetBufferImage(); //动态加载Wintab库 if(!LoadWintab()) { QMessageBox::warning(nullptr,"Load Wintab","加载Wintab32.dll失败"); } else { gpWTInfoW(0, 0, NULL); // AXIS axis{0}; AXIS axis; // //获取x范围 // gpWTInfoW(WTI_DEVICES,DVC_X,&axis); // m_tablet_ctx.max_x = axis.axMax; // //获取y范围 // memset(&axis,0,sizeof(AXIS)); // gpWTInfoW(WTI_DEVICES,DVC_Y,&axis); // m_tablet_ctx.max_y = axis.axMax; //获取压力范围 memset(&axis,0,sizeof(AXIS)); gpWTInfoW(WTI_DEVICES,DVC_NPRESSURE,&axis); m_tablet_ctx.max_p = axis.axMax; // LOGCONTEXTW wintab_ctx{}; LOGCONTEXTW wintab_ctx; gpWTInfoW(WTI_DEFCONTEXT, 0, &wintab_ctx); //关心的数据项x y 压力 wintab_ctx.lcPktData = PK_X |PK_Y |PK_NORMAL_PRESSURE; wintab_ctx.lcOptions = CXO_MESSAGES | CXO_CSRMESSAGES; //设置Wintab的输出坐标范围 = 数位板设备的逻辑范围 //默认情况下lcOutExtX lcOutExtY 为系统桌面大小 //wintab_ctx.lcOutExtX = wintab_ctx.lcInExtX; // wintab_ctx.lcOutExtY =wintab_ctx.lcInExtY*-1; wintab_ctx.lcOutExtY *=-1; //Wintab返回的系统桌面区域,wintab_ctx.lcSysExtX wintab_ctx.lcSysExtY 受系统显示缩放影响 //同时受调用进程的DPI感知特性影响 m_tablet_ctx.sys_x = wintab_ctx.lcSysOrgX; m_tablet_ctx.sys_y = wintab_ctx.lcSysOrgY; m_tablet_ctx.sys_w = wintab_ctx.lcSysExtX; m_tablet_ctx.sys_h = wintab_ctx.lcSysExtY; //lcInExtX lcInExtY 为设备的输入最大范围 m_tablet_ctx.max_x = wintab_ctx.lcOutExtX; m_tablet_ctx.max_y = qAbs(wintab_ctx.lcOutExtY); HWND hwnd = (HWND)this->winId(); m_wintab_handle = (unsigned int)gpWTOpenW((HWND)this->winId(),&wintab_ctx,TRUE); } m_SignPenpainter = new SignPenPainter; m_SignPenpainter->SetPainterRect(this->rect()); penRadius = 1.7; connect(m_SignPenpainter,SIGNAL(NeedsDisplay(QRect)),this,SLOT(NeedToUpdate(QRect))); // connect(m_SignPenpainter,SIGNAL(log(QString)),this,SLOT(log(QString))); } MainWindow::~MainWindow() { delete ui; } #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, long *result) { if(QString(eventType)=="windows_generic_MSG") { PMSG msg = static_cast(message); switch(msg->message) { case WT_CTXOPEN: { qDebug()<<"Tablet wintab open."<lParam; DWORD num = msg->wParam; if(gpWTPacket(hctx,num,buf)) { unsigned x = *(unsigned*)&buf[0]; unsigned y = *(unsigned*)&buf[4]; unsigned p = *(unsigned*)&buf[8]; // qDebug()<<"x:"<wParam); //获取系统指针设备类型 POINTER_INPUT_TYPE pointerType = PT_POINTER; if(GetPointerType(pointerId,&pointerType)) { //判断当前系统指针是否是为笔设备 if(pointerType==PT_PEN) { POINTER_PEN_INFO ppi{}; //获取与数字笔相关的信息 if(GetPointerPenInfo(pointerId,&ppi)) { //获取桌面坐标和笔压力 int x = GET_X_LPARAM(msg->lParam); int y = GET_Y_LPARAM(msg->lParam); int pressure = ppi.pressure*8; //统一转化为8192级压力 \ //通常绘图软件会使用Wintab/Windows Ink其中一种进行绘图,取决于系统环境支持和绘图软件设计 //Windows Ink的返回坐标与鼠标的全局坐标一致,不关心屏幕摆放位置,只要光标能到 //**注意:手写板/手写屏在未安装驱动的情况下,光标默认到不了扩展屏幕,通常是由驱动控制到达扩展屏幕或通过系统设置映射到扩展屏。 DrawSign_Ink(x,y,pressure); //qDebug()<<"ink x:"<(message); switch(msg->message) { case WT_CTXOPEN: { qDebug()<<"Tablet wintab open."<lParam; DWORD num = msg->wParam; if(gpWTPacket(hctx,num,buf)) { unsigned x = *(unsigned*)&buf[0]; unsigned y = *(unsigned*)&buf[4]; unsigned p = *(unsigned*)&buf[8]; // qDebug()<<"x:"<wParam); // //获取系统指针设备类型 // POINTER_INPUT_TYPE pointerType = PT_POINTER; // if(GetPointerType(pointerId,&pointerType)) // { // //判断当前系统指针是否是为笔设备 // if(pointerType==PT_PEN) // { // POINTER_PEN_INFO ppi{}; // //获取与数字笔相关的信息 // if(GetPointerPenInfo(pointerId,&ppi)) // { // //获取桌面坐标和笔压力 // int x = GET_X_LPARAM(msg->lParam); // int y = GET_Y_LPARAM(msg->lParam); // int pressure = ppi.pressure*8; //统一转化为8192级压力 // \ // //通常绘图软件会使用Wintab/Windows Ink其中一种进行绘图,取决于系统环境支持和绘图软件设计 // //Windows Ink的返回坐标与鼠标的全局坐标一致,不关心屏幕摆放位置,只要光标能到 // //**注意:手写板/手写屏在未安装驱动的情况下,光标默认到不了扩展屏幕,通常是由驱动控制到达扩展屏幕或通过系统设置映射到扩展屏。 // // DrawSign_Ink(x,y,pressure); // //qDebug()<<"ink x:"<ClearDraw(); ui->plainTextEdit->clear(); update(); } void MainWindow::NeedToUpdate(QRect rect) { // this->update(QRect(0,0,120,120)); this->update(rect); } void MainWindow::paintEvent(QPaintEvent *event) { // QPainter painter(this); // painter.setRenderHints(QPainter::SmoothPixmapTransform // |QPainter::TextAntialiasing |QPainter::HighQualityAntialiasing); // if(m_pImage) // painter.drawImage(rect(),*m_pImage,m_pImage->rect()); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing, true); m_SignPenpainter->DrawRect(&painter,this->rect()); QWidget::paintEvent(event); } void MainWindow::resizeEvent(QResizeEvent *event) { ResetBufferImage(); QMainWindow::resizeEvent(event); m_SignPenpainter->SetPainterRect(this->rect()); m_SignPenpainter->SetPenType(1); } void MainWindow::mousePressEvent(QMouseEvent *event) { // qDebug() << __FUNCTION__ << event->pos(); if(!isUsedMouse) return; QPoint pt1 = event->pos(); QPoint p2 = mapToGlobal(pt1); m_mouse_pressed = false; m_SignPenpainter->SetStressSwitch(false); m_SignPenpainter->MousePressed(event->pos(),penRadius * 1.3,10,10); } void MainWindow::mouseMoveEvent(QMouseEvent *event) { // qDebug() << __FUNCTION__ << event->pos(); if(!isUsedMouse) return; QString msg = QString("use ink. pos: %1 %2").arg(event->pos().x()).arg(event->pos().y()); ui->plainTextEdit->appendPlainText(msg); ui->plainTextEdit->appendPlainText("use mouse."); m_SignPenpainter->SetStressSwitch(false); m_SignPenpainter->MouseMoved(event->pos(),penRadius * 1.3,10,10); } void MainWindow::mouseReleaseEvent(QMouseEvent *event) { // qDebug() << __FUNCTION__ << event->pos(); if(!isUsedMouse) return; m_mouse_pressed = true; m_SignPenpainter->MouseReleased(event->pos(),penRadius); m_SignPenpainter->SetStressSwitch(false); } void MainWindow::ResetBufferImage() { int w = width(); int h = height(); if(w<0 || h<0) return; if(m_pPainter!=nullptr) { delete m_pPainter; } if(m_pImage!=nullptr) { delete m_pImage; } //重新创建新的后台缓冲图像对象 #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) m_pImage = new QImage(w,h,QImage::Format_RGBA8888); #else m_pImage = new QImage(w,h,QImage::Format_ARGB32_Premultiplied); #endif m_pImage->fill(Qt::transparent); //重新创建新的绘图器 m_pPainter = new QPainter(m_pImage); m_pPainter->setRenderHints(QPainter::SmoothPixmapTransform |QPainter::TextAntialiasing |QPainter::HighQualityAntialiasing); } void MainWindow::DrawSign_Base(int x, int y, int p,bool is_ink) { if(isUsedMouse) return; if(m_pImage==nullptr ) return; if(m_tablet_ctx.max_x==0 || m_tablet_ctx.sys_w==0) return ; static QPoint last_pt(0,0); static unsigned last_pressure = 0; static bool down = false; //接触时第一个点只保存不画 if(last_pressure==0 && p>0) { down = true; last_pressure = p; last_pt = QPoint(x,y); MousePressed(last_pt,p,m_tablet_ctx.max_p,true); return; } //离开时 if(last_pressure>0 && p==0) { last_pressure = 0; MouseReleased(last_pt); //抬笔时强制更新 update(); down = false; return; } if(p==0) { return; } int ps = 6; //笔宽 int pw = (int)(ps*(p*1.0/m_tablet_ctx.max_p)+0.5); QPen pen(is_ink?Qt::blue:Qt::red,pw,Qt::SolidLine); pen.setCapStyle(Qt::RoundCap); m_pPainter->setPen(pen); QPoint cur_pt(x,y); // m_pPainter->drawLine(last_pt, cur_pt); MouseMoved(cur_pt,p,m_tablet_ctx.max_p,true); //todo 刷新机制 // static long long t1 = vk_timestamp(); // long long t2 = vk_timestamp(); // if(t2-t1>33) // { // //FPS=30 // update(); // t1 = t2; // } update(); last_pressure = p; //保存当前点 last_pt = cur_pt; } void MainWindow::DrawSign_Wintab(const TabletDataPacket &pkt) { if(isUsedMouse) return; qDebug() << __FUNCTION__ << "wintab."; ui->plainTextEdit->appendPlainText("wintab."); //计算Wintab坐标--->全局桌面屏幕坐标 //当扩展屏幕摆放在左边时,系统起始坐标为负,加上系统原始点得到正确的桌面坐标dx dy double dx = (pkt.x*1.0/m_tablet_ctx.max_x)*m_tablet_ctx.sys_w+m_tablet_ctx.sys_x; double dy = (pkt.y*1.0/m_tablet_ctx.max_y)*m_tablet_ctx.sys_h+m_tablet_ctx.sys_y; QPoint wpt = mapFromGlobal(QPoint(dx,dy)); DrawSign_Base(wpt.x(),wpt.y(),pkt.p,false); qDebug()<< __FUNCTION__ << "pkt.x:"<plainTextEdit->appendPlainText(msg); QPoint pt = mapFromGlobal(QPoint(ink_x,ink_y)); DrawSign_Base(pt.x(),pt.y(),ink_p,true); } void MainWindow::MousePressed(QPointF currentPoint, double force, double maxForce,bool isStressSwitch) { m_SignPenpainter->SetStressSwitch(isStressSwitch); m_SignPenpainter->MousePressed(currentPoint,penRadius * 1.3,force,maxForce); } void MainWindow::MouseMoved(QPointF currentPoint, double force, double maxForce,bool isStressSwitch) { m_SignPenpainter->SetStressSwitch(isStressSwitch); m_SignPenpainter->MouseMoved(currentPoint,penRadius * 1.3,force,maxForce); } void MainWindow::MouseReleased(QPointF currentPoint) { m_SignPenpainter->MouseReleased(currentPoint,penRadius); m_SignPenpainter->SetStressSwitch(false); } void MainWindow::on_checkBox_stateChanged(int arg1) { if(ui->checkBox->isChecked()) isUsedMouse = true; else isUsedMouse = false; #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) isUsedInk = ui->checkBox_ink->isChecked(); #endif }