目录
- 引言
- 1、生成过程
- 1.1 饼图
- 2.2 扇形图
- 3.3 可选择扇形按钮
- 1.4 新的扇形画法*GraphicItem
- 2、训练过程
- 3、错误原因
- 4、涉及知识点
引言
因为项目需要绘制一个中间为圆心,包含数个扇形的可选择按钮。正好ChatGpt使用起来比较成熟,因此使用询问的方式得到两部分代码。最终修改后的效果如动图:
1、生成过程
1.1 饼图
目前ChatGpt给出的代码还有缺陷,一次运行不起来。需要人为添加头文件或处理逻辑关系,不过已经很强大了。
生成的效果为:
//饼图案例
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QPainter>
#include <QDebug>
#include <QTableWidget>
#include <QHeaderView>
#include <QMessageBox>
#include <QMouseEvent>class SectorChartButton : public QPushButton {
public:SectorChartButton(QTableWidget* tableWidget, QWidget* parent = nullptr) : QPushButton(parent), m_tableWidget(tableWidget) {setFixedSize(200, 200);setStyleSheet("border: none");}protected:void paintEvent(QPaintEvent* event) override {Q_UNUSED(event)QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);// Draw background fan shapepainter.setPen(Qt::NoPen);painter.setBrush(QColor(200, 200, 200));painter.drawEllipse(rect());// Draw concentric circlepainter.setPen(QPen(QColor(0, 0, 0), 3));painter.setBrush(QColor(255, 255, 255));int x = width() / 4;int y = height() / 4;int w = width() / 2;int h = height() / 2;painter.drawEllipse(x, y, w, h);// Draw fan shapespainter.setPen(QPen(QColor(0, 0, 0), 3));painter.setBrush(QColor(255, 0, 0));painter.drawPie(rect(), 30 * 16, 120 * 16);painter.setBrush(QColor(0, 255, 0));painter.drawPie(rect(), 150 * 16, 120 * 16);painter.setBrush(QColor(0, 0, 255));painter.drawPie(rect(), 270 * 16, 120 * 16);// Draw lines from center to edge of each fan shapepainter.drawLine(width() / 2, height() / 2, width() / 2 + (width() - 20) / 4 * qCos(qDegreesToRadians(75)), height() / 2 - (height() - 20) / 4 * qSin(qDegreesToRadians(75)));painter.drawLine(width() / 2, height() / 2, width() / 2 - (width() - 20) / 4 * qSin(qDegreesToRadians(60)), height() / 2 + (height() - 20) / 4 * qCos(qDegreesToRadians(60)));painter.drawLine(width() / 2, height() / 2, width() / 2 - (width() - 20) / 4 * qCos(qDegreesToRadians(75)), height() / 2 - (height() - 20) / 4 * qSin(qDegreesToRadians(75)));}void mousePressEvent(QMouseEvent* event) override {QRectF rect = this->rect();QPointF center = rect.center();QPointF pos = event->localPos();qreal angle = qRadiansToDegrees(qAtan2(pos.y() - center.y(), pos.x() - center.x())) + 360;if (angle >= 30 && angle < 150) {m_tableWidget->insertRow(0);QTableWidgetItem* item1 = new QTableWidgetItem("Red sector");QTableWidgetItem* item2 = new QTableWidgetItem("Column 1 data");m_tableWidget->setItem(0, 0, item1);m_tableWidget->setItem(0, 1, item2);QMessageBox::information(this, "Sector Chart Button", "You clicked on the red sector!");}else if (angle >= 150 && angle < 270) {m_tableWidget->insertRow(0);QTableWidgetItem* item1 = new QTableWidgetItem("Green sector");QTableWidgetItem* item2 = new QTableWidgetItem("Column 2 data");m_tableWidget->setItem(0, 1, item1);m_tableWidget->setItem(0, 2, item2);QMessageBox::information(this, "Sector Chart Button", "You clicked on the green sector!");}else if (angle >= 270 || angle < 30) {m_tableWidget->insertRow(0);QTableWidgetItem* item1 = new QTableWidgetItem("Blue sector");QTableWidgetItem* item2 = new QTableWidgetItem("Column 3 data");m_tableWidget->setItem(0, 2, item1);m_tableWidget->setItem(0, 3, item2);QMessageBox::information(this, "Sector Chart Button", "You clicked on the blue sector!");}}private:QTableWidget* m_tableWidget;
};int main(int argc, char* argv[]) {QApplication app(argc, argv);QWidget widget;widget.setFixedSize(600, 400);// Create table widgetQTableWidget tableWidget(&widget);tableWidget.setColumnCount(4);tableWidget.setHorizontalHeaderLabels({ "Column 1", "Column 2", "Column 3", "Column 4" });tableWidget.verticalHeader()->setVisible(false);tableWidget.setGeometry(250, 50, 300, 300);// Create sector chart buttonSectorChartButton* button = new SectorChartButton(&tableWidget, &widget);button->move(50, 100);button->show();widget.show();return app.exec();
}
2.2 扇形图
ChatGpt实现效果如下,支持鼠标停留变颜色以及单击保留选择项。
生成代码:
//扇形图案例#include <QApplication>
#include <QWidget>
#include <QPainter>
#include <QMouseEvent>
#include <QDebug>class ColorWheel : public QWidget
{
public:ColorWheel(QWidget *parent = nullptr) : QWidget(parent){setFixedSize(200, 200);setMouseTracking(true);}protected:void paintEvent(QPaintEvent*) override{QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);QRectF rect = QRectF(10, 10, 180, 180);qreal startAngle[] = {0, 120, 240};qreal spanAngle[] = {120, 120, 120};QColor colors[] = {Qt::red, Qt::green, Qt::blue};for (int i = 0; i < 3; i++) {if (m_hoveredSector == i || m_selectedSector == i) {painter.setPen(colors[i]);painter.setBrush(colors[i]);} else {painter.setPen(colors[i].darker());painter.setBrush(Qt::NoBrush);}painter.drawPie(rect, startAngle[i] * 16, spanAngle[i] * 16);}}void mouseMoveEvent(QMouseEvent* event) override{int x = event->x();int y = event->y();int sector = getSector(x, y);if (sector != m_hoveredSector) {m_hoveredSector = sector;update();if (sector >= 0 && sector < 3) {QColor color;switch (sector) {case 0: color = Qt::red; break;case 1: color = Qt::green; break;case 2: color = Qt::blue; break;}qDebug() << "Hovering over sector" << sector << ", color is" << color.name();}}}void leaveEvent(QEvent*) override{m_hoveredSector = -1;update();}void mousePressEvent(QMouseEvent* event) override{int x = event->x();int y = event->y();int sector = getSector(x, y);if (sector != m_selectedSector) {m_selectedSector = sector;update();if (sector >= 0 && sector < 3) {QColor color;switch (sector) {case 0: color = Qt::red; break;case 1: color = Qt::green; break;case 2: color = Qt::blue; break;}qDebug() << "Selected sector" << sector << ", color is" << color.name();}}}private:int getSector(int x, int y){int dx = x - 100;int dy = y - 100;int r = qSqrt(dx*dx + dy*dy);if (r < 90) {qreal angle = qAtan2(dy, dx) * 180 / M_PI;if (angle < 0) angle += 360;int sector = static_cast<int>(angle / 120);return sector;}return -1;}int m_hoveredSector = -1;int m_selectedSector = -1;
};int main(int argc, char *argv[])
{QApplication a(argc, argv);ColorWheel w;w.show();return a.exec();
}
3.3 可选择扇形按钮
根据以上代码修正后得到该按钮,代码如下:
#include <QApplication>
#include <QWidget>
#include <QPainter>
#include <QMouseEvent>
#include <QDebug>class ColorWheel : public QWidget
{
public:ColorWheel(QWidget* parent = nullptr) : QWidget(parent){setFixedSize(200, 200);setMouseTracking(true);}protected:void paintEvent(QPaintEvent*) override{QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);QRectF rect = QRectF(10, 10, 180, 180);qreal startAngle[] = { 90, 210, 330 };qreal spanAngle[] = { 120, 120, 120 };QColor colors[] = { Qt::red, Qt::green, Qt::blue };int x = width() / 4;int y = height() / 4;int w = width() / 2;int h = height() / 2;QRectF innerRect = QRectF(x, y, w, h);if (m_init){//默认绘制第一个图形painter.setPen(colors[0]);painter.setBrush(Qt::NoBrush);painter.drawPie(rect, startAngle[0] * 16, spanAngle[0] * 16);// Draw concentric circlepainter.setPen(Qt::NoPen);painter.setBrush(QColor(255, 255, 255));painter.drawEllipse(x, y, w, h);painter.setPen(colors[0]);painter.drawArc(innerRect, startAngle[0] * 16, spanAngle[0] * 16);}for (int i = 0; i < 3; i++) {if (m_hoveredSector == i || m_selectedSector == i) {painter.setPen(colors[i]);painter.setBrush(Qt::NoBrush);painter.drawPie(rect, startAngle[i] * 16, spanAngle[i] * 16);// Draw concentric circlepainter.setPen(Qt::NoPen);painter.setBrush(QColor(255, 255, 255));painter.drawEllipse(x, y, w, h);painter.setPen(colors[i]);painter.drawArc(innerRect, startAngle[i] * 16, spanAngle[i] * 16);m_lastSelect = m_selectedSector;}else {painter.setPen(colors[i].darker());painter.setBrush(Qt::NoBrush);}//painter.drawPie(rect, startAngle[i] * 16, spanAngle[i] * 16);}if (!m_init && (m_selectedSector == -1)){//保留之前的选择painter.setPen(colors[m_lastSelect]);painter.setBrush(Qt::NoBrush);painter.drawPie(rect, startAngle[m_lastSelect] * 16, spanAngle[m_lastSelect] * 16);// Draw concentric circlepainter.setPen(Qt::NoPen);painter.setBrush(QColor(255, 255, 255));painter.drawEllipse(x, y, w, h);painter.setPen(colors[m_lastSelect]);painter.drawArc(innerRect, startAngle[m_lastSelect] * 16, spanAngle[m_lastSelect] * 16);}m_init = false;}//void mouseMoveEvent(QMouseEvent* event) override//{// int x = event->x();// int y = event->y();// int sector = getSector(x, y);// if (sector != m_hoveredSector) {// m_hoveredSector = sector;// update();// if (sector >= 0 && sector < 3) {// QColor color;// switch (sector) {// case 0: color = Qt::red; break;// case 1: color = Qt::green; break;// case 2: color = Qt::blue; break;// }// qDebug() << "Hovering over sector" << sector << ", color is" << color.name();// }// }//}//void leaveEvent(QEvent*) override//{// m_hoveredSector = -1;// update();//}void mousePressEvent(QMouseEvent* event) override{int x = event->x();int y = event->y();int sector = getSector(x, y);if (sector != m_selectedSector) {m_selectedSector = sector;update();if (sector >= 0 && sector < 3) {QColor color;switch (sector) {case 0: color = Qt::red; break;case 1: color = Qt::green; break;case 2: color = Qt::blue; break;}qDebug() << "Selected sector" << sector << ", color is" << color.name();}}}private:int getSector(int x, int y){int dx = x - 100;int dy = y - 100;int r = qSqrt(dx * dx + dy * dy);if (r < 90) {qreal angle = qAtan2(-dy, dx) * 180 / M_PI;if (angle < 90) angle += 360;int sector = static_cast<int>((angle - 90) / 120);return sector;}return -1;}int m_hoveredSector = -1;int m_selectedSector = -1;bool m_init = true;int m_lastSelect = -1;
};int main(int argc, char* argv[])
{QApplication a(argc, argv);ColorWheel w;w.show();return a.exec();
}
1.4 新的扇形画法*GraphicItem
可以通过QPainterPath和QRegion的方式来绘制。QPatinterPath含有substrate方法,然后通过QPainter绘制。同时QRegion也有对应的直接使用QPainter绘制的方法。setClipRegion
可以设置当前可绘画的区域。
void Widget::paintEvent(QPaintEvent *event)
{Q_UNUSED(event);QPainter painter(this);QPainterPath path; // 定义QPainterPath对象int x = width() / 2; // 获取widget的宽度int y = height() / 2; // 获取widget的高度int radius = qMin(x, y); // 确定半径QRectF rect(x - radius, y - radius, radius * 2, radius * 2); // 定义外接矩形path.moveTo(x, y); // 将起点移动到圆心path.arcTo(rect, 90, -120); // 添加90到210度的椭圆弧线,注意这里的角度单位是以1/16度来表示的path.lineTo(x, y); // 将路径连接回中心点int smallRadius = radius / 2; // 定义小扇形的半径QRectF smallRect(x - smallRadius, y - smallRadius, smallRadius * 2, smallRadius * 2); // 定义小扇形的外接矩形QPainterPath smallPath; // 定义小扇形的路径smallPath.moveTo(x, y); // 将起点移动到圆心smallPath.arcTo(smallRect, 90, -120); // 添加90到210度的椭圆弧线,注意这里的角度单位是以1/16度来表示的QRegion bigRegion(path.toFillPolygon().toPolygon()); // 定义大扇形的QRegionQRegion smallRegion(smallPath.toFillPolygon().toPolygon()); // 定义小扇形的QRegionbigRegion = bigRegion.subtracted(smallRegion); // 大扇形减去小扇形QBrush brush(Qt::NoBrush); // 创建空画刷painter.setBrush(brush); // 设置画刷为空,即不填充图形QPen pen(Qt::black); // 定义画笔,颜色为黑色pen.setWidth(2); // 设置画笔宽度painter.setPen(pen); // 设置画笔painter.setClipRegion(bigRegion); // 设置绘制区域painter.drawPath(path); // 绘制大扇形的路径轮廓painter.setClipping(false); // 取消原来的绘制区域painter.setClipRegion(smallRegion); // 设置绘制区域为小扇形的QRegionpainter.drawPath(smallPath); // 绘制小扇形的路径轮廓
}
2、训练过程
这里给出了训练过程,目前中文语料比较少,因此得到的有效信息不充足。所以每次都是中文翻译为英语后去问答,出乎意料的是代码基本修改下都能跑起来。
3、错误原因
如果指出ChatGPT的错误,它会给出部分错误原因,虽然有时候和给出的错误原因不太符合代码,但在后续自己Debug的过程中能发现很多有用的信息。如在计算角度使用QAtan2 时应取-dy。
In this modified code, a new m_selectedSector variable has been added to track which sector is currently selected by the user (i.e. the one the user clicked on). The paintEvent() method has also been updated to fill in the color of the selected sector even when the mouse is not moving. This is done by checking whether m_selectedSector is equal to the current sector being painted (i), and if so, filling in the sector with its color.In addition, a new mousePressEvent() method has been added to handle the case where the user clicks on a sector. When this happens, m_selectedSector is updated, and the widget is repainted with the selected sector filled in with its color. The code also outputs the color of the selected sector to the console using qDebug().Finally, the leaveEvent() method has been updated to reset both m_hoveredSector and m_selectedSector to -1 when the mouse leaves the widget, which causes all of the sectors to be drawn with no fill color.
在此修改后的代码中,添加了一个新的m_selectedSector变量来跟踪用户当前选择的扇区(即用户单击的扇区)。paintEvent() 方法也已更新,即使在鼠标不移动时也能填充所选扇区的颜色。这是通过检查m_selectedSector是否等于当前正在绘制的扇区 (i) 来完成的,如果是,则用其颜色填充扇区。 此外,还添加了一个新的 mousePressEvent() 方法来处理用户单击扇区的情况。发生这种情况时,m_selectedSector将更新,并且小部件将重新绘制,并用其颜色填充所选扇区。该代码还使用 qDebug() 将所选扇区的颜色输出到控制台。 最后,当鼠标离开小部件时,leaveEvent() 方法已更新为将 m_hoveredSector 和 m_selectedSector 重置为 -1,这会导致绘制所有扇区时没有填充颜色。
The error was caused by a mistake in the calculation of the angle variable. Instead of using +dy, it should be -dy, since the y-axis is inverted in the coordinate system used by QPainter (i.e. positive y is downwards). This causes the angle to be calculated correctly, which in turn allows the correct sector to be determined based on the angle.I hope this corrected method works better for you. Please let me know if you have any further questions or issues.
该错误是由角度变量计算错误引起的。它不应该使用 +dy,而应该是 -dy,因为 y 轴在 QPainter 使用的坐标系中是反转的(即正 y 向下)。这会导致正确计算角度,从而允许根据角度确定正确的扇区。 我希望这种更正后的方法对您更有效。如果您有任何其他问题或问题,请告诉我。
The issue with the previous implementation was that it incorrectly assumed that sector 1 starts at 120 degrees and sector 2 starts at 240 degrees, which causes the two sectors to be calculated inversely. The corrected implementation addresses this issue by correctly identifying sector 1 as the sector starting at 30 degrees, and sector 2 as the sector starting at 150 degrees.I apologize for any confusion caused by my earlier responses, and I hope this updated implementation resolves the issue. Please let me know if you have any further questions or concerns.
先前实现的问题在于,它错误地假定扇区 1 从 120 度开始,扇区 2 从 240 度开始,这会导致两个扇区的计算相反。更正后的实现通过正确标识扇区 1 作为从 30 度开始的扇区,将扇区 2 标识为从 150 度开始的扇区来解决此问题。 对于我之前的回复造成的任何混乱,我深表歉意,我希望这个更新的实现可以解决这个问题。如果您有任何其他问题或疑虑,请告诉我。
4、涉及知识点
------待更新-------------------------------------------------