Qt on Android 字体挑选



  • Qt on Android 字体挑选

    在开发安卓应用的时候,发现,字体是个很大的问题,毕竟安卓上没有微软雅黑,不能直接设置微软雅黑。

    方法如下:

    1. 使用 qrc 资源文件加载 ttf,但是毕竟微软雅黑的块头在那,这么嗨大。在编译 qrc 的时候,cpu 占用极高。

    2. 挑选安卓系统上现有的字体

      在我的魅蓝 note 2 上,使用 Qt.fontFamilies() 可以打印安卓系统上的字体库。

      AndroidClock
      AndroidClock-Large
      Carrois Gothic SC
      Clockopia
      Coming Soon
      Cutive Mono
      Dancing Script
      Droid Sans Mono
      Lohit Odia
      MotoyaLMaru
      Noto Color Emoji
      Noto Naskh Arabic
      Noto Naskh Arabic UI
      Noto Sans
      Noto Sans Armenian
      Noto Sans Balinese
      Noto Sans Batak
      Noto Sans Bengali
      Noto Sans Bengali UI
      Noto Sans Buginese
      Noto Sans Buhid
      Noto Sans Canadian Aboriginal
      Noto Sans Cham
      Noto Sans Cherokee
      Noto Sans Coptic
      Noto Sans Devanagari
      Noto Sans Devanagari UI
      Noto Sans Ethiopic
      Noto Sans Georgian
      Noto Sans Glagolitic
      Noto Sans Gujarati
      Noto Sans Gujarati UI
      Noto Sans Gurmukhi
      Noto Sans Gurmukhi UI
      Noto Sans Hanunoo
      Noto Sans Hebrew
      Noto Sans Javanese
      Noto Sans JP
      Noto Sans Kannada
      Noto Sans Kannada UI
      Noto Sans Kayah Li
      Noto Sans KR
      Noto Sans Lao
      Noto Sans Lao UI
      Noto Sans Lepcha
      Noto Sans Limbu
      Noto Sans Malayalam
      Noto Sans Malayalam UI
      Noto Sans Meetei Mayek
      Noto Sans Myanmar
      Noto Sans Myanmar UI
      Noto Sans Ol Chiki
      Noto Sans Rejang
      Noto Sans Saurashtra
      Noto Sans SC
      Noto Sans Sinhala
      Noto Sans Sundanese
      Noto Sans Syloti Nagri
      Noto Sans Symbols
      Noto Sans Tagbanwa
      Noto Sans Tai Le
      Noto Sans Tai Tham
      Noto Sans Tai Viet
      Noto Sans Tamil
      Noto Sans Tamil UI
      Noto Sans TC
      Noto Sans Telugu
      Noto Sans Telugu UI
      Noto Sans Thaana
      Noto Sans Thai
      Noto Sans Thai UI
      Noto Sans Tifinagh
      Noto Sans Yi
      Noto Serif
      Roboto
      Roboto Condensed
      Source Han Sans CN
      Source Han Sans JP
      Source Han Sans KR
      

    但是看起来貌似都不靠谱呢。现在是使用 NotoSansHans-DemiLight 这个字体(7.95MB 呢)。

    求推荐。



  • Android最好字体不设置。在Windows phone系统上,字体也要换成等线字体。



  • @jiangcaiyang

    什么是等线字体?



  • @qyvlik Windows Phone 8特有的字体。是默认字体。



  • @qyvlik 说:

    字体是个很大的问题,毕竟安卓上没有微软雅黑

    刚才无意中看到墨迹天气的安卓包里assets目录下有个font文件夹你可以参考一下

    0_1449800814870_upload-c975aaef-24f7-41d0-8d45-4970035cf61a



  • @Zero 嗯,可以将字体放在assets文件夹中。这样的好处是不必在程序启动前就将该字体载入其中。


 

走马观花

最近的回复

  • F

    @QQ-690D15264BFFE58B6C76CFE35B63FCF1共享一下自己编译的Qt库 中说:

    SourceForge 上不去了。。。。。不知道大家能不能用我传上去的东西了。。。

    SF好了。。。。。。。。。
    不知道哪里来的强。。。。。。。。

    read more
  • 简介 自绘方案 QPainter QWidget+QPainter 示例 QQuickPaintedItem+QPainter 示例 关于QPainter Qml Canvas Qml Shapes QOpenGLWidget / QOpenGLWindow Qml SceneGraph Qml QQuickFrameBufferObject Qml ShaderEffect QVulkanWindow 简介

    本文是《Qml组件化编程》系列文章的第七篇,涛哥会罗列Qt中的所有自绘方案,并提供一些案例和说明。

    Qt自带的组件,外观都是固定的,一般可以通过qss/Qml style等方式进行定制。

    如果要实现外观特殊的组件,就需要自己绘制了。

    注:文章主要发布在涛哥的博客知乎专栏-涛哥的Qt进阶之路

    自绘方案

    Qt中的自绘方案有这么一些:

    QWidget+QPainter / QQuickPaintedItem+QPainter Qml Canvas Qml Shapes QOpenGLWidget / QOpenGLWindow Qml QQuickFrameBufferObject Qml SceneGraph Qml ShaderEffect QVulkanWindow

    (GraphicsView和QWidget的绘制类似,就不讨论了)

    QPainter

    QPainter是一个功能强大的画笔,QWidget中的各种控件如QPushButton、QLable等都是用QPainter画出来的。

    (QWidget的控件在绘制时,还增加了qss样式表,让UI定制变得更加方便。)

    QWidget+QPainter 示例

    QWidget中使用QPainter的方法,是重载paintEvent事件,这里示例绘制一个进度条:

    预览

    //MainWindow.h #pragma once #include <QMainWindow> class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = 0); ~MainWindow(); protected: void paintEvent(QPaintEvent *event) override; void timerEvent(QTimerEvent *event) override; private: QList<QColor> mColorList; int mCurrent = 0; }; //MainWindow.cpp #include "MainWindow.h" #include <QPainter> #include <QtMath> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { resize(400, 300); mColorList << QColor(51, 52, 54) << QColor(75, 85, 86) << QColor(87, 103, 103) << QColor(95, 119, 121) << QColor(101, 132, 134) << QColor(104, 146, 145) << QColor(104, 158, 158) << QColor(101, 169, 168) << QColor(92, 182, 180) << QColor(79, 194, 191); //每秒触发60次定时器,即刷新率60FPS startTimer(1000 / 60); } MainWindow::~MainWindow() { } void MainWindow::timerEvent(QTimerEvent *) { mCurrent =(mCurrent + 3) % 360; update(); } void MainWindow::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.setRenderHints(QPainter::Antialiasing|QPainter::TextAntialiasing); //原点x坐标 qreal a = 100; //原点y坐标 qreal b = 100; //半径 qreal r = 80; //每个小圆的半径递增值 qreal roffset = 2; //每个小圆的角度递增值 qreal angleOffset = 30; qreal currentangle = mCurrent ; for (int i = 0; i < mColorList.length(); i++) { qreal r0 = i * roffset; qreal angle = currentangle + i * angleOffset; qreal x0 = r * cos(qDegreesToRadians(angle)) + a; qreal y0 = r * sin(qDegreesToRadians(angle)) + b; painter.setPen(mColorList[i]); painter.setBrush(QBrush(mColorList[i])); painter.drawEllipse(x0 - r0, y0 - r0, 2 * r0, 2 * r0); } } QQuickPaintedItem+QPainter 示例

    QQuickPaintedItem继承自QQuickItem,而QQuickItem就是Qml中的Item。

    QQuickPaintedItem通过重载paint函数,就可以使用QPainter绘制。

    自定义的QQuickPaintedItem子类需要注册到Qml中才能使用,注册类型或者注册实例都可以,具体可以参考《 Qml组件化编程5-Qml与C++交互》

    这里示例QQuickPaintedItem 中使用 QPainter绘制一个阴阳八卦:

    预览

    //PBar.h #pragma once #include <QQuickPaintedItem> class PBar : public QQuickPaintedItem { Q_OBJECT public: PBar(QQuickItem *parent = nullptr); void paint(QPainter *painter) override; void timerEvent(QTimerEvent *event) override; private: QList<QColor> mColorList; int mCurrent = 0; }; //PBar.cpp #include "PBar.h" #include <QPainter> #include <QtMath> PBar::PBar(QQuickItem *parent) : QQuickPaintedItem (parent) { mColorList << QColor(51, 52, 54) << QColor(75, 85, 86) << QColor(87, 103, 103) << QColor(95, 119, 121) << QColor(101, 132, 134) << QColor(104, 146, 145) << QColor(104, 158, 158) << QColor(101, 169, 168) << QColor(92, 182, 180) << QColor(79, 194, 191); //每秒触发60次定时器,即刷新率60FPS startTimer(1000 / 60); } void PBar::paint(QPainter *painter) { //原点x坐标 qreal a = 100; //原点y坐标 qreal b = 100; //半径 qreal r = 80; qreal r1 = r / 2; qreal r2 = r / 6; qreal currentangle = mCurrent; painter->save(); painter->setRenderHints(QPainter::Antialiasing|QPainter::TextAntialiasing); //red 部分 { painter->setBrush(QBrush(QColor(128, 1, 1))); QPainterPath path(QPointF(a + r * cos(qDegreesToRadians( currentangle )), b - r * sin(qDegreesToRadians(currentangle )))); path.arcTo(a - r, b - r, r * 2, r * 2, currentangle, 180); path.arcTo(a + r1 * cos(qDegreesToRadians(currentangle + 180)) - r1, b - r1 * sin(qDegreesToRadians(currentangle + 180)) - r1, r1 * 2, r1 * 2, currentangle + 180, 180); path.arcTo(a + r1*cos(qDegreesToRadians(currentangle)) - r1, b - r1 * sin(qDegreesToRadians(currentangle)) - r1, r1 * 2, r1 * 2, currentangle + 180, -180 ); painter->drawPath(path); } //blue 部分 { painter->setBrush(QBrush(QColor(1, 1, 128))); QPainterPath path(QPointF(a + r * cos(qDegreesToRadians( currentangle )), b - r * sin(qDegreesToRadians(currentangle )))); path.arcTo(a - r, b - r, r * 2, r * 2, currentangle, -180); path.arcTo(a + r1 * cos(qDegreesToRadians(currentangle + 180)) - r1, b - r1 * sin(qDegreesToRadians(currentangle + 180)) - r1, r1 * 2, r1 * 2, currentangle + 180, 180); path.arcTo(a + r1*cos(qDegreesToRadians(currentangle)) - r1, b - r1 * sin(qDegreesToRadians(currentangle)) - r1, r1 * 2, r1 * 2, currentangle + 180, -180 ); painter->drawPath(path); } { // red 小圆 painter->setBrush(QBrush(QColor(128, 1, 1))); QPainterPath path; path.addEllipse(a + r1 * cos(qDegreesToRadians(currentangle)) - r2, b - r1 * sin(qDegreesToRadians(currentangle )) - r2, r2 * 2, r2 * 2); painter->drawPath(path); } { //blue 小圆 painter->setBrush(QBrush(QColor(1, 1, 128))); QPainterPath path; path.addEllipse(a + r1 * cos(qDegreesToRadians(180 + currentangle)) - r2, b - r1 * sin(qDegreesToRadians(180 + currentangle)) - r2, r2 * 2, r2 * 2); painter->drawPath(path); } painter->restore(); } void PBar::timerEvent(QTimerEvent *event) { (void)event; mCurrent =(mCurrent + 3) % 360; update(); } //main.cpp #include <QGuiApplication> #include <QQmlApplicationEngine> #include "PBar.h" int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); qmlRegisterType<PBar>("PBar", 1, 0, "PBar"); QQmlApplicationEngine engine; const QUrl url(QStringLiteral("qrc:/main.qml")); QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) { if (!obj && url == objUrl) QCoreApplication::exit(-1); }, Qt::QueuedConnection); engine.load(url); return app.exec(); } //main.qml import QtQuick 2.0 import QtQuick.Window 2.0 import PBar 1.0 Window { visible: true width: 640 height: 480 title: qsTr("Hello PBar") PBar { anchors.fill: parent } } 关于QPainter

    QPainter底层使用CPU做光栅化渲染,这种方式在没有GPU的设备中能够很好地工作。

    (我的好友"Qt侠-刘典武"就是这方面的实战专家,他手上有将近150个精美的自绘组件,比官方还要多,有需要的同学可以联系他 QQ517216493)

    然而时代在飞速发展,很多设备都带上了GPU,QPainter在GPU设备上,将不能发挥GPU的全部实力。

    (刘典武也在积极跟进GPU绘制)

    这里提一下,有个叫QUItCoding的组织,开发了一套QNanoPainter,接口和QPainter一致,

    在大部分场景下都拥有不错的性能。其底层是基于nanovg的GPU加速。

    不过QNanoPainter并没有合并进Qt官方,具体原因不清楚, 有可能是因为性能并不是100%达标的。

    Qml Canvas

    Qml中提供了Canvas组件,接口和html中的Canvas基本一致,可以直接copy html中的Canvas代码(极少部分不能用)。

    当然QPainter实现的功能,也都可以移植到Canvas中。

    Canvas渲染性能并不太好,如果有性能要求,还是不要用Canvas了。

    这里示例绘制一个笑脸

    预览

    //main.qml import QtQuick 2.0 import QtQuick.Window 2.0 Window { visible: true width: 640 height: 480 title: qsTr("Hello Canvas") Canvas { id: canvas anchors.fill: parent onPaint: { var ctx = canvas.getContext('2d'); ctx.beginPath(); ctx.arc(75,75,50,0,Math.PI*2,true); // 绘制 ctx.moveTo(110,75); ctx.arc(75,75,35,0,Math.PI,false); // 口(顺时针) ctx.moveTo(65,65); ctx.arc(60,65,5,0,Math.PI*2,true); // 左眼 ctx.moveTo(95,65); ctx.arc(90,65,5,0,Math.PI*2,true); // 右眼 ctx.stroke(); } } } Qml Shapes

    Qt5.10开始,Qml增加了Quick.Shapes功能。这是目前官方提供的自绘途径中,兼顾性能和易用性的最佳选择。

    Shapes底层为GPU渲染(基于SceneGraph),QPainter能绘制的基础图元,都可以用Shapes实现。Shapes再配合上Qml中的

    属性绑定和属性动画,可以轻易实现各式各样的动态、酷炫的UI。

    (后续的自定义组件,涛哥将会优先使用Shapes。)

    这里示例实现一个任意圆角的Rectangle组件:

    预览

    // TRoundRect.qml import QtQuick 2.12 import QtQuick.Controls 2.5 import QtQuick.Shapes 1.12 Shape { id: root //左上角是否圆角 property bool leftTopRound: true //左下角是否圆角 property bool leftBottomRound: true //右上角是否圆角 property bool rightTopRound: true //右下角是否圆角 property bool rightBottomRound: true //圆角半径 property real radius //颜色 property color color: "red" //多重采样抗锯齿 layer.enabled: true layer.samples: 8 //平滑处理 smooth: true //反走样抗锯齿 antialiasing: true ShapePath { fillColor: color startX: leftTopRound ? radius : 0 startY: 0 fillRule: ShapePath.WindingFill PathLine { x: rightTopRound ? root.width - radius : root.width y: 0 } PathArc { x: root.width y: rightTopRound ? radius : 0 radiusX: rightTopRound ? radius : 0 radiusY: rightTopRound ? radius : 0 } PathLine { x: root.width y: rightBottomRound ? root.height - radius : root.height } PathArc { x: rightBottomRound ? root.width - radius : root.width y: root.height radiusX: rightBottomRound ? radius : 0 radiusY: rightBottomRound ? radius : 0 } PathLine { x: leftBottomRound ? radius : 0 y: root.height } PathArc { x: 0 y: leftBottomRound ? root.height - radius : root.height radiusX: leftBottomRound ? radius : 0 radiusY: leftBottomRound ? radius : 0 } PathLine { x: 0 y: leftTopRound ? radius : 0 } PathArc { x: leftTopRound ? radius : 0 y: 0 radiusX: leftTopRound ? radius : 0 radiusY: leftTopRound ? radius : 0 } } }

    看一下TRoundRect的用法

    import QtQuick 2.0 import QtQuick.Controls 2.5 Rectangle { width: 800 height: 600 Rectangle { //背景红色,衬托一下 x: 10 width: 100 height: 160 color: "red" } TRoundRect { id: roundRect x: 40 y: 10 width: 200 height: 160 radius: 40 leftTopRound: lt.checked rightTopRound: rt.checked leftBottomRound: lb.checked rightBottomRound: rb.checked color: "#A0333666" //半透明色 } Grid { x: 300 y: 10 columns: 2 spacing: 10 CheckBox { id: lt text: "LeftTop" checked: true } CheckBox { id: rt text: "RightTop" checked: true } CheckBox { id: lb text: "LeftBottom" checked: true } CheckBox { id: rb text: "rightBottom" checked: true } } } QOpenGLWidget / QOpenGLWindow

    有的同学学习过OpenGL这类图形渲染API,Qt为OpenGL提供了便利的窗口和上下文环境。

    QOpenGLWidget用来在QWidget框架中集成OpenGL渲染,QOpenGLWindow用在Qml框架。

    使用方法都是子类重载下面三个函数:

    void initializeGL(); void paintGL(); void resizeGL(int w, int h);

    这里可以参考官方的示例:

    QOpenGLWidget示例

    QOpenGLWindow示例

    Qt对OpenGL系列的函数都做了封装,一般使用QOpenGLFunctions就够了,QOpenGLFunctions是基于OpenGL ES 2.0 API的跨平台实现,删减了个别API。

    相应的有一个未删减的OpenGLES2 的封装:QOpenGLFunctions_ES2。

    当然为了兼容所有OpenGL版本,Qt分别封装了相应的类

    预览

    有特殊版本需要的时候,可以把QOpenGLFunctions换成相应的类。

    还有一个OpenGL ES3.0的封装, QOpenGLExtraFunctions,可以在支持OpenGL ES 3.0的设备上使用。

    使用这些functions,一定要在有OpenGL上下文环境的地方,先调用一下initializeOpenGLFunctions。有些版本的init有返回值的,要注意判断并处理。

    Qml SceneGraph

    Qml基于GPU实现了一套渲染框架,这个框架就是SceneGraph。

    SceneGraph提供了很多GPU渲染相关的功能,以方便进行自绘制,都是以QSG开头的类,如下图所示:

    预览

    使用方式是在QQuickItem的子类中,重载updatePaintNode函数:

    QSGNode *TaoItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *) { QSGSimpleRectNode *n = static_cast<QSGSimpleRectNode *>(node); if (!n) { n = new QSGSimpleRectNode(); n->setColor(Qt::red); } n->setRect(boundingRect()); return n; }

    在使用Qml框架的程序中,使用这些QSG功能,将自定义渲染直接加入SceneGraph框架的渲染流程,无疑是性能最优的。

    不过问题在于,这些QSG有点难以使用。需要有一定的OpenGL或DirectX相关图形学知识,并理解SceneGraph的节点交换机制,才能用好。

    而懂OpenGL的人,有更好的选择,就是直接使用OpenGL的API。下面的QQuickFrameBufferObject就是一种途径。

    Qml QQuickFrameBufferObject

    QQuickFramebufferObject继承于QQuickItem(Qml中将它当作一个Item就可以了),用来在一个framebuffer object(FBO)上做渲染,

    SceneGraph框架会将这个FBO渲染到屏幕上。

    使用的方式是,实现一个QQuickFramebufferObject::Renderer类。

    这个类里面始终是拥有OpenGL上下文环境的,内存也是被SceneGraph框架管理的,只要理解了渲染流程,用起来还是很方便的。

    涛哥在Qml中集成 视频播放器 和 3D模型渲染的时候,就使用了这个FBO。

    可以参考这两个例子:

    Qml渲染3D模型

    FFmpeg解码,Qml/OpenGL转码渲染

    Qml ShaderEffect

    学习过图形学的人,都应该听说过大名鼎鼎的Shadertoy

    只要一点奇妙的Shader代码,就能渲染出各种酷炫的效果。

    Qml中提供了ShaderEffect组件,就可以用来做ShaderToy那样的特效。

    可以参考qyvlik的代码仓库:

    qyvlik-ShaderToy.qml

    以及我很久以前写的例子:

    Tao-ShaderToy

    360能量球

    Qml中还有个神奇的ShaderEffectSource,可以用在普通Item的layer.effect中,

    比如这个例子,就用ShaderEffectSource做了倒影特效:

    倒影特效

    QVulkanWindow

    OpenGL的下一代,已经进化为vulkan了。

    Qt 5.10开始,也提供了vulkan的支持。

    涛哥水平有限,这次只提一下,就先不展开说了。

    转载声明

    文章出自涛哥的博客
    文章采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可, 转载请注明出处, 谢谢合作 © 涛哥

    联系方式 作者 武威的涛哥 开发理念 弘扬鲁班文化,传承工匠精神 博客 https://jaredtao.github.io github https://github.com/jaredtao 知乎 https://www.zhihu.com/people/wentao-jia 邮箱 jared2020@163.com 微信 xsd2410421 QQ 759378563

    请放心联系我,乐于提供咨询服务,也可洽谈商务合作相关事宜。

    打赏

    weixin
    zhifubao

    如果觉得涛哥写的还不错,还请为涛哥打个赏,您的赞赏是涛哥持续创作的源泉。

    read more
  • 简介 环境说明 QtCreator折叠全部代码 QtCreator属性生成 QtCreator注释代码 QtCreator代码片段 QtCreator代码格式化 QtCreator会话管理 结尾 简介

    本文是《Qt实用技能汇总》系列文章的第一篇,涛哥将教大家,一些QtCreator的实用技巧。

    工欲善其事,必先利其器。

    这个系列,全是干货!

    环境说明

    下文以Windows平台的QtCreator为参考,其它平台的菜单栏入口和快捷键 请以实际为准。

    QtCreator版本以Qt5.6.x及以上安装包所带的都行,再旧的版本不讨论。

    QtCreator折叠全部代码

    折叠全部代码,支持C++和Qml。操作方式为:

    光标焦点放在代码文本中,之后 菜单栏: 编辑->Advanced->Toggle Fold All

    预览

    这个功能没有快捷键

    QtCreator属性生成

    经常需要给自定义的QObject类写一些属性,QtCreator是可以自动生成get、set函数以及change信号的。

    只要写上Q_PROPERTY那一行,光标放在Q_PROPERTY上, 用右键菜单 -> Refactor -> Generate Missing Q_PROPERTY Memory 即可生成。

    也可以使用快捷键,光标放在Q_PROPERTY上,按Alt + Enter。

    预览

    QtCreator注释代码

    快捷键,注释当前行代码或者当前选中的多行代码

    Ctrl + /

    已经注释掉的,再按一次取消注释。

    QtCreator代码片段

    前面的Q_PROPERTY自动生成,其实就是一种代码片段。

    比如经常要写这样一段代码

    if (pObj) { delete pObj; pObj = nullptr; }

    其中的pObj出现了多次,在不同的地方只是pObj这个名字不同,其它if 和 delete操作一模一样。

    可以把这段代码封装成模板函数,也可以做成QtCreator的代码片段。(不建议定义宏, 不类型安全和不方便调试)

    模板是这样的:

    template<class T> safeDelete (T *pObj) { if (pObj) { delete pObj; pObj = nullptr; } }

    代码片段是在写代码时就把实际的代码生成出来了,模板是编译的时候才去生成。所以代码片段可以加快编译速度。

    代码片段是这么做的,在菜单的 工具->选项 弹出选项窗口,然后到文本编辑器->片段->下拉选C++

    预览

    添加一个片段,起名字叫safeD,并填上内容

    if ($$) { delete $$; $$ = nullptr; }

    预览

    写好后点击确定。再回到代码中,输入safeD,按回车就会自动补全前面的片段。

    光标出现在$$的地方,有一个高亮颜色,此时只要输入一个名字,按下回车键,后续的地方自动替换成输入的名字了。

    预览

    QtCreator代码格式化

    都9102年了,如果还有人跟你计较大括号要不要换行、指针符号靠左还是靠右这种问题,请用自动格式化工具怼他/她。

    QtCreator支持很多种格式化工具,涛哥用的是clang-format。VisualStudio 2017、2019、以及VSCode也支持clang-format

    的,都不需要额外安装任何插件。配置起来很简单,只要在项目pro文件同级目录下,放一个配置好的.clang-format的文件就行了。

    团队合作的时候,使用同一个.clang-format配置文件,大家的代码格式就都一致了。

    clang-format有默认的google、llvm等格式可选,也可以自定义。下面是一个涛哥使用的自定义配置文件,并做了详细的注释

    --- # 语言: None, Cpp, Java, JavaScript, ObjC, Proto, TableGen, TextProto Language: Cpp # BasedOnStyle: WebKit # 访问说明符(public、private等)的偏移 AccessModifierOffset: -4 # 开括号(开圆括号、开尖括号、开方括号)后的对齐: Align, DontAlign, AlwaysBreak(总是在开括号后换行) AlignAfterOpenBracket: AlwaysBreak # 连续赋值时,对齐所有等号 AlignConsecutiveAssignments: false # 连续声明时,对齐所有声明的变量名 AlignConsecutiveDeclarations: false # 左对齐逃脱换行(使用反斜杠换行)的反斜杠 AlignEscapedNewlines: Right # 水平对齐二元和三元表达式的操作数 AlignOperands: true # 对齐连续的尾随的注释 AlignTrailingComments: true # 允许函数声明的所有参数在放在下一行 AllowAllParametersOfDeclarationOnNextLine: true # 允许短的块放在同一行 AllowShortBlocksOnASingleLine: false # 允许短的case标签放在同一行 AllowShortCaseLabelsOnASingleLine: false # 允许短的函数放在同一行: None, InlineOnly(定义在类中), Empty(空函数), Inline(定义在类中,空函数), All AllowShortFunctionsOnASingleLine: Empty # 允许短的if语句保持在同一行 AllowShortIfStatementsOnASingleLine: false # 允许短的循环保持在同一行 AllowShortLoopsOnASingleLine: false # 总是在定义返回类型后换行(deprecated) AlwaysBreakAfterDefinitionReturnType: None # 总是在返回类型后换行: None, All, TopLevel(顶级函数,不包括在类中的函数), # AllDefinitions(所有的定义,不包括声明), TopLevelDefinitions(所有的顶级函数的定义) AlwaysBreakAfterReturnType: None # 总是在多行string字面量前换行 AlwaysBreakBeforeMultilineStrings: false # 总是在template声明后换行 AlwaysBreakTemplateDeclarations: true # false表示函数实参要么都在同一行,要么都各自一行 BinPackArguments: false # false表示所有形参要么都在同一行,要么都各自一行 BinPackParameters: false # 大括号换行,只有当BreakBeforeBraces设置为Custom时才有效 BraceWrapping: # class定义后面 AfterClass: true # 控制语句后面 AfterControlStatement: true # enum定义后面 AfterEnum: true # 函数定义后面 AfterFunction: true # 命名空间定义后面 AfterNamespace: true # ObjC定义后面 AfterObjCDeclaration: false # struct定义后面 AfterStruct: true # union定义后面 AfterUnion: true # extern 定义后面 AfterExternBlock: true # catch之前 BeforeCatch: true # else 之前 BeforeElse: true # 缩进大括号 IndentBraces: false SplitEmptyFunction: true SplitEmptyRecord: true SplitEmptyNamespace: true # 在二元运算符前换行: None(在操作符后换行), NonAssignment(在非赋值的操作符前换行), All(在操作符前换行) BreakBeforeBinaryOperators: All # 在大括号前换行: Attach(始终将大括号附加到周围的上下文), Linux(除函数、命名空间和类定义,与Attach类似), # Mozilla(除枚举、函数、记录定义,与Attach类似), Stroustrup(除函数定义、catch、else,与Attach类似), # Allman(总是在大括号前换行), GNU(总是在大括号前换行,并对于控制语句的大括号增加额外的缩进), WebKit(在函数前换行), Custom # 注:这里认为语句块也属于函数 BreakBeforeBraces: Allman # 继承列表的逗号前换行 BreakBeforeInheritanceComma: false # 在三元运算符前换行 BreakBeforeTernaryOperators: true # 在构造函数的初始化列表的逗号前换行 BreakConstructorInitializersBeforeComma: false # 初始化列表前换行 BreakConstructorInitializers: BeforeComma # Java注解后换行 BreakAfterJavaFieldAnnotations: false BreakStringLiterals: true # 每行字符的限制,0表示没有限制 ColumnLimit: 160 # 描述具有特殊意义的注释的正则表达式,它不应该被分割为多行或以其它方式改变 CommentPragmas: '^ IWYU pragma:' # 紧凑 命名空间 CompactNamespaces: false # 构造函数的初始化列表要么都在同一行,要么都各自一行 ConstructorInitializerAllOnOneLineOrOnePerLine: true # 构造函数的初始化列表的缩进宽度 ConstructorInitializerIndentWidth: 4 # 延续的行的缩进宽度 ContinuationIndentWidth: 4 # 去除C++11的列表初始化的大括号{后和}前的空格 Cpp11BracedListStyle: false # 继承最常用的指针和引用的对齐方式 DerivePointerAlignment: false # 关闭格式化 DisableFormat: false # 自动检测函数的调用和定义是否被格式为每行一个参数(Experimental) ExperimentalAutoDetectBinPacking: false # 固定命名空间注释 FixNamespaceComments: true # 需要被解读为foreach循环而不是函数调用的宏 ForEachMacros: - foreach - Q_FOREACH - BOOST_FOREACH IncludeBlocks: Preserve # 对#include进行排序,匹配了某正则表达式的#include拥有对应的优先级,匹配不到的则默认优先级为INT_MAX(优先级越小排序越靠前), # 可以定义负数优先级从而保证某些#include永远在最前面 IncludeCategories: - Regex: '^"(llvm|llvm-c|clang|clang-c)/' Priority: 2 - Regex: '^(<|"(gtest|gmock|isl|json)/)' Priority: 3 - Regex: '.*' Priority: 1 IncludeIsMainRegex: '(Test)?$' # 缩进case标签 IndentCaseLabels: true IndentPPDirectives: None # 缩进宽度 IndentWidth: 4 # 函数返回类型换行时,缩进函数声明或函数定义的函数名 IndentWrappedFunctionNames: false JavaScriptQuotes: Leave JavaScriptWrapImports: true # 保留在块开始处的空行 KeepEmptyLinesAtTheStartOfBlocks: true # 开始一个块的宏的正则表达式 MacroBlockBegin: '' # 结束一个块的宏的正则表达式 MacroBlockEnd: '' # 连续空行的最大数量 MaxEmptyLinesToKeep: 1 # 命名空间的缩进: None, Inner(缩进嵌套的命名空间中的内容), All NamespaceIndentation: All # 使用ObjC块时缩进宽度 ObjCBlockIndentWidth: 4 # 在ObjC的@property后添加一个空格 ObjCSpaceAfterProperty: true # 在ObjC的protocol列表前添加一个空格 ObjCSpaceBeforeProtocolList: true PenaltyBreakAssignment: 2 PenaltyBreakBeforeFirstCallParameter: 19 # 在一个注释中引入换行的penalty PenaltyBreakComment: 300 # 第一次在<<前换行的penalty PenaltyBreakFirstLessLess: 120 # 在一个字符串字面量中引入换行的penalty PenaltyBreakString: 1000 # 对于每个在行字符数限制之外的字符的penalty PenaltyExcessCharacter: 1000000 # 将函数的返回类型放到它自己的行的penalty PenaltyReturnTypeOnItsOwnLine: 60 # 指针和引用的对齐: Left, Right, Middle PointerAlignment: Right #RawStringFormats: # - Delimiter: pb # Language: TextProto # BasedOnStyle: google # 允许重新排版注释 ReflowComments: false # 允许排序#include SortIncludes: true SortUsingDeclarations: true # 在C风格类型转换后添加空格 SpaceAfterCStyleCast: false # 模板关键字后面添加空格 SpaceAfterTemplateKeyword: true # 在赋值运算符之前添加空格 SpaceBeforeAssignmentOperators: true # 开圆括号之前添加一个空格: Never, ControlStatements, Always SpaceBeforeParens: ControlStatements # 在空的圆括号中添加空格 SpaceInEmptyParentheses: false # 在尾随的评论前添加的空格数(只适用于//) SpacesBeforeTrailingComments: 1 # 在尖括号的<后和>前添加空格 SpacesInAngles: false # 在容器(ObjC和JavaScript的数组和字典等)字面量中添加空格 SpacesInContainerLiterals: true # 在C风格类型转换的括号中添加空格 SpacesInCStyleCastParentheses: false # 在圆括号的(后和)前添加空格 SpacesInParentheses: false # 在方括号的[后和]前添加空格,lamda表达式和未指明大小的数组的声明不受影响 SpacesInSquareBrackets: false # 标准: Cpp03, Cpp11, Auto Standard: Cpp11 # tab宽度 TabWidth: 4 # 使用tab字符: Never, ForIndentation, ForContinuationAndIndentation, Always UseTab: Never ...

    怎么使用呢?QtCreator中打开要格式化的代码文件,按快捷键 Ctrl + I 就是格式化当前行。

    Ctrl + A选中全部内容,再按Ctrl + I就是格式化全部。

    VS是把.clang-format文件放在和sln文件同级目录即可,快捷键一般是 先按Ctrl + K 再按Ctrl + D

    VSCode是打开的文件夹根目录有.clang-format即可,格式化一般在右键菜单。

    QtCreator会话管理

    QtCreator的"会话管理"和"最近使用"功能配合,是涛哥接触过的所有IDE/Editor中,管理项目最好用的,没有之一。

    包括VisualStudio、VSCode、AndroidStudio、XCode、Unity3D Editor等等,都只有"最近使用",没有"会话管理"。

    预览

    如上图,左边是会话列表,右边是最近使用列表。

    会话管理的最大用处是,同时打开多个Qt项目,以及快速切换并还原状态。

    涛哥用示例来说明:

    涛哥正在开发TaoQuick项目,这个项目包含两个不同路径下的pro项目, 每个项目分别有自己的子项目。

    预览

    当我正在调试TaoView.cpp文件,并且打了断点的时候,有小伙伴来问我关于另一个项目HelloCI的一些问题。

    这时候我需要把代码切换到HelloCI项目,有些人可能会想着再打开一个QtCreator,当然这样也行,就是

    窗口太多了容易搞混了。涛哥更信赖“会话管理”功能,切换到HelloCI这个会话,做了一些处理。

    完了之后,涛哥又切换回了TaoQuick这个会话,QtCreator就自动恢复到了刚才看的代码TaoView.cpp,而且断点也还在。

    又过了一段时间,涛哥需要重启一下电脑,重启后打开QtCreator,直接点开TaoQuick这个会话,又给我切换回

    刚才调试的TaoQuick.cpp文件了,项目结构展开和重启之前是一样的,只有断点没有了。

    简而言之,大家把平常用的多个相关的项目,放进一个会话里面,就可以放心地关掉QtCreator。下一次想打开的时候,只要点一下会话就可以了。

    预览

    结尾

    这次就分享这么多了,以上内容,大部分都可以在TaoQuick的代码仓库中看到

    https://github.com/jaredtao/taoquick

    转载声明

    文章出自涛哥的博客
    文章采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可, 转载请注明出处, 谢谢合作 © 涛哥

    联系方式 作者 涛哥 开发理念 弘扬鲁班文化,传承工匠精神 博客 https://jaredtao.github.io github https://github.com/jaredtao 知乎 https://www.zhihu.com/people/wentao-jia 邮箱 jared2020@163.com 微信 xsd2410421 QQ 759378563

    请放心联系我,乐于提供咨询服务,也可洽谈商务合作相关事宜。

    打赏

    weixin
    zhifubao

    如果觉得涛哥写的还不错,还请为涛哥打个赏,您的赞赏是涛哥持续创作的源泉。

    read more
  • 简介 先看预览图 新的渐变效果 条形进度条 圆形进度条 简介

    本文是《Qml组件化编程》系列文章的第六篇,涛哥将教大家,进度条组件的定制。

    顺便说一下,涛哥的TaoQuick项目正式开源了, 系列文章中的所有功能,包括动态换皮肤、切换多语言等等,都集成在了TaoQuick中,

    同时涛哥也在TaoQuick中使用了持续集成(CI)技术,目前已经能够自动编译、发布Windows和 Macos平台的软件包,可以在github的Release界面下载体验。

    互联网行业很流行的DevOps理念,在TaoQuick项目中得到了最佳的实践。

    (linux平台的发布工具linuxdeployqt暂时还有点问题,涛哥后续会搞定的)

    地址在这https://github.com/jaredtao/TaoQuick, 赶快去star吧。

    注:文章主要发布在涛哥的博客知乎专栏-涛哥的Qt进阶之路

    先看预览图

    预览

    新的渐变效果

    Qt 5.12 加入了新的渐变效果,一共180种,效果来自这个网站https://webgradients.com

    按照帮助文档的介绍,可以通过下面这两种方式使用

    Rectangle { y: 0; width: 80; height: 80 gradient: Gradient.NightFade } Rectangle { y: 0; width: 80; height: 80 gradient: "NightFade" }

    涛哥立即想到了,枚举不就是数字嘛

    Rectangle { y: 0; width: 80; height: 80 gradient: 1 } Rectangle { y: 0; width: 80; height: 80 gradient: 2 } Rectangle { y: 0; width: 80; height: 80 gradient: 3 }

    试了一下,这样也是可以啊,哈哈。

    于是涛哥就把180种渐变效果都拉出来看看。

    预览

    Qt只支持水平和垂直的渐变,其中有小部分是不能用的,所以只有165个能用。

    看一下展示全部渐变的Qml代码:

    import QtQuick 2.9 import QtQuick.Controls 2.5 Item { anchors.fill: parent GridView { id: g anchors.fill: parent anchors.margins: 20 cellWidth: 160 cellHeight: 160 model: 180 //这里的数据Model直接给个数字180 clip: true property var invalidList: [27, 39, 40, 45, 71, 74, 105, 111, 119, 130, 135, 141] //这几个是不能用的,看过运行报错后手动列出来的。 delegate: Item{ width: 160 height: 160 Rectangle{ width: 150 height: 150 anchors.centerIn: parent color: "white" radius: 10 Text { anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top anchors.topMargin: 2 text: index + 1 } Rectangle { width: 100 height: width radius: width / 2 //编号在列表里的,直接渐变赋值为null,就不会在Qml运行时报警告了 gradient: g.invalidList.indexOf(modelData + 1) < 0 ? modelData + 1 : null anchors.centerIn: parent anchors.verticalCenterOffset: 10 } } } } } 条形进度条

    普通进度条的原理,就是有一个比较长的矩形做背景,在上面放一个颜色不同的矩形,其宽度跟着百分比变化,

    100%时宽度与背景一致。

    可以写一个很简要的进度条。

    Rectangle { id: back width: 300 height: 50 radius: height / 2 color: "white" Rectangle { id: front //宽度是 背景宽度 * 百分比 width: percent / 100 * parent.width height: parent.height radius: parent.radius color: "red" } }

    再添加一点元素,在右侧放一个文本,表示百分比,或者放图片。甚至给进度条加个闪光特效。

    经过一系列的加工,封装成一个综合的组件,最终结果如下:

    //NormalProgressBar.qml import QtQuick 2.12 import QtQuick.Controls 2.12 Item { id: r property int percent: 0 implicitWidth: 200 implicitHeight: 16 //枚举, 表示右侧Bar的类型 enum BarType { Text, //右侧放文本 SucceedOrFailed, //右侧放图片表示成功和失败,没有100%就是失败 NoBar //右侧不放东西 } //只读属性,内置一些颜色 readonly property color __backColor: "#f5f5f5" readonly property color __blueColor: "#1890ff" readonly property color __succeedColor: "#52c41a" readonly property color __failedColor: "#f5222d" //背景色,默认值 property color backgroundColor: __backColor //前景色 property color frontColor: { switch (barType) { case TNormalProgress.BarType.SucceedOrFailed: return percent === 100 ? __succeedColor : __failedColor default: return __blueColor } } //文字 property string text: String("%1%").arg(percent) //渐变 0-180 除掉不能用的,165种渐变任你选 property int gradientIndex: -1 //闪烁特效 property bool flicker: false //右侧Bar类型 property var barType: TNormalProgress.BarType.Text Text { id: t enabled: barType === TNormalProgress.BarType.Text visible: enabled text: r.text anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right } Image { id: image source: percent === 100 ? "qrc:/Core/Image/ProgressBar/ok_circle.png" : "qrc:/Core/Image/ProgressBar/fail_circle.png" height: parent.height width: height enabled: barType === TNormalProgress.BarType.SucceedOrFailed visible: enabled anchors.right: parent.right } property var __right: { switch (barType) { case TNormalProgress.BarType.Text: return t.left case TNormalProgress.BarType.SucceedOrFailed: return image.left default: return r.right } } Rectangle { //背景 id: back anchors.left: parent.left anchors.right: __right anchors.rightMargin: 4 height: parent.height radius: height / 2 color: backgroundColor Rectangle { //前景 id: front width: percent / 100 * parent.width height: parent.height radius: parent.radius color: frontColor gradient: gradientIndex === -1 ? null : gradientIndex Rectangle { //前景上的闪光特效 id: flick height: parent.height width: 0 radius: parent.radius color: Qt.lighter(parent.color, 1.2) enabled: flicker visible: enabled NumberAnimation on width { running: visible from: 0 to: front.width duration: 1000 loops: Animation.Infinite; } } } } } 圆形进度条

    将一个Rectangle做成圆形: 宽高相等,半径为宽度一半。

    再把 颜色设置为透明,边框不透明,边框加粗一点,就是一个圆环了。

    Rectangle { id: back width: 120 height: width radius: width / 2 color: "transparent" border.width: 10 border.color: "white" }

    接下来给圆环贴上一个圆形渐变色,渐变按照百分比来做。

    import QtGraphicalEffects 1.12 Rectangle { id: back width: 120 height: width radius: width / 2 color: "transparent" border.width: 10 border.color: "white" ConicalGradient { anchors.fill: back source: back gradient: Gradient { GradientStop { position: 0.0; color: "white" } GradientStop { position: percent / 100 ; color: "red" } GradientStop { position: percent / 100 + 0.001; color: "white" } GradientStop { position: 1.0; color: "white" } } } }

    渐变从0 到 percent处都是有渐变颜色的, 再从percent + 0.001 到1.0处,都是背景色,这样就是一个简易的圆形进度条了。

    不过这里percent为100的情况,圆形渐变处理不了,我们可以特殊处理,直接让背景圆环变成前景色就行了。(既然都100%了,背景肯定是全部被遮住了,那就让背景做前景,藏掉真正的前景)

    ```qml import QtGraphicalEffects 1.12 Rectangle { id: back width: 120 height: width radius: width / 2 color: "transparent" border.width: 10 border.color: percent === 100 ? "red" : "white" //百分比为100时显示为前景,否则显示为背景 ConicalGradient { anchors.fill: back source: back enabled: percent != 100 //百分比不为100时有效 visible: enabled //百分比不为100时有效 gradient: Gradient { GradientStop { position: 0.0; color: "white" } GradientStop { position: percent / 100 ; color: "red" } GradientStop { position: percent / 100 + 0.001; color: "white" } GradientStop { position: 1.0; color: "white" } } } }

    再加点料,封装成组件

    //CircleProgressBar.qml import QtQuick 2.12 import QtQuick.Controls 2.12 import QtGraphicalEffects 1.12 Item { id: r property int percent: 0 enum BarType { Text, SucceedOrFailed, NoBar } readonly property color __backColor: "#f5f5f5" readonly property color __blueColor: "#1890ff" readonly property color __succeedColor: "#52c41a" readonly property color __failedColor: "#f5222d" property color backgroundColor: __backColor property color frontColor: { switch (barType) { case TNormalProgress.BarType.SucceedOrFailed: return percent === 100 ? __succeedColor : __failedColor default: return __blueColor } } property string text: String("%1%").arg(percent) property var barType: TNormalProgress.BarType.Text Rectangle { id: back color: "transparent" anchors.fill: parent border.color: percent === 100 ? frontColor : backgroundColor border.width: 10 radius: width / 2 } Text { id: t enabled: barType === TNormalProgress.BarType.Text visible: enabled text: r.text anchors.centerIn: parent verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter } Image { id: image source: percent === 100 ? "qrc:/Core/Image/ProgressBar/ok.png" : "qrc:/Core/Image/ProgressBar/fail.png" enabled: barType === TNormalProgress.BarType.SucceedOrFailed visible: enabled scale: 2 anchors.centerIn: parent } ConicalGradient { anchors.fill: back source: back enabled: percent != 100 visible: enabled smooth: true antialiasing: true gradient: Gradient { GradientStop { position: 0.0; color: frontColor } GradientStop { position: percent / 100 ; color: frontColor } GradientStop { position: percent / 100 + 0.001; color: backgroundColor } GradientStop { position: 1.0; color: backgroundColor } } } }

    最后,来个合影

    Item { id: r anchors.fill: parent Grid { id: g anchors.fill: parent anchors.margins: 10 columns: 2 spacing: 10 Column { width: g.width / 2 - 10 height: g.height /2 - 10 spacing: 10 TNormalProgress { width: parent.width backgroundColor: gConfig.reserverColor NumberAnimation on percent { from: 0; to: 100; duration: 5000; running: true; loops: Animation.Infinite} } TNormalProgress { width: parent.width backgroundColor: gConfig.reserverColor flicker: true percent: 50 } TNormalProgress { width: parent.width backgroundColor: gConfig.reserverColor barType: TNormalProgress.BarType.SucceedOrFailed percent: 70 } TNormalProgress { width: parent.width backgroundColor: gConfig.reserverColor barType: TNormalProgress.BarType.SucceedOrFailed percent: 100 } TNormalProgress { width: parent.width backgroundColor: gConfig.reserverColor barType: TNormalProgress.BarType.NoBar percent: 50 gradientIndex: 12 } } Row { width: g.width / 2 - 10 height: g.height /2 - 10 spacing: 10 TCircleProgress { width: 120 height: 120 backgroundColor: gConfig.reserverColor NumberAnimation on percent { from: 0; to: 100; duration: 5000; running: true; loops: Animation.Infinite} } TCircleProgress { width: 120 height: 120 backgroundColor: gConfig.reserverColor barType: TNormalProgress.BarType.SucceedOrFailed percent: 75 } TCircleProgress { width: 120 height: 120 backgroundColor: gConfig.reserverColor barType: TNormalProgress.BarType.SucceedOrFailed percent: 100 } } Row { width: g.width / 2 - 10 height: g.height /2 - 10 spacing: 10 TCircleProgress { width: 120 height: 120 backgroundColor: gConfig.reserverColor text: String("%1天").arg(percent) NumberAnimation on percent { from: 0; to: 100; duration: 5000; running: true; loops: Animation.Infinite} } TCircleProgress { id: ppppp width: 120 height: 120 backgroundColor: gConfig.reserverColor barType: TNormalProgress.BarType.SucceedOrFailed SequentialAnimation { running: true loops: Animation.Infinite NumberAnimation { target: ppppp property: "percent" from: 0 to: 100 duration: 3000 } PauseAnimation { duration: 500 } } } TCircleProgress { width: 120 height: 120 backgroundColor: gConfig.reserverColor percent: 100 } } Column { width: g.width / 2 - 10 height: g.height /2 - 10 spacing: 10 } Column { width: g.width / 2 - 10 height: g.height /2 - 10 spacing: 10 } } }

    效果如下:

    预览

    read more

关注我们

微博
QQ群











召唤伊斯特瓦尔