嵌入式Qt 开发一个音乐播放器
上篇文章:RK3568源码编译与交叉编译环境搭建,进行了OK3568开发板软件开发环境搭建,通过编译RK3568的源码,可以得到Qt开发的交叉编译相关工具。
本篇,就来在搭建好的软件开发中,进行Qt软件的开发测试。由于Qt是支持跨平台的,因此本篇的音乐播放器,先在Windows上编写调试,功能调好后再通过交叉编译,在Linux板子上测试。
在第一篇的开箱介绍中,体验了OK3568板子自带的Qt界面,有视频播放器、音乐播放器等,这些都实现了基本的播放功能,但没有对操作界面做更加丰富的开发,所以,本篇来实现一个界面更加优美,操作更新方便的音乐播放器软件,可以实现音乐列表的显示与选择播放、歌词显示等,先来看下最终的效果:
本篇的Qt代码从野火Linux开发板的例程中移植修改而来,下面分析下程序的代码结构。
1 音乐播放器开发总体结构
整个Qt音乐播放器项目的代码结构如下:
- 主代码中是音乐播放器相关的代码,包括:
- 音乐播放器主界面
- 唱片动画界面:在音乐播放时显示一个唱片转动的动画效果
- 操作按钮界面:实现播放、暂停、继续、上一首、下一首、进度调节,音量调节
- 歌词显示界面:当有对应的歌词时,显示歌词
- 歌词加载与计算
- Ui代码中使用一些Qt的基本功能,包括:
- 一个Qt界面基类
- 滑条功能类
- 图标按钮显示类
- 列表功能类
- Skin中是一些图片资源和字体/皮肤定义
- 最后是编译的中间文件和编译结果存储的目录
先看下程序的主要代码实现。
首先是整体的主界面部分,进行各项功能的初始化显示,主要包括:
- 唱片界面初始化
- 歌词界面初始化
- 按钮界面初始化
- 播放列表初始化
void MusicPlayer::InitWidget()
{QtWidgetTitleBar *widgetTitle = new QtWidgetTitleBar(this);widgetTitle->SetScalSize(Skin::m_nScreenWidth, 60);widgetTitle->SetBackground(Qt::transparent);widgetTitle->setFont(QFont(Skin::m_strAppFontNormal));widgetTitle->SetTitle(tr("音乐播放器"), "#ffffff", 24);connect(widgetTitle, SIGNAL(signalBackHome()), this, SIGNAL(signalBackHome()));QHBoxLayout *horLayoutCentor = new QHBoxLayout();horLayoutCentor->setContentsMargins(0, 0, 0, 0);horLayoutCentor->setSpacing(0);//唱片界面m_recorder = new WidgetRecord(this);horLayoutCentor->addWidget(m_recorder, 1);//歌词界面m_lyricWidget = new LyricWidget(this);horLayoutCentor->addWidget(m_lyricWidget, 1);//按钮界面m_playerToolBar = new WidgetToolBar(this);connect(m_playerToolBar, SIGNAL(play()), this, SLOT(SltMusicPlay()));connect(m_playerToolBar, SIGNAL(pause()), this, SLOT(SltMusicPause()));connect(m_playerToolBar, SIGNAL(toolBarClicked(int)), this, SLOT(SltToolbarClicked(int)));QVBoxLayout *verLayoutAll = new QVBoxLayout(this);verLayoutAll->setContentsMargins(0, 0, 0, 0);verLayoutAll->setSpacing(0);verLayoutAll->addWidget(widgetTitle, 1);verLayoutAll->addLayout(horLayoutCentor, 6);verLayoutAll->addWidget(m_playerToolBar);// 播放列表m_widgetMusicList = new MusicPlayListWidget(this);connect(m_widgetMusicList, SIGNAL(signalMediaChanged(QString, QString)), this, SLOT(SltCurrentSongChanged(QString, QString)));m_widgetMusicList->hide();connect(m_playerToolBar, SIGNAL(next()), m_widgetMusicList->playList(), SLOT(next()));connect(m_playerToolBar, SIGNAL(previous()), m_widgetMusicList->playList(), SLOT(previous()));connect(m_playerToolBar, SIGNAL(currentPostionChanged(int)), this, SLOT(SltChangePostion(int)));
}
初始化各个界面后,还要连接对应的槽函数,如点击操作按钮后的处理、切换播放列表中的音乐时的处理等。
2 自定义控件的代码
在介绍音乐播放器代码之前,需要先介绍一些Qt自定义控件的代码,这些代码属于公共代码,写好了之后,不仅音乐播放器可以用,后续开发其它功能,也可以复用这部分代码。
2.1 qtwidgetbase
这个cpp文件中定义了3个类:
2.1.1 QtWidgetBase
窗口基类,该类的一个主要功能是可以向窗口内添加图片形式的按钮,并且通过鼠标事件,给出哪个按钮被按下:
void QtWidgetBase::addBtn(int index, QtPixmapButton *btn)
{m_btns.insert(index, btn);this->update();
}
void QtWidgetBase::mousePressEvent(QMouseEvent *e)
{QRect rect;foreach (QtPixmapButton *btn, m_btns){ScaleRect(rect, btn->rect());if (rect.contains(e->pos())){if (btn->isCheckAble()){btn->setChecked(!btn->isChecked());emit signalBtnClicked(btn->id());}else{btn->setPressed(true);}this->update();break;}}if (rect.contains(e->pos())){this->update();}QWidget::mousePressEvent(e);
}
2.1.2 QtWidgetTitleBar
窗口的标题栏,用于绘制窗口上方的标题,如显示“音乐播放器”这几个字,另外,标题栏右侧,还支持一个应用退出(返回)的图标按钮:
void QtWidgetTitleBar::SetTitle(const QString &title, const QColor &textClr, const int &fontSize)
{this->m_strTitle = title;this->m_colorText = textClr;this->m_nFontSize = fontSize;this->update();
}void QtWidgetTitleBar::paintEvent(QPaintEvent *)
{QPainter painter(this);painter.setRenderHints(QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing);painter.scale(m_scaleX, m_scaleY);QRect rect(0, 0, m_nBaseWidth, m_nBaseHeight);if (m_pixmapBackground.isNull()){painter.fillRect(rect, m_colorBackground);}else{painter.drawPixmap(rect, m_pixmapBackground);}// 绘制文字QFont font = painter.font();font.setPixelSize(m_nFontSize);painter.setFont(font);painter.setPen(m_colorText);painter.drawText(rect, Qt::AlignCenter, m_strTitle);
}
2.1.3 QtAnimationWidget
属性动画移动窗体,该类用于在窗口中实现一些简单的动画效果,如音乐播放器会有一个唱片转动和唱针抬起放下的动作。
该类通过调用Qt的QPropertyAnimation属性动画类,实现相关的功能:
QtAnimationWidget::QtAnimationWidget(QWidget *parent) : QtWidgetBase(parent)
{m_bShow = false;m_animation = new QPropertyAnimation(this, "pos");m_colorBackground = QColor("#ffffff");connect(m_animation, SIGNAL(finished()), this, SLOT(SltAnimationFinished()));connect(m_animation, SIGNAL(finished()), this, SIGNAL(signalAnimationFinished()));
}void QtAnimationWidget::StartAnimation(const QPoint &startPos, const QPoint &endPos, int duration, bool bShow)
{if (m_animation->state() != QPropertyAnimation::Stopped){m_animation->stop();}if (!this->isVisible())this->setVisible(true);m_animation->setDuration(duration);m_animation->setStartValue(startPos);m_animation->setEndValue(endPos);m_bShow = bShow;m_animation->start();
}
QPropertyAnimation类的主要API接口:
- setStartValue:指定动作的初始状态
- setEndValue:指定动作的最终状态
- setDuration:指定动画时长,单位为毫秒
- start:进行动画播放
2.2 qtsliderbar
用于实现自定义滑条控件,分为水平滑条与竖直滑条,音乐播放器的播放进度,需要用到水平滑条,音乐播放器的音量调节,需要用到竖直滑条。滑条的基本形状是一个具有一定粗细的直线代表滑条,上面一个圆形代表滑块,可以是两个颜色不同的同心圆。
画水平滑条的主要代码如下:
//画水平滑条
void QtSliderBar::drawHorizontalBar(QPainter *painter)
{painter->save();QPen pen(m_colorSlider, m_nSliderSize, Qt::SolidLine, Qt::RoundCap);painter->setPen(pen);int nY = this->height() / 2;int radius = m_nHandleSize / 2;painter->drawLine(radius, nY, this->width() - radius, nY);// 先绘制那个大圆QRect rect(m_nOffset - radius, nY - radius, m_nHandleSize, m_nHandleSize);painter->setPen(Qt::NoPen);painter->setBrush(m_bShowHandleBg ? m_colorHandleBg : m_colorHandle);painter->drawEllipse(rect.left(), rect.top(), m_nHandleSize, m_nHandleSize);QPainterPath path;path.moveTo(radius, nY);path.lineTo((m_nOffset >= m_nHandleSize) ? m_nOffset - radius : radius, nY);path.addEllipse(rect.left() + radius / 2, nY - radius / 2, radius, radius);pen.setColor(m_colorHandle);painter->setPen(pen);painter->setBrush(m_colorHandle);painter->drawPath(path);painter->restore();
}
2.3 qtpixmapbutton
图片按钮,顾名思义就是(小)图片形式的按钮,支持未按下和按下后的两种样式的图片显示。如音乐播放器的这些操作按钮,都是图片形式的按钮
该类在构造函数时,将按钮的id,图片按钮要显示的位置,未按下时和按下时要展示的图片,通过参数传入即可:
QtPixmapButton::QtPixmapButton(int id, QRect rect, QPixmap pixmapNormal, QPixmap pixmapPressed) :m_nId(id),m_rect(rect),m_strText(""),m_pixmapNormal(pixmapNormal),m_pixmapPressed(pixmapPressed),m_bPressed(false),m_bVisible(true),m_bCheckAble(false),m_bChecked(false),m_bEnable(true)
{}QPixmap QtPixmapButton::pixmap()
{return (m_bPressed || m_bChecked) ? m_pixmapPressed : m_pixmapNormal;
}
2.4 qtlistwidget
这个cpp文件中定义了2个类:
- QtListWidgetItem
- QtListWidget
2.4.1 QtListWidgetItem
该类用于表示列表中的每一个项,可以是字符,并且可以带有图片(图标):
class QtListWidgetItem
{
public:QtListWidgetItem(int id, const QStringList &args);QtListWidgetItem(int id, const QString &name);QtListWidgetItem(int id, const QString &name, const QPixmap &icon);QtListWidgetItem(int id, const QString &path, const QString &name, const QPixmap &icon);int m_nId;QString m_strText;QPixmap m_pixmapIcon;QString m_strBaseName;QString m_strPath;QRect m_rect;QStringList m_strMultiParameters;bool m_bPressed;
};
2.4.2 QtListWidget
项目的显示,可以按列显示,也可以按行显示,并且支持鼠标的滑动显示,音乐播放器的音乐列表,就是按列显示的:
该组件的类定义如下:
class QtListWidget : public QtWidgetBase
{Q_OBJECT
public:typedef enum{None,LeftDirection,RightDirection,UpDirection,DownDirection} MoveDirection;Q_PROPERTY(int xPos READ movePos WRITE setMovePos)explicit QtListWidget(QWidget *parent = 0);~QtListWidget();QtListWidgetItem *currentItem();void AddItem(int id, QtListWidgetItem *item);void SetItems(const QMap<int, QtListWidgetItem *> &items);void SetBackground(const QPixmap &pixmap);void SetBackground(const QColor &color);void setItemSize(int size);void setHoriazontal(bool bOk);void setAlignment(Qt::Alignment aligns);void setScaleSize(int w, int h);public slots:void setPrevIndex();void setCurrentIndex(int index);void setNexIndex();signals:void currentItemClicked(QtListWidgetItem *item);void currentIndexClicked(int index);private:void setMovePos(int nValue);int movePos() { return m_nStartPos; }void ClearItems();private:bool m_bPressed;QPoint m_startPos;int m_nStartPos;int m_nMoveEndValue;bool m_bRecovery;protected:QMap<int, QtListWidgetItem *> m_listItems;int m_alignment;QColor m_backgroundColor;QPixmap m_pixmapWallpaper;QColor m_colorText;int m_nStartIndex;int m_nCurrentIndex;int m_nMargin;int m_nSpace;int m_nItemShowCnt;int m_nItemSize;bool m_bHorizontal;QPropertyAnimation *m_animationMove;int m_nDirection;protected:QSize sizeHint() const;void resizeEvent(QResizeEvent *e);void mousePressEvent(QMouseEvent *e);void mouseReleaseEvent(QMouseEvent *);void mouseMoveEvent(QMouseEvent *e);void paintEvent(QPaintEvent *);void drawHorizontalItem(QPainter *painter);void drawVerticalItem(QPainter *painter);virtual void drawItemInfo(QPainter *painter, QtListWidgetItem *item);
};
3 音乐播放器代码
上面介绍了一些公用的自定义组件,下面就是利用这些自定义组件,以及Qt的各种功能,实现一个音乐播放器。
3.1 音乐播放
音乐播放部分,主要有三部分功能:
- 音频解码播放:使用Qt自带的媒体播放器组件QMediaPlayer进行音频播放
- 播放时的按钮操作:上一首、暂停/继续、下一首、播放进度调节、音量调节、音乐列表按钮
- 唱片动画界面:在音乐播放时显示一个唱片转动的动画效果,以及唱针放下抬起的动作
3.1.1 QMediaPlayer播放音频
这里使用Qt自带的QMediaPlayer组件进行音频的播放,QMediaPlayer播放音频的基本步骤为:
- 创建一个QMediaPlayer
- 连接播放位置变化的槽函数
- 设置播放的音乐文件
- 设置音量
- 开始播放
player = new QMediaPlayer;
connect(player, SIGNAL(positionChanged(qint64)), this, SLOT(positionChanged(qint64)));
player->setMedia(QUrl::fromLocalFile("./Music/test.mp3"));
player->setVolume(50);
player->play();
本项目的音频播放初始化部分:
void MusicPlayer::InitPlayer()
{m_player = new QMediaPlayer(this);m_player->setPlaylist(m_widgetMusicList->playList());connect(m_player, SIGNAL(durationChanged(qint64)), this, SLOT(SltDurationChanged(qint64)));connect(m_player, SIGNAL(positionChanged(qint64)), this, SLOT(SltPostionChanged(qint64)));connect(m_player, SIGNAL(error(QMediaPlayer::Error)), this, SLOT(SltMediaError(QMediaPlayer::Error)));m_volumeSlider = new QtSliderBar(this);m_volumeSlider->SetHorizontal(false);m_volumeSlider->SetValue(100);m_volumeSlider->hide();connect(m_volumeSlider, SIGNAL(currentValueChanged(int)), m_player, SLOT(setVolume(int)));
}
3.1.2 播放操作按钮
音乐播放时,需要用到播放、暂停、继续、上一首、下一首、进度调节、音量调节等操作按钮,这里的对应按钮使用图标显示为对应的按钮,通过加载QPixmap来实现:
void MusicToolBar::InitWidget()
{// 按钮m_btns.insert(0, new QtPixmapButton(0, QRect(12, 4, 60, 60), QPixmap(":/images/music/ic_prev.png"), QPixmap(":/images/music/ic_prev_pre.png")));m_btns.insert(1, new QtPixmapButton(1, QRect(64, 4, 60, 60), QPixmap(":/images/music/ic_play.png"), QPixmap(":/images/music/ic_pause.png")));m_btns.insert(2, new QtPixmapButton(2, QRect(128, 4, 60, 60), QPixmap(":/images/music/ic_next.png"), QPixmap(":/images/music/ic_next_pre.png")));m_btns.insert(3, new QtPixmapButton(3, QRect(680, 4, 60, 60), QPixmap(":/images/music/ic_volume.png"), QPixmap(":/images/music/ic_volume_pre.png")));m_btns.insert(4, new QtPixmapButton(4, QRect(740, 4, 60, 60), QPixmap(":/images/music/ic_list.png"), QPixmap(":/images/music/ic_list_pre.png")));m_btns.value(1)->setCheckAble(true);connect(this, SIGNAL(signalBtnClicked(int)), this, SLOT(SltBtnClicket(int)));// 进度条m_progressBar = new QtSliderBar(this);m_progressBar->SetHorizontal(true);m_progressBar->SetMaxValue(0);m_progressBar->SetValue(0);// 进度条的值改变, 转变为MusicToolBar的信号currentPostionChanged()connect(m_progressBar, SIGNAL(currentValueChanged(int)), this, SIGNAL(currentPostionChanged(int)));
}
3.1.3 播放时的动画界面
为了在播放时能有视觉上的动态效果,这里加了一个唱片转动的动画效果,当音乐播放时,唱针移动到唱片上,唱片开始转动。
MusicRecord::MusicRecord(QWidget *parent) : QtWidgetBase(parent)
{m_nRotateCD = 0;m_nRotateHandle = 0;m_bPlay = false;m_nBaseWidth = 400;m_nBaseHeight = 350;m_handlePixmap = new PixmapItem(QPixmap(":/images/music/ic_handle.png"), this);m_pixmapRecord = QPixmap(":/images/music/ic_blackrecord.png");m_pixmapArtist = QPixmap(":/images/music/ic_cd.png");m_timer = new QTimer(this);m_timer->setInterval(100);connect(m_timer, SIGNAL(timeout()), this, SLOT(SltCicleRun()));m_animationHandle = new QPropertyAnimation(m_handlePixmap, "ratote");m_animationHandle->setDuration(300);m_animationHandle->setEasingCurve(QEasingCurve::Linear);connect(m_animationHandle, SIGNAL(valueChanged(QVariant)), this, SLOT(SltHandleMove(QVariant)));
}
void MusicRecord::Start()
{m_bPlay = true;m_timer->start();m_animationHandle->setStartValue(0);m_animationHandle->setEndValue(30);m_animationHandle->start();this->update();
}void MusicRecord::Stop()
{m_bPlay = false;m_timer->stop();m_animationHandle->setStartValue(30);m_animationHandle->setEndValue(0);m_animationHandle->start();this->update();
}
3.2 播放列表
播放列表功能用来实现音频文件的显示,以及手动指定切换要播放的音乐。
3.2.1 读取音乐文件
这里使用Qt的QMediaPlaylist组件来实现音乐列表的管理。QMediaPlaylist是一个列表,它可以保存媒体文件,包括媒体路径等信息,它具有着列表的性质,比如添加删除插入等,但它能做的,比单纯的储存要多得多。设置播放顺序,对播放的控制,保存到本地,从本地读取,都可以很方便地实现。
void MusicPlayListWidget::ScanDirMedias(const QString &path)
{QDir dir(path);if (!dir.exists())return;dir.setFilter(QDir::Dirs | QDir::Files);dir.setSorting(QDir::DirsFirst);QFileInfoList list = dir.entryInfoList();int index = m_mapItems.size();for (int i = 0; i < list.size(); i++){QFileInfo fileInfo = list.at(i);if (fileInfo.fileName() == "." || fileInfo.fileName() == ".."){continue;}if (fileInfo.isDir()){ScanDirMedias(fileInfo.filePath());}else if (fileInfo.suffix() == "mp3"){QString strName = fileInfo.baseName().toLocal8Bit().constData();QString strPath = fileInfo.absoluteFilePath().toLocal8Bit().constData();m_playList->addMedia(QUrl::fromLocalFile(strPath));m_mapItems.insert(index, new QtListWidgetItem(index, strPath, strName, QPixmap()));index++;}}
}
3.2.2 音乐列表界面
音乐列表的具体界面实现,主要是在对应的矩形位置,显示各个音乐文件的名称:
MusicPlayListWidget::MusicPlayListWidget(QWidget *parent) : QtAnimationWidget(parent)
{this->setAttribute(Qt::WA_TranslucentBackground);m_colorBackground = QColor("#5362b5");m_nBaseWidth = 405;m_nBaseHeight = 350;m_nYPos = 60;m_listWidget = new PlayListWidget(this);QVBoxLayout *verLayout = new QVBoxLayout(this);verLayout->setContentsMargins(0, 10, 0, 10);verLayout->setSpacing(0);verLayout->addStretch(1);verLayout->addWidget(m_listWidget, 9);// 播放列表(创建一个QMediaPlaylist))m_playList = new QMediaPlaylist(this);m_playList->setPlaybackMode(QMediaPlaylist::Loop);connect(m_playList, SIGNAL(currentIndexChanged(int)), this, SLOT(SltCurrMediaChanged(int)));connect(m_listWidget, SIGNAL(currentIndexClicked(int)), m_playList, SLOT(setCurrentIndex(int)));
}
3.3 歌词显示
歌词显示功能,需要在音乐播放的时候,能根据播放的进度,显示对应的歌词。
3.3.1 歌词显示界面
歌词界面的具体界面实现,主要是在对应的矩形位置,显示当前音乐播放位置前后的音乐歌词,并高亮当前播放的那句歌词:
void LyricWidget::drawLyricLines(QPainter *painter)
{painter->save();QTextOption option;option.setWrapMode(QTextOption::WordWrap);option.setAlignment(Qt::AlignCenter);QRect rect(0, 0, m_nBaseWidth, 52);int index = 0;for (int i = 0; i < m_nShowCount; i++){rect = QRect(rect.left(), rect.bottom(), rect.width(), LYRIC_LINE_HEIGHT);index = m_nStartIndex + i;QString strText = index < m_strLyricLines.size() ? m_strLyricLines.at(index) : "";painter->setPen(index == m_nCurrentIndex ? Qt::white : Qt::black);painter->drawText(rect, strText, option);}painter->restore();
}
3.3.2 歌词显示时间计算
通过下面这个函数,计算当前音乐播放的位置:
void LyricWidget::SetLyricPostion(int postion)
{if (!m_bLyricLoad)return;m_lyricFactory->findIndex(postion);
}
4 交叉编译与测试
如果是在Windows上编写的程序,并且支持Windows上运行,在Qt程序编写好后,可以先在Windows查看运行效果。
通过交叉编译,来测试音乐播放器在OK3568-C板子上的播放效果。
为了方便每次的编译,我这里编写一个my3568build.sh的编译脚本:
#! /bin/bashmkdir -p build
cd buildexport PATH=/home/xxpcb/myTest/OK3568/sourcecode/OK3568-linux-source/buildroot/output/OK3568/host/bin:$PATHqmake .. && make
然后执行该脚本即可编译:
./my3568build.sh
将编译成功的可执行文件MusicPlayer复制到OK3568-C板子中,然后在其同目录下创建一个music文件夹,里面放入若干个mp3音乐文件和对应的歌词文件,然后就可以测试了:
文件复制到板子,我这里使用的ADB无线传输的方式,比较方便,具体ADB操作演示,可参考上篇文章最后的Qt交叉编译与测试部分
需注意的是,板子里的这个Linux系统,不支持中文的显示,所以我把歌名都先改成了字母后再复制到板子中进行测试。
5 总结
本篇介绍了使用Qt开发一个音乐播放器,首先是一个Qt自定义控件的介绍,包括滑条、图标按钮、列表等,然后使用这些自定义组件,以及Qt的各种功能,实现一个音乐播放器,具有基础的音乐播放、暂停继续、歌曲列表显示,歌曲切换等功能。代码可以在Windows上运行,通过交叉编译,可以在OK-3568这块Linux板子上运行。
相关文章:
嵌入式Qt 开发一个音乐播放器
上篇文章:RK3568源码编译与交叉编译环境搭建,进行了OK3568开发板软件开发环境搭建,通过编译RK3568的源码,可以得到Qt开发的交叉编译相关工具。 本篇,就来在搭建好的软件开发中,进行Qt软件的开发测试。由于…...
2023秋招万得集团AI算法岗面经分享
本专栏分享 计算机小伙伴秋招春招找工作的面试经验和面试的详情知识点 专栏首页:秋招算法类面经分享 主要分享计算机算法类在面试互联网公司时候一些真实的经验 2022年 11.22下午AI算法岗面试 (1)一面35min 1、自我介绍 2、科研:长文本MRC...
RoI Transformer论文翻译详解
Learning RoI Transformer for Oriented Object Detection in Aerial Images 0.摘要 航空图像中的目标检测是计算机视觉中一个活跃而又具有挑战性的任务,因为它具有鸟瞰视角、高度复杂的背景和变化的物体外观。特别是在航空图像中检测密集的目标时,基于…...
Prometheus 自动发现监控AWS EC2实例
本文章简述对接自动发现AWS云EC2实例 前提环境: PromethuesGrafanaAWS IAM权限 涉及参考文档: AWS EC2Grafana 通用监控模板 一、IAM 用户创建 1、创建Prometheus 策略 策略规则: {"Version": "2012-10-17",&quo…...
从recat源码角度看setState流程
setState setState() 将对组件 state 的更改排入队列批量推迟更新,并通知 React 需要使用更新后的 state 重新渲染此组件及其子组件。其实setState实际上不是异步,只是代码执行顺序不同,有了异步的感觉。 使用方法 setState(stateChange | u…...
【Java|golang】1234. 替换子串得到平衡字符串---双指针
有一个只含有 ‘Q’, ‘W’, ‘E’, ‘R’ 四种字符,且长度为 n 的字符串。 假如在该字符串中,这四个字符都恰好出现 n/4 次,那么它就是一个「平衡字符串」。 给你一个这样的字符串 s,请通过「替换一个子串」的方式,…...
自监督表征学习方法——BYOL(Bootstrap Your Own Latent)
自监督表征学习方法——BYOL(Bootstrap Your Own Latent) 参考文献:《Bootstrap Your Own Latent A New Approach to Self-Supervised Learning》 1.前言背景 学习良好的图像表示是计算机视觉中的一个关键挑战,因为它允许对下游任务进行有效的训练。许…...
均衡负载集群(LBC)-1
均衡负载集群(LBC) 客户–>通过Internet—>负载调度器—>n台真实服务器 负载调度器: 软件:LVS;Nginx;Haproxy硬件:F5; LVS架构: 使用到C/S(B/S…...
WebSocket
关于WebSocket: WebSocket 协议在2008年诞生,2011年成为国际标准。现在所有浏览器都已经支持了。 WebSocket 的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话…...
GA-PEG-GA,Glutaric Acid-PEG-Glutaric Acid,戊二酸-聚乙二醇-戊二酸供应
英文名称:Glutaric Acid-PEG-Glutaric Acid,GA-PEG-GA 中文名称:戊二酸-聚乙二醇-戊二酸 GA-PEG-GA是一种线性双功能PEG羧酸试剂。PEG和羧基COOH之间存在C4酯键。PEG羧酸可用于与氨基反应,与NHS和DCC、EDC等肽偶联试剂反应。 P…...
使用sqlmap + burpsuite sql工具注入拿flag
使用sqlmap burpsuite sql工具注入拿flag 记录一下自己重新开始学习web安全之路③。 目标网站:http://mashang.eicp.vip:1651/7WOY59OBj74nTwKzs3aftsh1MDELK2cG/ 首先判断网站是否存在SQL注入漏洞 1.找交互点 发现只有url这一个交互点,搜索框和登录…...
替代AG9300|替代NCS8823|CS5260 Type-C转VGA视频转换方案
替代AG9300|替代NCS8823|CS5260 Type-C转VGA视频转换方案 CS5260是一款是一款实现USB TYPE-C到VGA视频转换的单片机解决方案转换器。CS5260支持USB Type-C显示端口交替模式,CS5260可以将视频和音频流从USB Type-C接口传输到VGA端口。在CS5260芯片中,显示…...
乐鑫特权隔离机制的 OTA 固件升级
固件空中升级 (OTA, Over-The-Air) 是任何联网设备的重要功能之一,支持开发人员通过远程更新固件,以发布新功能或修复错误。乐鑫特权隔离框架中包含两类应用程序:受保护的应用程序 (protected_app) 和用户应用程序 (user_app) ,这…...
C++数据结构 —— 二叉搜索树
目录 1.二叉搜索树的基本概念 1.1二叉搜索树的基本特征 2.二叉搜索树的实现 2.1数据的插入(迭代实现) 2.2数据的搜索(迭代实现) 2.3中序遍历(递归实现) 2.4数据的删除(迭代实现) 2.5数据的搜索(递归实现) 2.6数据的插入(递归实现) 2.7数据的删除(递归实现) 2.8类的完…...
Maven面试题及答案
1、Maven有哪些优点和缺点 优点: 1、简化项目依赖管理 2、方便与持续集成工具(Jenkins)整合 3、有助于多模块项目开发,比如一个模块开发好后发布到仓库,依赖该模块时可以直接从远程仓库更新,不用自己手动去编译 4、有很多插件&am…...
WebRTC系列-Qos系列之接收放RTX处理
文章目录 1. RTX详解1.1 RTX包头解析1.2 RTX包中的OSN2. RTX在WebRTC中处理2.1 组包2.2 解包2.3 发送及接收处理流程2.3.1 发送流程2.3.2 rtx标记的设置流程2.3.3 解析流程2.3.4 RTX解包在上一篇 WebRTC系列-Qos系列之接收NACK文章中分析了接收到nack后解析的主要流程。在WebR…...
国内能否炒伦敦金,2023国际十大正规伦敦金交易平台排名
在目前的投资市场环境中,现货黄金是一种屡见不鲜的投资选择,它依靠国际化的投资环境,成为了世界范围内投资者的重要选择对象。进行现货黄金投资,人们除了要认识市场发展基本现状之外,更要做好基本面和技术面分析工作&a…...
react路由 - react-router-dom
react路由 现代的前端应用大多都是 SPA(单页应用程序),也就是只有一个 HTML 页面的应用程序。因为它的用户体验更好、对服务器的压力更小,所以更受欢迎。为了有效的使用单个页面来管理原来多页面的功能,前端路由应运而…...
01-RTOS
对于裸机而言,对于RTOS而言即:对于裸机,打游戏意味着不能回消息 回消息意味着不能打游戏对于RTOS 打游戏和裸机的切换只需要一个时间片节拍 1ms 从宏观来看 就是同时进行的两件事(但要在这两件事情的优先级一样的情况下࿰…...
信息安全管理
信息安全管理信息安全管理信息安全风险管理信息安全管理体系应急响应与灾难恢复应急响应概况信息系统灾难修复灾难恢复相关技术信息安全管理 管理概念:组织、协调、控制的活动,核心过程的管理控制 管理对象和组成:包括人员在内相关资产&…...
深度学习tips
1、datasets_make函数中最后全部转化为numpy形式 datanp.array(data)否则会出现问题,比如数据是103216,经过trainloader生成tensor后(batch_size为30),发现生成的数据为: data.shape #(10,) data[0].shape…...
2023-2-13 刷题情况
替换子串得到平衡字符串 题目描述 有一个只含有 ‘Q’, ‘W’, ‘E’, ‘R’ 四种字符,且长度为 n 的字符串。 假如在该字符串中,这四个字符都恰好出现 n/4 次,那么它就是一个「平衡字符串」。 给你一个这样的字符串 s,请通过…...
[HSCSEC 2023] rev,pwn,crypto,Ancient-MISC部分
比赛后有讲解,没赶上,前20比赛完1小时提交WP,谁会大半夜的起来写WP。总的感觉pwn,crypto过于简单,rev有2个难的不会,其它不是我的方向都感觉过于难,一个都没作。revDECOMPILEONEOONE入门题,一个…...
SpringBoot 接入 Spark
本文主要介绍 SpringBoot 与 Spark 如何对接,具体使用可以参考文章 SpringBoot 使用 Spark pom 文件添加 maven 依赖 spark-core:spark 的核心库,如:SparkConfspark-sql:spark 的 sql 库,如:s…...
在线支付系列【23】支付宝开放平台产品介绍
有道无术,术尚可求,有术无道,止于术。 文章目录前言支付产品App 支付手机网站支付电脑网站支付新当面资金授权当面付营销产品营销活动送红包会员产品App 支付宝登录人脸认证信用产品芝麻 GO芝麻先享芝麻免押芝麻工作证安全产品交易安全防护其…...
Python绝对路径和相对路径详解
在介绍绝对路径和相对路径之前,先要了解一下什么是当前工作目录。什么是当前工作目录每个运行在计算机上的程序,都有一个“当前工作目录”(或 cwd)。所有没有从根文件夹开始的文件名或路径,都假定在当前工作目录下。注…...
基于多进程的并发编程
一:不同平台基于多进程并发编程的实现 1.Windows平台 参考博文:Windows 编程(多进程) 更多API: 1)waitForSingleObject:等待一个内核对象变为已通知状态 2)GetExitCodeProcess:获取…...
Flask入门(4):CBV和FBV
目录4.CBV和FBV4.1 继承 views.View4.2 继承 views.MethodView4.CBV和FBV 前面的例子中,都是基于视图函数构建视图(FBV),和Django一样,Flask也有基于类构建视图(CBV)的方法。这种方式用的不多&…...
Qt OpenGL(三十九)——Qt OpenGL 核心模式-在雷达坐标系中绘制飞行的飞机
提示:本系列文章的索引目录在下面文章的链接里(点击下面可以跳转查看): Qt OpenGL 核心模式版本文章目录 Qt OpenGL(三十九)——Qt OpenGL 核心模式-在雷达坐标系中绘制飞行的飞机 一、场景 在之前绘制完毕雷达显示图之后,这时候,我们能匹配的场景就更广泛了,比如说…...
系统应用 odex 转 dex
说下为什会有这个需求,以某系统应用为例,我们通过 adb 获取到的 apk 反编译查看只有少部分代码和资源,关键代码看不到。 经过一系列操作,把 odex 转换为 dex 可以看到源码。 工具下载 Smali 下载 1、使用 adb shell pm list pa…...
企业局域网的组建与网站建设论文/营销方案案例范文
今天分享一份来自中国移动的《5G落地:应用融合与创新》。如需下载《5G落地:应用融合与创新》,请扫码前往我们公众号对话框回复“落地”。ps:链接有效期至3月5日,建议尽快下载保存哦!编辑:陈颖思…...
wordpress4.7.4伪静态/故事型软文广告
要是还没有安装Tomcat 服务器 可以点击此 http://tomcat.apache.org/download-60.cgi链接 在官网下载Tomcat服务器. 具体安装方法 这里不做介绍. 在安装好Tomcat之后 接着来就来配置 在桌面建立一个文件夹取名Test(也可以建立在其他的硬盘上)在Test文件下建立WEB-INF文件夹(这…...
网站描文本链接怎么做/产品宣传方式有哪些
上一节中我们对Activity一些基本的概念进行了了解,什么是Activity,Activity的生命周期,如何去启动一个Activity等,本节我们继续来学习Activity,前面也讲了一个App一般都是又多个Activity构成的,这就涉及到了多个Activity间数据传递的问题了,那么本节继续学习Activity的使…...
合肥城市建设网站/百度影响力排名顺序
在C语言中对于float,用4字节存储,比如1.618000 ,在内存中为 160 26 207 631.000000, 在内存中为 0 0 128 63那么我有4个字节数据,比如{160 26 207 63},怎么转成float呢?其…...
如何免费推广网站/杭州网站优化平台
对于一个良好的系统,界面一般都会用到统一的风格。如此,客户体验也肯定不错,给人一种有始有终的感觉。在开发上,也就要求技术上具有很大程度上的统一口径,利于开发,提高了生产力。后期维护时也能很明显感觉…...
自己能自学网站建设吗/整站优化的公司
题目描述 六一儿童节,老师带了很多好吃的巧克力到幼儿园。每块巧克力j的重量为w[j],对于每个 小朋友i,当他分到的巧克力大小达到h[i] (即w[j]>h[i]),他才会上去表演节目。老师 的目标是将巧克力分发给孩子们,使得最…...