You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FLTK 1.3:如何确定fl_line()的绘图上下文?

问题:FLTK中fl_line()绘图上下文的控制方法

我正在为Linux寻找轻量C++ GUI框架,因此尝试在Ubuntu上熟悉FLTK 1.3。编写了可运行的代码,以30FPS绘制随机颜色的类信号波形动画,但存在问题:完全无法预测波形绘制位置——主窗口、按钮或弹出对话框上。

我似乎不理解如何为fl_line()这类绘图函数定义「上下文」。每次调用fl_line()时,线条都会绘制在不可预测的位置:聚焦按钮时线条画在按钮上;弹出对话框时,线条覆盖对话框及其按钮;关闭对话框后,线条仅覆盖「Close」按钮。

我的问题是:如何预测fl_line()的绘图「视觉上下文」(FLTK中是否有此类术语)?

原代码

#include <FL/Fl.H>
#include <FL/Fl_Button.H>
#include <FL/fl_message.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Double_Window.H>
#include <FL/fl_draw.H>
#include <cstdlib>

namespace simulator {

class Graph : public Fl_Widget {
  unsigned char color_[3] = {0, 0, 0};

public:
  Graph(int X, int Y, int W, int H)
  : Fl_Widget(X, Y, W, H) {
  }

  void draw(void) {
    this->plot();
  }

  void plot() {
    // Pick some trash color in a trash way.
    color_[0] = rand() % 256;
    color_[1] = rand() % 256;
    color_[2] = rand() % 256;
    fl_color(color_[0], color_[1], color_[2]);

    // Draw some random "audio wave"-like thing
    // Dont care about cleaning the scene before drawing.
    int old[2] = {0, 0};
    for (int i = 0; i < 400; ++i) {
      auto x = i * 2;
      auto y = rand() % 400;
      fl_line(old[0], old[1], x, y);
      old[0] = x;
      old[1] = y;
    }
  }
};

class Main_Window : public Fl_Window {
  //public Fl_Double_Window {
  Fl_Button button_ {10, 10, 75, 25, "Close"};
  Graph graph_;

public:
  Main_Window()
  //: Fl_Double_Window(200, 100, 1200, 800, "Simulator")
  : Fl_Window(200, 100, 1200, 800, "Simulator")
  , graph_(10, 100, 500, 500) {

    begin();
    Fl::visual(FL_RGB); 
    button_.align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE | FL_ALIGN_CLIP | FL_ALIGN_WRAP);
    // Button will invoke dialog "Want to exit?" with "yes/no" buttons.
    button_.callback([](Fl_Widget* sender, void* window) {
      printf("callback called, sender %p, window %p\n", sender, window);
      reinterpret_cast<Main_Window*>(window)->hide();
    }, this);
    
    // Maybe this is some illegal/unneeded/not-perfect widget adding code, and maybe widget adding is not needed at all; Lets forget about this for now.
    add(&button_);
    add(&graph_);

    end();
    show();
    graph_.show();
  }

  void hide() override {
    fl_message_hotspot(false);
    fl_message_title("Close window");
    if (fl_choice("Are you sure you want exit?", "No", "Yes", nullptr) == 1) {
      Fl_Window::hide();
    }
  }

  void plot() {
    graph_.plot();
  }
};
} // namespace

// 30 FPS wave animation.
void winUpdate(void *_data) {
  auto *window = reinterpret_cast<simulator::Main_Window*>(_data);
  Fl::add_timeout(1.0 / 30.0, winUpdate, window);
  window->plot();
}

int main(int argc, char *argv[]) {
  simulator::Main_Window window;
  window.show(argc, argv);
  Fl::add_timeout(1.0 / 30.0, winUpdate, &window);
  return Fl::run();
}

编译命令

/usr/bin/x86_64-linux-gnu-g++-12 test.cpp -o test -I/usr/include/c++/12 -std=c++20 -Wuninitialized -Wall -Wextra -Werror -Wno-deprecated-declarations -Wno-sign-compare -Wno-type-limits -Wunused-function -ggdb -fno-strict-aliasing -fwrapv -fno-stack-protector -fno-strict-overflow -Wfatal-errors `fltk-config --ldflags` -lfltk -lfltk_images -ljpeg -lstdc++ -lXfixes -lXext

解决方案

FLTK里没有专门的「视觉上下文」术语,但绘图操作始终绑定在当前激活的绘图表面上——这个表面通常是FLTK内部调度重绘时的目标Widget。你直接在定时器里调用graph_.plot()的问题在于:此时FLTK的绘图上下文可能不是Graph控件,而是任何正在处理重绘的控件(比如按钮、对话框),导致线条画到错误的地方。

核心修复步骤

  1. 让FLTK管理绘图时机:不要直接调用plot(),而是调用graph_.redraw()。这会告诉FLTK需要重绘Graph控件,FLTK会在合适的时机自动切换到Graph的绘图上下文,调用其draw()方法。
  2. 限制绘图区域:在Graph的draw()方法中,先设置裁剪区域,确保所有绘图操作只在控件自身范围内进行;同时绘制背景清理旧内容。
  3. 使用局部坐标:FLTK的绘图函数默认使用窗口全局坐标,需要转换为Graph控件的局部坐标(加上控件的x、y偏移),或者用裁剪区域自动约束。

修改后的关键代码

修改Graph类的draw和plot方法

void draw(void) {
    // 先绘制背景,清理之前的内容
    fl_color(FL_WHITE);
    fl_rectf(x(), y(), w(), h());
    // 设置裁剪区域,确保绘图只在控件范围内
    fl_push_clip(x(), y(), w(), h());
    this->plot();
    fl_pop_clip();
}

void plot() {
    color_[0] = rand() % 256;
    color_[1] = rand() % 256;
    color_[2] = rand() % 256;
    fl_color(color_[0], color_[1], color_[2]);

    int old[2] = {x(), y() + rand() % h()}; // 起始点使用控件局部坐标
    for (int i = 1; i < w(); i += 2) {
        auto curr_x = x() + i;
        auto curr_y = y() + rand() % h();
        fl_line(old[0], old[1], curr_x, curr_y);
        old[0] = curr_x;
        old[1] = curr_y;
    }
}

修改定时器函数winUpdate

void winUpdate(void *_data) {
    auto *window = reinterpret_cast<simulator::Main_Window*>(_data);
    Fl::add_timeout(1.0 / 30.0, winUpdate, window);
    window->graph_.redraw(); // 替换为redraw,让FLTK调度绘图
}

修复说明

  • redraw()会标记Graph控件为需要重绘,FLTK的事件循环会在合适的时机调用其draw()方法,此时绘图上下文自动切换到Graph的表面,所有fl_line()操作都会正确绘制在Graph控件内。
  • fl_push_clip()设置了绘图的边界,即使坐标超出控件范围,也不会绘制到其他控件上。
  • 绘图坐标加上了x()和y()偏移,确保波形在Graph控件的局部区域内生成,不会跑到窗口的左上角(全局坐标原点)。

内容的提问来源于stack exchange,提问作者pavelkolodin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 05:37:28