1、 头文件(.h)
1.1 boardlayer.h
#ifndef BOARDLAYER_H
#define BOARDLAYER_H
#include "qk2dview.h"
#include <QImage>
class BoardLayer : public Qk2dViewLayer
{
public:
BoardLayer();
const QImage& image() const { return m_image; }
QTransform imageToWorld() const { return m_imgToWorld; }
QTransform worldToImage() const { return m_worldToImg; }
private:
void draw_ex(QPainter &painter) override;
void calcTransform();
private:
QImage m_image;
QPointF m_origin;
double m_pixelSize;
QRectF m_worldRect; // image rect in world coordinate
QTransform m_imgToWorld; // convert image coordinate to world coordinate
QTransform m_worldToImg; // convert world coordinate to image coordinate
};
#endif // BOARDLAYER_H
1.2 component.h
#ifndef COMPONENT_H
#define COMPONENT_H
#include <QPointF>
#include <QSizeF>
#include <vector>
struct Component
{
Component(const QPointF ¢er, const QSizeF &size, float angle = 0)
: m_pos(center)
, m_size(size)
, m_angle(angle)
, m_unitVecX()
, m_unitVecY()
, m_vertex()
, m_selected(false)
{
calcVertex();
}
void calcVertex();
bool contains(const QPointF &point);
QPointF m_pos;
QSizeF m_size;
float m_angle;
QPointF m_unitVecX;
QPointF m_unitVecY;
QPointF m_vertex[8];
bool m_selected;
};
extern std::vector<Component> g_m_components;
#endif // COMPONENT_H
1.3 componentlayer.h
#ifndef COMPONENTLAYER_H
#define COMPONENTLAYER_H
#include "qk2dview.h"
#include "component.h"
class ComponentLayer : public Qk2dViewLayer
{
public:
ComponentLayer();
void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mouseDoubleClickEvent(QMouseEvent *event) override;
private:
void draw_ex(QPainter &painter) override;
private:
Component* m_component;
int m_hitVertex;
};
#endif // COMPONENTLAYER_H
1.4 qk2dview.h
#ifndef QK2DVIEW_H
#define QK2DVIEW_H
#include <QWidget>
#include <QPainter>
#include <QTransform>
#include <QMouseEvent>
#include <memory>
#include <array>
class Qk2dViewLayer;
class Qk2dView : public QWidget
{
Q_OBJECT
public:
enum Layer
{
BoardLayer = 0,
RecipeLayer,
ComponentLayer,
AlgorithmLayer,
ResultLayer,
};
explicit Qk2dView(QWidget *parent = nullptr);
void setRoiRect(const QRectF &roi);
void setDrawer(Layer layer, std::shared_ptr<Qk2dViewLayer> drawer);
std::shared_ptr<Qk2dViewLayer> getDrawer(Layer layer);
void setDrawerVisiable(Layer layer, bool visiable);
const QTransform& winToWorld() const { return m_winToWorld; }
const QTransform& worldToWin() const { return m_worldToWin; }
protected:
void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mouseDoubleClickEvent(QMouseEvent *event) override;
void paintEvent(QPaintEvent *event) override;
void wheelEvent(QWheelEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
private:
void calcTransform();
private:
static const int m_numOfLayers = ResultLayer+1;
std::array<std::shared_ptr<Qk2dViewLayer>, m_numOfLayers> m_layers;
QRectF m_roiRect; // visiable area in world coordinate
QTransform m_winToWorld;
QTransform m_worldToWin;
bool m_isDragging;
QPointF m_dragStartPos;
};
class Qk2dViewLayer
{
public:
Qk2dViewLayer();
void setView(Qk2dView *view);
void setVisiable(bool visiable);
bool visiable() const;
void draw(QPainter &painter);
virtual void mousePressEvent(QMouseEvent */*event*/) {}
virtual void mouseReleaseEvent(QMouseEvent */*event*/) {}
virtual void mouseMoveEvent(QMouseEvent */*event*/) {}
virtual void mouseDoubleClickEvent(QMouseEvent */*event*/) {}
protected:
virtual void draw_ex(QPainter &painter) = 0;
Qk2dView *m_view;
bool m_visiable;
};
#endif // QK2DVIEW_H
1.5 recipelayer.h
#ifndef RECIPELAYER_H
#define RECIPELAYER_H
#include "qk2dview.h"
class RecipeLayer : public Qk2dViewLayer
{
public:
RecipeLayer();
void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
private:
void draw_ex(QPainter &painter) override;
private:
bool m_selecting;
QPointF m_selectStartPos;
QPointF m_selectEndPos;
};
#endif // RECIPELAYER_H
1.6 resultlayer.h
#ifndef RESULTLAYER_H
#define RESULTLAYER_H
#include "qk2dview.h"
class ResultLayer : public Qk2dViewLayer
{
public:
ResultLayer();
void setText(const QString& text, bool status);
private:
void draw_ex(QPainter &painter) override;
private:
QString m_text;
bool m_status;
};
#endif // RESULTLAYER_H
1.7 mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "qk2dview.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
Qk2dView m_view;
};
#endif // MAINWINDOW_H
2、 .cpp文件
2.1 boardlayer.cpp
#include "boardlayer.h"
#include <QPointF>
#include <QPainter>
#include <QDebug>
BoardLayer::BoardLayer()
: Qk2dViewLayer()
// , m_image("D:/3d_images/full_board_1/origin.jpg")
, m_image("D:/image/N.jpg")
, m_origin(650, 50) // origin at (650, 50)
, m_pixelSize(0.03) // 30 um / pixel
, m_worldRect()
, m_imgToWorld()
, m_worldToImg()
{
calcTransform();
}
void BoardLayer::draw_ex(QPainter &painter)
{
QRectF rect = m_view->worldToWin().mapRect(m_worldRect);
painter.drawImage(rect, m_image);
}
void BoardLayer::calcTransform()
{
double trans_x = m_origin.x() - m_image.width()*m_pixelSize;
double trans_y = m_origin.y() + m_image.height()*m_pixelSize;
m_imgToWorld = QTransform::fromScale(m_pixelSize, -m_pixelSize);
m_imgToWorld *= QTransform::fromTranslate(trans_x, trans_y);
m_worldToImg = m_imgToWorld.inverted();
m_worldRect = m_imgToWorld.mapRect(m_image.rect()).normalized();
}
2.2 component.cpp
#include "component.h"
const static double PI = 3.1415926;
std::vector<Component> g_m_components = {
Component(QPointF(393.5, 198), QSizeF(33, 33), 45),
Component(QPointF(562.5, 198), QSizeF(33, 33)),
Component(QPointF(398.5, 140), QSizeF(23, 23), 120),
Component(QPointF(567, 140), QSizeF(23, 23))
};
void Component::calcVertex()
{
double radAngle = m_angle * PI / 180;
m_unitVecX = QPointF(cos(radAngle), sin(radAngle));
m_unitVecY = QPointF(-sin(radAngle), cos(radAngle));
QPointF vecx = m_unitVecX*m_size.width()/2;
QPointF vecY = m_unitVecY*m_size.height()/2;
m_vertex[0] = m_pos - vecx - vecY;
m_vertex[1] = m_pos - vecY;
m_vertex[2] = m_pos + vecx - vecY;
m_vertex[3] = m_pos + vecx;
m_vertex[4] = m_pos + vecx + vecY;
m_vertex[5] = m_pos + vecY;
m_vertex[6] = m_pos - vecx + vecY;
m_vertex[7] = m_pos - vecx;
}
bool Component::contains(const QPointF &point)
{
bool result(false);
int i = 0, j = 3;
for(; i < 4; j = i++)
{
if(((m_vertex[2*i].y()>point.y()) != (m_vertex[2*j].y()>point.y())) &&
(point.x() < (m_vertex[2*j].x()-m_vertex[2*i].x()) * (point.y()-m_vertex[2*i].y())
/ (m_vertex[2*j].y()-m_vertex[2*i].y()) + m_vertex[2*i].x()))
{
result = !result;
}
}
return result;
}
2.3 componentlayer.cpp
#include "componentlayer.h"
ComponentLayer::ComponentLayer()
: m_component()
, m_hitVertex(-1)
{
}
void ComponentLayer::mousePressEvent(QMouseEvent *event)
{
if(m_component)
{
event->accept();
QPointF pos = m_view->winToWorld().map(event->localPos());
// control points hit test
m_hitVertex = -1;
QPointF dist = pos - m_component->m_pos;
if(fabs(dist.x()) <= 1 && fabs(dist.y()) <= 1)
{
m_hitVertex = 8;
return;
}
for(int i = 0; i < 8; ++i)
{
QPointF dist = pos - m_component->m_vertex[i];
if(fabs(dist.x()) <= 1 && fabs(dist.y()) <= 1)
{
m_hitVertex = i;
return; // hit one of the control posints
}
}
}
}
void ComponentLayer::mouseReleaseEvent(QMouseEvent *event)
{
if(m_component) // clear ROI editing hit vertex
{
event->accept();
if(m_hitVertex != -1)
{
m_hitVertex = -1;
}
}
}
void ComponentLayer::mouseMoveEvent(QMouseEvent *event)
{
if(m_component) // editing component ROI
{
event->accept();
if(m_hitVertex != -1)
{
QPointF pos = m_view->winToWorld().map(event->localPos());
QPointF vec = pos - m_component->m_pos;
switch(m_hitVertex)
{
case 0:
case 2:
case 4:
case 6:
{
double width = fabs(QPointF::dotProduct(vec, m_component->m_unitVecX)) * 2;
double height = fabs(QPointF::dotProduct(vec, m_component->m_unitVecY)) * 2;
m_component->m_size.setWidth(width);
m_component->m_size.setHeight(height);
}break;
case 1:
case 5:
{
double height = fabs(QPointF::dotProduct(vec, m_component->m_unitVecY)) * 2;
m_component->m_size.setHeight(height);
}break;
case 3:
case 7:
{
double width = fabs(QPointF::dotProduct(vec, m_component->m_unitVecX)) * 2;
m_component->m_size.setWidth(width);
}break;
case 8:
{
m_component->m_pos = pos;
}break;
}
m_component->calcVertex();
m_view->update();
}
}
}
void ComponentLayer::mouseDoubleClickEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton)
{
QPointF pos = m_view->winToWorld().map(event->localPos());
m_component = nullptr;
for(auto & compnent : g_m_components)
{
if(compnent.contains(pos))
{
m_component = &compnent;
event->accept();
break;
}
}
m_view->update();
}
}
void ComponentLayer::draw_ex(QPainter &painter)
{
if(m_component)
{
QPen pen;
pen.setWidth(2);
pen.setCosmetic(true);
pen.setColor(Qt::yellow);
painter.setPen(pen);
painter.setTransform(m_view->worldToWin());
// draw roi rect
painter.drawLine(m_component->m_vertex[0], m_component->m_vertex[2]);
painter.drawLine(m_component->m_vertex[2], m_component->m_vertex[4]);
painter.drawLine(m_component->m_vertex[4], m_component->m_vertex[6]);
painter.drawLine(m_component->m_vertex[6], m_component->m_vertex[0]);
// draw polarity
painter.drawLine(m_component->m_vertex[1] + 0.3*(m_component->m_vertex[5] - m_component->m_vertex[1]),
m_component->m_vertex[0] + 0.3*(m_component->m_vertex[2]-m_component->m_vertex[0]));
painter.drawLine(m_component->m_vertex[1] + 0.3*(m_component->m_vertex[5] - m_component->m_vertex[1]),
m_component->m_vertex[0] + 0.7*(m_component->m_vertex[2]-m_component->m_vertex[0]));
painter.setBrush(Qt::yellow);
for(int i = 0; i < 8; ++i)
{
painter.drawEllipse(m_component->m_vertex[i], 0.5, 0.5);
}
painter.drawLine(m_component->m_pos-QPointF(0.5, 0), m_component->m_pos+QPointF(0.5, 0));
painter.drawLine(m_component->m_pos-QPointF(0, 0.5), m_component->m_pos+QPointF(0, 0.5));
}
}
2.4 qk2dview.cpp
#include "qk2dview.h"
#include <QWheelEvent>
#include <QPainter>
#include <QTransform>
#include <QDebug>
Qk2dViewLayer::Qk2dViewLayer()
: m_view(nullptr)
, m_visiable(true)
{
}
void Qk2dViewLayer::setView(Qk2dView *view)
{
m_view = view;
}
void Qk2dViewLayer::setVisiable(bool visiable)
{
m_visiable = visiable;
}
bool Qk2dViewLayer::visiable() const
{
return m_visiable;
}
void Qk2dViewLayer::draw(QPainter &painter)
{
painter.save();
draw_ex(painter);
painter.restore();
}
Qk2dView::Qk2dView(QWidget *parent)
: QWidget(parent)
, m_layers()
, m_roiRect()
, m_winToWorld()
, m_worldToWin()
, m_isDragging(false)
, m_dragStartPos()
{
m_layers.fill(nullptr);
setMouseTracking(true);
setRoiRect(QRectF(270, 30, 410, 290));
}
void Qk2dView::setRoiRect(const QRectF &roi)
{
m_roiRect = roi.normalized();
calcTransform();
update();
}
void Qk2dView::setDrawer(Layer layer, std::shared_ptr<Qk2dViewLayer> drawer)
{
if(m_layers[layer])
{
m_layers[layer]->setView(nullptr);
}
if(drawer)
{
drawer->setView(this);
}
m_layers[layer] = drawer;
update();
}
std::shared_ptr<Qk2dViewLayer> Qk2dView::getDrawer(Layer layer)
{
return m_layers[layer];
}
void Qk2dView::setDrawerVisiable(Layer layer, bool visiable)
{
if(m_layers[layer])
{
m_layers[layer]->setVisiable(visiable);
update();
}
}
void Qk2dView::mousePressEvent(QMouseEvent *event)
{
if(event->type() == QEvent::MouseButtonDblClick)
{
event->setAccepted(false);
return;
}
else
{
QWidget::mousePressEvent(event);
if(event->button() == Qt::MidButton)
{
m_isDragging = true;
m_dragStartPos = event->localPos();
}
for(auto it = m_layers.rbegin(); it != m_layers.rend(); ++it)
{
if((*it) && (*it)->visiable())
{
(*it)->mousePressEvent(event);
if(event->isAccepted())
{
break;
}
}
}
}
}
void Qk2dView::mouseReleaseEvent(QMouseEvent *event)
{
QWidget::mouseReleaseEvent(event);
if(m_isDragging)
{
m_isDragging = false;
}
for(auto it = m_layers.rbegin(); it != m_layers.rend(); ++it)
{
if((*it) && (*it)->visiable())
{
(*it)->mouseReleaseEvent(event);
if(event->isAccepted())
{
break;
}
}
}
}
void Qk2dView::mouseMoveEvent(QMouseEvent *event)
{
QWidget::mouseMoveEvent(event);
if(m_isDragging)
{
QPointF pos = event->localPos();
QPointF trans = pos - m_dragStartPos;
m_dragStartPos = pos;
setRoiRect(m_roiRect.translated(-trans.x() * m_winToWorld.m11(), -trans.y() * m_winToWorld.m22()));
}
for(auto it = m_layers.rbegin(); it != m_layers.rend(); ++it)
{
if((*it) && (*it)->visiable())
{
(*it)->mouseMoveEvent(event);
if(event->isAccepted())
{
break;
}
}
}
}
void Qk2dView::mouseDoubleClickEvent(QMouseEvent *event)
{
QWidget::mouseDoubleClickEvent(event);
for(auto it = m_layers.rbegin(); it != m_layers.rend(); ++it)
{
if((*it) && (*it)->visiable())
{
(*it)->mouseDoubleClickEvent(event);
if(event->isAccepted())
{
break;
}
}
}
}
void Qk2dView::paintEvent(QPaintEvent *event)
{
QWidget::paintEvent(event);
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.fillRect(rect(), QColor(20, 20, 20));
for(auto it = m_layers.begin(); it != m_layers.end(); ++it)
{
if(*it && (*it)->visiable())
{
(*it)->draw(painter);
}
}
}
// 滚轮滚动
void Qk2dView::wheelEvent(QWheelEvent *event)
{
QWidget::wheelEvent(event);
int numDegrees = event->angleDelta().y() / 8;
double zoomFactor = 1 - numDegrees / 15.0 * 0.1;
QRectF viewRect = m_winToWorld.mapRect(QRectF(rect()));
QPointF pos = m_winToWorld.map(event->position());
double top = pos.y() + (viewRect.top() - pos.y()) * zoomFactor;
double bottom = pos.y() + (viewRect.bottom() - pos.y()) * zoomFactor;
double left = pos.x() + (viewRect.left() - pos.x()) * zoomFactor;
double right = pos.x() + (viewRect.right() - pos.x()) * zoomFactor;
QRectF rect(left, top, right-left, bottom-top);
setRoiRect(rect);
}
void Qk2dView::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
calcTransform();
}
// 计算转换(平移和缩放)
void Qk2dView::calcTransform()
{
double w2hRatio = m_roiRect.width() / m_roiRect.height();
double w(width()), h(height());
double tempWidth = height() * w2hRatio;
QRectF windowRect; // in window coordinate system
if(tempWidth <= width())
{
w = tempWidth;
windowRect = QRectF((width()-w)/2.0, 0, w, h);
}
else
{
h = width() / w2hRatio;
windowRect = QRectF(0, (height()-h) / 2.0, w, h);
}
double scale_x, scale_y, trans_x, trans_y;
scale_x = windowRect.width()/m_roiRect.width();
scale_y = -windowRect.height()/m_roiRect.height();
trans_x = windowRect.left() - scale_x*m_roiRect.left();
trans_y = windowRect.bottom() - scale_y*m_roiRect.top();
m_worldToWin = QTransform::fromScale(scale_x, scale_y);
m_worldToWin *= QTransform::fromTranslate(trans_x, trans_y);
m_winToWorld = m_worldToWin.inverted();
}
1.5 recipelayer.cpp
#include "recipelayer.h"
#include "component.h"
#include <QDebug>
RecipeLayer::RecipeLayer()
: Qk2dViewLayer()
, m_selecting(false)
, m_selectStartPos()
, m_selectEndPos()
{
}
void RecipeLayer::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton)
{
QPointF pos = m_view->winToWorld().map(event->localPos());
m_selecting = true;
m_selectStartPos = pos;
m_selectEndPos = pos;
}
}
void RecipeLayer::mouseReleaseEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton)
{
if(m_selecting) // selecting
{
m_selecting = false;
QRectF rect = QRectF(m_selectStartPos, m_selectEndPos).normalized();
for(auto & compnent : g_m_components)
{
if(rect.contains(compnent.m_pos))
{
compnent.m_selected = true;
}
else if(!(event->modifiers() & Qt::ControlModifier))
{
compnent.m_selected = false;
}
}
}
}
m_view->update();
}
void RecipeLayer::mouseMoveEvent(QMouseEvent *event)
{
QPointF pos = m_view->winToWorld().map(event->localPos());
if(m_selecting)
{
m_selectEndPos = pos;
}
m_view->update();
}
void RecipeLayer::draw_ex(QPainter &painter)
{
QPen pen;
pen.setWidth(2);
pen.setCosmetic(true);
painter.setTransform(m_view->worldToWin());
for(const auto & compnent : g_m_components)
{
if(compnent.m_selected)
{
pen.setColor(Qt::green); // 选中
}
else
{
pen.setColor(Qt::blue); // 未选中
}
painter.setPen(pen);
// draw roi rect
painter.drawLine(compnent.m_vertex[0], compnent.m_vertex[2]);
painter.drawLine(compnent.m_vertex[2], compnent.m_vertex[4]);
painter.drawLine(compnent.m_vertex[4], compnent.m_vertex[6]);
painter.drawLine(compnent.m_vertex[6], compnent.m_vertex[0]);
// draw polarity
painter.drawLine(compnent.m_vertex[1] + 0.3*(compnent.m_vertex[5] - compnent.m_vertex[1]),
compnent.m_vertex[0] + 0.3*(compnent.m_vertex[2]-compnent.m_vertex[0]));
painter.drawLine(compnent.m_vertex[1] + 0.3*(compnent.m_vertex[5] - compnent.m_vertex[1]),
compnent.m_vertex[0] + 0.7*(compnent.m_vertex[2]-compnent.m_vertex[0]));
}
if(m_selecting) // ROI区域触发
{
pen.setColor(Qt::yellow);
painter.setPen(pen);
painter.setBrush(QColor(255, 255, 255, 100));
painter.drawRect(QRectF(m_selectStartPos, m_selectEndPos));
}
}
1.6 resultlayer.pp
#include "resultlayer.h"
ResultLayer::ResultLayer()
: Qk2dViewLayer()
, m_text("Good")
, m_status(true)
{
}
void ResultLayer::setText(const QString& text, bool status)
{
m_text = text;
m_status = status;
if(m_view)
{
m_view->update();
}
}
void ResultLayer::draw_ex(QPainter &painter)
{
QPen pen;
pen.setWidth(2);
pen.setCosmetic(true);
QBrush brush;
QColor color;
if(m_status)
{
color = QColor(0, 255, 0, 150);
}
else
{
color = QColor(255, 0, 0, 150);
}
pen.setColor(color);
brush.setColor(color);
painter.setPen(pen);
painter.setBrush(brush);
QRectF winRect = m_view->rect();
QFont font = painter.font();
font.setBold(true);
font.setPixelSize(std::min(winRect.width(), winRect.height())/1.5);
painter.setFont(font);
painter.drawText(winRect, Qt::AlignHCenter | Qt::AlignVCenter, m_text);
}
2.7 mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "boardlayer.h"
#include "recipelayer.h"
#include "componentlayer.h"
#include "resultlayer.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, m_view(this)
{
ui->setupUi(this);
setCentralWidget(&m_view);
std::shared_ptr<BoardLayer> boardLayer = std::make_shared<BoardLayer>();
m_view.setDrawer(Qk2dView::BoardLayer, boardLayer);
std::shared_ptr<RecipeLayer> recipeLayer = std::make_shared<RecipeLayer>();
m_view.setDrawer(Qk2dView::RecipeLayer, recipeLayer);
std::shared_ptr<ComponentLayer> compLayer = std::make_shared<ComponentLayer>();
m_view.setDrawer(Qk2dView::ComponentLayer, compLayer);
std::shared_ptr<ResultLayer> resultLayer = std::make_shared<ResultLayer>();
m_view.setDrawer(Qk2dView::ResultLayer, resultLayer);
resultLayer->setText("OK", true);
// resultLayer->setVisiable(false);
}
MainWindow::~MainWindow()
{
delete ui;
}
2.8 main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}