接下来是工具集,也就是最终调用的编译器工具链,vs支持gcc和clang,linux_x64对应gcc,linux_clang_x64对应clang,此外还有arm平台的支持,选用什么工具链看对应平台和个人喜好,我这里选择了gcc。
然后是“远程生成根”这个选项,截图里未给出,这是远程编译时vs存放整个项目的路径,默认在你的家目录下的.vs目录里,你也可以根据自己的需要修改这一路径,我们演示用的项目就直接使用默认值了。
生成根选项后是设置调用cmake程序时的参数的,只要把需要的参数原样填入输入框即可,这里我们没用到也就不截图了。
vs2019中一个强大的功能就是可以把cmake中由系统或是模块产生的变量的值显示出来(需要在cache成功刷新之后,也就是cmakelists文件保存后或手动在项目菜单中单击为项目生成缓存):
接着我们点击显示高级选项,因为想要vs能提供代码补全还需要一点设置:
在这里你可以设置cmake生成什么类型的makefile,cmake的运行目录和编译完成后程序的安装目录,以及cmake本身所在的路径(如果你把cmake安装到了不太常规的地方例如/opt)。
其中重点关注IntellSense选项,这是选择代码补全的引擎:
可以看到所有选项都是由平台名称-编译器名称-32位/64位这种格式组成的,默认值是空,我们想要代码补全可用就要选择和远程环境完全对应的那种模式。
另外右上角一直有直接编辑json文件的按钮,如果你讨厌gui的话可以选择它。
最后我们保存修改,vs会自动刷新cache,现在我们可以进行远程开发了。
编写CMakeLists.txt前面说过cmake项目的组织需要依靠CMakeLists.txt,现在我们来编写它。
我们的测试项目会使用Qt,随机显示一些不同引擎产生的随机数,然后把它们显示在图表中。选择这个示例是为了更好的展示cmake项目的能力,但是远程开发gui程序在vs上目前还有些困难:
vs运行远程环境的程序依靠ssh,然而Linux的gui程序运行需要连接xserver(通常连接信息在环境变量中),ssh启动的shell环境里没有这些环境变量,你可能还需要额外设置程序启动时的命令行参数,否则运行会发生错误。
这是Qt自身的原因,Qt依赖自己的moc系统,和原生c++有些出入,因此代码补全时会经常找不到类型等(clion没有此类问题)。
vs自身的问题,虽然Qt自己支持cmake,但是vs在远程环境调用moc时不能正常工作,自定义widget会报类似找不到vtable等问题。
qt vs tool无法在远程环境工作。
虽然有以上的缺陷,但是我们编写单个文件的项目并且不自定义widget,同时只编译生成程序而不运行的话还是没有问题的。
下面来看看CMakeLists.txt是如何编写的:
project(LinuxQtExample) # 设置c++语言标准,我使用c++17 set(CMAKE_CXX_STANDARD 17) cmake_minimum_required (VERSION 3.10) set(CMAKE_INCLUDE_CURRENT_DIR ON) # 自动调用moc, uic, rcc set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) # 找到这些Qt组件 find_package(Qt5Widgets REQUIRED) find_package(Qt5Core REQUIRED) find_package(Qt5Gui REQUIRED) find_package(Qt5Charts REQUIRED) # 将源代码添加到此项目的可执行文件。 add_executable (LinuxQt "main.cpp") # 将Qt的库链接至程序 target_link_libraries(LinuxQt Qt5::Core Qt5::Widgets Qt5::Gui Qt5::Charts)更多如何用cmake构建Qt程序的内容请移步这里。
编写测试代码上述设置结束后就可以着手编写代码了,代码提示和补全也能工作了(虽然对于Qt的部分补全不正常,但是c++标准库的补全是可以正常工作的):
#include <QApplication> #include <QBarCategoryAxis> #include <QBarSet> #include <QBarSeries> #include <QChart> #include <QChartView> #include <QPushButton> #include <QString> #include <QStringList> #include <QValueAxis> #include <QVBoxLayout> #include <iostream> #include <random> // 这个函数里变量名起的很烂,因为是示例我偷懒了,请你不要在实际项目中写出这种代码 // 创建柱状图数据的函数 // std::random_device的某些实现在Windows上存在bug,每次运行会返回同样的结果序列,linux没问题 // QtCharts的所有类型/函数都在对应的命名空间中,和其他的QtWidgets不同 static QtCharts::QBarSeries* createSeries() { auto dataSet1 = new QtCharts::QBarSet("mt19937"); auto seed = std::random_device{}(); std::uniform_int_distribution<int> u(0, 100); std::mt19937 rd1(seed); for (int i = 0; i < 10; ++i) { auto a = u(rd1); std::cout << a << std::endl; *dataSet1 << a; } auto dataSet2 = new QtCharts::QBarSet("minstd_rand"); std::minstd_rand rd2(seed); for (int i = 0; i < 10; ++i) { auto a = u(rd2); std::cout << a << std::endl; *dataSet2 << a; } auto dataSet3 = new QtCharts::QBarSet("default"); std::default_random_engine rd3(seed); for (int i = 0; i < 10; ++i) { auto a = u(rd3); std::cout << a << std::endl; *dataSet3 << a; } auto dataSet4 = new QtCharts::QBarSet("ranlux48"); std::ranlux48 rd4(seed); for (int i = 0; i < 10; ++i) { auto a = u(rd4); std::cout << a << std::endl; *dataSet4 << a; } auto dataSet5 = new QtCharts::QBarSet("knuth_b"); std::knuth_b rd5(seed); for (int i = 0; i < 10; ++i) { auto a = u(rd5); std::cout << a << std::endl; *dataSet5 << a; } auto barSeries = new QtCharts::QBarSeries; barSeries->append(dataSet1); barSeries->append(dataSet2); barSeries->append(dataSet3); barSeries->append(dataSet4); barSeries->append(dataSet5); return barSeries; } int main(int argc, char* argv[]) { QApplication app(argc, argv); auto chart = new QtCharts::QChart; // 创建Y轴显示数据 auto axisY = new QtCharts::QValueAxis; axisY->setRange(0, 100); axisY->setTickCount(10); axisY->setTitleText("Y轴"); chart->addAxis(axisY, Qt::AlignLeft); // x轴显示10次取随机数的结果 QStringList x; for (int i = 0; i < 10; ++i) { x << QString::number(i+1); } auto axisX = new QtCharts::QBarCategoryAxis; axisX->append(x); chart->addAxis(axisX, Qt::AlignBottom); auto barSeries = createSeries(); chart->addSeries(barSeries); chart->setTitle("随机数分布图"); // 显示图例以及让图例摆放在图表的底部 chart->legend()->setVisible(true); chart->legend()->setAlignment(Qt::AlignBottom); // 显示chart的容器 auto view = new QtCharts::QChartView(chart); view->setRenderHint(QPainter::Antialiasing); auto layout = new QVBoxLayout; layout->addWidget(view); // 点击按钮刷新显示的数据 auto button = new QPushButton("点击刷新"); QObject::connect(button, &QPushButton::clicked, [chart]() { // removeAll会帮你删除原来的series,所以不必担心内存泄漏 chart->removeAllSeries(); auto barSeries = createSeries(); chart->addSeries(barSeries); }); layout->addWidget(button, Qt::AlignCenter); auto window = new QWidget; window->setLayout(layout); window->setWindowTitle("图表"); // 图表默认会显示成最小,为了不让图表缩成一团需要给一个固定的大小 window->resize(700, 500); window->show(); app.exec(); }代码中使用了utf8编码的中文字符串,你需要设置源文件的编码为utf8以免在Linux上运行时出现乱码。具体见。
运行测试