贴张效果图:

定制包括:

1. 周边阴影

2. 菜单项的元素(分割符, 控制ICON大小, 文字显示位置与颜色, 子菜单指示符)

菜单内的效果, 部分可以使用stylesheet实现, 但要做到这样的定制化, stylesheet是做不到的

下面介绍如何实现这些效果:

1. 实现阴影效果

默认的Qt菜单QMenu的效果是这样的

1) 首先需要去除下拉阴影(Drop shadow)

Qt的菜单是继承QWidget然后自绘的, dropshadow不属于自绘范围, 是windows popup类型窗口默认的样式, 无法通过正常途径去除

可以从源码中看到调用过程大概是这样:

qmenu::popup -> qwidget::show() -> QWidgetPrivate::show_helper() -> show_sys();

 而这时候, 还未调用qmenu::paintevent

而且不能去除QMenu的Popup 属性, 因为QMenu的实现依赖Popup属性, 例如:

QMenuPrivate::activateAction中使用QApplication::activePopupWidget()函数

在windows平台下:

menu.h

#ifndef MENU_H#define MENU_H#include

class Menu : publicQMenu

{

Q_OBJECTpublic:explicit Menu(QWidget *parent = 0);explicit Menu(const QString &title);protected:virtual bool event(QEvent *event);

signals:publicslots:

};#endif //MENU_H

menu.cpp

#include "menu.h"

Menu::Menu(QWidget*parent) :

QMenu(parent)

{

}

Menu::Menu(const QString &title) :

QMenu(title)

{

}bool Menu::event(QEvent *event)

{static bool class_amended = false;if (event->type() ==QEvent::WinIdChange)

{

HWND hwnd= reinterpret_cast(winId());if (class_amended == false)

{

class_amended= true;

DWORD class_style=::GetClassLong(hwnd, GCL_STYLE);

class_style&= ~CS_DROPSHADOW;

::SetClassLong(hwnd, GCL_STYLE, class_style);

}}return QWidget::event(event);

}

大概思路是: 在event中截获QEvent::WinIdChange事件, 然后获得窗口handle,  使用GetClassLong / SetClassLong 去除 CS_DROPSHADOW flags, 即可去除阴影

2) 使用dwm实现环绕阴影

优点:系统内置支持

缺点: 仅在vista以上并开启aero特效的情况, 使菜单有阴影环绕.

#pragma comment( lib, "dwmapi.lib" )#include"dwmapi.h"

bool Menu::event(QEvent *event)

{static bool class_amended = false;if (event->type() ==QEvent::WinIdChange)

{

HWND hwnd= reinterpret_cast(winId());if (class_amended == false)

{

class_amended= true;

DWORD class_style=::GetClassLong(hwnd, GCL_STYLE);

class_style&= ~CS_DROPSHADOW;

::SetClassLong(hwnd, GCL_STYLE, class_style);

}

DWMNCRENDERINGPOLICY val=DWMNCRP_ENABLED;

::DwmSetWindowAttribute(hwnd, DWMWA_NCRENDERING_POLICY,&val, sizeof(DWMNCRENDERINGPOLICY));//This will turn OFF the shadow//MARGINS m = {0};//This will turn ON the shadow

MARGINS m = {-1};

HRESULT hr= ::DwmExtendFrameIntoClientArea(hwnd, &m);if( SUCCEEDED(hr) )

{//do more things

}

}return QWidget::event(event);

}

简单地修改一下event的实现即可

3) 手动绘制阴影

1. CCustomMenu 继承 QMenu

voidCCustomMenu::_Init()

{//必须设置popup, 因为QMenuPrivate::activateAction中使用QApplication::activePopupWidget()函数

this->setWindowFlags(Qt::Popup |Qt::FramelessWindowHint);this->setAttribute(Qt::WA_TranslucentBackground);this->setObjectName("CustomMenu"); //以objectname 区分Qt内置菜单和CCustomMenu

}

设置菜单背景透明

objectname是为了在绘制时区分不同风格的菜单(比如原生Qmenu与CCustomMenu或者其他CCustomMenu2等)

2. 实现CCustomStyle (参考Qt的源码 QFusionStyle)

CCustomStyle继承自QProxyStyle, Qt控件中的基础元素都是通过style控制, style比stylesheet更底层, 可以做到更精细的控制

/**@brief 定制菜单style

@author lwh*/

class CCustomStyle : publicQProxyStyle

{

Q_OBJECTpublic:

CCustomStyle(QStyle*style = 0);void drawControl(ControlElement control, const QStyleOption *option,

QPainter*painter, const QWidget *widget) const;void drawPrimitive(PrimitiveElement element, const QStyleOption *option,

QPainter*painter, const QWidget *widget) const;int pixelMetric ( PixelMetric pm, const QStyleOption * opt, const QWidget * widget) const;private:void _DrawMenuItem(const QStyleOption *option,

QPainter*painter, const QWidget *widget) const;

QPixmap _pixShadow ;//阴影图片

};

首先需要调整菜单项与边框的距离, 用于绘制阴影

在pixelMetric 中添加

if(pm ==PM_MenuPanelWidth)return 6; //调整边框宽度, 以绘制阴影

pixelMetric 中描述了像素公制可取的一些值,一个像素公制值是单个像素在样式中表现的尺寸.

然后再drawPrimitive实现阴影绘制

void CCustomStyle::drawPrimitive( PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget ) const{if(element ==PE_FrameMenu)

{

painter->save();

{if(_pixShadow.isNull()|| widget->objectName() != "CustomMenu") //fix bug: Qt的内置菜单显示不正常(如TextEdit右键菜单)

{

painter->restore();return__super::drawPrimitive(element, option, painter, widget);

}

QSize szThis= option->rect.size();

QPixmap pixShadowBg=_DrawNinePatch(szThis, _pixShadow);

painter->drawPixmap(option->rect, pixShadowBg);

}

painter->restore();return;

}

__super::drawPrimitive(element, option, painter, widget);

}

QStyle::PE_FrameMenu      Frame for popup windows/menus; see also QMenu.

注意: 绘制完直接return

_DrawNinePatch是以九宫格形式绘制,

将这样一张小的阴影图绘制到窗口时, 如果直接拉伸, 会变得非常模糊.

而九宫格形式可以绘制出相对漂亮的背景, 这种技巧同样可以应用在其他控件上.

const QPixmap _DrawNinePatch( QSize szDst, const QPixmap &srcPix )

{//绘制背景图到, 以九宫格形式

QPixmap dstPix(szDst);

dstPix.fill(QColor(255, 255, 255, 0));

QPainter painter;

painter.begin(&dstPix);int nW =szDst.width();int nH =szDst.height();int nWBg =srcPix.width();int nHBg =srcPix.height();

QPoint m_ptBgLT(10, 10);

QPoint m_ptBgRB(19, 19);

QPoint ptDstLT(m_ptBgLT.x(), m_ptBgLT.y());

QPoint ptDstRB(nW-(nWBg-m_ptBgRB.x()), nH-(nHBg-m_ptBgRB.y()));//LT

painter.drawPixmap(QRect(0,0,ptDstLT.x(), ptDstLT.y()), srcPix, QRect(0,0,m_ptBgLT.x(), m_ptBgLT.y()));//MT

painter.drawPixmap(QRect(ptDstLT.x(),0, ptDstRB.x()-ptDstLT.x(), ptDstLT.y()), srcPix, QRect(m_ptBgLT.x(),0,m_ptBgRB.x()-m_ptBgLT.x(), m_ptBgLT.y()));//RT

painter.drawPixmap(QRect(ptDstRB.x(),0,nW-ptDstRB.x(), ptDstLT.y()), srcPix, QRect(m_ptBgRB.x(),0,nWBg-m_ptBgRB.x(), m_ptBgLT.y()));//LM

painter.drawPixmap(QRect(0,ptDstLT.y(),ptDstLT.x(), ptDstRB.y()-ptDstLT.y()), srcPix, QRect(0,m_ptBgLT.y(),m_ptBgLT.x(), m_ptBgRB.y()-m_ptBgLT.y()));//MM

painter.drawPixmap(QRect(ptDstLT.x(),ptDstLT.y(),ptDstRB.x()-ptDstLT.x(), ptDstRB.y()-ptDstLT.y()), srcPix, QRect(m_ptBgLT.x(),m_ptBgLT.y(),m_ptBgRB.x()-m_ptBgLT.x(), m_ptBgRB.y()-m_ptBgLT.y()));//RM

painter.drawPixmap(QRect(ptDstRB.x(),ptDstLT.y(), nW-ptDstRB.x(), ptDstRB.y()-ptDstLT.y()), srcPix, QRect(m_ptBgRB.x(),m_ptBgLT.y(), nWBg-m_ptBgRB.x(), m_ptBgRB.y()-m_ptBgLT.y()));//LB

painter.drawPixmap(QRect(0,ptDstRB.y(),ptDstLT.x(), nH-ptDstRB.y()), srcPix, QRect(0,m_ptBgRB.y(),m_ptBgLT.x(), nHBg-m_ptBgRB.y()));//MB

painter.drawPixmap(QRect(ptDstLT.x(),ptDstRB.y(),ptDstRB.x()-ptDstLT.x(), nH-ptDstRB.y()), srcPix, QRect(m_ptBgLT.x(),m_ptBgRB.y(),m_ptBgRB.x()-m_ptBgLT.x(), nHBg-m_ptBgRB.y()));//RB

painter.drawPixmap(QRect(ptDstRB.x(),ptDstRB.y(),nW-ptDstRB.x(), nH-ptDstRB.y()), srcPix, QRect(m_ptBgRB.x(),m_ptBgRB.y(),nWBg-m_ptBgRB.x(), nHBg-m_ptBgRB.y()));

painter.end();returndstPix;

}

2.  绘制菜单项

1) 控制ICON大小

在pixelMetric中:

if (pm ==QStyle::PM_SmallIconSize)return 12; //返回ICON的大小

2) 绘制菜单项内容

void CCustomStyle::drawControl( ControlElement control, const QStyleOption *option, QPainter *painter, const QWidget *widget ) const{switch(control )

{caseCE_MenuItem:

{

_DrawMenuItem(option, painter, widget);return; //直接返回, 否则会被super::drawcontrol覆盖

}

}

__super::drawControl(control, option, painter, widget);

}

1 void CCustomStyle::_DrawMenuItem(const QStyleOption *option, QPainter *painter, const QWidget *widget ) const

2 {3 painter->save();4

5 if (const QStyleOptionMenuItem *menuItem = qstyleoption_cast(option))6 {7 //先绘制一层背景(否则在透明情况下, 会直接透过去);

8 painter->setPen(colItemBg);9 painter->setBrush(colItemBg);10 painter->drawRect(option->rect);11

12 if (menuItem->menuItemType ==QStyleOptionMenuItem::Separator) {13 int w = 0;14 if (!menuItem->text.isEmpty()) { //绘制分隔符文字

15 painter->setFont(menuItem->font);16 proxy()->drawItemText(painter, menuItem->rect.adjusted(5, 0, -5, 0), Qt::AlignLeft |Qt::AlignVCenter,17 menuItem->palette, menuItem->state & State_Enabled, menuItem->text,18 QPalette::Text);19 w = menuItem->fontMetrics.width(menuItem->text) + 5;20 }21 painter->setPen(colSeparator);22 bool reverse = menuItem->direction ==Qt::RightToLeft;23 painter->drawLine(menuItem->rect.left() + 5 + (reverse ? 0 : w), menuItem->rect.center().y(),24 menuItem->rect.right() - 5 - (reverse ? w : 0), menuItem->rect.center().y());25 painter->restore();26 return;27 }28 bool selected = menuItem->state & State_Selected && menuItem->state &State_Enabled;29 if(selected) {30 QRect r = option->rect;31 painter->fillRect(r, colItemHighlight);32 }33 bool checkable = menuItem->checkType !=QStyleOptionMenuItem::NotCheckable;34 bool checked = menuItem->checked;35 bool sunken = menuItem->state &State_Sunken;36 bool enabled = menuItem->state &State_Enabled;37

38 bool ignoreCheckMark = false;39 int checkcol = qMax(menuItem->maxIconWidth, 20);40

41 if (qobject_cast(widget))42 ignoreCheckMark = true; //ignore the checkmarks provided by the QComboMenuDelegate

43

44 if (!ignoreCheckMark) {45 //Check

46 QRect checkRect(option->rect.left() + 7, option->rect.center().y() - 6, 14, 14);47 checkRect = visualRect(menuItem->direction, menuItem->rect, checkRect);48 if(checkable) {49 if (menuItem->checkType &QStyleOptionMenuItem::Exclusive) {50 //Radio button 未实现

51 if (checked ||sunken) {52 /*painter->setRenderHint(QPainter::Antialiasing);53 painter->setPen(Qt::NoPen);54

55 QPalette::ColorRole textRole = !enabled ? QPalette::Text:56 selected ? QPalette::HighlightedText : QPalette::ButtonText;57 painter->setBrush(option->palette.brush( option->palette.currentColorGroup(), textRole));58 painter->drawEllipse(checkRect.adjusted(4, 4, -4, -4));59 */

60 }61 } else{62 //Check box

63 if (menuItem->icon.isNull()) {64 QStyleOptionButton box;65 box.QStyleOption::operator=(*option);66 box.rect =checkRect;67 if (checked)68 box.state |=State_On;69 proxy()->drawPrimitive(PE_IndicatorCheckBox, &box, painter, widget);70

71 }72 }73 }74 } else { //ignore checkmark

75 if (menuItem->icon.isNull())76 checkcol = 0;77 else

78 checkcol = menuItem->maxIconWidth;79 }80

81 //Text and icon, ripped from windows style

82 bool dis = !(menuItem->state &State_Enabled);83 bool act = menuItem->state &State_Selected;84 const QStyleOption *opt =option;85 const QStyleOptionMenuItem *menuitem =menuItem;86

87 QPainter *p =painter;88 QRect vCheckRect = visualRect(opt->direction, menuitem->rect,89 QRect(menuitem->rect.x() + 4, menuitem->rect.y(),90 checkcol, menuitem->rect.height()));91 if (!menuItem->icon.isNull()) {92 QIcon::Mode mode = dis ?QIcon::Disabled : QIcon::Normal;93 if (act && !dis)94 mode =QIcon::Active;95 QPixmap pixmap;96

97 int smallIconSize = proxy()->pixelMetric(PM_SmallIconSize, option, widget);98 QSize iconSize(smallIconSize, smallIconSize);99 if (const QComboBox *combo = qobject_cast(widget))100 iconSize = combo->iconSize();101 if (checked)102 pixmap = menuItem->icon.pixmap(iconSize, mode, QIcon::On);103 else

104 pixmap = menuItem->icon.pixmap(iconSize, mode);105

106 int pixw =pixmap.width();107 int pixh =pixmap.height();108

109 QRect pmr(0, 0, pixw, pixh);110 pmr.moveCenter(vCheckRect.center());111 painter->setPen(colText);//menuItem->palette.text().color()

112 if (checkable && checked) {113 QStyleOption opt = *option;114 if(act) {115 QColor activeColor =mergedColors(116 colItemBg, //option->palette.background().color(),

117 colItemHighlight //option->palette.highlight().color());

118 );119 opt.palette.setBrush(QPalette::Button, activeColor);120 }121 opt.state |=State_Sunken;122 opt.rect =vCheckRect;123 proxy()->drawPrimitive(PE_PanelButtonCommand, &opt, painter, widget);124 }125 painter->drawPixmap(pmr.topLeft(), pixmap);126 }127 if(selected) {128 painter->setPen(colText);//menuItem->palette.highlightedText().color()

129 } else{130 painter->setPen(colText); //menuItem->palette.text().color()

131 }132 intx, y, w, h;133 menuitem->rect.getRect(&x, &y, &w, &h);134 int tab = menuitem->tabWidth;135 QColor discol;136 if(dis) {137 discol = colDisText; //menuitem->palette.text().color()

138 p->setPen(discol);139 }140 int xm = windowsItemFrame + checkcol + windowsItemHMargin + 2;141 int xpos = menuitem->rect.x() +xm;142

143 QRect textRect(xpos, y + windowsItemVMargin, w - xm - windowsRightBorder - tab + 1, h - 2 *windowsItemVMargin);144 QRect vTextRect = visualRect(opt->direction, menuitem->rect, textRect);145 QString s = menuitem->text;146 if (!s.isEmpty()) { //draw text

147 p->save();148 int t = s.indexOf(QLatin1Char('\t'));149 int text_flags = Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip |Qt::TextSingleLine;150 if (!__super::styleHint(SH_UnderlineShortcut, menuitem, widget))151 text_flags |=Qt::TextHideMnemonic;152 text_flags |=Qt::AlignLeft;153 if (t >= 0) {154 QRect vShortcutRect = visualRect(opt->direction, menuitem->rect,155 QRect(textRect.topRight(), QPoint(menuitem->rect.right(), textRect.bottom())));156 if (dis && !act && proxy()->styleHint(SH_EtchDisabledText, option, widget)) {157 p->setPen(colText);//menuitem->palette.light().color()

158 p->drawText(vShortcutRect.adjusted(1, 1, 1, 1), text_flags, s.mid(t + 1));159 p->setPen(discol);160 }161 p->drawText(vShortcutRect, text_flags, s.mid(t + 1));162 s =s.left(t);163 }164 QFont font = menuitem->font;165 //font may not have any "hard" flags set. We override166 //the point size so that when it is resolved against the device, this font will win.167 //This is mainly to handle cases where someone sets the font on the window168 //and then the combo inherits it and passes it onward. At that point the resolve mask169 //is very, very weak. This makes it stonger.

170 font.setPointSizeF(QFontInfo(menuItem->font).pointSizeF());171

172 if (menuitem->menuItemType ==QStyleOptionMenuItem::DefaultItem)173 font.setBold(true);174

175 p->setFont(font);176 if (dis && !act && proxy()->styleHint(SH_EtchDisabledText, option, widget)) {177 p->setPen(menuitem->palette.light().color());178 p->drawText(vTextRect.adjusted(1, 1, 1, 1), text_flags, s.left(t));179 p->setPen(discol);180 }181 p->drawText(vTextRect, text_flags, s.left(t));182 p->restore();183 }184

185 //Arrow 绘制子菜单指示符

186 if (menuItem->menuItemType == QStyleOptionMenuItem::SubMenu) {//draw sub menu arrow

187 int dim = (menuItem->rect.height() - 4) / 2;188 PrimitiveElement arrow;189 arrow = option->direction == Qt::RightToLeft ?PE_IndicatorArrowLeft : PE_IndicatorArrowRight;190 int xpos = menuItem->rect.left() + menuItem->rect.width() - 3 -dim;191 QRect vSubMenuRect = visualRect(option->direction, menuItem->rect,192 QRect(xpos, menuItem->rect.top() + menuItem->rect.height() / 2 - dim / 2, dim, dim));193 QStyleOptionMenuItem newMI = *menuItem;194 newMI.rect =vSubMenuRect;195 newMI.state = !enabled ?State_None : State_Enabled;196 if(selected)197 newMI.palette.setColor(QPalette::ButtonText, //此处futionstyle 有误, QPalette::Foreground改为ButtonText

198 colIndicatorArrow);//newMI.palette.highlightedText().color()

199 else

200 newMI.palette.setColor(QPalette::ButtonText,201 colIndicatorArrow);202

203 proxy()->drawPrimitive(arrow, &newMI, painter, widget);204 }205 }206 painter->restore();207 }

_DrawMenuItem

_DrawMenuItem的代码较长,  但比较简单, 都是一些条件判断加上绘图语句, 需要自己修改pallete的颜色

值得注意的是: 在透明情况下, 应先绘制一层menu item 的背景, 否则会直接透过去

3) 最后还要重写一下QMenu的addMenu

以使子菜单也生效

QAction * CCustomMenu::addMenu( CCustomMenu *menu )

{returnQMenu::addMenu(menu);

}

CCustomMenu* CCustomMenu::addMenu( const QString &title )

{

CCustomMenu*menu = new CCustomMenu(title, this);

addAction(menu->menuAction());returnmenu;

}

CCustomMenu* CCustomMenu::addMenu( const QIcon &icon, const QString &title )

{

CCustomMenu*menu = new CCustomMenu(title, this);

menu->setIcon(icon);

addAction(menu->menuAction());returnmenu;

}

编译需要VS2010+Qt5.

PS:

关于QMenu如何处理菜单消失, 参考我的另一篇blog Qt中QMenu的菜单关闭处理方法

qmenu基本用法_剖析QMenu Qt完全定制化菜单相关推荐

  1. qmenu基本用法_使用QProxyStyle定制QMenu (二)

    显示一个还没定制的右键菜单 要在右键单击时显示弹出式的上下文菜单(a context menu)就要用到QWidget的contextMenuPolicy : Qt::ContextMenuPolic ...

  2. [SpringBoot2]web场景_静态资源规则与定制化

    静态资源目录 只要静态资源放在类路径下: called /static (or /public or /resources or /META-INF/resources 访问 : 当前项目根路径/ + ...

  3. exdoll机器人_打造国内领先的定制化人工智能机器人品牌, EXDOLL新品惊艳亮相_TOM新闻...

    原本只能在科幻电影中见到的人工智能,如今已越来越多的出现在我们的日常生活中,而随着AI技术和仿真技术的迅猛发展,人工智能机器人有望被大规模的应用于企业服务.医疗服务与生活服务中. AlphaGo和机器 ...

  4. challenge是什么意思_英语单词学习-challenge是什么意思_翻译_用法_例句

    我们在学习英语这门科目的时候,不仅要重视听说读写,对于英语的基础知识也要重视,只有基础知识掌握到了,才能把英语这门科目学的更好.为了大家能够多学习一些英语单词,小编就带大家先了解一下英语单词学习-ch ...

  5. MATLAB中fix啥意思,matlab fix函数用法_常见问题解析,matlab

    matlab syms什么意思_常见问题解析 matlab中syms的意思是定义多个变量,可以用来创建符号变量x和y,语法是"syms x y":也可以创建一些符号变量.函数和数组 ...

  6. matlab中的fix,matlab fix函数用法_常见问题解析

    matlab syms什么意思_常见问题解析 matlab中syms的意思是定义多个变量,可以用来创建符号变量x和y,语法是"syms x y":也可以创建一些符号变量.函数和数组 ...

  7. qt 在点击菜单下的动作之后获取该菜单的名称

    文章目录 qt 在点击菜单下的动作之后获取该菜单的名称 问题: 解决过程: qt 在点击菜单下的动作之后获取该菜单的名称 问题: QMenu 类下有4个信号: void aboutToHide() v ...

  8. Windows下Qt Creator设置IDE菜单字体样式

    Windows下Qt Creator设置IDE菜单字体样式 本文为解决以下问题: IDE的菜单字体太小 IDE的菜单字体样式,可以更换为微软雅黑 创建样式文件 在Qt Creator目录下C:\Qt\ ...

  9. tkinter向文本框里加内容_给tkinter文本框添加右键菜单

    给tkinter文本框添加右键菜单 需求:直接右键点击使用tkinter创建的文本框是不会弹出菜单的.我们需要实现右键点击tkinter框架下的Entry对象.Text对象后弹出右键菜单可复制.粘贴和 ...

最新文章

  1. Python之面向对象进阶
  2. 六大重点工程构筑兰州大数据产业
  3. 循环数组的最大子段和
  4. VTK:Filtering之Glyph2D
  5. 随时间的反向传播算法 BPTT
  6. 我的北京游戏开发总结【三】
  7. BZOJ3675 [APIO2014]序列分割
  8. GIT学习笔记2--基本使用
  9. Mysql学习总结(27)——Mysql数据库字符串函数
  10. 文件共享服务器灾备,搭建容灾性强、可管理的的文件共享服务器-windows server 2008 DFS续...
  11. hive内置函数_Hive Query生命周期 —— 钩子(Hook)函数篇
  12. 经典游戏扫雷详解--你也可以写出扫雷和玩好扫雷
  13. SSM项目实战 —— 物流管理系统的实现
  14. LeetCode 刷题: Fizz Buzz
  15. Cisco switchport trunk encapsulation dot1q 详解
  16. 倪海厦天纪笔记16_倪海厦《天纪》——人间道听课笔记
  17. SQL面试必考——计算留存率
  18. 90后都开始养生了,你怎么能少了这本宝典丨钛空舱
  19. 数据分析过程中,发现数值缺失,怎么办?
  20. Python基础:函数的返回值return

热门文章

  1. 微信网页登录授权详解(前端)
  2. java indexof 子字符串_Java中字符串中子串的查找共有四种方法(indexof())
  3. 电脑开机时嘀嘀4声开不了机
  4. 「第三章」跨站脚本攻击(XSS)
  5. Java实现微信加密数据解密算法
  6. 案例式c语言程序设计彭文艺答案,彭文艺
  7. windows vue3 安装及搭建
  8. Android系统开发之烧录开发板
  9. doxygen教程-4-快速上手配置文件
  10. LeetCode第50题思悟——Pow(x, n)(powx-n)