使用Qt制作一个流程变更申请流程进度以及未读消息提醒

news2024/11/28 10:06:35

1.1加载界面:

界面要素:
成员信息
变更位置申请
接受消息列表
根据角色加载对应界面。

1.2发起变更申请:

  • 用户点击“发起变更申请”按钮。
  • 变更申请对话框可编辑,
  • 用户填写申请信息:
    申请方(自动填充)。
    目的方(选择老师或组长)。
    若为老师角色 则可选其他组(除副班长) 若为组长则固定老师
  • 提交申请:
  • 流程显示
  • 查看对应未读消息提醒
  • 处理未读信息

界面效果展示

在这里插入图片描述

  • 用户点击“提交”按钮,将申请信息发送至老师

1.3目标方(老师或组长)查看或待处理申请列表。

  • 选择某个申请查看详细信息同意或驳回申请:
    驳回 内容清空
    目标方选择操作:
    同意:
  • 更新申请状态 为“已同意”。
  • 更新流程操作1\2\3
    更新申请方的组别信息(如果目的方是老师)。
    通知申请方其申请已被同意。
    驳回:
    更新申请状态为“已驳回”。
    通知申请方其申请未被批准。
    记录变更历史:

记录所有变更申请的历史信息。
返回申请列表:

目标方返回变更申请列表,继续处理其他申请。

2 源代码展示

2.1 mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QTreeWidget>
#include <QComboBox>
#include <QPushButton>
#include <QVBoxLayout>
#include <QLabel>
#include <QHBoxLayout>
#include <QWidget>
#include <QTabWidget>
#include <QTableWidget>
#include <QTableWidgetItem>
#include <QStackedWidget>

#include "processprogressdisplaywidget.h"
#include "BubblelTipButton.h"
class MainWindow : public QMainWindow {
    Q_OBJECT

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

private:
    void setupMembersListTab();  // 设置成员列表标签页
    void setupDynamicInfoTab();  // 设置动态信息标签页
    void fillTargetComboBox();    // 填充目标方ComboBox

private slots:
    void onMemberSelected(QTreeWidgetItem *item);  // 响应成员选择
    void onInitiateChangeClicked();                  // 响应发起变更按钮点击
    void onSendRequestClicked();                      // 响应发送请求按钮点击

    void OnPage1ButtonAddClicked();//列表按钮槽函数
    void OnPage2ButtonAddClicked();//消息按钮槽函数
    void confirmMessage(); // 确认消息的槽函数
    void rejectMessage(); // 驳回消息的槽函数

private:
    QStackedWidget *stackedWidget; // 堆叠窗口,用于切换不同页面
    QWidget *page1;
    QWidget *page2;
    QPushButton *page1Button;              // 发起变更按钮
    BubblelTipButton  *page2Button;              // 发起变更按钮
     QPushButton *page3Button;              // 发起变更按钮
    QTreeWidget *membersTreeWidget;                  // 成员树形控件
    QComboBox *applicantComboBox;                    // 申请方ComboBox
    QComboBox *targetComboBox;                       // 目标方ComboBox
    QPushButton *initiateChangeButton;              // 发起变更按钮
    QPushButton *sendButton;                         // 发送按钮
    QTabWidget *tabWidget;                           // 标签控件
    QTableWidget *dynamicInfoTable;                 // 动态信息显示表格

    ProcessProgressDisplayWidget* DisplayW;
     int messageCount; // 记录当前消息数量
};

#endif // MAINWINDOW_H


2.2 mainwindow.cpp

#include "mainwindow.h"
#include <QApplication>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QComboBox>
#include <QPushButton>
#include <QTreeWidget>
#include <QTableWidget>
#include <QHeaderView>

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),messageCount(0)
{
    setWindowTitle("人员分配管理系统");

    QVBoxLayout *verticalLayout = new QVBoxLayout();
    verticalLayout->setSpacing(0);
    verticalLayout->setObjectName(QString::fromUtf8("verticalLayout"));
    verticalLayout->setContentsMargins(0, 0, 0, 0);
    page1Button = new QPushButton(this);
    page1Button->setStyleSheet("QPushButton { "
                               "background-image: url(../Data/list.png); "
                               "border: none; "
                               "text-align: center; "
                               "color: white; "
                               "padding: 0px; "
                               "}");
    //page1Button->setText("链表");
    page1Button->setMinimumSize(QSize(30, 30));
    page1Button->setMaximumSize(QSize(30, 30));
    page2Button = new BubblelTipButton(this);
    page2Button->setText("消息");
    page2Button->setMinimumSize(QSize(40, 30));
    page2Button->setMaximumSize(QSize(40, 30));
    // page3Button = new QPushButton(this);
    QSpacerItem *verticalSpacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
    verticalLayout->addWidget(page1Button);
    verticalLayout->addWidget(page2Button);
    // verticalLayout->addWidget(page3Button);
    verticalLayout->addItem(verticalSpacer);
    verticalLayout->setSpacing(2);

    QVBoxLayout* Vlayout = new QVBoxLayout();
    QHBoxLayout *layout = new QHBoxLayout();
    layout->addLayout(verticalLayout);
    layout->addLayout(Vlayout);
    layout->setStretch(1, 10);
    QWidget* CurrentWidget = new QWidget(this);
    CurrentWidget->setLayout(layout);
    setCentralWidget(CurrentWidget);

    stackedWidget = new QStackedWidget(this);
    page1 = new QWidget();
    page2 = new QWidget();

    setupMembersListTab();
    setupDynamicInfoTab();
    stackedWidget->addWidget(page1);
    stackedWidget->addWidget(page2);
    Vlayout->addWidget(stackedWidget);
    connect(page1Button, SIGNAL(clicked()), this, SLOT(OnPage1ButtonAddClicked()));
    connect(page2Button, SIGNAL(clicked()), this, SLOT(OnPage2ButtonAddClicked()));
}

MainWindow::~MainWindow() {
    // 析构函数
}

void MainWindow::setupMembersListTab() {
    QHBoxLayout *layout = new QHBoxLayout();

    // 左侧树形控件显示成员信息
    membersTreeWidget = new QTreeWidget(this);
    membersTreeWidget->setHeaderLabels(QStringList() << "成员信息");

    // 添加成员信息
    QTreeWidgetItem *teacherA = new QTreeWidgetItem(membersTreeWidget, QStringList() << "老师A");
    QTreeWidgetItem *groupLeaderB = new QTreeWidgetItem(membersTreeWidget, QStringList() << "组长B");
    QTreeWidgetItem *deputyB1 = new QTreeWidgetItem(groupLeaderB, QStringList() << "副组长B1");
    new QTreeWidgetItem(groupLeaderB, QStringList() << "成员H2");
    new QTreeWidgetItem(groupLeaderB, QStringList() << "成员H3");
    new QTreeWidgetItem(groupLeaderB, QStringList() << "成员H4");

    QTreeWidgetItem *groupLeaderC = new QTreeWidgetItem(membersTreeWidget, QStringList() << "组长C");
    QTreeWidgetItem *deputyC1 = new QTreeWidgetItem(groupLeaderC, QStringList() << "副组长C1");
    new QTreeWidgetItem(groupLeaderC, QStringList() << "成员H7");
    new QTreeWidgetItem(groupLeaderC, QStringList() << "成员H8");
    new QTreeWidgetItem(groupLeaderC, QStringList() << "成员H9");

    QTreeWidgetItem *groupLeaderD = new QTreeWidgetItem(membersTreeWidget, QStringList() << "组长D");
    QTreeWidgetItem *deputyD1 = new QTreeWidgetItem(groupLeaderD, QStringList() << "副组长D1");
    new QTreeWidgetItem(groupLeaderD, QStringList() << "成员H10");
    new QTreeWidgetItem(groupLeaderD, QStringList() << "成员H11");
    new QTreeWidgetItem(groupLeaderD, QStringList() << "成员H12");

    layout->addWidget(membersTreeWidget, 1);

    // 右侧变更申请信息区域
    QWidget *requestWidget = new QWidget(this);
    QVBoxLayout *requestLayout = new QVBoxLayout(requestWidget);

    applicantComboBox = new QComboBox(this);
    applicantComboBox->setEditable(true);
    targetComboBox = new QComboBox(this);
    sendButton = new QPushButton("发送申请", this);
    initiateChangeButton = new QPushButton("发起变更", this);
    initiateChangeButton->setEnabled(false); // 禁用发起变更按钮
    applicantComboBox->setEnabled(false);
    targetComboBox->setEnabled(false);
    sendButton->setEnabled(false);  // 初始禁用发送按钮

    // 添加布局
    QHBoxLayout *initiateLayout = new QHBoxLayout();
    initiateLayout->addWidget(new QLabel("变更位置申请信息", this));
    initiateLayout->addStretch();
    initiateLayout->addWidget(initiateChangeButton);

    requestLayout->addLayout(initiateLayout);
    DisplayW = new ProcessProgressDisplayWidget(this);
    DisplayW->setProcessList(QStringList()<<"编辑信息"<<"选择源"<<"待反馈");
    DisplayW->setCurrentStep(0);
    requestLayout->addWidget(DisplayW);

    requestLayout->addWidget(applicantComboBox);
    requestLayout->addWidget(targetComboBox);
    requestLayout->addWidget(sendButton);

    layout->addWidget(requestWidget, 3);

    connect(membersTreeWidget, &QTreeWidget::itemClicked, this, &MainWindow::onMemberSelected);
    connect(initiateChangeButton, &QPushButton::clicked, this, &MainWindow::onInitiateChangeClicked);
    connect(sendButton, &QPushButton::clicked, this, &MainWindow::onSendRequestClicked);

    page1->setLayout(layout);
}

void MainWindow::setupDynamicInfoTab() {
    QVBoxLayout *layout = new QVBoxLayout();

    dynamicInfoTable = new QTableWidget(this);
    dynamicInfoTable->setColumnCount(4);
    dynamicInfoTable->setHorizontalHeaderLabels(QStringList() << "申请方" << "目标方" << "申请信息" << "状态");
    // 设置最后一列自动填充
    dynamicInfoTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
    layout->addWidget(dynamicInfoTable);
    page2->setLayout(layout);
}

void MainWindow::fillTargetComboBox() {
    targetComboBox->clear();
    for (int i = 0; i < membersTreeWidget->topLevelItemCount(); ++i) {
        QTreeWidgetItem *item = membersTreeWidget->topLevelItem(i);
        targetComboBox->addItem(item->text(0));
        for (int j = 0; j < item->childCount(); ++j) {
            QTreeWidgetItem *child = item->child(j);
            QString memberName = child->text(0);
            // 检查成员是否为副组长
            if (!memberName.contains("副组长")) { // 过滤副组长
                targetComboBox->addItem(memberName); // 添加到目标组合框
            }
        }
    }
}

void MainWindow::onMemberSelected(QTreeWidgetItem *item) {
    if (item) {
        applicantComboBox->setCurrentText(item->text(0));  // 设置申请方
    }
    DisplayW->setCurrentStep(0);
    applicantComboBox->setEnabled(false);
    targetComboBox->setEnabled(false);
    sendButton->setEnabled(false);  // 启用发送按钮
    // 判断是否选择了副组长
    QString selectedItemText = item->text(0);
    if (selectedItemText.contains("副组长")) { // 假设副组长的名称包含“副组长”
        initiateChangeButton->setEnabled(false); // 禁用发起变更按钮
    } else {
        initiateChangeButton->setEnabled(true); // 启用发起变更按钮
    }

}

void MainWindow::onInitiateChangeClicked() {
    applicantComboBox->setEnabled(true);
    targetComboBox->setEnabled(true);
    sendButton->setEnabled(true);  // 启用发送按钮
    fillTargetComboBox();  // 填充目标方下拉框
    DisplayW->setCurrentStep(1);
}

void MainWindow::onSendRequestClicked() {
    int rowCount = dynamicInfoTable->rowCount();
    QString sender = "H2";
    QString destination = "D";
    QString reason = "位置变更申请";

    // Update Table Widget
    int row = dynamicInfoTable->rowCount();
    dynamicInfoTable->insertRow(row);
    dynamicInfoTable->setItem(row, 0, new QTableWidgetItem(sender));
    dynamicInfoTable->setItem(row, 1, new QTableWidgetItem(destination));
    dynamicInfoTable->setItem(row, 2, new QTableWidgetItem(reason));
    dynamicInfoTable->setRowHeight(row, 50); // 设置其他行高度为50
    // Status with buttons
    QWidget *statusWidget = new QWidget(this);
    QHBoxLayout *statusLayout = new QHBoxLayout(statusWidget);
    QPushButton *confirmButton = new QPushButton("确认");
    QPushButton *rejectButton = new QPushButton("驳回");

    // Set row and column data using properties
    confirmButton->setProperty("row", row);
    confirmButton->setProperty("col", 3);
    rejectButton->setProperty("row", row);
    rejectButton->setProperty("col", 3);
    // Add buttons to the layout
    statusLayout->addWidget(confirmButton);
    statusLayout->addWidget(rejectButton);
    statusWidget->setLayout(statusLayout);
    dynamicInfoTable->setCellWidget(row, 3, statusWidget);


    connect(confirmButton, SIGNAL(clicked()), this, SLOT(confirmMessage()));
    connect(rejectButton, SIGNAL(clicked()), this, SLOT(rejectMessage()));

    // Update message count
    messageCount++;
    page2Button->setMsgNumber(messageCount);

    applicantComboBox->clear();
    targetComboBox->clear();
    sendButton->setEnabled(false);  // 发送后禁用按钮
    DisplayW->setCurrentStep(2);
}

void MainWindow::OnPage1ButtonAddClicked()
{
    stackedWidget->setCurrentIndex(0);
}

void MainWindow::OnPage2ButtonAddClicked()
{
    stackedWidget->setCurrentIndex(1);
}

void MainWindow::confirmMessage()
{
    QPushButton *button = qobject_cast<QPushButton *>(sender());
    int row = button->property("row").toInt(); // 获取行索引
    int col = 3; // 状态列

    dynamicInfoTable->removeCellWidget(row, 3); // 删除旧的按钮 widget
    dynamicInfoTable->setItem(row, 3, new QTableWidgetItem("已确认"));
    messageCount--;
    page2Button->setMsgNumber(messageCount);
    stackedWidget->setCurrentIndex(0);
}

void MainWindow::rejectMessage()
{
    QPushButton *button = qobject_cast<QPushButton *>(sender());
    int row = button->property("row").toInt(); // 获取行索引
    int col = 3; // 状态列
    dynamicInfoTable->removeCellWidget(row, 3); // 删除旧的按钮 widget
    dynamicInfoTable->setItem(row, 3, new QTableWidgetItem("已驳回"));
    messageCount--;
    page2Button->setMsgNumber(messageCount);
    stackedWidget->setCurrentIndex(1);
}

2.3 processprogressdisplaywidget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

//流程进度展示控件
class ProcessProgressDisplayWidget : public QWidget
{
    Q_OBJECT

public:
    ProcessProgressDisplayWidget(QWidget *parent = nullptr);
    ~ProcessProgressDisplayWidget()override;
    void setProcessList(const QStringList &value);
    void setCurrentStep(int value);

protected:
    void paintEvent(QPaintEvent *event)override;

private:
    QStringList processList;
    int currentStep{1};
    QColor lineColor{"#C0C4CC"};
    QColor stepsCompletedColor{"#41CD52"};//已完成步骤的颜色
    QColor stepUncompleteColor{"#333333"};//未完成步骤的颜色
    QColor currentStepColor{"#FC5531"};//当前步骤颜色
};
#endif // WIDGET_H


2.4 processprogressdisplaywidget.cpp

实现了一个自定义的小部件,用于展示一个过程进度条,并显示各步骤的圆形节点

  • drawSmallCircle 是一个辅助函数,用于绘制带渐变颜色的圆形,以及圆形内居中的文本
    函数通过 QPainter::save() 和 QPainter::restore() 来确保绘制时的状态不影响外部环境。
    使用了 QLinearGradient 创建圆的渐变效果,增加视觉层次感
    根据控件的宽高、步骤数量,计算出每个节点圆圈的半径、间距和位置。
  • 循环遍历每个步骤节点,根据 currentStep 来设置不同状态(已完成、当前步骤、未完成)的颜色,并绘制连接线和文本。
    每个步骤使用 drawSmallCircle 函数绘制,并在圆圈下方显示步骤名称
#include "processprogressdisplaywidget.h"
#include <QPainter>
#include <QPaintEvent>
#include <QDebug>

ProcessProgressDisplayWidget::ProcessProgressDisplayWidget(QWidget *parent)
    : QWidget(parent)
{
    setPalette(Qt::white); // 设置背景为白色
}

ProcessProgressDisplayWidget::~ProcessProgressDisplayWidget()
{
}

//drawSmallCircle 是一个辅助函数,用于绘制带渐变颜色的圆形,以及圆形内居中的文本
void drawSmallCircle(QPainter * painter, int x, int y,int radius,const QColor & color,const QString & text)
{
    QRect boundingRect = QRect(-radius,-radius,radius * 2,radius * 2);// 定义圆的边界矩形

    painter->save();// 保存当前的 QPainter 状态
    painter->translate(x,y);// 将绘制中心平移到 (x, y)

     // 创建线性渐变效果,从矩形的左上角到右下角
    QLinearGradient linearGradient(boundingRect.topLeft(),boundingRect.bottomRight());
    linearGradient.setColorAt(0.0,color); // 渐变起始颜色
    linearGradient.setColorAt(1.0,color.lighter()); // 渐变结束颜色

    painter->setBrush(linearGradient);// 设置画刷为线性渐变
    painter->setPen(QPen(QBrush(linearGradient), 3,Qt::SolidLine,Qt::RoundCap,Qt::RoundJoin));// 设置画笔,使用渐变的笔刷和圆角

    painter->drawEllipse(boundingRect);// 绘制圆形

    painter->setBrush(Qt::white);// 设置画刷为白色,用于绘制圆内部
    painter->drawEllipse(boundingRect.adjusted(1,1,-1,-1));// 绘制稍微缩小的白色圆,制造圆边效果

    painter->setPen(color);// 设置画笔颜色
    painter->drawText(boundingRect,Qt::AlignCenter,text);// 在圆形内居中绘制文字

    painter->restore();// 恢复 QPainter 状态
}

void ProcessProgressDisplayWidget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);// 创建 QPainter 对象,用于绘制控件
    painter.setRenderHint(QPainter::Antialiasing,true);// 开启反锯齿
    auto font = painter.font();
    font.setPixelSize(20);// 设置字体大小为20像素
    font.setBold(true);// 设置字体为粗体
    painter.setFont(font);

    auto rect = event->rect(); // 获取控件的矩形区域
    auto width = rect.width(); // 获取控件的宽度
    auto height = rect.height();// 获取控件的高度

    int lineY = static_cast<int>(height * 0.3) ;// 进度条的纵坐标位置
    int startX = 80;// 起始的横坐标
    int size = processList.size();// 获取步骤数量

    int radius = 20;// 圆的半径
    int spacerLenth = (width - 2 * startX - (radius * 2 * size)) / (size - 1);// 计算每两个圆之间的间距
    for(int i = 0;i < size;++i)
    {
        // 计算每个圆心的横坐标
        int x = startX + radius + (spacerLenth * i) + (radius * 2 * i);
        QColor * color;
        if(i < (size - 1))
        {
            painter.save();
            if(i < currentStep)
            {
                color = &stepsCompletedColor;// 已完成步骤的颜色
            }
            else
            {
                color = &lineColor;// 还未完成步骤的颜色
            }
            painter.setPen(QPen(*color,3));// 设置画笔颜色
            // 绘制连接两个圆的线条
            painter.drawLine(QPoint(x + radius,lineY),QPoint(x + radius + spacerLenth,lineY));
            painter.restore();
        }
        // 设置当前步骤、已完成步骤和未完成步骤的颜色
        if(i < currentStep)
        {
            color = &stepsCompletedColor; //已完成步骤
        }
        else if(i == currentStep)
        {
            color = &currentStepColor; //当前步骤
        }
        else
        {
            color = &stepUncompleteColor; //未完成步骤
        }
        // 绘制每个步骤的圆圈和数字
        drawSmallCircle(&painter,x,lineY+1,radius,*color,QString::number(i+1));

         // 计算文本框的宽度
        int textRectW;
        if(i == 0 || i == (size - 1))
        {
            textRectW = 2 * (startX - 20) + radius * 2;
        }
        else
        {
            textRectW = spacerLenth + radius * 2;
        }

        // 计算文本框的起始横坐标
        int rextRectX;
        if(i == 0)
        {
            rextRectX = 20;
        }
        else if(i == (size - 1))
        {
            rextRectX = startX + i * (radius * 2) + spacerLenth * (i - 1) + (spacerLenth - (textRectW / 2) + radius);
        }
        else
        {
            rextRectX = startX + spacerLenth/2 + spacerLenth * (i - 1) + i * 2 * radius;
        }

         // 计算文本框的位置和大小
        QRect textRect = QRect(rextRectX,
                               lineY + radius * 2,
                               textRectW,
                               height - lineY - radius * 2);
        // 设置画笔颜色
        painter.setPen(*color);
        // 绘制步骤名称
        painter.drawText(textRect,Qt::AlignTop | Qt::AlignHCenter,processList[i]);
    }
}

//设置当前步骤
void ProcessProgressDisplayWidget::setCurrentStep(int value)
{
    currentStep = value;
    update();
}

void ProcessProgressDisplayWidget::setProcessList(const QStringList &value)
{
    processList = value;
    currentStep = 0;
    update();
}

消息提示红点BubblelTipButton.h


#pragma once
 
#include <QPushButton>
 
class BubblelTipButton : public QPushButton
{
	Q_OBJECT
 
public:
	BubblelTipButton(QWidget *parent);
	~BubblelTipButton();
 
	void setMsgNumber(int number);
 
protected:
	virtual void paintEvent(QPaintEvent * event);
 
private:
	int bubbleWidth;
	int m_msgNumber;
};

消息提示红点BubblelTipButton.cpp

#include <QPainter>
#include <QIcon>
#include "BubblelTipButton.h"
 
BubblelTipButton::BubblelTipButton(QWidget *parent)
	: QPushButton(parent)
	, bubbleWidth(20)
	, m_msgNumber(0)
{
}
 
BubblelTipButton::~BubblelTipButton()
{
}
 
void BubblelTipButton::setMsgNumber(int number)
{
	m_msgNumber = number;
	update();
}
 
void BubblelTipButton::paintEvent(QPaintEvent * event)
{
	QPainter painter(this);
	QRect rt = rect();
	painter.setRenderHint(QPainter::Antialiasing, true);
 
	QPoint pointMsg(0, 15);
	QPixmap pixmapMsg(QString::fromLocal8Bit("../Data/TestImage/message.png"));
	//painter.drawPixmap(pointMsg, pixmapMsg);
	QRect rt2 = QRect(rt.left(), rt.top(), rt.right() - 10, rt.bottom());
	painter.drawPixmap(rt2, pixmapMsg); // 在按钮矩形区域内绘制图片  
	if (m_msgNumber > 0)
	{
		if (m_msgNumber > 9)
		{
			bubbleWidth = 23;
		}
		if (m_msgNumber > 99)
		{
			bubbleWidth = 27;
		}
		
		QRect rt1 = QRect(rt.right() - bubbleWidth, rt.top(), bubbleWidth, bubbleWidth);
		painter.setPen(Qt::red);
		painter.setBrush(QBrush(Qt::red));
		painter.drawEllipse(rt1);
 
		painter.setPen(Qt::white);
		painter.drawText(rt1, Qt::AlignCenter, QString::number(m_msgNumber));
	}
}

main.cpp

#include "mainwindow.h"

#include <QApplication>

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

2疑难点

int spacerLenth = (width - 2 * startX - (radius * 2 * size)) / (size - 1); 这行用于计算进度条上两个圆形之间的间距

2.1参数解释:

  • width: 窗口的总宽度。
  • startX: 第一个圆距离窗口左侧的偏移量。
  • radius: 圆的半径。
  • size: 总共的步骤数,也就是圆的数量

2.2 公式解释:

1.width - 2 * startX:这是去掉两边空白区域后,绘制所有圆和它们之间间距的总可用宽度。
2.radius * 2 * size:这是所有圆形的总宽度。因为每个圆的直径是 2 * radius,
所以总共有 size 个圆,它们的总宽度为 radius * 2 * size。
3.width - 2 * startX - (radius * 2 * size):这是除去圆形本身所占的宽度后,剩余的可用宽度(用于间距的总宽度)。
4.(size - 1):总共 size 个圆,那么圆与圆之间的间距总共会有 size - 1 个。

2.3 示例说明:

假设:

width = 600 (窗口宽度为600像素)
startX = 80 (圆形距离窗口左边和右边各80像素的空白)
radius = 20 (每个圆的半径为20像素)
size = 5 (总共有5个步骤/圆)

2.3.1计算可用绘制区域宽度

width−2∗startX=600−2∗80=440
去掉两边的空白区域后,可用宽度为440像素

2.3.2计算所有圆形所占的总宽度

radius∗2∗size=20∗2∗5=200
5个圆形的总宽度为200像素

2.3.3计算用于间距的总宽度

440−200=240
在所有圆形绘制完后,剩下240像素的空间用于圆之间的间距

2.3.4计算圆之间的间距

spacerLenth= 240/(5-1) = 240/4 = 60
所以,两个圆形之间的间距为60像素

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2235581.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Markdown 全面教程:从基础到高级

Markdown 全面教程&#xff1a;从基础到高级 Markdown 是一种轻量级的标记语言&#xff0c;它的设计目标是使书写和阅读文档变得简单而直观。无论是撰写博客、编写文档还是创建 README 文件&#xff0c;Markdown 都是一个非常实用的工具。 目录 Markdown 简介Markdown 的基…

无插件H5播放器EasyPlayer.js关于硬解码和软解码的详细介绍

在当今这个多媒体内容日益丰富的时代&#xff0c;视频播放体验的重要性不言而喻.EasyPlayer.js H5播放器作为一款专为现代Web环境设计的播放器&#xff0c;它不仅提供了流畅的播放体验&#xff0c;还特别注重性能优化。EasyPlayer.js支持多种解码方式&#xff0c;包括硬解码和软…

Multi‐modal knowledge graph inference via media convergenceand logic rule

摘要 媒体融合通过处理来自不同模式的信息并将其应用于不同的领域来实现。传统的知识图很难利用多媒体特征&#xff0c;因为从其他模态引入大量信息降低了表示学习的有效性&#xff0c;并降低了知识图推理的有效性。为了解决这一问题&#xff0c;提出了一种基于媒体融合和规则…

大模型应用编排工具Dify二开之工具和模型页面改造

1.前言 简要介绍下 dify&#xff1a; ​ 一款可以对接市面上主流大模型的任务编排工具&#xff0c;可以通过拖拽形式进行编排形成解决某些业务场景的大模型应用。 背景信息&#xff1a; ​ 环境&#xff1a;dify-0.8.3、docker-21 ​ 最近笔者在做 dify的私有化部署和二次…

【数学】通用三阶矩阵特征向量的快速求法 超简单!!!

目录 三个定理1、3个特征值&#xff08;即根互不相等&#xff09;例题实践2、2个特征值&#xff08;即有一个双重根&#xff09;3、1个特征值&#xff08;即有一个三重根&#xff09;定理证明 三个定理 本定理适用于 所有三阶矩阵 的特征向量求法&#xff01; 1、3个特征值&…

MapReduce 的 Shuffle 过程

MapReduce 的 Shuffle 过程指的是 MapTask 的后半程&#xff0c;以及ReduceTask的前半程&#xff0c;共同组成的。 从 MapTask 中的 map 方法结束&#xff0c;到 ReduceTask 中的 reduce 方法开始&#xff0c;这个中间的部分就是Shuffle。是MapReduce的核心&#xff0c;心脏。 …

【WebRTC】视频采集模块中各个类的简单分析

目录 1.视频采集模块中的类1.1 视频采集基础模块&#xff08;VideoCaptureModule&#xff09;1.2 视频采集工厂类&#xff08;VideoCaptureFactory&#xff09;1.3 设备信息的实现&#xff08;DeviceInfoImpl&#xff09;1.4 视频采集的实现&#xff08;VideoCaptureImpl&#…

江协科技STM32学习- P40 硬件SPI读写W25Q64

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是黄桃罐头&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​…

智慧场馆:安全、节能与智能化管理的未来

在当今社会&#xff0c;智慧场馆已经成为了现代场馆建设的一种重要模式。通过整合先进技术和智能系统&#xff0c;智慧场馆致力于提供全方位的解决方案&#xff0c;以实现场馆的安全性、节能性和智能化管理。本文将深入探讨智慧场馆如何实现安全、节能和全面智能化&#xff0c;…

Facebook与人工智能:推动社交媒体发展的新动力

在数字化时代的浪潮中&#xff0c;社交媒体已成为人们日常生活不可或缺的一部分。作为全球最大的社交平台之一&#xff0c;Facebook凭借其庞大的用户基础和先进的技术&#xff0c;正积极探索与人工智能&#xff08;AI&#xff09;的结合&#xff0c;以推动社交媒体的不断发展。…

【论文复现】自动化细胞核分割与特征分析

本文所涉及所有资源均在这里可获取。 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; 论文复现 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f496; 自动化细胞核分割与特征分析 引言效果展示HoverNet概述HoverNet原理分析整…

阿里云文本内容安全处理

1、什么是内容安全 内容安全是一款基于AI算法和云计算技术&#xff0c;对多媒体内容的不宜或违规内容提供识别和标注的产品。该产品&#xff0c;支持对各行业及业务场景下的图片、视频、文本、语音等对象进行检测&#xff0c;可以帮助您提高内容审核效率、提高平台内容质量和用…

双指针算法习题解答

1.移动零 题目链接&#xff1a;283. 移动零 - 力扣&#xff08;LeetCode&#xff09; 题目解析&#xff1a;该题要求将数组中为0的元素全部转移到数组的末尾&#xff0c;同时不能改变非零元素的相对位置。 解题思路&#xff1a;我们可以用变量dest和cur将该数组分为三个区域。…

idea、pycharm等软件的文件名红色怎么变绿色

1.问题 有时候在写完代码打算提交的时候&#xff0c;会遇到某个资源文件不是绿色的&#xff0c;不能提交 2.解决方法 右键该文件——git——添加即可 3.不同颜色含义 3.1 蓝色&#xff08;Blue&#xff09; 含义&#xff1a;文件被修改了但尚未保存。蓝色通常表示文件自上…

Python进阶之IO操作

文章目录 一、文件的读取二、文件内容的写入三、之操作文件夹四、StringIO与BytesIO 一、文件的读取 在python里面&#xff0c;可以使用open函数来打开文件&#xff0c;具体语法如下&#xff1a; open(filename, mode)filename&#xff1a;文件名&#xff0c;一般包括该文件所…

ECharts折线图背景渐变设置

目录 引入 1.在一个HTML文件中编写两个图表 2.渐变背景 引入 如何在一个HTML文件中编写两个图表&#xff1a;&#xff08;这个例子基于这个篇文章的基础&#xff09;一篇搞懂前端获取数据-CSDN博客 一个例子&#xff1a; 1.在一个HTML文件中编写两个图表 重点在于名字的不重…

基于SpringBoot的“乐校园二手书交易管理系统”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“乐校园二手书交易管理系统”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统首页界面图 用户注册界面图 二手…

CSS弹性布局:灵活布局的终极指南

在网页设计中&#xff0c;CSS 弹性布局&#xff08;Flexbox&#xff09;是一个不可或缺的工具。它能帮助你轻松地排列和对齐元素&#xff0c;尤其是在响应式设计中表现出色。今天&#xff0c;我们就来深入探讨一下 Flexbox 的各个属性&#xff0c;让你彻底掌握这个强大的布局工…

OpenJDK Vendor下载选择

首先JDK可以通过idea进行安装 File➡️Project Structure➡️SDK➡️Add SDK➡️Download JDK 然后在JDK版本选择时&#xff0c;Idea提供了很多版本&#xff0c;让我茫然了 OpenJDK国外厂商 供应商 说明 Amazon Corretto 亚马逊云基于OpenJDK构建&#xff0c;收费 Eclipse…

SAP-ABAP开发-ONLINE 程序、DIALOG屏幕开发

目录 一、Online 程序概览 1、程序类型 2、Online程序的主要对象 二、界面 1、SAP的屏幕开发 2、屏幕功能实现 3、界面中的事件块&#xff08;Event Block&#xff09; 4、界面的创建 三、简单界面元素 1、文本/输入框控件 2、数据检查 3、一些常用的关键字 四、复…