Qt中绘制不规则控件

在Qt中绘制不规则控件可通过设置遮罩(Mask)实现。以下是详细步骤:

  1. ‌继承目标控件‌:如QPushButton或QWidget。
  2. ‌重写resizeEvent‌:当控件大小变化时,更新遮罩形状。
  3. ‌创建遮罩区域‌:使用QRegion或QPainterPath定义形状(如圆形、多边形)。
  4. ‌应用遮罩‌:使用setMask方法设置控件的可见区域。
  5. ‌重写paintEvent‌:绘制控件外观,确保与遮罩一致。
  6. ‌处理事件区域‌:如重写hitButton检查点击事件是否在遮罩内。

一、示例代码:圆形按钮

cpp 复制代码
#include <QPushButton>
#include <QPainter>
#include <QResizeEvent>
#include <QRegion>

class CircleButton : public QPushButton {
public:
    CircleButton(QWidget *parent = nullptr) : QPushButton(parent) {
        setFixedSize(100, 100); // 建议设置为正方形以确保正圆
    }

protected:
    void resizeEvent(QResizeEvent *event) override {
        // 创建圆形遮罩
        QRegion region(rect(), QRegion::Ellipse);
        setMask(region);
        QPushButton::resizeEvent(event);
    }

    void paintEvent(QPaintEvent *event) override {
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing); // 抗锯齿
        painter.setBrush(Qt::blue); // 填充颜色
        painter.drawEllipse(rect()); // 绘制圆形
        painter.setPen(Qt::white);
        painter.drawText(rect(), Qt::AlignCenter, "Click Me"); // 文字
    }

    bool hitButton(const QPoint &pos) const override {
        // 判断点击位置是否在圆内
        QPoint center = rect().center();
        int radius = width() / 2;
        int dx = pos.x() - center.x();
        int dy = pos.y() - center.y();
        return (dx*dx + dy*dy) <= (radius * radius);
    }
};

二、示例代码:不规则窗口

cpp 复制代码
#include <QMainWindow>
#include <QPainter>
#include <QRegion>

class MainWindow : public QMainWindow {
public:
    MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) {
        setWindowFlags(Qt::FramelessWindowHint); // 无边框
        setAttribute(Qt::WA_TranslucentBackground); // 透明背景
        resize(300, 300);
    }

protected:
    void paintEvent(QPaintEvent *event) override {
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing);
        painter.setBrush(Qt::blue);
        painter.drawEllipse(rect()); // 绘制窗口内容
    }

    void resizeEvent(QResizeEvent *event) override {
        QRegion region(rect(), QRegion::Ellipse);
        setMask(region); // 设置窗口遮罩
    }
};

三、图像遮罩创建不规则形状的窗口

文件 mainwindow.h

cpp 复制代码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QPixmap>
#include <QMouseEvent>

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

protected:
    void paintEvent(QPaintEvent *event) override;   // 绘制窗口
    void mousePressEvent(QMouseEvent *event) override; // 点击事件
    void mouseMoveEvent(QMouseEvent *event) override;  // 拖动窗口

private:
    QPixmap m_pixmap;      // 存储形状图片
    QPoint m_dragPos;      // 记录拖动位置
    bool isPointValid(const QPoint &pos); // 检查点击位置是否有效
};

#endif // MAINWINDOW_H

文件 mainwindow.cpp

cpp 复制代码
#include "mainwindow.h"
#include <QPainter>
#include <QBitmap>
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    // 加载图片(确保资源路径正确)
    m_pixmap.load(":/images/shape.png");
    if (m_pixmap.isNull()) {
        qWarning("Failed to load image!");
        return;
    }

    // 设置窗口属性
    setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
    setAttribute(Qt::WA_TranslucentBackground);
    setFixedSize(m_pixmap.size()); // 窗口大小与图片一致

    // 设置遮罩(仅显示非透明区域)
    setMask(m_pixmap.mask());
}

MainWindow::~MainWindow() {}

// 绘制窗口
void MainWindow::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event);
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing); // 抗锯齿
    painter.drawPixmap(0, 0, m_pixmap);
}

// 检查点击位置是否在非透明区域
bool MainWindow::isPointValid(const QPoint &pos)
{
    if (m_pixmap.isNull()) return true;
    return m_pixmap.toImage().pixelColor(pos).alpha() > 0;
}

// 鼠标按下事件
void MainWindow::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton && isPointValid(event->pos())) {
        m_dragPos = event->globalPos() - frameGeometry().topLeft();
        event->accept();
    } else {
        event->ignore(); // 透明区域不响应点击
    }
}

// 鼠标拖动事件
void MainWindow::mouseMoveEvent(QMouseEvent *event)
{
    if (event->buttons() & Qt::LeftButton) {
        move(event->globalPos() - m_dragPos);
        event->accept();
    }
}

文件 main.cpp

cpp 复制代码
#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

关键代码解释

  1. ‌设置遮罩‌

    cpp 复制代码
    setMask(m_pixmap.mask()); // 使用图片的 alpha 通道生成遮罩

    这一步使得窗口仅显示图片的非透明区域。

  2. ‌透明背景‌

    cpp 复制代码
    setAttribute(Qt::WA_TranslucentBackground); 
    setWindowFlags(Qt::FramelessWindowHint);

    确保窗口背景透明,避免残留默认边框。

  3. ‌点击有效性检查‌

    cpp 复制代码
    bool MainWindow::isPointValid(const QPoint &pos) {
        return m_pixmap.toImage().pixelColor(pos).alpha() > 0;
    }

    通过检查像素的透明度,决定是否响应点击事件。

相关推荐
小羊没烦恼!1 小时前
T-SQL查询进阶--理解SQL SERVER中的分区表
服务器·开发语言·前端·javascript·html
爱吃香菜的初学者1 小时前
十六.Linux——多线程(2)
linux·运维·开发语言
jeo陽1 小时前
04-循环实战:素数、最大公约数、级数求和与枚举法
c语言·开发语言·数据结构
暖核2 小时前
kubeadm 集群运维:Worker 节点下线删除、整套集群销毁重建指南
运维·开发语言·php
tachibana22 小时前
Go 语言里的 Context 是什么?
开发语言·后端·golang·go·语言
天桥下的卖艺者2 小时前
R语言使用ggcube包轻松绘制文章中的各类3D图形
开发语言·3d·r语言
唐钰小宝B4 小时前
深度解读 JDK 8、JDK 11、JDK 17 和 JDK 21 的区别
java·开发语言·windows
知识分享小能手4 小时前
C学习教程,从入门到精通,C语言指针 —— 详细知识点总结(10)
c语言·开发语言·学习
熊猫钓鱼>_>4 小时前
开源鸿蒙平台 KMP 三方库 kotlin-csv 适配全流程:从编码假设到沙箱真机互通验证
开发语言·人工智能·华为·kotlin·harmonyos·鸿蒙
张张123y4 小时前
# 后端第七课:FastAPI 实战入门(用刚学的 Python 写第一个 API 接口)
开发语言·python·mysql·api·fastapi