还是一堆bug的状态

This commit is contained in:
2026-08-01 20:09:46 +08:00
parent 28b33fb820
commit 6ea03829bc
30 changed files with 84 additions and 2664 deletions
+1 -2
View File
@@ -1,6 +1,6 @@
if (${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_LIST_DIR})
cmake_minimum_required(VERSION 3.16)
cmake_policy(SET CMP0141 NEW)
# cmake_policy(SET CMP0141 NEW)
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<$<CONFIG:Debug,RelWithDebInfo>:Embedded>")
project(Radio)
set(Radio_VERSION 1.0.0)
@@ -12,7 +12,6 @@ if (${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_LIST_DIR})
include(${CMAKE_SOURCE_DIR}/main.cmake)
include(third_party/build_infra/end.cmake)
else ()
endif ()
-156
View File
@@ -1,156 +0,0 @@
# startRender(int fps) 作用流程 FAQ
## startRender(int fps) 做了什么
`startRender(int refreshTimesPreSecond)` 只做两件事:
```text
保存 mRefreshTimesPreSecond
调用 startRender()
```
对应代码流程:
```text
Plot::startRender(int fps)
d->mRefreshTimesPreSecond = fps
Plot::startRender()
```
## startRender() 做了什么
`startRender()` 的作用是启动 Plot 的渲染调度。
流程:
```text
检查 Plot 是否正在析构
mRenderEnabled = true
投递任务到 asio scheduler
取消旧 mRenderTimer
scheduleRenderTimer()
submitRender()
```
它会立即触发一次 `submitRender()`,不是等下一个 timer 周期。
## fps 参数现在是不是严格帧率上限
不是。
当前 `fps` 只决定 asio timer 的周期:
```text
interval = round(1000.0 / fps)
```
timer 到期后:
```text
submitRender()
scheduleRenderTimer()
```
所以它的真实语义是周期性 heartbeat / 兜底触发,不是硬性 FPS cap。
## 为什么不是严格限帧
因为当前渲染是事件驱动为主。
除了 timer 会触发 `submitRender()`,下面这些路径也会立即触发:
```text
RenderAble::markStateDirty()
Plot::markRenderStateDirty()
requestRender()
submitRender()
```
```text
RenderData::markInputDirty()
RenderAble::markRenderDirty()
requestRender()
submitRender()
```
```text
finishRender()
pipeline.hasNewState()
submitRender()
```
只要 State/Input 有新版本,系统可以绕过 timer 周期继续推进下一帧。
## asio 里面还有没有定时器
有。
相关成员和函数:
```text
PlotPrivate::mRenderTimer
PlotPrivate::mRefreshTimesPreSecond
Plot::scheduleRenderTimer()
RenderScheduler::initializePlotOnScheduler()
```
`RenderScheduler::initializePlotOnScheduler()` 为每个 Plot 创建 `asio::steady_timer`
`scheduleRenderTimer()` 使用 `mRefreshTimesPreSecond` 计算间隔并注册 `async_wait`
## timer 在当前架构里还有没有必要
当前仍然有必要保留。
原因:
```text
保持 startRender(int) 旧调用语义
保证没有显式 dirty 事件时仍有周期性检查
作为异常丢事件后的兜底触发
给性能测试框提供一个稳定的周期参考
```
但它不是渲染正确性的唯一入口。
当前正确性主要依赖:
```text
State dirty
Input dirty
finishRender 后续版本检查
```
## 如果以后要严格限帧应该怎么做
应该新增独立调度策略,而不是继续复用 `startRender(int)` 的现有语义。
建议拆成:
```text
Immediate
dirty 后立即 requestRender()
FixedRate
dirty 后只标记 pending,由 timer 周期统一 submitRender()
Throttle
dirty 后允许立即渲染,但两帧之间必须满足最小间隔
```
这样 `startRender(int)` 才能变成明确的帧率策略参数。
-687
View File
@@ -1,687 +0,0 @@
下面这个就是我建议你这个 **多图表后台渲染库**的完整模型。
核心目标是:
```text
元数据变化才计算
后台只算最新状态
主线程只贴图
多个图统一调度
不堆旧任务
不显示半成品
不因为 paintEvent 无意义重算
```
---
# 1. 总体管线
每个图都是这个流程:
```text
主线程修改元数据
meta_version++
调度器定时扫描
判断这个图是否需要提交 CPU 任务
生成元数据快照 Render_Input
扔进 CPU 线程池
CPU 线程生成 Pixel_Frame
发布到三缓冲 ready
主线程 update 合并
paintEvent 消费 ready,贴 front
```
也就是:
```text
Meta -> Render_Input -> CPU Render -> Pixel_Frame -> paintEvent drawImage
```
每种具体图只需要实现:
```text
元数据 -> 像素图
```
线程控制、版本控制、三缓冲、刷新合并都放到通用框架里。
---
# 2. 每个图不要自己开线程
一堆图不能每个图一个线程、一个 timer。
应该是:
```text
一个 Render_Scheduler
一个 CPU Thread_Pool
多个 Chart_Node
```
结构类似:
```text
Render_Scheduler
Chart_Node 1
Chart_Node 2
Chart_Node 3
...
Thread_Pool
```
调度器负责:
```text
扫描哪些图需要重算
限制每轮提交数量
限制单图提交频率
跳过不可见图
合并 update 区域
控制 CPU 线程池压力
```
具体图只负责:
```text
准备元数据
执行 CPU 绘制算法
提供 dirty rect / 绘制区域
```
---
# 3. 每个图的版本号
每个图建议维护这些版本:
```cpp
struct Chart_Versions {
std::uint64_t meta = 0;
std::uint64_t submitted = 0;
std::uint64_t running = 0;
std::uint64_t rendered = 0;
std::uint64_t requested_paint = 0;
std::uint64_t painted = 0;
};
```
语义:
```text
meta
主线程最新元数据版本。
submitted
已经提交给 CPU 线程池的版本。
running
当前 CPU 正在计算的版本。
rendered
后台已经生成像素图的版本。
requested_paint
已经请求主线程刷新的版本。
painted
paintEvent 已经贴出去的版本。
```
这里不要用 `OK``imageReady` 这种 bool。
bool 只能表示:
```text
有 / 没有
```
版本号能表示:
```text
是哪一版
新不新
有没有提交
有没有生成
有没有请求 update
有没有真正 paint
```
---
# 4. 元数据层:双缓冲或者快照
元数据是用户操作和数据输入修改的东西,比如:
```text
坐标范围
颜色表
缩放
图尺寸
数据源
采样参数
显示开关
曲线样式
```
主线程修改元数据:
```cpp
void Chart_Node::mark_meta_changed() {
++versions.meta;
}
```
调度器提交任务时生成快照:
```cpp
Render_Input Chart_Node::make_render_input() {
Render_Input input;
input.meta = edit_meta;
input.version = versions.meta;
return input;
}
```
如果元数据很小,直接拷贝快照。
如果元数据很大,用双缓冲或者共享不可变快照。
这一层一般不需要三缓冲。
原因是元数据中间版本可以丢,只要后台下一轮拿最新版本即可。
---
# 5. 任务层:single-flight
每个图同一时间最多一个后台任务。
不要这样:
```text
version 100 提交
version 101 提交
version 102 提交
version 103 提交
```
这样会造成旧任务堵新任务。
应该这样:
```text
一个图最多一个任务在飞
任务运行期间 meta 变了,只记录版本变化
任务完成后调度器下一轮再提交最新版本
```
任务状态:
```cpp
enum class Render_Job_State {
Idle,
Queued,
Running
};
```
提交条件:
```cpp
bool Chart_Node::need_submit(std::uint64_t now_ms) const {
if (!visible) {
return false;
}
if (!valid_size) {
return false;
}
if (job_state != Render_Job_State::Idle) {
return false;
}
if (versions.meta == versions.submitted) {
return false;
}
if (now_ms - last_submit_time < min_render_interval) {
return false;
}
return true;
}
```
核心原则:
```text
版本没变,不算。
图不可见,不算。
尺寸无效,不算。
已经有任务在跑,不算。
刚算过,间隔太短,不算。
```
---
# 6. 像素结果层:三缓冲
像素层建议三缓冲:
```cpp
struct Pixel_Frame {
std::uint64_t version = 0;
QImage image;
};
struct Pixel_Buffers {
Pixel_Frame front;
Pixel_Frame ready;
Pixel_Frame working;
};
```
三张图语义:
```text
front
主线程 paintEvent 当前稳定显示的图。
ready
后台已经生成好,等待主线程消费的图。
working
后台线程正在写入的图。
```
三缓冲解决的是:
```text
主线程读 front
后台写 working
ready 作为交接区
```
它不是为了替代版本号,而是为了控制图像内存所有权。
---
# 7. 后台任务完成后的发布规则
后台线程生成完 `working` 后,不直接碰 `front`
只发布到 `ready`
如果 `ready` 还没被主线程消费,新 `working` 又生成完了:
```text
旧 ready 丢掉
新 working 覆盖 ready
front 继续稳定显示
```
图表库不需要按帧播放旧图,应该追最新状态。
发布逻辑:
```cpp
void Chart_Node::publish_working_frame(Pixel_Frame frame) {
bool need_update = false;
{
std::scoped_lock lock(pixel_lock);
if (frame.version <= pixels.ready.version) {
return;
}
pixels.working = std::move(frame);
pixels.ready.swap(pixels.working);
versions.rendered = pixels.ready.version;
if (versions.rendered != versions.requested_paint) {
versions.requested_paint = versions.rendered;
need_update = true;
}
job_state = Render_Job_State::Idle;
}
if (need_update) {
scheduler->request_update(this);
}
}
```
状态变化示例:
```text
发布前:
front = 100
ready = 101
working = 102
发布后:
front = 100
ready = 102
working = 101
```
`ready = 101` 被丢掉,变成新的空闲 working 槽,后面会被覆盖。
---
# 8. 已经开始生成的 working 要不要停
不要停。
规则是:
```text
已经开始生成的 working:继续生成完。
还没开始的新任务:由调度器根据版本、间隔、状态决定是否提交。
```
原因:
```text
CPU 任务中途取消复杂。
生成完的新版本可以覆盖旧 ready。
single-flight 已经保证不会无限堆任务。
```
所以正确策略是:
```text
working 已经开始:继续。
ready 没消费:不等。
新 working 完成:覆盖旧 ready。
下一轮是否继续算:由调度器判断。
```
---
# 9. 主线程 paintEvent 消费规则
主线程 `paintEvent()` 前先尝试消费 `ready`
```cpp
void Chart_Node::prepare_paint() {
std::scoped_lock lock(pixel_lock);
if (pixels.ready.version <= pixels.front.version) {
return;
}
pixels.front.swap(pixels.ready);
versions.painted = pixels.front.version;
}
```
然后只贴 `front`
```cpp
void Chart_Node::paint(QPainter& painter) {
prepare_paint();
painter.drawImage(rect, pixels.front.image);
}
```
`paintEvent()` 不做:
```text
元数据计算
颜色映射
衰减计算
坐标采样
大数组遍历
后台等待
```
它只做:
```text
ready -> front
drawImage(front)
```
如果没有新图,也照样画旧 `front`
因为 Qt 可能因为窗口遮挡、缩放、系统刷新等原因主动触发 `paintEvent()`,不能假设每次 paint 都有新数据。
---
# 10. update 合并
后台发布新图以后,不要每个图都直接 `update()` 整个窗口。
应该由调度器合并刷新区域:
```text
图 A 有新图
图 B 有新图
图 C 有新图
合并 dirty rect
主线程 update(total_dirty_rect)
```
可以加一个 pending 标志,避免重复投递刷新请求:
```cpp
void Render_Scheduler::request_update(Chart_Node* chart) {
dirty_region += chart->rect();
if (paint_request_pending.exchange(true)) {
return;
}
QMetaObject::invokeMethod(widget, [this] {
paint_request_pending.store(false);
QWidget* w = widget;
QRegion region = std::move(dirty_region);
dirty_region = QRegion();
w->update(region);
}, Qt::QueuedConnection);
}
```
这样多个图连续完成渲染,也只触发一次合并后的主线程刷新。
---
# 11. 调度器 tick 流程
调度器定时扫描所有图:
```cpp
void Render_Scheduler::tick() {
std::uint64_t now_ms = current_time_ms();
int submitted_count = 0;
for (Chart_Node* chart : charts) {
if (submitted_count >= max_submit_per_tick) {
break;
}
if (!chart->need_submit(now_ms)) {
continue;
}
chart->submit_render(thread_pool, now_ms);
++submitted_count;
}
}
```
提交任务:
```cpp
void Chart_Node::submit_render(Thread_Pool& pool, std::uint64_t now_ms) {
Render_Input input;
{
std::scoped_lock lock(meta_lock);
input = make_render_input();
versions.submitted = input.version;
versions.running = input.version;
job_state = Render_Job_State::Queued;
last_submit_time = now_ms;
}
pool.enqueue([this, input = std::move(input)]() mutable {
run_render_task(std::move(input));
});
}
```
CPU 任务:
```cpp
void Chart_Node::run_render_task(Render_Input input) {
{
std::scoped_lock lock(job_lock);
job_state = Render_Job_State::Running;
}
Pixel_Frame frame;
frame.version = input.version;
frame.image = render_image(input);
publish_working_frame(std::move(frame));
}
```
---
# 12. 过期任务结果怎么处理
假设:
```text
提交 version 100
CPU 正在算
用户已经改到 version 110
version 100 算完
```
推荐默认策略:
```text
允许发布 version 100
但任务完成后,如果 meta 还是新于 submitted,下一轮马上提交最新 version 110
```
这样用户不会长时间看不到任何结果。
但是不排队算:
```text
101、102、103、104...
```
只算当前正在跑的 100,然后下一轮追最新的 110。
也就是:
```text
旧结果可以短暂显示
旧任务不排队
新任务追最新
```
如果某些图对实时一致性要求特别高,也可以配置成:
```text
任务完成时发现 version < meta_version,直接丢弃结果
```
但默认不建议这么激进,否则用户拖动时可能一直看不到更新。
---
# 13. 最终状态机
每个图的任务状态:
```text
Idle
↓ submit
Queued
↓ thread starts
Running
↓ publish
Idle
```
版本流向:
```text
meta_version
↓ submit
submitted_version
↓ run
running_version
↓ publish
rendered_version
↓ request update
requested_paint_version
↓ paintEvent
painted_version
```
像素缓冲流向:
```text
CPU writes working
↓ publish
working <-> ready
↓ paintEvent
ready <-> front
↓ drawImage(front)
```
如果 ready 没消费,新 working 又来了:
```text
working <-> ready
旧 ready 丢掉
front 不动
```
如果 meta 没变:
```text
不提交任务
不生成图
不 request update
```
如果没有新 ready
```text
paintEvent 只画旧 front
```
---
# 14. 最终设计原则
整个模型可以定成这几条:
```text
1. 每个图有自己的元数据版本。
2. 每个图同一时间最多一个 CPU 渲染任务。
3. 调度器统一扫描所有图,不让每个图自建线程和 timer。
4. 元数据层用快照或者双缓冲。
5. 像素结果层用三缓冲。
6. 三缓冲只管图像内存所有权,不替代版本号。
7. ready 只保存最新待显示图,不做帧队列。
8. 旧 ready 没被消费时,新 working 直接覆盖它。
9. 已经开始的 working 继续算完。
10. 是否继续提交新任务由版本号、single-flight、限速、可见性共同决定。
11. 主线程 paintEvent 只贴 front,不做重计算。
12. update 由调度器合并,不让每个图疯狂 update。
```
一句话总结:
```text
元数据版本号决定要不要算;
single-flight 决定不要堆任务;
CPU 线程池负责把 Meta 算成 Pixel
三缓冲负责 Pixel 的安全交接;
调度器负责多图公平和 update 合并;
paintEvent 只负责贴最新 front。
```
这个模型适合你的场景: **一堆图、多线程后台渲染、高频数据变化、主线程只做轻量显示**
-341
View File
@@ -1,341 +0,0 @@
我的建议是:
```text
不要靠主线程固定定时器轮询 update。
后台生成完新图后,提交一个刷新任务到 Qt 主事件循环。
如果你要限帧,再用单次 QTimer 做合并/节流。
```
也就是主线程刷新应该是 **事件驱动**,不是一直定时扫。
---
# 1. 不推荐固定主线程 timer 轮询 update
比如这种方式:
```text
主线程每 16ms 扫描所有图
如果 rendered_version > painted_version
update()
```
它能工作,但问题是:
```text
没有新图时也一直醒
图很多时每次都要扫一遍
后台刚生成完图,最多要等一个 timer 周期
主线程刷新逻辑和后台完成事件脱节
```
如果你有很多图,这种轮询会变成一种隐性开销。
尤其是图不可见、数据静止时,timer 仍然在跑,这不优雅。
---
# 2. 更推荐:后台发布 ready 后投递主线程刷新任务
后台线程生成完像素图:
```text
CPU 线程生成 working
发布到 ready
发现 rendered_version > requested_paint_version
投递一个 Qt 主线程任务
主线程里调用 update(region)
```
注意:后台线程不要直接调用 QWidget 的 `update()`。Qt GUI 对象应该在 GUI 线程使用。
应该用:
```cpp
QMetaObject::invokeMethod(widget, [this] {
widget->update(dirty_region);
}, Qt::QueuedConnection);
```
或者用 signal/slot,连接方式是 `Qt::QueuedConnection`
---
# 3. 但是要加 pending,避免疯狂投递
后台很多图同时完成时,不能每个图都投递一个主线程任务。
应该有一个全局 pending 标志:
```cpp
std::atomic_bool update_pending = false;
```
请求刷新:
```cpp
void Render_Scheduler::request_update(Chart_Node* chart) {
{
std::scoped_lock lock(dirty_lock);
dirty_region += chart->rect();
}
if (update_pending.exchange(true, std::memory_order_acq_rel)) {
return;
}
QMetaObject::invokeMethod(widget, [this] {
flush_update();
}, Qt::QueuedConnection);
}
```
主线程执行:
```cpp
void Render_Scheduler::flush_update() {
QRegion region;
{
std::scoped_lock lock(dirty_lock);
region = dirty_region;
dirty_region = QRegion();
}
update_pending.store(false, std::memory_order_release);
if (!region.isEmpty()) {
widget->update(region);
}
}
```
这样效果是:
```text
后台完成 100 个图
只投递 1 个主线程刷新任务
主线程合并 dirty region
调用一次 update(region)
```
这个比固定 timer 更合适。
---
# 4. Qt 的 update 本身也会合并
`update()` 不是马上 paint,它只是请求 Qt 稍后重绘。
所以:
```cpp
widget->update();
widget->update();
widget->update();
```
通常不会立刻触发三次 `paintEvent()`Qt 会合并。
但是你自己这层仍然要合并,因为:
```text
Qt 会合并 paint event
但你仍然可能投递了大量 QueuedConnection 任务
```
所以你自己的 `update_pending` 是为了减少主线程事件队列压力。
---
# 5. timer 应该用在哪里
timer 不是完全不用,而是用途要分清。
## 计算调度 timer:可以有
比如每 16ms / 33ms 扫描哪些图需要提交 CPU 任务:
```text
Render_Scheduler::tick()
扫描 meta_version
提交必要 CPU 任务
```
这个 timer 管的是:
```text
要不要开始后台计算
```
不是管:
```text
要不要 update
```
也就是说:
```text
计算调度可以定时
UI 刷新最好事件驱动
```
## 刷新节流 timer:可以用 single-shot
如果你想限制 UI 最多 60 FPS,可以不用立刻 `invokeMethod -> update()`,而是主线程启动一个单次 timer:
```text
后台 ready
投递主线程任务
主线程发现 flush_timer 没启动
启动 singleShot(16ms)
timer 到了再 update(region)
```
这样是 **节流 timer**,不是一直轮询 timer。
---
# 6. 最推荐的刷新模型
我建议你最终用这个:
```text
后台完成图像
publish ready
scheduler->request_update(chart)
合并 dirty region
如果没有 pending flush
invokeMethod 投递主线程 flush
主线程 flush_update()
widget->update(region)
Qt 稍后调用 paintEvent
paintEvent 中 ready -> front
drawImage(front)
```
这个模型里:
```text
没有新图,不投递刷新。
多个图完成,合并刷新。
主线程不轮询。
paintEvent 不计算。
```
---
# 7. 如果要限帧,用这个变体
```cpp
void Render_Scheduler::request_update(Chart_Node* chart) {
{
std::scoped_lock lock(dirty_lock);
dirty_region += chart->rect();
}
QMetaObject::invokeMethod(widget, [this] {
if (flush_timer.isActive()) {
return;
}
flush_timer.start(16);
}, Qt::QueuedConnection);
}
```
timer timeout
```cpp
void Render_Scheduler::flush_update() {
QRegion region;
{
std::scoped_lock lock(dirty_lock);
region = dirty_region;
dirty_region = QRegion();
}
if (!region.isEmpty()) {
widget->update(region);
}
}
```
这个语义是:
```text
后台可以随时完成图
但 UI 最多约 60Hz 请求 update
```
如果你想 30 FPS
```text
flush_timer.start(33)
```
如果你想尽快刷新:
```text
invokeMethod 之后直接 update(region)
```
---
# 8. 最终结论
你的主线程不需要启动一个固定 timer 去不停判断:
```text
rendered_version 是否变化
```
更合理的是:
```text
后台生成完新 ready 后,投递一个刷新任务到 Qt 主事件循环。
```
然后根据需求加节流:
```text
低延迟:QueuedConnection -> update(region)
限帧:QueuedConnection -> singleShot timer -> update(region)
```
所以最终分工是:
```text
后台线程:
生成图
发布 ready
请求调度器刷新
调度器:
合并 dirty region
合并 queued update 请求
可选限帧
主线程:
执行 update(region)
在 paintEvent 中消费 ready 并贴 front
```
一句话:
```text
计算可以用定时器调度,刷新不要用定时器轮询;刷新应该由后台完成事件驱动,再由主线程事件循环合并执行。
```
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 415 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 409 KiB

File diff suppressed because it is too large Load Diff
+7 -8
View File
@@ -1,10 +1,8 @@
set(module_dir "${CMAKE_CURRENT_LIST_DIR}/module")
set(dir "${module_dir}/radio")
file(GLOB_RECURSE srcs "${dir}/*.c" "${dir}/*.cpp" "${dir}/*.h" "${dir}/*.hpp" "${dir}/*.qrc")
add_executable(radio ${srcs})
target_link_libraries(radio PUBLIC Renderive Renderive_Diagnostics Renderive_Widget)
# 为了一起维护代码
#target_link_libraries(radio PUBLIC Demo_Gallery_Lib)
append_glob_source(radio_src ${dir})
add_executable(radio ${radio_src})
target_link_libraries(radio PUBLIC Psc_Widget Renderive_Qt)
if (MSVC)
target_compile_definitions(radio PRIVATE MockBackEnd)
target_compile_options(radio PRIVATE
@@ -14,13 +12,14 @@ endif ()
set_property(TARGET radio PROPERTY AUTOMOC ON)
set_property(TARGET radio PROPERTY AUTOUIC ON)
set_property(TARGET radio PROPERTY AUTORCC ON)
# 代码质量相关工具
set(radio_document_dirs
"${CMAKE_SOURCE_DIR}/module/radio"
"${CMAKE_SOURCE_DIR}/third_party/CPP_Core/Core"
"${CMAKE_SOURCE_DIR}/third_party/Graphic_Core/Renderive"
"${CMAKE_SOURCE_DIR}/third_party/Graphic_Core/Core"
)
set(radio_document_exclude_dirs
"${CMAKE_SOURCE_DIR}/third_party/Graphic_Core/Renderive/base/Virtual_Keyboard/googlepinyin"
"${CMAKE_SOURCE_DIR}/third_party/Graphic_Core/Core/base/Virtual_Keyboard/googlepinyin"
)
psc_add_doxygen_target(psc_radio_doxygen
PROJECT_NAME "Radio"
@@ -38,7 +37,7 @@ psc_add_graphviz_directory_target(psc_radio_graphviz
COLLAPSE_THRESHOLD 20
)
psc_add_cmake_graphviz_target(psc_radio_cmake_graphviz
TARGETS radio Renderive Renderive_Diagnostics Demo_Gallery Core_Static asio_object
TARGETS radio Core Renderive_Diagnostics Demo_Gallery Core_Static asio_object
OUTPUT_DIR "${CMAKE_BINARY_DIR}/docs/graphviz/radio"
FORMAT svg
)
+15 -19
View File
@@ -10,8 +10,7 @@
#include <memory_resource>
#include <utility>
#include <vector>
#include "../../../third_party/Renderive/Widget/base/Generate_Mock_Data.h"
#include "../../../third_party/Renderive/Renderive/base/Memory.h"
#include "Renderive/export.h"
namespace {
struct Mock_Config {
bool performance{};
@@ -43,6 +42,10 @@ double make_random_value(renderive::Range range) {
std::swap(range.origin, range.target);
return renderive::get_data(range);
}
renderive::Time_Of_Day current_render_time() {
const QTime now = QTime::currentTime();
return {now.isValid() ? now.msecsSinceStartOfDay() : -1};
}
void push_radio_data(Radio_Widget* rw) {
if (!rw || !rw->spectrogram_line || !rw->water_fall || !rw->time_power || !rw->audio_spectrogram_plot || !rw->progress_bar1 || !rw->progress_bar2)
return;
@@ -55,12 +58,12 @@ void push_radio_data(Radio_Widget* rw) {
rw->progress_bar1->setValue(middle_power);
}
{
rw->water_fall->give_data(QTime::currentTime(), make_random_data(rw->water_fall->power_range(), rw->water_fall->frequency_point_size(), renderive::Memory_Domain::Waterfall));
rw->water_fall->give_data(current_render_time(), make_random_data(rw->water_fall->power_range(), rw->water_fall->frequency_point_size(), renderive::Memory_Domain::Waterfall));
}
{
renderive::Range r = rw->time_power->value_axis()->coord_range();
renderive::Range nr = {r.middle() - r.length() / 4, r.middle() + r.length() / 4};
rw->time_power->give_data(QTime::currentTime(), make_random_value(nr));
rw->time_power->give_data(current_render_time(), make_random_value(nr));
}
{
auto r = rw->audio_spectrogram_plot->power_axis()->coord_range();
@@ -80,10 +83,10 @@ void push_radio_data_perf(Radio_Widget* rw) {
rw->progress_bar1->setValue(middle_power);
}
{
rw->water_fall->give_data(QTime::currentTime(), make_random_data({0, 160}, 1024, renderive::Memory_Domain::Waterfall));
rw->water_fall->give_data(current_render_time(), make_random_data({0, 160}, 1024, renderive::Memory_Domain::Waterfall));
}
{
rw->time_power->give_data(QTime::currentTime(), make_random_value({-6000, 6000}));
rw->time_power->give_data(current_render_time(), make_random_value({-6000, 6000}));
}
{
rw->audio_spectrogram_plot->give_data(make_random_data({-6000, 6000}, 512, renderive::Memory_Domain::Spectrum));
@@ -156,20 +159,13 @@ Back_End::Back_End() {
QTimer::singleShot(0, this, [this, config]() {
Radio_Widget* rw = Main_Widget::instance()->radio_widget;
if (config.performance) {
mock_thread = new QThread(this);
auto worker = new QObject;
worker->moveToThread(mock_thread);
QObject::connect(mock_thread, &QThread::finished, worker, &QObject::deleteLater);
QObject::connect(mock_thread, &QThread::started, worker, [worker, config, rw]() {
auto timer = new QTimer(worker);
timer->setTimerType(Qt::PreciseTimer);
QObject::connect(timer, &QTimer::timeout, worker, [config, rw]() {
for (int i = 0; i < config.batch_count; ++i)
push_radio_data_perf(rw);
});
timer->start(config.interval_ms);
auto timer = new QTimer(this);
timer->setTimerType(Qt::PreciseTimer);
QObject::connect(timer, &QTimer::timeout, this, [config, rw]() {
for (int i = 0; i < config.batch_count; ++i)
push_radio_data_perf(rw);
});
mock_thread->start();
timer->start(config.interval_ms);
return;
}
auto timer = new QTimer(this);
@@ -11,7 +11,7 @@
#include "../BaseWidget.h"
#include "../menu/BaseWidgetMenu.h"
#include "../BackEnd/MainWidget.h"
#include "../../../third_party/Renderive/Demo_Gallery/base/Tool.h"
#include "Demo_Gallery/base/export.h"
static renderive::Range frequent_range = {-12000, 12000};
static double max = 160;
QWidget* Intermediate_Frequency_Widget::create_spectrogram_power_plot() {
@@ -31,13 +31,13 @@ QWidget* Intermediate_Frequency_Widget::create_spectrogram_power_plot() {
auto overlay = create_renderable_node(get_root_renderable(), "Overlay_Renderable");
frequency_axis_cache->set_cache_mode(renderive::Renderable_Cache_Mode::Local_Pixel);
power_axis_cache->set_cache_mode(renderive::Renderable_Cache_Mode::Local_Pixel);
x_axis = renderive::Frequency_Axis::Builder(frequency_axis_cache, Qt::Horizontal)
x_axis = renderive::Frequency_Axis::Builder(frequency_axis_cache, renderive::Orientation::Horizontal)
.set_tick_length(-10)
.set_coord_range(frequent_range)
.set_tick_length(-10)
.set_sub_tick_length(-5)
.build();
y_axis = renderive::Axis::Builder(power_axis_cache, Qt::Vertical)
y_axis = renderive::Axis::Builder(power_axis_cache, renderive::Orientation::Vertical)
.set_coord_range({160, 0})
.set_unit_text("功率")
.build();
@@ -98,13 +98,13 @@ QWidget* Intermediate_Frequency_Widget::create_Afterglow_Plot() {
auto overlay = create_renderable_node(get_root_renderable(), "Overlay_Renderable");
frequency_axis_cache->set_cache_mode(renderive::Renderable_Cache_Mode::Local_Pixel);
power_axis_cache->set_cache_mode(renderive::Renderable_Cache_Mode::Local_Pixel);
x_axis = renderive::Frequency_Axis::Builder(frequency_axis_cache, Qt::Horizontal)
x_axis = renderive::Frequency_Axis::Builder(frequency_axis_cache, renderive::Orientation::Horizontal)
.set_coord_range(frequent_range)
.set_tick_length(-10)
.set_sub_tick_length(-5)
.set_use_drag(true)
.build();
y_axis = renderive::Axis::Builder(power_axis_cache, Qt::Vertical)
y_axis = renderive::Axis::Builder(power_axis_cache, renderive::Orientation::Vertical)
.set_coord_range({160, 0})
.set_use_drag(true)
.build();
+9 -9
View File
@@ -8,7 +8,7 @@
#include "../menu/BaseWidgetMenu.h"
#include "BackEnd.h"
#include "../BackEnd/MainWidget.h"
#include "../../../third_party/Renderive/Demo_Gallery/base/Tool.h"
#include "Demo_Gallery/base/export.h"
static bool use_Performance_shower = false;
static double max = 160;
static renderive::Range frequent_range = {-12000, 12000};
@@ -47,11 +47,11 @@ QWidget* Radio_Widget::create_spectrogram_power_plot() {
auto axis_cache = create_renderable_node(get_root_renderable(), "Power_Axis_Cache");
axis_cache->set_cache_mode(renderive::Renderable_Cache_Mode::Local_Pixel);
auto overlay = create_renderable_node(get_root_renderable(), "Overlay_Renderable");
x_axis = renderive::Frequency_Axis::Builder(axis_cache, Qt::Horizontal)
x_axis = renderive::Frequency_Axis::Builder(axis_cache, renderive::Orientation::Horizontal)
.set_coord_range(frequent_range)
.set_tick_length(-10)
.build();
y_axis = renderive::Axis::Builder(axis_cache, Qt::Vertical)
y_axis = renderive::Axis::Builder(axis_cache, renderive::Orientation::Vertical)
.set_coord_range({max, 0})
.set_unit_text("功率")
.build();
@@ -115,12 +115,12 @@ QWidget* Radio_Widget::create_water_fall_plot() {
auto overlay = create_renderable_node(get_root_renderable(), "Overlay_Renderable");
stream_cache->set_cache_mode(renderive::Renderable_Cache_Mode::Local_Pixel);
frequency_axis_cache->set_cache_mode(renderive::Renderable_Cache_Mode::Local_Pixel);
x_axis = renderive::Frequency_Axis::Builder(frequency_axis_cache, Qt::Horizontal)
x_axis = renderive::Frequency_Axis::Builder(frequency_axis_cache, renderive::Orientation::Horizontal)
.set_tick_length(-10)
.set_sub_tick_length(-5)
.set_coord_range(frequent_range)
.build();
y_axis = renderive::Time_Axis::Builder(time_axis, Qt::Vertical)
y_axis = renderive::Time_Axis::Builder(time_axis, renderive::Orientation::Vertical)
.set_time_point_size(100)
.build();
wf = renderive::Waterfall::Builder(data, x_axis, y_axis)
@@ -174,12 +174,12 @@ QWidget* Radio_Widget::create_audio_spectrogram_plot() {
auto overlay = create_renderable_node(get_root_renderable(), "Overlay_Renderable");
frequency_axis_cache->set_cache_mode(renderive::Renderable_Cache_Mode::Local_Pixel);
power_axis_cache->set_cache_mode(renderive::Renderable_Cache_Mode::Local_Pixel);
x_axis = renderive::Frequency_Axis::Builder(frequency_axis_cache, Qt::Horizontal)
x_axis = renderive::Frequency_Axis::Builder(frequency_axis_cache, renderive::Orientation::Horizontal)
.set_coord_range({-12000, 12000})
.set_tick_length(-10)
.set_sub_tick_length(-5)
.build();
y_axis = renderive::Axis::Builder(power_axis_cache, Qt::Vertical)
y_axis = renderive::Axis::Builder(power_axis_cache, renderive::Orientation::Vertical)
.set_coord_range({12000, -12000})
.set_unit_text("功率").build();
sp = renderive::Spectrum::Builder(data, x_axis, y_axis)
@@ -233,11 +233,11 @@ QWidget* Radio_Widget::create_time_power_plot() {
auto overlay = create_renderable_node(get_root_renderable(), "Overlay_Renderable");
stream_cache->set_cache_mode(renderive::Renderable_Cache_Mode::Local_Pixel);
power_axis_cache->set_cache_mode(renderive::Renderable_Cache_Mode::Local_Pixel);
x_axis = renderive::Time_Axis::Builder(time_axis, Qt::Horizontal)
x_axis = renderive::Time_Axis::Builder(time_axis, renderive::Orientation::Horizontal)
.set_tick_length(-10)
.set_sub_tick_length(-5)
.build();
y_axis = renderive::Axis::Builder(power_axis_cache, Qt::Vertical)
y_axis = renderive::Axis::Builder(power_axis_cache, renderive::Orientation::Vertical)
.set_coord_range({12000, -12000})
.build();
audio = renderive::Audio_Frequency::Builder(data, x_axis, y_axis)
@@ -9,7 +9,7 @@
#include "../component/ShowTime.h"
#include "../Global.h"
#include "../menu/BaseWidgetMenu.h"
#include "Demo_Gallery/base/Tool.h"
#include "Demo_Gallery/base/export.h"
QWidget* Sweep_Frequency_Widget::create_spectrogram_power_plot() {
class Sweep_Frequent_Plot : public renderive::Latency_Eager_Plot {
public:
@@ -27,10 +27,10 @@ QWidget* Sweep_Frequency_Widget::create_spectrogram_power_plot() {
auto overlay = create_renderable_node(get_root_renderable(), "Overlay_Renderable");
x_axis_cache->set_cache_mode(renderive::Renderable_Cache_Mode::Local_Pixel);
y_axis_cache->set_cache_mode(renderive::Renderable_Cache_Mode::Local_Pixel);
x_axis = renderive::Axis::Builder(x_axis_cache, Qt::Horizontal)
x_axis = renderive::Axis::Builder(x_axis_cache, renderive::Orientation::Horizontal)
.set_coord_range({0, 100})
.set_tick_length(-10).build();
y_axis = renderive::Axis::Builder(y_axis_cache, Qt::Vertical)
y_axis = renderive::Axis::Builder(y_axis_cache, renderive::Orientation::Vertical)
.set_coord_range({100, 0})
.set_tick_length(-10).build();
sf = renderive::Sweep_Frequency::Builder(data, x_axis, y_axis)
+2 -6
View File
@@ -1,6 +1,6 @@
#ifndef Global_H
#define Global_H
#include "Renderive/architecture/export.h"
#include "Renderive/export.h"
#include "BackEnd/BackEnd.h"
#include <QApplication>
#include <QDebug>
@@ -14,7 +14,7 @@
#include <QtWidgetsDepends>
#include <functional>
#include <vector>
#include "psc_global_include/Singleton.hpp"
#include "private_psc_global_include/Singleton.hpp"
class Global : public Psc::Singleton<Global> {
public:
explicit Global();
@@ -27,10 +27,6 @@ QRect get_max_rect(const QRect& rect, double WHRate);
int calculate_optimal_font_size(const QString& text, const QSize& space_size, const QFont& font);
int calculate_optimal_font_size(const std::vector<QString>& text_list, const QSize& space_size, const QFont& font);
QPushButton* create_button(const QString& text, int font_size, const std::function<void(void)>& func = nullptr);
namespace renderive {
template <typename T>
class Roll_Object;
}
class Base_Widget_Menu;
void init_click_button(QPushButton* button, const QString& text, int font_size, const std::function<void(void)>& func, renderive::Roll_Object<QPushButton>*);
QPushButton* create_click_button(const QString& text, int font_size, const std::function<void(void)>& func, renderive::Roll_Object<QPushButton>*);
@@ -1,97 +0,0 @@
#ifndef Toolbox_Circular_Linked_List_H
#define Toolbox_Circular_Linked_List_H
#include <vector>
namespace Toolbox {
template<typename T>
struct Circular_Linked_List_Node {
Circular_Linked_List_Node *prev = nullptr, *next = nullptr;
T data;
};
template<typename T>
class Circular_Linked_List {
public:
Circular_Linked_List_Node<T> *head = nullptr;
Circular_Linked_List_Node<T> *cur = nullptr;
void init(const std::vector<T> &data_list) {
head = new Circular_Linked_List_Node<T>();
head->data = data_list.front();
Circular_Linked_List_Node<T> *prev = head;
int n = data_list.size();
for (int i = 1; i < n - 1; i++) {
auto node = new Circular_Linked_List_Node<T>();
node->data = data_list[i];
node->prev = prev;
prev->next = node;
prev = node;
}
auto tail = new Circular_Linked_List_Node<T>();
tail->data = data_list.back();
tail->prev = prev;
prev->next = tail;
tail->next = head;
head->prev = tail;
cur = head;
}
Circular_Linked_List_Node<T> *move(int step = 1) {
if (step == 0) return nullptr;
return step > 0 ? next(step) : prev(-step);
}
Circular_Linked_List_Node<T> *next(int step = 1) {
if (step <= 0) return nullptr;
for (int i = 0; i < step; i++) {
cur = cur->next;
}
return cur;
}
Circular_Linked_List_Node<T> *prev(int step = 1) {
if (step <= 0) return nullptr;
for (int i = 0; i < step; i++) {
cur = cur->prev;
}
return cur;
}
void insert_next(const T &t, Circular_Linked_List_Node<T> *that = nullptr) {
if (that == nullptr) that = cur;
auto node = new Circular_Linked_List_Node<T>();
node->data = t;
node->next = that->next;
node->prev = that;
that->next = node;
}
void insert_prev(const T &t, Circular_Linked_List_Node<T> *that = nullptr) {
if (that == nullptr) that = cur;
auto node = new Circular_Linked_List_Node<T>();
node->data = t;
node->prev = that->prev;
node->next = that;
that->prev = node;
}
std::vector<Circular_Linked_List_Node<T> *> to_vector() {
if (head == nullptr) return {};
std::vector<Circular_Linked_List_Node<T> *> ret;
Circular_Linked_List_Node<T> *cur = head;
do {
ret.push_back(cur);
cur = cur->next;
} while (cur != head);
return ret;
}
void clear() {
if (head == nullptr) return;
Circular_Linked_List_Node<T> *cur = head;
do {
Circular_Linked_List_Node<T> *temp = cur;
cur = cur->next;
delete temp;
} while (cur != head);
head = nullptr;
}
~Circular_Linked_List() {
clear();
}
};
}
#endif
-102
View File
@@ -1,102 +0,0 @@
#ifndef Toolbox_Node_H
#define Toolbox_Node_H
#include <algorithm>
#include <queue>
#include <vector>
namespace Toolbox {
template<typename That>
class Node {
public:
explicit Node() = default;
int index() {
if (!father) return -1;
auto it = std::find(father->sons.begin(), father->sons.end(), static_cast<That *>(this));
if (it == father->sons.end()) return -1;
return static_cast<int>(std::distance(father->sons.begin(), it));
}
bool root() { return !father; }
bool leaf() { return sons.empty(); }
bool first() {
if (!father) return false;
return this == father->sons.front();
}
bool last() {
if (!father) return false;
return this == father->sons.back();
}
That *previous() {
if (!father) return nullptr;
int i = index() - 1;
if (i < 0) return nullptr;
return father->sons[i];
}
That *next() {
if (!father) return nullptr;
int i = index() + 1;
if (i <= 0 || i >= static_cast<int>(father->sons.size())) return nullptr;
return father->sons[i];
}
That *last_child() {
if (sons.empty()) return nullptr;
return sons.back();
}
That *first_child() {
if (sons.empty()) return nullptr;
return sons.front();
}
That *root_node() {
That *ret = static_cast<That *>(this);
while (ret->father != nullptr) {
ret = ret->father;
}
return ret;
}
int deep() {
That *ret = static_cast<That *>(this);
int depth = 0;
while (ret->father != nullptr) {
ret = ret->father;
depth++;
}
return depth;
}
std::vector<That *> deep_traversed() {
std::vector<That *> ret;
ret.push_back(static_cast<That *>(this));
for (auto &child: sons) {
std::vector<That *> child_values = child->deep_traversed();
ret.insert(ret.end(), child_values.begin(), child_values.end());
}
return ret;
}
std::vector<That *> sequence_traversed() {
std::vector<That *> ret;
std::queue<That *> que;
That *that = static_cast<That *>(this);
que.push(that);
while (!que.empty()) {
That *cur = que.front();
que.pop();
int n = static_cast<int>(cur->sons.size());
ret.push_back(cur);
for (int i = 0; i < n; ++i) {
que.push(cur->sons[i]);
}
}
return ret;
}
std::vector<That *> descendants() {
std::vector<That *> ret = sequence_traversed();
if (!ret.empty()) ret.erase(ret.begin());
return ret;
}
That *father = nullptr;
std::vector<That *> sons;
};
template<typename NodeType>
class Node_Manager {
public:
std::vector<NodeType *> roots;
};
}
#endif
-74
View File
@@ -1,74 +0,0 @@
#ifndef Toolbox_Roll_Widget_H
#define Toolbox_Roll_Widget_H
#include <QObject>
#include <QHBoxLayout>
#include <algorithm>
#include <functional>
#include <vector>
namespace Toolbox {
template<typename T>
class Roll_Object : QObject {
public:
explicit Roll_Object(QObject *parent = nullptr) : QObject(parent) {}
explicit Roll_Object(std::vector<T *> widgets, std::function<void(T *)> enter_func,
std::function<void(T *)> leave_func, QObject *parent) :
widgets(std::move(widgets)), enter_func(enter_func), leave_func(leave_func), QObject(parent) {
if (!this->widgets.empty() && enter_func) enter_func(this->widgets[index]);
}
int index = 0;
std::vector<T *> widgets;
std::function<void(T *)> enter_func;
std::function<void(T *)> leave_func;
T *cur() {
if (widgets.empty()) return nullptr;
return widgets[index];
}
void set_cur(T *cur) {
if (widgets.empty()) return;
auto it = std::find(widgets.begin(), widgets.end(), cur);
if (it == widgets.end()) return;
int new_index = static_cast<int>(std::distance(widgets.begin(), it));
if (leave_func) leave_func(widgets[index]);
index = new_index;
if (enter_func) enter_func(widgets[index]);
widgets[index]->update();
}
void roll_right() {
if (widgets.empty()) return;
if (leave_func) leave_func(widgets[index]);
widgets[index]->update();
if (widgets.back() != widgets[index]) {
index++;
} else {
index = 0;
}
if (enter_func) enter_func(widgets[index]);
widgets[index]->update();
}
void roll_left() {
if (widgets.empty()) return;
if (leave_func) leave_func(widgets[index]);
widgets[index]->update();
if (widgets.front() != widgets[index]) {
index--;
} else {
index = static_cast<int>(widgets.size()) - 1;
}
if (enter_func) enter_func(widgets[index]);
widgets[index]->update();
}
QHBoxLayout *hbox_layout(int space) {
auto ret = new QHBoxLayout;
ret->setMargin(0);
ret->setSpacing(space);
for (auto widget: widgets) {
ret->addWidget(widget, 1);
}
return ret;
}
};
}
#endif
@@ -1,18 +0,0 @@
#ifndef Toolbox_Singleton_H
#define Toolbox_Singleton_H
namespace Toolbox {
template<typename Derived>
class Singleton {
public:
static Derived *instance() {
static Derived instance;
return &instance;
}
Singleton(const Singleton &) = delete;
Singleton &operator=(const Singleton &) = delete;
protected:
Singleton() = default;
~Singleton() = default;
};
}
#endif
@@ -1,29 +0,0 @@
#ifndef Toolbox_Singleton_Widget_H
#define Toolbox_Singleton_Widget_H
#include <QDebug>
namespace Toolbox {
template<typename T>
class Singleton_Widget {
public:
static T *instance() {
if (!instance) {
qInfo() << "error Singleton_Widget 未创建" << instance;
}
return instance;
}
Singleton_Widget() {
if (instance) {
qInfo() << "error Singleton_Widget 重复创建" << instance;
}
instance = static_cast<T *>(this);
}
virtual ~Singleton_Widget() {}
Singleton_Widget(T &&) = delete;
Singleton_Widget(const T &) = delete;
void operator=(const T &) = delete;
protected:
static T *instance;
};
template<typename T> T *Singleton_Widget<T>::instance = nullptr;
}
#endif
-7
View File
@@ -1,7 +0,0 @@
#include "Node.hpp"
#include "Circular_Linked_List.hpp"
#include "Singleton.hpp"
#include "Roll_Object.h"
#include "Singleton_Widget.hpp"
#include "view/FileFolderBar.h"
#include "pinyin/VirtualKeyBoard.h"
Binary file not shown.
@@ -1,41 +0,0 @@
#include "FileFolderBar.h"
#include <QPainter>
#include <queue>
namespace Toolbox {
File_Folder_Bar::File_Folder_Bar(QWidget *parent) : QWidget(parent) {
//setAttribute(Qt::WA_OpaquePaintEvent);
}
File_Folder_Bar::~File_Folder_Bar() {
}
void File_Folder::draw(int index, QPainter *painter) {
static QImage file_folder = QImage(":fileFolder.png");
int x = 10 + 20 * deep;
int y = 10 + 30 * index;
// painter->drawImage(x, y, file_folder);
painter->drawImage(0, 0, file_folder);
painter->drawText(x + 20, y + 10, name);
}
void File_Folder_Bar::paintEvent(QPaintEvent *event) {
QWidget::paintEvent(event);
QPainter painter(this);
int index = 0;
for (File_Folder *&root: roots) {
std::queue<File_Folder *> que;
root->deep = 0;
que.push(root);
while (!que.empty()) {
File_Folder *cur = que.front();
que.pop();
int n = cur->sons.size();
cur->draw(index++, &painter);
for (int i = 0; i < n; ++i) {
if (cur->is_show_sons) {
cur->sons[i]->deep = cur->deep + 1;
que.push(cur->sons[i]);
}
}
}
}
painter.end();
}
}
@@ -1,25 +0,0 @@
#ifndef Toolbox_File_Folder_Bar_H
#define Toolbox_File_Folder_Bar_H
#include <QWidget>
#include "../Node.hpp"
namespace Toolbox {
class File_Folder : public Node<File_Folder> {
public:
QString name;
bool is_show_sons = false;
int deep = 0;
void draw(int index, QPainter *painter);
};
class File_Folder_Bar : public QWidget, public Node_Manager<File_Folder> {
Q_OBJECT
public:
explicit File_Folder_Bar(QWidget *parent = nullptr);
~File_Folder_Bar() override;
protected:
void paintEvent(QPaintEvent *event) override;
};
}
#endif
@@ -1,6 +0,0 @@
<RCC>
<qresource prefix="/">
<file>fileFolder.png</file>
<file>arrowDown.png</file>
</qresource>
</RCC>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

+1 -1
View File
@@ -1,6 +1,6 @@
#include "ExpandInput.h"
#include "../BackEnd/MainWidget.h"
#include "Widget/base/export.h"
#include "Renderive/export.h"
#include <QAction>
#include <QPainter>
Expand_Input::Expand_Input(QWidget *parent) : QLineEdit(parent) {
+1 -2
View File
@@ -1,4 +1,4 @@
#include "Renderive/architecture/export.h"
#include "Renderive/export.h"
#include <QApplication>
#ifdef _WIN32
#include <QTextCodec>
@@ -6,7 +6,6 @@
#endif
#include "./BackEnd/MainWidget.h"
#include "Application.h"
#include "Renderive/plot/Plot.h"
int main(int argc, char* argv[]) {
Application app(argc, argv);
Application::setAttribute(Qt::AA_UseHighDpiPixmaps);
+1 -1
View File
@@ -42,4 +42,4 @@ function Fetch-RepoEx
Fetch-RepoEx "build_infra"
Fetch-RepoEx "CPP_Core"
Fetch-RepoEx "Renderive"
Fetch-RepoEx "Core"
+39
View File
@@ -0,0 +1,39 @@
include_guard(GLOBAL)
#set(base_dir "C:/Program Files/Microsoft Visual Studio/18/Enterprise/VC/Tools/MSVC/14.50.35717/bin/Hostx64/x64")
#set(CMAKE_C_COMPILER "${base_dir}/cl.bat" CACHE FILEPATH "")
#set(CMAKE_CXX_COMPILER "{${base_dir}}/cl.bat" CACHE FILEPATH "")
#set(CMAKE_MAKE_PROGRAM "C:/Program Files/Microsoft Visual Studio/18/Enterprise/Common7/IDE/CommonExtensions/Microsoft/CMake/Ninja/ninja.exe" CACHE FILEPATH "")
function(_append_msvc_init_options variable)
set(value "${${variable}}")
separate_arguments(existing_options WINDOWS_COMMAND "${value}")
foreach (option IN LISTS ARGN)
list(FIND existing_options "${option}" option_index)
if (option_index EQUAL -1)
list(APPEND existing_options "${option}")
endif ()
endforeach ()
list(JOIN existing_options " " value)
set(${variable} "${value}" PARENT_SCOPE)
endfunction()
_append_msvc_init_options(
CMAKE_CXX_FLAGS_INIT
/wd4819
/utf-8
/Zc:preprocessor
/DNOMINMAX
/D_USE_MATH_DEFINES
/EHsc
/bigobj
)
_append_msvc_init_options(
CMAKE_EXE_LINKER_FLAGS_RELEASE_INIT
/PROFILE
)
_append_msvc_init_options(
CMAKE_SHARED_LINKER_FLAGS_RELEASE_INIT
/PROFILE
)
_append_msvc_init_options(
CMAKE_MODULE_LINKER_FLAGS_RELEASE_INIT
/PROFILE
)