Qt鼠标移入时改变QPushButton图标和颜色

本文介绍如何使用CSS样式设置按钮的hover效果,并通过设置图片作为背景实现图标按钮的显示效果。文章探讨了设置按钮样式中遇到的问题及解决方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ui->pushButton->setStyleSheet("QPushButton:hover{border-image:url(:/pic/logo.png);color:red}");
设置flat为true,点击鼠标依然出现按钮本色,增加border-style:outset;后,按钮安静的显示图标,没有任何变化了,实现了网页图标按钮的样子
<think>我们正在开发一个Qt OpenGL程序,用于可视化动更新的圆柱体数据。数据以QVector<double>形式提供,包含圆柱体外圆周上点的xyz坐标。同,我们需要实现鼠标交互:左键旋转场景、右键平移、滚轮缩放。 步骤: 1. 创建Qt OpenGL窗口(使用QOpenGLWidget) 2. 设置鼠标事件处理(按下、移动、释放、滚轮) 3. 实现相机控制(旋转、平移、缩放) 4. 解析QVector<double>数据,将其转换为OpenGL可绘制的顶点数据(注意:数据是圆柱体外圆周上的点,我们需要构建圆柱面的三角形网格) 5. 动更新数据重新绘制 注意:由于数据是动更新的,我们需要在更新重新构建圆柱面并刷新显示。 圆柱面构建思路: - 圆柱体由多个圆环(每个圆环由多个点组成)沿高度方向排列构成。 - 假设我们有一系列高度层,每个高度层上有一个圆,圆上等角度分布若干个点。 - 数据格式:QVector<double>中每3个元素表示一个点的x,y,z坐标。这些点应该按圆环排列(即先一个圆环上的所有点,再下一个圆环)。 如何从给定的点集构建圆柱面? - 假设有m个高度层(圆环),每个圆环有n个点,则总点数为m*n。 - 数据排列:点0到点n-1是第一个圆环,点n到点2n-1是第二个圆环,以此类推。 - 构建三角形带(Triangle Strip)或三角形(Triangles): 我们可以将圆柱面分解为多个四边形(由两个三角形组成),每个四边形由相邻两个圆环上的相邻两点构成。 绘制方法: - 使用GL_TRIANGLES或GL_TRIANGLE_STRIP。这里我们使用GL_TRIANGLES,因为逻辑更清晰。 三角形构建: - 对于每个圆环(除了最后一个)每个圆环上的点(除了最后一个点): 假设当前点在圆环i,位置j,则: 顶点A = (i, j) 顶点B = (i, j+1) 顶点C = (i+1, j) 顶点D = (i+1, j+1) 两个三角形:ABCBCD(注意顺序保证逆针为正面) 即:三角形1: A, B, C 三角形2: B, D, C 注意:最后一个点与第一个点相连(形成闭合圆环),所以当j等于n-1,下一个点应该是当前圆环的第一个点(索引为i*n+0)。 动更新:当数据更新,我们重新构建顶点数组并更新顶点缓冲区(VBO)。 相机控制: - 使用模型视图矩阵投影矩阵。 - 旋转:通过鼠标左键拖拽计算旋转角度,更新相机方向(或旋转模型)。 - 平移:通过鼠标右键拖拽计算平移,更新相机位置(或模型位置)。 - 缩放:通过滚轮调整相机距离(或缩放模型)。 实现: - 使用QOpenGLWidget,重写initializeGL, resizeGL, paintGL。 - 在initializeGL中初始化OpenGL函数、设置初始状(如深度测试、背景色)。 - 使用QOpenGLBuffer(VBO)存储顶点数据。 - 使用QOpenGLShaderProgram加载并编译着色器。 着色器:简单的顶点着色器片元着色器。 由于问题要求完整示例,我们将编写一个完整的类。 注意:我们假设圆柱面的数据是动更新的,因此提供一个更新数据的接口(例如setCylinderData(const QVector<double>& data))。 另外,我们还需要存储当前的数据点个数(圆环数每环点数)以便正确构建网格。 类定义: class CylinderWidget : public QOpenGLWidget, protected QOpenGLFunctions 成员变量: QOpenGLShaderProgram *program; QOpenGLBuffer vbo; QVector3D rotationAngle; // 或者使用旋转矩阵 QVector3D translation; float scale; QPoint lastPos; // 记录鼠标上一次位置,用于拖拽 // 圆柱数据 int numRings; // 圆环数(高度层数) int numPointsPerRing; // 每个圆环上的点数 QVector<QVector3D> vertices; // 存储从QVector<double>转换来的顶点 步骤: 1. 初始化:创建着色器程序,初始化VBO,设置相机初始参数。 2. 在setCylinderData中,将输入的QVector<double>转换为QVector3D数组,并计算numRingsnumPointsPerRing(注意:总点数=numRings * numPointsPerRing,所以numPointsPerRing = 总点数/numRings,但需要确保整除)。 3. 更新VBO:将新的顶点数据上传到GPU。 4. 在paintGL中,设置模型视图投影矩阵,绘制三角形。 鼠标事件: - mousePressEvent: 记录按下的位置 - mouseMoveEvent: 根据按下的按钮计算旋转或平移量,更新旋转角度或平移向量,然后更新显示 - wheelEvent: 调整缩放因子 注意:旋转平移操作通常作用在模型矩阵上,但也可以理解为移动相机。这里我们采用旋转平移模型的方式(即模型矩阵)。 矩阵设置: 在paintGL中: modelMatrix = 平移矩阵 * 旋转矩阵 * 缩放矩阵 视图矩阵可以设置为单位矩阵(因为我们移动模型),投影矩阵使用透视投影。 透视投影:在resizeGL中设置,或者根据窗口大小在paintGL中设置。 由于我们使用QOpenGLWidget,推荐使用现代OpenGL(3.0+核心配置文件),但Qt默认是兼容性配置文件。我们使用QOpenGLFunctions提供的函数。 着色器代码: 顶点着色器: #version 330 core layout (location = 0) in vec3 position; uniform mat4 model; uniform mat4 view; uniform mat4 projection; void main() { gl_Position = projection * view * model * vec4(position, 1.0); } 片元着色器: #version 330 core out vec4 FragColor; void main() { FragColor = vec4(1.0, 0.5, 0.2, 1.0); // 固定颜色(橙色) } 注意:在Qt中,我们使用QOpenGLShaderProgram,并添加着色器代码字符串。 由于是示例,我们使用固定颜色。实际中可能需要光照,但问题没有要求,所以省略。 实现细节: - 在initializeGL中,初始化OpenGL函数(initializeOpenGLFunctions()),设置背景色(glClearColor),启用深度测试(glEnable(GL_DEPTH_TEST))。 - 创建着色器程序,编译并链接。 - 创建VBO,并绑定。 更新数据: - 当调用setCylinderData,我们将新的数据转换为QVector3D数组,并重新计算numRingsnumPointsPerRing(注意:数据点数必须是每环点数的整数倍,否则报错)。 - 然后,构建顶点索引(用于绘制三角形)?或者我们使用另一种方法:在paintGL中,我们根据numRingsnumPointsPerRing计算需要绘制的三角形个数,然后使用glDrawArrays(GL_TRIANGLES)需要每个三角形三个顶点重复,这样会浪费内存。因此,我们也可以使用索引缓冲(IBO)来绘制。但为了简单,我们使用非索引方式,即构建一个包含所有三角形顶点的数组(每个三角形3个顶点),这样每个顶点可能在多个三角形中重复出现。 考虑到动更新简单性,我们采用非索引方式(即直接构建三角形顶点数组)。这样,每次数据更新,我们重新构建一个大的顶点数组(包含所有三角形的顶点),然后上传到VBO。 构建三角形顶点数组的步骤: - 总三角形个数:每个四边形由2个三角形构成,总四边形数 = (numRings-1) * numPointsPerRing - 总三角形顶点数 = 总三角形个数 * 3 = (numRings-1) * numPointsPerRing * 2 * 3 - 遍历每个圆环(从0到numRings-2)每个圆环上的点(从0到numPointsPerRing-1): currentRing = i nextRing = i+1 currentPointIndex = j nextPointIndex = (j+1) % numPointsPerRing A = 顶点[i][j] B = 顶点[i][nextPointIndex] C = 顶点[i+1][j] D = 顶点[i+1][nextPointIndex] 三角形1:A, B, C 三角形2:B, D, C 注意:这里我们假设圆柱面是封闭的,所以最后一个点与第一个点相连(使用取模)。 这样,我们构建一个大的顶点数组(每个顶点是QVector3D),然后上传到VBO。 注意:这样构建的顶点数组会很大,因为每个顶点被重复存储(每个顶点最多被6个三角形共享,但这里我们每个三角形独立存储顶点)。如果数据量很大,建议使用索引缓冲(IBO)glDrawElements。但为了简单,我们先使用非索引方式。 类定义如下(在头文件中):</think>下面是一个完整的Qt OpenGL程序,用于可视化圆柱面数据并实现鼠标交互功能。该程序支持动更新QVector<double>数据,并实现左键旋转、右键平移、滚轮缩放功能。 ```cpp #include <QOpenGLWidget> #include <QOpenGLFunctions> #include <QOpenGLShaderProgram> #include <QOpenGLBuffer> #include <QVector> #include <QMouseEvent> #include <QWheelEvent> #include <QVector3D> #include <QMatrix4x4> class CylinderWidget : public QOpenGLWidget, protected QOpenGLFunctions { Q_OBJECT public: explicit CylinderWidget(QWidget *parent = nullptr) : QOpenGLWidget(parent), vbo(QOpenGLBuffer::VertexBuffer) { setMouseTracking(true); } void setCylinderData(const QVector<double> &data) { vertices.clear(); for (int i = 0; i < data.size(); i += 3) { vertices.append(QVector3D(data[i], data[i+1], data[i+2])); } update(); // 触发重绘 } protected: void initializeGL() override { initializeOpenGLFunctions(); glClearColor(0.1f, 0.1f, 0.1f, 1.0f); glEnable(GL_DEPTH_TEST); // 创建着色器程序 program.addShaderFromSourceCode(QOpenGLShader::Vertex, "#version 330 core\n" "layout(location = 0) in vec3 position;\n" "uniform mat4 model;\n" "uniform mat4 view;\n" "uniform mat4 projection;\n" "void main() {\n" " gl_Position = projection * view * model * vec4(position, 1.0);\n" "}"); program.addShaderFromSourceCode(QOpenGLShader::Fragment, "#version 330 core\n" "out vec4 FragColor;\n" "void main() {\n" " FragColor = vec4(0.8, 0.3, 0.2, 1.0);\n" // 圆柱颜色 "}"); program.link(); // 初始化VBO vbo.create(); vbo.bind(); vbo.allocate(vertices.constData(), vertices.size() * sizeof(QVector3D)); } void resizeGL(int w, int h) override { glViewport(0, 0, w, h); projection.setToIdentity(); projection.perspective(45.0f, static_cast<float>(w)/h, 0.1f, 100.0f); } void paintGL() override { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); program.bind(); // 设置模型矩阵 QMatrix4x4 model; model.translate(translation); model.rotate(rotation.x(), QVector3D(1, 0, 0)); model.rotate(rotation.y(), QVector3D(0, 1, 0)); model.scale(scale); // 设置视图矩阵(相机位置) QMatrix4x4 view; view.translate(0, 0, -5.0f); program.setUniformValue("model", model); program.setUniformValue("view", view); program.setUniformValue("projection", projection); // 更新顶点数据 vbo.bind(); program.enableAttributeArray(0); program.setAttributeBuffer(0, GL_FLOAT, 0, 3, sizeof(QVector3D)); // 绘制圆柱面(使用三角形条带) for (int i = 0; i < rings - 1; ++i) { glDrawArrays(GL_TRIANGLE_STRIP, i * pointsPerRing, pointsPerRing * 2); } program.release(); } void mousePressEvent(QMouseEvent *event) override { lastMousePos = event->pos(); } void mouseMoveEvent(QMouseEvent *event) override { QPoint delta = event->pos() - lastMousePos; if (event->buttons() & Qt::LeftButton) { // 左键旋转 rotation.setX(rotation.x() + delta.y() * 0.5f); rotation.setY(rotation.y() + delta.x() * 0.5f); } else if (event->buttons() & Qt::RightButton) { // 右键平移 translation.setX(translation.x() + delta.x() * 0.01f); translation.setY(translation.y() - delta.y() * 0.01f); } lastMousePos = event->pos(); update(); } void wheelEvent(QWheelEvent *event) override { // 滚轮缩放 float delta = event->angleDelta().y() * 0.001f; scale *= (1.0f + delta); scale = qBound(0.1f, scale, 5.0f); // 限制缩放范围 update(); } private: QOpenGLShaderProgram program; QOpenGLBuffer vbo; QVector<QVector3D> vertices; // 相机控制参数 QVector3D translation = QVector3D(0, 0, 0); QVector3D rotation = QVector3D(0, 0, 0); float scale = 1.0f; // 圆柱参数(根据实际数据调整) const int rings = 20; // 圆环数量 const int pointsPerRing = 36; // 每个圆环点数 QMatrix4x4 projection; QPoint lastMousePos; }; // 在main.cpp中使用示例 #include <QApplication> #include <QTimer> int main(int argc, char *argv[]) { QApplication app(argc, argv); CylinderWidget widget; widget.resize(800, 600); widget.show(); // 生成测试圆柱数据(半径1.0,高度2.0) QVector<double> cylinderData; for (int i = 0; i < widget.rings; ++i) { float y = -1.0f + 2.0f * i / (widget.rings - 1); for (int j = 0; j < widget.pointsPerRing; ++j) { float angle = 2 * M_PI * j / widget.pointsPerRing; cylinderData << qCos(angle) << y << qSin(angle); } } widget.setCylinderData(cylinderData); // 模拟动更新数据(每秒更新) QTimer timer; QObject::connect(&timer, &QTimer::timeout, [&]() { // 这里可以修改cylinderData实现动效果 widget.setCylinderData(cylinderData); }); timer.start(1000); return app.exec(); } #include "main.moc" // 包含moc文件 ``` ### 程序说明 1. **数据结构**: - 使用`QVector<QVector3D>`存储圆柱顶点 - 输入数据格式:连续的xyz坐标序列 2. **OpenGL核心功能**: - 使用VBO存储顶点数据 - GLSL 330核心着色器 - 三角形条带绘制圆柱面 - 透视投影(`perspective(45.0f, aspect, 0.1f, 100.0f)`) 3. **鼠标交互**: - **左键拖动**:绕X/Y轴旋转(`rotation.x()/y()`) - **右键拖动**:在XY平面平移(`translation.x()/y()`) - **滚轮**:缩放场景(`scale *= (1.0f + delta)`) 4. **动更新**: - 调用`setCylinderData()`更新数据 - 示例包含定更新逻辑 ### 测试数据生成 程序包含圆柱数据生成算法: $$x = \cos(\theta), \quad y = h, \quad z = \sin(\theta)$$ 其中: - $\theta = \frac{2\pi j}{N}$($N$为每环点数) - $h \in [-1, 1]$(高度线性分布) ### 性能优化建议 1. 使用索引缓冲对象(IBO)减少顶点重复 2. 实现双缓冲机制避免渲染冲突 3. 添加法线数据支持光照计算 4. 使用实例化渲染提高大批量数据性能 ### 相关问题 1. 如何在Qt OpenGL中实现更高效的顶点数据更新? 2. 如何为圆柱面添加纹理贴图? 3. 怎样实现基于物理的光照模型(PBR)增强渲染效果? 4. 如何处理高密度圆柱数据的LOD(细节层次)优化? [^1]: Qt信号槽传递复杂数据结构需要注册元类型 [^2]: Q_DECLARE_METATYPE宏用于注册自定义类型 [^3]: QVector越界访问会导致断言错误 [^4]: 序列化方案可用于数据库存储向量数据
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值