QT_滾輪縮放_座標軸
阿新 • • 發佈:2020-11-27
實現功能:滾輪縮放,滑鼠左鍵按住移動,縮放時刻度線跟著一起移動,縮放時以滑鼠的位置為中心
效果圖:
工程檔案:
mywidget.h
#ifndef MYWIDGET_H #define MYWIDGET_H #include <QWidget> #include "ui_mywidget.h" #include <QPaintEvent> #include <QtGui> QT_BEGIN_NAMESPACE namespace Ui { class MyWidget; } QT_END_NAMESPACE class MyWidget : publicQWidget { Q_OBJECT public: MyWidget(QWidget *parent = nullptr); ~MyWidget(); QPointF ObjectPtoDisplayP(QPointF objPoint); QPointF DisplayPtoObjectP(QPointF disPoint); QPointF WidgetPtoObjectP(QPointF wigPoint);//把相對於widget的座標,轉化為以畫布的座標 QPointF ObjectPtoWidgetP(QPointF objPoint);//把相對於以畫布的座標,轉化為widget的座標 QPointF ValuePtoObjectP(QPointF valPoint); QPointF ObjectPtoValueP(QPointF objPoint); //放大,以放縮的中心scale_center為準點,按照放縮比例scale_value,對點pos_before的x、y座標進行放大 QPointF scaleIn(QPointF pos_before, QPointF scale_center, double scale_value); //縮小,以放縮的中心scale_center為準點,按照放縮比例scale_value,對點pos_before的x、y座標進行縮小QPointF scaleOut(QPointF pos_before, QPointF scale_center, double scale_value); protected: void paintEvent(QPaintEvent *event); void wheelEvent(QWheelEvent *event); void mousePressEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); private: Ui::MyWidget *ui; int margin_size; // QImage image; QPointF paint_org; //widget座標系,是整體顯示區域的左上角起點 QSize paint_size_first; //畫布的大小 QSize paint_size_old; QSize paint_size_new; QPointF paint_center_old; //display座標系 QPointF paint_center_new; //display座標系 double offset_x; double offset_y; QPointF rect_center; //以畫布為座標系 QPointF rect_topl; //矩形框的左上角,display座標系 QPointF rect_bottomr; //矩形框的右下角,display座標系 double scale_value; //縮放的比例 QPointF mousepress_org; //display座標系,儲存滑鼠移動中的過程值或釋放左鍵時的位置點座標 QPointF axis_x_old; //display座標系 QPointF axis_y_old; //display座標系 double axis_scale; double offsetv_x; double offsetv_y; double pixel_per_mm; QPointF mouse_current_pos; //object座標系 }; #endif // MYWIDGET_H
main.cpp:
#include "mywidget.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); MyWidget w; w.show(); return a.exec(); }
mywidget.cpp:
#include "mywidget.h" #include "ui_mywidget.h" MyWidget::MyWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::MyWidget) { ui->setupUi(this); margin_size =35;//空白邊距 paint_org = QPointF(margin_size + 20, margin_size - 20); //確定繪畫image的左上角 paint_size_first = QSize(this->width() - margin_size * 2, this->height() - margin_size * 2);//QSize 類代表一個矩形區域的大小,寬度和高度,計算畫布的大小 paint_size_old = paint_size_first; paint_size_new = paint_size_first; paint_center_old = QPointF(paint_size_first.width() / 2, paint_size_first.height() / 2);//以畫布左上角為原點,確定畫布的中心點座標,是在display的座標 paint_center_new = paint_center_old; offset_x = 0; offset_y = paint_size_new.height(); //矩形框的中心以畫布的中心為中心,以畫布左上角為原點,確定畫矩形框的中心點座標 rect_center = QPointF(paint_size_new.width() / 2, paint_size_new.height() / 2); //15指的是矩形框跟畫布之間的間隙 rect_topl = QPointF(rect_center.rx() - (paint_size_new.height() / 2 -15 ), rect_center.ry() + (paint_size_new.height() / 2 -15 )); rect_bottomr = QPointF(rect_center.rx() + (paint_size_new.height() / 2-15 ), rect_center.ry() - (paint_size_new.height() / 2-15 )); //縮放比例初始化 scale_value = 1.05; //儲存滑鼠移動中的過程值或釋放左鍵時的位置點座標,這裡初始化 mousepress_org = QPointF(0,0); //把畫布的中心賦值給x、y軸的原點 axis_x_old = axis_y_old = QPointF(rect_center.rx(), rect_center.ry()); axis_scale = 20.0; offsetv_x = rect_center.rx(); offsetv_y = rect_center.ry(); pixel_per_mm = (double)(rect_bottomr.rx() - rect_topl.rx()) / 600; mouse_current_pos = QPointF(rect_center.rx(), rect_center.ry());//滑鼠當前位置顯示點,初始化為顯示矩形中心位置座標 //此屬性儲存是否為小部件啟用滑鼠跟蹤 //如果禁用了滑鼠跟蹤(預設設定),則當移動滑鼠時,視窗小部件僅在至少按下一個滑鼠按鈕時接收滑鼠移動事件 //如果啟用了滑鼠跟蹤,即使沒有按下任何按鈕,小部件也會接收滑鼠移動事件 this->setMouseTracking(true); this->resize(800,800); } MyWidget::~MyWidget() { delete ui; } void MyWidget::paintEvent(QPaintEvent *event) { //當視窗重新整理時,按照當前視窗的大小,重新整理paint_size_new畫布的大小 paint_size_new = QSize(this->width() - margin_size * 2, this->height() - margin_size * 2); paint_org = QPointF(margin_size + 20, margin_size - 20); //確定繪畫image的左上角 QImage image = QImage(QSize(this->width() - margin_size * 2, this->height() - margin_size * 2), QImage::Format_RGB32);//建立畫布物件 QColor backColor = qRgb(255, 255, 255);//設定為白色 image.fill(backColor);//填充畫布為白色,沒有這一步則畫布預設為黑色 //因為視窗大小改變導致畫布大小發生改變 //當畫布的大小發生改變時 if (paint_size_new != paint_size_old) { offset_x = 0; offset_y = paint_size_new.height(); paint_center_new = QPointF(paint_size_new.width() / 2, paint_size_new.height() / 2); //根據變化的畫布大小,改變矩形框的位置 rect_center = paint_center_new - paint_center_old + rect_center;//根據畫布的中心,移動矩形的中心,保持兩個中心重合 rect_topl = paint_center_new - paint_center_old + rect_topl; rect_bottomr = paint_center_new - paint_center_old + rect_bottomr; paint_size_old = paint_size_new; paint_center_old = paint_center_new; } offsetv_x = rect_center.rx(); offsetv_y = rect_center.ry(); pixel_per_mm = (double)(rect_bottomr.rx() - rect_topl.rx()) / 600; //用於繪畫畫布 QPainter painterimage(this); //用於繪畫畫布上的內容 //painter在畫布上作畫時,需要得到object的座標才行,所以當以display為座標進行輸入時,需要轉化為object才行 QPainter painter(&image); QPen pen(Qt::black); painter.setPen(pen); // painter.setRenderHint(QPainter::Antialiasing, true); QRectF rec(DisplayPtoObjectP(rect_topl), DisplayPtoObjectP(rect_bottomr));//矩形框的左上角和右下角的座標 painter.drawRect(rec);//畫矩形框 painter.drawPoint(DisplayPtoObjectP(rect_center));//畫出中心點 painterimage.drawImage(paint_org, image); //在滑鼠的當前點位置,繪畫當前點的座標數值,精度為1個小數點 painter.drawText(mouse_current_pos, "(" + QString::number(ObjectPtoValueP(mouse_current_pos).rx(), 'f', 1) + "mm, " + QString::number(ObjectPtoValueP(mouse_current_pos).ry(), 'f', 1) + "mm)"); //x 軸的主直線,畫在畫布的最下方,跟畫布重合 painter.drawLine(DisplayPtoObjectP(QPointF(0, rect_center.ry())), DisplayPtoObjectP(QPointF(image.width(), rect_center.ry()))); //Y 軸的主直線,畫在畫布的最左方,跟畫布邊緣重合,如果完全重合就看不到直線,所以豎線向左移動一點 painter.drawLine(DisplayPtoObjectP(QPointF(rect_center.rx(), 0)), DisplayPtoObjectP(QPointF(rect_center.rx(), image.height()))); //此四行是在座標軸的端點處各畫一條長刻度線,不建議新增 // painter.drawLine(ObjectPtoWidgetP(DisplayPtoObjectP(QPointF(0, 0))), ObjectPtoWidgetP(DisplayPtoObjectP(QPointF(0, -10)))); // painter.drawLine(ObjectPtoWidgetP(DisplayPtoObjectP(QPointF(0, 0))), ObjectPtoWidgetP(DisplayPtoObjectP(QPointF(-10, 0)))); // painter.drawLine(ObjectPtoWidgetP(DisplayPtoObjectP(QPointF(image.width(), 0))), ObjectPtoWidgetP(DisplayPtoObjectP(QPointF(image.width(), -10)))); // painter.drawLine(ObjectPtoWidgetP(DisplayPtoObjectP(QPointF(0, image.height()))), ObjectPtoWidgetP(DisplayPtoObjectP(QPointF(-10, image.height())))); //此四行,是始終在座標軸的端點,顯示端點的座標值,不建議新增 // painter.drawText(ObjectPtoWidgetP(DisplayPtoObjectP(QPointF(0, -20))), QString::number((double)(0 - offsetv_x) / pixel_per_mm, 'f', 1)); // painter.drawText(ObjectPtoWidgetP(DisplayPtoObjectP(QPointF(-30, 0))), QString::number((double)(0 - offsetv_y) / pixel_per_mm, 'f', 1)); // painter.drawText(ObjectPtoWidgetP(DisplayPtoObjectP(QPointF(image.width(), -20))), QString::number((double)(image.width() - offsetv_x) / pixel_per_mm, 'f', 1)); // painter.drawText(ObjectPtoWidgetP(DisplayPtoObjectP(QPointF(-30, image.height()))), QString::number((double)(image.height() - offsetv_y) / pixel_per_mm, 'f', 1)); //繪畫座標軸標題 painter.drawText(DisplayPtoObjectP(QPointF(image.width()-45, rect_center.ry()-20)), QString(tr("X[mm]"))); // painter.rotate(-90); painter.drawText(DisplayPtoObjectP(QPointF(rect_center.rx()-50, image.height()-20)), QString(tr("Y[mm]"))); // painter.rotate(90); //此處兩個判斷語句,主要保證當主視窗大小變化時,x、y軸上的0刻度位置始終保持在各自軸的中心位置 //x軸 if (rect_center.rx() > 0 && rect_center.rx() < image.width()) { painter.drawLine(DisplayPtoObjectP(QPointF(rect_center.rx(), rect_center.ry())),DisplayPtoObjectP(QPointF(rect_center.rx(), rect_center.ry()-10))); painter.drawText(DisplayPtoObjectP(QPointF(rect_center.rx(), rect_center.ry()-20)), QString::number((double)(rect_center.rx() - offsetv_x) / pixel_per_mm, 'f', 1)); } //y軸 // if (rect_center.ry() > 0 && rect_center.ry() < image.height()) // { // painter.drawLine(ObjectPtoWidgetP(DisplayPtoObjectP(QPointF(0, rect_center.ry()))), ObjectPtoWidgetP(DisplayPtoObjectP(QPointF(-10, rect_center.ry())))); // painter.drawText(ObjectPtoWidgetP(DisplayPtoObjectP(QPointF(-30, rect_center.ry()))), QString::number((double)(rect_center.ry() - offsetv_y) / pixel_per_mm, 'f', 1)); // } //保證axis_scale在(17.5至30)範圍內 if (axis_scale >= 35.0) { axis_scale = axis_scale / 2; } else if (axis_scale <= 15.0) { axis_scale = axis_scale * 2; } //x軸負半軸刻度線和座標值 axis_x_old = QPointF(rect_center.rx(),rect_center.ry()); int i = 0; while (axis_x_old.rx() > axis_scale) { axis_x_old.rx() = axis_x_old.rx() - axis_scale; i++; if (axis_x_old.rx() > 0 && axis_x_old.rx() < image.width()) { //每5次,畫一條長刻度線,並顯示刻度值 if (i % 5 == 0) { //長刻度線 painter.drawLine(DisplayPtoObjectP(QPointF(axis_x_old.rx(), axis_x_old.ry())), DisplayPtoObjectP(QPointF(axis_x_old.rx(), axis_x_old.ry()-10))); painter.drawText(DisplayPtoObjectP(QPointF(axis_x_old.rx(), axis_x_old.ry()-20)), QString::number((double)(axis_x_old.rx() - offsetv_x) / pixel_per_mm, 'f', 1)); } else { //短刻度線 painter.drawLine(DisplayPtoObjectP(QPointF(axis_x_old.rx(), rect_center.ry())), DisplayPtoObjectP(QPointF(axis_x_old.rx(), rect_center.ry()-5))); } } } //x正半軸刻度線和座標值 axis_x_old = QPointF(rect_center.rx(), rect_center.ry()); i = 0; while ((image.width() - axis_x_old.rx()) > axis_scale) { axis_x_old.rx() = axis_x_old.rx() + axis_scale; i++; if (axis_x_old.rx() > 0 && axis_x_old.rx() < image.width()) { if (i % 5 == 0) { painter.drawLine(DisplayPtoObjectP(QPointF(axis_x_old.rx(), rect_center.ry())), DisplayPtoObjectP(QPointF(axis_x_old.rx(), rect_center.ry()-10))); painter.drawText(DisplayPtoObjectP(QPointF(axis_x_old.rx(), rect_center.ry()-20)), QString::number((double)(axis_x_old.rx() - offsetv_x) / pixel_per_mm, 'f', 1)); } else { painter.drawLine(DisplayPtoObjectP(QPointF(axis_x_old.rx(), rect_center.ry())), DisplayPtoObjectP(QPointF(axis_x_old.rx(), rect_center.ry()-5))); } } } //y負半軸刻度線和座標值 axis_y_old = QPointF(rect_center.rx(), rect_center.ry()); i = 0; while (axis_y_old.ry() > axis_scale) { axis_y_old.ry() = axis_y_old.ry() - axis_scale; i++; if (axis_y_old.ry() > 0 && axis_y_old.ry() < image.height()) { if (i % 5 == 0) { painter.drawLine(DisplayPtoObjectP(QPointF(rect_center.rx(), axis_y_old.ry())), DisplayPtoObjectP(QPointF(rect_center.rx()-10, axis_y_old.ry()))); painter.drawText(DisplayPtoObjectP(QPointF(rect_center.rx()-60, axis_y_old.ry())), QString::number((double)(axis_y_old.ry() - offsetv_y) / pixel_per_mm, 'f', 1)); } else { painter.drawLine(DisplayPtoObjectP(QPointF(rect_center.rx(), axis_y_old.ry())), DisplayPtoObjectP(QPointF(rect_center.rx()-5, axis_y_old.ry()))); } } } //y正半軸刻度線和座標值 axis_y_old = QPointF(rect_center.rx(), rect_center.ry()); i = 0; while ((image.height() - axis_y_old.ry()) > axis_scale) { axis_y_old.ry() = axis_y_old.ry() + axis_scale; i++; if (axis_y_old.ry() > 0 && axis_y_old.ry() < image.height()) { if (i % 5 == 0) { painter.drawLine(DisplayPtoObjectP(QPointF(rect_center.rx(), axis_y_old.ry())), DisplayPtoObjectP(QPointF(rect_center.rx()-10, axis_y_old.ry()))); painter.drawText(DisplayPtoObjectP(QPointF(rect_center.rx()-60, axis_y_old.ry())), QString::number((double)(axis_y_old.ry() - offsetv_y) / pixel_per_mm, 'f', 1)); } else { painter.drawLine(DisplayPtoObjectP(QPointF(rect_center.rx(), axis_y_old.ry())), DisplayPtoObjectP(QPointF(rect_center.rx()-5, axis_y_old.ry()))); } } } // painter.drawLine(DisplayPtoObjectP(QPointF(rect_center.rx()+0, rect_center.ry()+0)), DisplayPtoObjectP(QPointF((100 - offsetv_y) / pixel_per_mm, (100 - offsetv_y) / pixel_per_mm))); painterimage.drawImage(paint_org, image); } void MyWidget::wheelEvent(QWheelEvent *event) { //在滾輪滾動時,把滑鼠的當前點,轉化為畫布的座標 QPointF mousepos = WidgetPtoObjectP(event->pos()); if (event->delta() > 0) { //以滑鼠的當前點為中心,按照縮放比例scale_value,進行放大 //對矩形框進行放大 rect_center = scaleIn(rect_center, ObjectPtoDisplayP(mousepos), scale_value); rect_topl = scaleIn(rect_topl, ObjectPtoDisplayP(mousepos), scale_value); rect_bottomr = scaleIn(rect_bottomr, ObjectPtoDisplayP(mousepos), scale_value); //對座標軸的刻度進行同步縮放 axis_scale = axis_scale * scale_value; } else { //縮小 rect_center = scaleOut(rect_center, ObjectPtoDisplayP(mousepos), scale_value); rect_topl = scaleOut(rect_topl, ObjectPtoDisplayP(mousepos), scale_value); rect_bottomr = scaleOut(rect_bottomr, ObjectPtoDisplayP(mousepos), scale_value); //對座標軸的刻度進行同步縮放 axis_scale = axis_scale / scale_value; } // 沒有這個重新整理,每次進行了滾輪,繪畫事件不會立即生效 repaint(); } void MyWidget::mouseMoveEvent(QMouseEvent *event) { //pos():返回滑鼠游標相對於接收事件的小部件的位置 mouse_current_pos = WidgetPtoObjectP(event->pos());//重新整理滑鼠的當前焦點 //按住左鍵時移動 if (event->buttons() & Qt::LeftButton) { QPointF mousepos_move = ObjectPtoDisplayP(WidgetPtoObjectP(event->pos())); //mousepos_move - mousepress_org是每次移動之間的差值,代表要把矩形框移動多遠 rect_center = mousepos_move - mousepress_org + rect_center; rect_topl = mousepos_move - mousepress_org + rect_topl; rect_bottomr = mousepos_move - mousepress_org + rect_bottomr; mousepress_org = mousepos_move; } repaint(); } void MyWidget::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { mousepress_org = ObjectPtoDisplayP(WidgetPtoObjectP(event->pos())); } } //把以左上角為原點的畫布座標系,轉為以左下角為原點的座標系 QPointF MyWidget::ObjectPtoDisplayP(QPointF objPoint) { return QPointF(objPoint.rx() - offset_x, -objPoint.ry() + offset_y); } QPointF MyWidget::DisplayPtoObjectP(QPointF disPoint) { return QPointF(disPoint.rx() + offset_x, -disPoint.ry() + offset_y); } //以放縮的中心scale_center為準點,按照放縮比例scale_value,對點pos_before的x、y座標進行放大 QPointF MyWidget::scaleIn(QPointF pos_before, QPointF scale_center, double scale_value) { QPointF temp; temp.rx() = (double)(pos_before.rx() - scale_center.rx()) * scale_value + scale_center.rx(); temp.ry() = (double)(pos_before.ry() - scale_center.ry()) * scale_value + scale_center.ry(); return temp; } //以放縮的中心scale_center為準點,按照放縮比例scale_value,對點pos_before的x、y座標進行縮小 QPointF MyWidget::scaleOut(QPointF pos_before, QPointF scale_center, double scale_value) { QPointF temp; temp.rx() = (double)(pos_before.rx() - scale_center.rx()) / scale_value + scale_center.rx(); temp.ry() = (double)(pos_before.ry() - scale_center.ry()) / scale_value + scale_center.ry(); return temp; } //把相對於widget的座標,轉化為以畫布的座標 QPointF MyWidget::WidgetPtoObjectP(QPointF wigPoint) { return QPointF(wigPoint - paint_org); } //把相對於以畫布的座標,轉化為widget的座標 QPointF MyWidget::ObjectPtoWidgetP(QPointF objPoint) { return QPointF(objPoint + paint_org); } QPointF MyWidget::ValuePtoObjectP(QPointF valPoint) { return DisplayPtoObjectP(QPointF(valPoint.rx() * pixel_per_mm + offsetv_x, valPoint.ry() * pixel_per_mm + offsetv_y)); } QPointF MyWidget::ObjectPtoValueP(QPointF objPoint) { return QPointF((double)(ObjectPtoDisplayP(objPoint).rx() - offsetv_x) / pixel_per_mm, (double)(ObjectPtoDisplayP(objPoint).ry() - offsetv_y) / pixel_per_mm); }