Qt:悬浮按钮

9 篇文章 1 订阅

Qt:悬浮按钮

前言

基于QPushButton实现的悬浮按钮功能,并且加入了一些动画,目前实现了基本的功能,满足可用,其他功能细节正在完善中。实现思路计算坐标然后绘制,尽可能减少贴图的使用。
效果图如下,代码自取,请放心食用。有好的想法建议欢迎留言交流,会不定期回复。

效果图

在效果图

源码

懒得写描述了,自己看吧,直接拿去用也不是不行。

fr_floating_action_button.h

#ifndef FR_FLOATING_ACTION_BUTTON_H
#define FR_FLOATING_ACTION_BUTTON_H

#include <stdint.h>

#include <qpushbutton.h>
#include <qanimationgroup.h>
#include <qpropertyanimation.h>


class FrFloatingActionButton : public QPushButton {
  Q_OBJECT

public:
  explicit FrFloatingActionButton(QWidget *parent, uint8_t index);
  virtual ~FrFloatingActionButton();

  void set_path(const QString &path);
  void set_text(const QString &text);
  void set_icon(const QString &icon);

  void Show(bool is);
  void ReCalAnimation();

protected:
  virtual void paintEvent(QPaintEvent *event);
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
  virtual void enterEvent(QEnterEvent *event);
#else
  virtual void enterEvent(QEvent *event);
#endif
  virtual void leaveEvent(QEvent *event);

private:
  void draw_border(QPainter& painter);
  void draw_text(QPainter& painter);

private slots:
  void transpond_click_signal();

signals:
  void Clicked(QString text, QString path, quint8 index);
  void ButtonActived(quint8 index, bool is);

private:
  const uint8_t index_;
  QString path_;
  QString text_;
  QString icon_;

  QPropertyAnimation* zoom_in_;
  QPropertyAnimation* zoom_out_;

  QPropertyAnimation* float_out_;
  QPropertyAnimation* float_back_;
};

#endif // FR_FLOATING_ACTION_BUTTON_H

fr_floating_action_button.cpp

#include "fr_floating_action_button.h"

#include <math.h>

#include <qevent.h>
#include <qpainter.h>


#define DURATION 100

FrFloatingActionButton::FrFloatingActionButton(QWidget *parent, uint8_t index)
  : QPushButton(parent)
  , index_(index)
  , zoom_in_(new QPropertyAnimation(this, "geometry", this))
  , zoom_out_(new QPropertyAnimation(this, "geometry", this))
  , float_out_(new QPropertyAnimation(this, "pos", this))
  , float_back_(new QPropertyAnimation(this, "pos", this)) {

  connect(this, &FrFloatingActionButton::clicked, this, &FrFloatingActionButton::transpond_click_signal);
  connect(zoom_out_, &QPropertyAnimation::finished, this, &FrFloatingActionButton::hide);
}

FrFloatingActionButton::~FrFloatingActionButton() {
}

void FrFloatingActionButton::set_path(const QString &path) {
  path_ = path;
}

void FrFloatingActionButton::set_text(const QString &text) {
  text_ = text;
}

void FrFloatingActionButton::set_icon(const QString &icon) {
  icon_ = icon;
}

void FrFloatingActionButton::Show(bool is) {
  if (index_ == 0) {
    setVisible(is);
    return;
  }

  if (path_.isEmpty()) {
    setVisible(false);
    return;
  }

  if (is) {
    if (!isVisible()) {
      setVisible(is);
      zoom_in_->start();
    }
  } else {
    zoom_out_->start();
  }
}

void FrFloatingActionButton::ReCalAnimation() {
  zoom_in_->setStartValue(QRect(pos() * 1.25 , size() / 2.0));
  zoom_in_->setEndValue(QRect(pos(), size()));
  zoom_in_->setDuration(DURATION);

  zoom_out_->setStartValue(QRect(pos(), size()));
  zoom_out_->setEndValue(QRect(pos() * 1.25 , size() / 2.0));
  zoom_out_->setDuration(DURATION);

  QPoint offset;
  double step = width() * 0.025;

  switch (index_) {
    case 1:
      offset = QPoint(0, -step * 2);
      break;
    case 2:
      offset = QPoint(step * std::sqrt(3), -step);
      break;
    case 3:
      offset = QPoint(step * std::sqrt(3), step);
      break;
    case 4:
      offset = QPoint(0, step * 2);
      break;
    case 5:
      offset = QPoint(-step * std::sqrt(3), step);
      break;
    case 6:
      offset = QPoint(-step * std::sqrt(3), -step);
      break;
    default:
      break;
  }
  float_out_->setStartValue(pos());
  float_out_->setEndValue(pos() + offset);
  float_out_->setDuration(DURATION);

  float_back_->setStartValue(pos() + offset);
  float_back_->setEndValue(pos());
  float_back_->setDuration(DURATION);
}

void FrFloatingActionButton::paintEvent(QPaintEvent *event) {
  double side = qMin(double(width()), double(height()) / (std::sqrt(3) / 2.0));

  QPainter painter(this);

  painter.setRenderHint(QPainter::Antialiasing);
  painter.translate(width() / 2, height() / 2);
  painter.scale(side / 210.0, side / 210.0);
  painter.setPen(Qt::NoPen);
  painter.setBrush(Qt::NoBrush);

  draw_border(painter);
  draw_text(painter);
}

void FrFloatingActionButton::draw_border(QPainter &painter) {
  painter.save();

  QPen pen(Qt::darkCyan, 2);

  painter.setPen(pen);
  painter.setBrush(QColor(78, 128, 196, 128));

  static double sqtr3 = std::sqrt(3);
  QPointF* points = new QPointF[6];
  int i = 0;
  points[i++] = QPointF(50.0, sqtr3 * 50.0);
  points[i++] = QPointF(100, 0.0);
  points[i++] = QPointF(50.0, -sqtr3 * 50.0);
  points[i++] = QPointF(-50.0, -sqtr3 * 50.0);
  points[i++] = QPointF(-100.0, 0.0);
  points[i++] = QPointF(-50.0, sqtr3 * 50.0);
  painter.drawPolygon(points, 6);
  delete[] points;

  painter.restore();
}

void FrFloatingActionButton::draw_text(QPainter &painter) {
  painter.save();

  painter.setPen(Qt::black);
  painter.setFont(QFont("Times", 24));
  painter.drawText(QRectF(-100, -100, 200, 200), Qt::AlignCenter, text_);

  painter.restore();
}

void FrFloatingActionButton::transpond_click_signal() {
  emit Clicked(text_, path_, index_);
}

#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
void FrFloatingActionButton::enterEvent(QEnterEvent *event) {
#else
void FrFloatingActionButton::enterEvent(QEvent *event) {
#endif
  float_out_->start();
  emit ButtonActived(1 << index_, true);
}

void FrFloatingActionButton::leaveEvent(QEvent *event) {
  float_back_->start();
  emit ButtonActived(1 << index_, false);
}

fr_floating_action_button_panel.h

#ifndef FR_FLOATING_ACTION_BUTTON_PANEL_H
#define FR_FLOATING_ACTION_BUTTON_PANEL_H

#include <stdint.h>

#include <qwidget.h>
#include <qvariant.h>
#include <qjsondocument.h>
#include <qtimer.h>


class FrFloatingActionButton;

class FrFloatingActionButtonPanel : public QWidget
{
  Q_OBJECT

public:
  explicit FrFloatingActionButtonPanel(QWidget *parent = nullptr);
  ~FrFloatingActionButtonPanel();

protected:
  virtual void resizeEvent(QResizeEvent *event);
  virtual void showEvent(QShowEvent *event);

private:
  bool load_configuer();
  int load_buttons(const QString& path);
  void adjust_buttons_size();

private slots:
  void process_click(const QString& text, const QString& path, quint8 index);
  void active_buttons(quint8 index, bool is);
  void deactive_buttons();
  void zoom_in_button();
  void zoom_out_button();

signals:
  void Clicked(QString text, QString path);

private:
  QVariant values_;

  FrFloatingActionButton** buttons_;

  uint8_t actived_buttons_;
  QTimer* keep_timer_;

  QTimer* zoom_in_timer_;
};

#endif // FR_FLOATING_ACTION_BUTTON_PANEL_H

fr_floating_action_button_panel.cpp

#include "fr_floating_action_button_panel.h"

#include <stdint.h>
#include <math.h>

#include <qpainter.h>
#include <qevent.h>
#include <qfile.h>
#include <qstring.h>

#include <fr_floating_action_button.h>


FrFloatingActionButtonPanel::FrFloatingActionButtonPanel(QWidget *parent)
  : QWidget(parent)
  , buttons_(nullptr)
  , actived_buttons_(0)
  , keep_timer_(new QTimer(this))
  , zoom_in_timer_(new QTimer(this)) {

  setWindowFlags(Qt::FramelessWindowHint | Qt::Tool | Qt::WindowStaysOnTopHint);
  setAttribute(Qt::WA_TranslucentBackground);

  buttons_ = new FrFloatingActionButton*[7];
  for (int i = 0; i < 7; ++i) {
    buttons_[i] = new FrFloatingActionButton(this, i);

    connect(buttons_[i], &FrFloatingActionButton::Clicked, this, &FrFloatingActionButtonPanel::process_click);
    connect(buttons_[i], &FrFloatingActionButton::ButtonActived, this, &FrFloatingActionButtonPanel::active_buttons);
    buttons_[i]->setVisible(i == 0);
  }

  keep_timer_->setSingleShot(true);
  connect(keep_timer_, &QTimer::timeout, this, &FrFloatingActionButtonPanel::deactive_buttons);

  bool ok = load_configuer();
  if (ok) {
    load_buttons(QString());
  } else {
    ; //配置文件错误
  }
}

FrFloatingActionButtonPanel::~FrFloatingActionButtonPanel() {
  if (nullptr != buttons_) {
    delete[] buttons_;
    buttons_ = nullptr;
  }
}

void FrFloatingActionButtonPanel::resizeEvent(QResizeEvent *event) {
  QWidget::resizeEvent(event);
  adjust_buttons_size();
}


void FrFloatingActionButtonPanel::showEvent(QShowEvent *event) {
  QWidget::showEvent(event);
  adjust_buttons_size();
}

bool FrFloatingActionButtonPanel::load_configuer() {
  QFile file("system_buttons/config.json");
  if(!file.open(QIODevice::ReadWrite)) {
    return false;
  }

  QByteArray json = file.readAll();

  QJsonParseError ok;
  values_ = QJsonDocument::fromJson(json, &ok).toVariant();

  return ok.error == QJsonParseError::NoError;
}

int FrFloatingActionButtonPanel::load_buttons(const QString &path) {
  auto values = values_.toList();
  auto tmp = path;

  QVariantMap last;
  last["index"] = QString();
  last["text"] = QString("哦吼");
  last["icon"] = QString("");

  while (!tmp.isEmpty()) {
    for (int i = 0; i < values.size(); ++i) {
      auto map = values[i].toMap();

      if(map.value("index").toString() == tmp.left(1)) {
        last = map;
        tmp.remove(0, 1);
        values = map.value("children").toList();
        continue;
      }
    }
  }

  if (values.isEmpty()) {
    return 0;
  }

  for (int i = 1; i < 7; ++i) {
    buttons_[i]->hide();
    buttons_[i]->set_path(QString());
  }

  values.insert(0, last);
  for (int i = 0; i < values.size() && i < 7; ++i) {
    auto map = values[i].toMap();
    if (i == 0) {
      buttons_[i]->set_path(path);
    } else {
      buttons_[i]->set_path(path + map.value("index").toString());
    }
    buttons_[i]->set_text(map.value("text").toString());
    buttons_[i]->set_icon(map.value("icon").toString());
    buttons_[i]->setVisible((i == 0) | !path.isEmpty());
  }

  buttons_[0]->update();

  return values.size() - 1;
}

void FrFloatingActionButtonPanel::adjust_buttons_size() {
  int margin = 0;
  bool by_witdh = double(width() - margin * 2) / 5.0 < double(height() - margin * 2) / (3 * std::sqrt(3));

  int button_width = 0, button_height = 0;
  if (by_witdh) {
    margin = width() / 20.0;
    button_width = double(width() - margin * 2) * 0.4;
    button_height = double(width() - margin * 2) * 0.2 * sqrt(3);
  } else {
    margin = height() / 20.0;
    button_width = double(height() - margin * 2) / (3.0 * sqrt(3)) * 2;
    button_height = double(height() - margin * 2) / 3.0;
  }

  QList<QPoint> poses;
  poses << QPoint(button_width * 0.75 + margin, button_height + margin)
        << QPoint(button_width * 0.75 + margin, margin)
        << QPoint(button_width * 1.50 + margin, button_height / 2 + margin)
        << QPoint(button_width * 1.50 + margin, button_height * 1.5 + margin)
        << QPoint(button_width * 0.75 + margin, button_height * 2.0 + margin)
        << QPoint(margin, button_height * 1.5 + margin)
        << QPoint(margin, button_height / 2 + margin);

  for (int i = 0; i < 7; ++i) {
    buttons_[i]->setGeometry(poses[i].x(), poses[i].y(), button_width, button_height);
    buttons_[i]->ReCalAnimation();
  }
}

void FrFloatingActionButtonPanel::process_click(const QString &text, const QString &path, quint8 index) {
  if (index == 0) {
    // 向上
    if (!path.isEmpty()) {
      load_buttons(path.left(path.size() - 1));
    } else {
      // 进入设置模式
    }
  } else {
    // 向下
    if(load_buttons(path) == 0) {
      emit Clicked(text, path);
    }
  }
}

void FrFloatingActionButtonPanel::active_buttons(quint8 index, bool is) {
  if (is) {
    if (index == 1) {
      zoom_in_timer_->start(25);

      connect(zoom_in_timer_, &QTimer::timeout, this, &FrFloatingActionButtonPanel::zoom_in_button);
      disconnect(zoom_in_timer_, &QTimer::timeout, this, &FrFloatingActionButtonPanel::zoom_out_button);
    }
    keep_timer_->stop();
  } else {
    keep_timer_->start(200);
  }
}

void FrFloatingActionButtonPanel::deactive_buttons() {
  zoom_in_timer_->start(25);
  disconnect(zoom_in_timer_, &QTimer::timeout, this, &FrFloatingActionButtonPanel::zoom_in_button);
  connect(zoom_in_timer_, &QTimer::timeout, this, &FrFloatingActionButtonPanel::zoom_out_button);
}

void FrFloatingActionButtonPanel::zoom_in_button() {
  bool is = false;
  for (int i = 1; i < 7; ++i) {
    if (!buttons_[i]->isVisible()) {
      buttons_[i]->Show(true);
      is = true;
      break;
    }
  }

  if (!is) {
    zoom_in_timer_->stop();
  }
}

void FrFloatingActionButtonPanel::zoom_out_button() {
  static int i = 1;

  buttons_[i++]->Show(false);

  i %= 7;

  if (i == 0) {
    zoom_in_timer_->stop();
    i = 1;
  }
}

json配置文件格式样例

[
{
    "index":1,
    "text":"功能a",
    "children":[
        {
            "index":1,
            "text":"功能aa",
            "children":[
                {
                    "index":1,
                    "text":"功能aaa",
                    "children":[
                    ]
                },
                {
                    "index":2,
                    "text":"功能aab",
                    "children":[
                    ]
                },
                {
                    "index":3,
                    "text":"功能aac",
                    "children":[
                    ]
                }
            ]
        },
        {
            "index":2,
            "text":"功能ab",
            "children":[]
        },
        {
            "index":3,
            "text":"功能ac",
            "children":[]
        },
        {
            "index":4,
            "text":"功能ad",
            "children":[]
        },
        {
            "index":5,
            "text":"功能ae",
            "children":[]
        }
    ]
},
{
    "index":2,
    "text":"功能b",
    "children":[
    ]
},
{
    "index":3,
    "text":"功能c",
    "children":[
    ]
},
{
    "index":4,
    "text":"功能d",
    "children":[
    ]
},
{
    "index":5,
    "text":"功能e",
    "children":[
    ]
},
{
    "index":6,
    "text":"功能f",
    "children":[
    ]
}
]
  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Qt是一个跨平台的应用程序开发框架,提供了丰富的库和工具,供开发者使用来创建各种类型的应用程序。VLC是一个开源的媒体播放器,支持多种音频和视频格式。 悬浮按钮是指在界面上以浮动的方式展示出来的按钮,通常用于提供快捷的操作入口或者对某个功能进行开关操作。在Qt中,我们可以使用QToolButton或QPushButton实现悬浮按钮的效果。 首先,我们需要创建一个新的窗口或者对话框来容纳悬浮按钮。然后,我们可以使用QToolButton或QPushButton来创建一个按钮,并且设置其父对象为我们的窗口或者对话框。 悬浮按钮的特性可以通过设置按钮的属性来实现。比如,我们可以设置按钮的大小、位置、文本、图标、样式等。此外,还可以为按钮添加相应的信号和槽函数,以实现按钮被点击时的响应。 对于Qt中的悬浮按钮来说,可以使用setToolButtonStyle函数来设置按钮的样式,以实现悬浮按钮的外观效果。Qt还提供了一些现成的样式,可以根据需要选择合适的样式,或者自定义样式来满足我们的需求。 对于VLC的悬浮按钮来说,则需要借助VLC的API来实现。VLC提供了丰富的接口,供开发者使用来控制播放器的各种功能。我们可以通过使用VLC的API来创建悬浮按钮,并且设置按钮的行为和外观。 总之,Qt和VLC都提供了丰富的功能来实现悬浮按钮的效果。通过合理使用这些框架和工具,我们可以轻松地在应用程序中添加悬浮按钮,提供更好的用户体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值