在Linux系统中使用字符图案和VNC运行Qt Widgets程序
大部分服务器并没有GUI,运行的是基础的Linux系统,甚至是容器。如果我们需要在这些系统中运行带有GUI功能的Qt程序,一般情况下就会报错,比如:
$ ./collidingmice
qt.qpa.xcb: could not connect to display
qt.qpa.plugin: From 6.5.0, xcb-cursor0 or libxcb-cursor0 is needed to load the Qt xcb platform plugin.
qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found.
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.Available platform plugins are: linuxfb, wayland-egl, vnc, eglfs, wayland, vkkhrdisplay, minimalegl, minimal, offscreen, textui, xcb.
如果现实情况不允许我们安装图形界面,是不是就完全无法使用这些程序了呢? 答案是否定的。本文介绍一些使用GUI程序的例子。
1. Qt的Platform插件机制
Qt 的Platform插件,是用来适配各种GUI环境的框架。从上面出错的提示我们就能看到,它支持很多类的GUI,比如linux的framebuf,以及vnc。 为什么Qt能够支持如此多的平台呢?主要原因是Qt本身把GUI完全独立于OS来完全重写,我的一篇文章里详细跟踪了这种从像素开始造轮子的原理:
Qt Desktop Widgets 控件绘图原理逐步分析拆解
其实就是Qt从像素级别在内存中实现了完整的GUI。因此,Qt的程序运行可以完全脱离GUI。
2. 探索:bash下的奶酪鼠鼠
理论上,我们只要有一个支持显示图案的方法,以及一套读取键鼠的驱动,就可以贯通Qt的GUI了。我们签出Qt6.8.1的源码,在 plugins/platform里,加入一个测试插件,就叫"textui"。这个插件是从 minmal直接修改而来,功能不完全,仅用于测试。
这个插件实现了一个字符界面的 backingStore,可以把一幅图的轮廓用彩色的字符显示出来:
#ifndef QBACKINGSTORE_MINIMAL_H
#define QBACKINGSTORE_MINIMAL_H#include <qpa/qplatformbackingstore.h>
#include <qpa/qplatformwindow.h>
#include <QtGui/QImage>
#include <QThread>
#include "textcall.h"
QT_BEGIN_NAMESPACEclass QTextUIBackingStore : public QPlatformBackingStore
{
public:QTextUIBackingStore(QWindow *window);~QTextUIBackingStore();QPaintDevice *paintDevice() override;void flush(QWindow *window, const QRegion ®ion, const QPoint &offset) override;void resize(const QSize &size, const QRegion &staticContents) override;void initEvent();void exitEvent();
private:QImage mImage;QThread * m_keyThread;textInput * m_textInput;textCall * m_textCall;
};QT_END_NAMESPACE#endif
绘图代码:
#include "qtextuibackingstore.h"
#include "qscreen.h"
#include <QtCore/qdebug.h>
#include <qpa/qplatformscreen.h>
#include <private/qguiapplication_p.h>
#include <QKeyEvent>
#include <ncurses.h>
#include "textgraph_bash.h"QT_BEGIN_NAMESPACE
WINDOW * scr = nullptr;
QColor cls_standard[]{QColor(0,0,0),QColor(255,0,0),QColor(0,255,0),QColor(0,0,255),QColor(255,255,0),QColor(255,0,255),QColor(0,255,255),QColor(255,255,255)};
#define PFF(W,h,w,c) (h * (W*3) + w * 3 + c)using namespace Qt::StringLiterals;QTextUIBackingStore::QTextUIBackingStore(QWindow *window): QPlatformBackingStore(window)
{scr = initscr();start_color();init_pair(1, COLOR_RED, COLOR_BLACK);init_pair(2, COLOR_GREEN, COLOR_BLACK);init_pair(3, COLOR_BLUE, COLOR_BLACK);init_pair(4, COLOR_YELLOW, COLOR_BLACK);init_pair(5, COLOR_MAGENTA, COLOR_BLACK);init_pair(6, COLOR_CYAN, COLOR_BLACK);init_pair(7, COLOR_WHITE, COLOR_BLACK);initEvent();
}QTextUIBackingStore::~QTextUIBackingStore()
{endwin();exitEvent();
}QPaintDevice *QTextUIBackingStore::paintDevice()
{return &mImage;
}
void QTextUIBackingStore::initEvent()
{m_textInput = new textInput;m_textCall = new textCall;m_keyThread = new QThread;m_textInput->moveToThread(m_keyThread);QObject::connect(m_textInput,&textInput::keyPress,m_textCall,&textCall::onKeyPress,Qt::QueuedConnection);m_keyThread->start();
}
void QTextUIBackingStore::exitEvent()
{m_keyThread->terminate();m_textInput->deleteLater();m_textCall->deleteLater();QObject::disconnect(m_textInput,&textInput::keyPress,m_textCall,&textCall::onKeyPress);}
void QTextUIBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset)
{Q_UNUSED(window);Q_UNUSED(region);Q_UNUSED(offset);unsigned short int tw,th;if (!get_bash_size(&tw,&th))return;const int img_width = mImage.width();const int img_height = mImage.height();const double sw = img_width *1.0 / tw;const double sh = img_height*1.0 / th;const int dw = sw/2;const int dh = sh/2;const int ds = (dw * 2 + 1) * (dh * 2 +1);const char pAMP[] = " .,`'\":;-+??{]XCUOP#%B@";const int lev = sizeof(pAMP)-1;for (int h = 0;h < th;++h){for (int w = 0;w<tw;++w){const double c_y = h * sh ;const double c_x = w * sw;float r=0,g=0,b=0, p = 0;for (int iy = -dh;iy<=dh;++iy){int y = qRound( c_y + iy);if (y<0) y = 0;if (y>=img_height) y = img_height - 1;QRgb *line = reinterpret_cast<QRgb*>(mImage.scanLine(y));for (int ix = 0; ix <=dw; ++ix) {int x = qRound(c_x + ix);if (x<0) x = 0;if (x>=img_width) x = img_width-1;QRgb &rgb = line[x];r += qRed(rgb);g += qGreen(rgb);b += qBlue(rgb);//p += 0.2989 * r + 0.5870 * g + 0.1140 * b;p += (r + g + b)/3;}}float r1 = double(r) /ds/ 256;float g1 = double(g) /ds/ 256;float b1 = double(b) /ds/ 256;float mindis = 1e99;int mini = 7;for (int i = 1;i<8;++i){float r2,g2,b2;cls_standard[i].getRgbF(&r2,&g2,&b2);double dis = (r1-r2)*(r1-r2) + (g1-g2)*(g1-g2) + (b1-b2)*(b1-b2);if (dis < mindis){mindis = dis;mini = i;}}attron(COLOR_PAIR(mini));p /=256;p/=ds;if (p<0.5)attron(A_NORMAL);elseattron(A_BOLD);p *=lev;if (p>=lev)p = lev -.1;int nv = p;mvprintw(h,w,"%c",pAMP[nv]); // 在指定位置输出字符 A}}refresh(); // 刷新屏幕显示
}void QTextUIBackingStore::resize(const QSize &size, const QRegion &)
{QImage::Format format = QGuiApplication::primaryScreen()->handle()->format();if (mImage.size() != size)mImage = QImage(size, format);
}QT_END_NAMESPACE
上述代码,直接读取 backingStore的图片,并转换为近似色彩的字符图案。我们举例使用Qt Widgets的经典程序 collidingmice 来测试:
$ ./collidingmice -platform textui
可以看到,在bash中,鼠鼠可以自由地在字符奶酪里运动。
注意,由于我们没有实现键鼠事件,所以这个程序只能看,不能玩。
3. 实操:使用VNC平台
上面的例子,我们利用bash下 ncurse库,实现了基本的字符显示。ncurse也可以捕捉键鼠,但是由于字符界面的分辨率非常糟糕,要输入汉字、精确拖放都是不行的。其实,Linux下Qt早就想到了我们的需求,提供了VNC插件。
./collidingmice -platform vnc:size=640x640,port=5902
而后,在一台具有GUI的机器,比如windows上,远程VNC到 5902端口,即可看到同样的界面:
4. Qt GUI 程序开发建议
既然Qt GUI程序可以支持这种平台重载,那就要考虑到对应的使用方式,尤其是对 minimal平台的支持。
- minimal平台是一个空壳,不做任何绘图和键鼠事件捕获,因此,可以在自己的代码中加入判断,允许用–service等类似字眼的开关,打开一个后台模式,让GUI程序默默在后台运行功能。
- 对VNC平台,可以在代码中加入允许从命令行设置最大化的功能,让GUI随着vnc的窗口设置而变。
相关文章:

在Linux系统中使用字符图案和VNC运行Qt Widgets程序
大部分服务器并没有GUI,运行的是基础的Linux系统,甚至是容器。如果我们需要在这些系统中运行带有GUI功能的Qt程序,一般情况下就会报错,比如: $ ./collidingmice qt.qpa.xcb: could not connect to display qt.qpa.plu…...

Python基于EasyOCR进行路灯控制箱图像文本识别项目实战
说明:这是一个机器学习实战项目(附带数据代码文档视频讲解),如需数据代码文档视频讲解可以直接到文章最后关注获取。 1.项目背景 随着城市化进程的加快,智能城市建设成为了现代社会发展的重要方向。路灯作为城市基础设…...
Github 2024-12-28 Rust开源项目日报 Top10
根据Github Trendings的统计,今日(2024-12-28统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Rust项目10TypeScript项目1Python项目1egui: 一个简单、快速且高度可移植的 Rust GUI 库 创建周期:1903 天开发语言:Rust协议类型:Apache Li…...
提升生产力工具
VSCODE插件 干货:用好这13款VSCode插件,工作效率提升10倍 - 程序员柠檬 - 博客园 Sourcetrail Sourcetrail 是一个开源且免费的源码阅读工具,以其强大的代码导航、可视化及跨平台支持特性,成为开发者理解复杂代码库的得力助手。…...
【蓝桥杯——物联网设计与开发】系列前言
前言 本系列博客是博主为准备2024年第十五届蓝桥杯大赛物联网设计与开发赛道而写,经过4个月学习备战,最终获得全国一等奖。 从第十六届蓝桥杯大赛开始,物联网赛道更换竞赛实训平台。之前的博客,可以借鉴代码思想,但引脚…...

【Java基础】02.Java数据类型
目录 Java 数据类型 3.1 java程序中 “” 号的使用 3.2 java中的数据类型 3.2.1 基本数据类型:数值型 (1)整数类型 (2)浮点(小数)类型 3.2.2 基本数据类型:字符型 3.2.3 基本…...

Python爬虫(一)- Requests 安装与基本使用教程
文章目录 前言一、简介及安装1. 简介2. 安装 Requests2.1 安装2.2 检查安装是否成功 二、使用 Requests 发送 HTTP 请求1. 发送 GET 请求2. 发送 POST 请求3. 发送 PUT 请求4. 发送 DELETE 请求5. 发送 HEAD 请求6. 发送 OPTIONS 请求 三、传递参数1. GET 请求传递 URL 参数1.1…...
线段树保姆级教程
买水果 Description 水果姐今天心情不错,来到了水果街。 水果街有n家水果店,呈直线结构,编号为1~n,每家店能买水果也能卖水果,并且同一家店卖与买的价格一样。 学过oi的水果姐迅速发现了一个赚钱的方法:…...

logback之自定义过滤器
logback有两种过滤器,一种是context中的过滤器叫TurboFilter,是一个全局的过滤器,会影响所有的日志记录。另一种是Appender中的过滤器,只对所在的append有效。两者大同小异,这里我们以Appender的过滤器为例。 &#x…...

如何用CSS3创建圆角矩形并居中显示?
在网页设计中,圆角矩形因其美观和现代感而被广泛使用,居中显示元素也是一个常见的需求。今天,我们将学习如何使用CSS3的border-radius属性来创建圆角矩形,并将其居中显示在页面上。 如果你正在学习CSS,那么这个实例将非…...
Java 开发中的指定外部 Jar 路径详解
哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云/阿里云/华为云/51CTO;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互…...

python爬虫--小白篇【selenium自动爬取文件】
一、问题描述 在学习或工作中需要爬取文件资源时,由于文件数量太多,手动单个下载文件效率低,操作麻烦,采用selenium框架自动爬取文件数据是不二选择。如需要爬取下面网站中包含的全部pdf文件,并将其转为Markdown格式。…...

TI毫米波雷达原始数据解析之Lane数据交换
TI毫米波雷达原始数据解析之Lane数据交换 背景Lane 定义Lane 确认确认LVDS Lane 数量的Matlab 代码数据格式参考 背景 解析使用mmWave Studio 抓取的ADC Data Lane 定义 芯片与DCA100之间的数据使用LVDS接口传输,使用mmWave Studio 配置过程中有一个选项是LVDS L…...

overscroll-behavior-解决H5在ios上过度滚动的默认行为
1. 问题 开发H5的过程中,经常会有android和ios两边系统需要兼容的情况。在ios上一直有个问题是当H5内容触及到页面顶部或底部时,还是可以被人为的往下或往下拉动界面。当然可能有的情况是比较适用的,比如你往下拉动,然后在导航栏…...

Nacos配置中心总结
Nacos配置中心总结 Nacos配置文件的加载顺序和优先级 加载顺序 nacos作为配置中心时,需要在bootstrap.yml文件中添加nacos config相关的配置,这样系统启动时就能先去拉取nacos server上的配置了。拉取过来后会和本地配置文件进行合并。 bootstrap.ym…...

rouyi(前后端分离版本)配置
从gitee上下载,复制下载地址,到 点击Clone,下载完成, 先运行后端,在运行前端 运行后端: 1.配置数据库,在Navicat软件中,连接->mysql->名字自己起(rouyi-vue-blog),用户名roo…...

超大规模分类(一):噪声对比估计(Noise Contrastive Estimation, NCE)
NCE损失对应的论文为《A fast and simple algorithm for training neural probabilistic language models》,发表于2012年的ICML会议。 背景 在2012年,语言模型一般采用n-gram的方法,统计单词/上下文间的共现关系,比神经概率语言…...

Windows 下安装 triton 教程
目录 背景解决方法方法一:(治标不治本)方法二:(triton-windows)- 安装 MSVC 和 Windows SDK- vcredist 安装- whl 安装- 验证 背景 triton 目前官方只有Linux 版本,若未安装,则会出…...

复盘与导出工具最新版9.15重磅发布-全新UI兼容所有windows系统
在9.11版本的基础上大更新: 1.应付费用户需求修复当更换明亮风格时软件超过电脑屏幕的bug!!!!! 2.支持所有windows版本,32/64位的win xp/7/8/10/11 3.修复开盘啦涨停原因排序bug 4.全新ui风格 5提前爆料:.9.2版本的分开…...

家用电器销售系统|Java|SSM|JSP|
【技术栈】 1⃣️:架构: B/S、MVC 2⃣️:系统环境:Windowsh/Mac 3⃣️:开发环境:IDEA、JDK1.8、Maven、Mysql5.7 4⃣️:技术栈:Java、Mysql、SSM、Mybatis-Plus、JSP、jquery,html 5⃣️数据库可…...

C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
LLM基础1_语言模型如何处理文本
基于GitHub项目:https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken:OpenAI开发的专业"分词器" torch:Facebook开发的强力计算引擎,相当于超级计算器 理解词嵌入:给词语画"…...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

Netty从入门到进阶(二)
二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...

FFmpeg:Windows系统小白安装及其使用
一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】,注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录(即exe所在文件夹)加入系统变量…...
微服务通信安全:深入解析mTLS的原理与实践
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、引言:微服务时代的通信安全挑战 随着云原生和微服务架构的普及,服务间的通信安全成为系统设计的核心议题。传统的单体架构中&…...
boost::filesystem::path文件路径使用详解和示例
boost::filesystem::path 是 Boost 库中用于跨平台操作文件路径的类,封装了路径的拼接、分割、提取、判断等常用功能。下面是对它的使用详解,包括常用接口与完整示例。 1. 引入头文件与命名空间 #include <boost/filesystem.hpp> namespace fs b…...

EasyRTC音视频实时通话功能在WebRTC与智能硬件整合中的应用与优势
一、WebRTC与智能硬件整合趋势 随着物联网和实时通信需求的爆发式增长,WebRTC作为开源实时通信技术,为浏览器与移动应用提供免插件的音视频通信能力,在智能硬件领域的融合应用已成必然趋势。智能硬件不再局限于单一功能,对实时…...