【问题标题】:How do I control when code executes in the on_draw event of gktmm3-Gtk::DrawingArea如何控制代码在 gktmm3-Gtk::DrawingArea 的 on_draw 事件中执行的时间
【发布时间】:2014-09-03 21:11:57
【问题描述】:

我在 Ubuntu 12.04 LTS 32 位上使用带有 gtkmm3 的 GNU 工具链的 C++11。 我一直在尝试Programming with gtkmm 3 中 gtkmm3 的一些示例。

基于那里的17.2.1.Example,我从Gtk::DrawingArea(这里的MyDrawingArea)继承并覆盖on_draw()事件处理程序,如下所示:

MyDrawingArea.hpp

...

protected:

  bool on_draw ( const Cairo::RefPtr<Cairo::Context>& cr ) override;

MyDrawingArea.cpp (又快又脏,只是为了概念演示)

double y{ 10 };
double x{ 10 };

bool MyDrawingArea::on_draw( const Cairo::RefPtr<Cairo::Context>& cr )
{
        this->get_window( )->freeze_updates( );
        cr->set_line_width( 3.0 );
        cr->set_source_rgb( 1, 0, 0 );
        cr->move_to( x, y );
        cr->line_to( x * 2, y * 2 );
        cr->stroke( );
        this->get_window( )->thaw_updates( );
        x += 50;
        y += 50;
        return true;
 }

此代码绘制一条对角线。当我通过应用程序中的事件处理程序调用MyDrawingArea.queue_draw() 时,我希望它的位置发生变化。这有效:

事件 1: 活动二:

我的问题是,很明显,on_draw() 事件处理程序会在每次重新绘制窗口时触发。简单地移动应用程序的主窗口(包含MyDrawingArea)会导致on_draw() 触发,并且该行将呈现在新位置。

如何控制我的on_draw() 事件中的代码何时运行,以便仅当我在应用程序代码中调用MyDrawingArea.queue_draw() 时重新呈现该行,但在其他时间保持之前的状态? (我不认为我在问如何防止on_draw() 被解雇,但也许这就是必须发生的事情?)

只需设置一个标志,通知我的事件已进行调用,然后才运行我的代码来呈现该行会引发其他问题,因为当未设置该标志时,我会丢失之前呈现的所有内容。

这似乎是不可能的情况:对于每个on_draw() 事件,要么在新位置重新绘制线条,要么在我使用标志调用我的绘图逻辑时完全删除,并且on_draw() 在该标志被触发时触发没有设置。

我应该如何处理这个问题?我是否需要逻辑来管理两个不同的on_draw() 选项:如果应用程序进行调用,则呈现该行的新版本,如果不是,则重新绘制旧版本?这变得复杂 - 我想我错过了一些东西。我应该使用不同的事件吗?我可以通过某种方式从Cairo::Context 获得有关谁打电话给On_draw() 等的信息,这将有助于我解决这个问题吗?

【问题讨论】:

    标签: c++ gtk cairo gtkmm


    【解决方案1】:

    如果您打算使用切换按钮打开/关闭 Gtk.DrawingArea,只需询问切换按钮是否处于活动状态,然后重绘该区域。

    <!------ language python ------>
    def on_draw (self, widget):
        # your cairo draw here
        ......
        ......
        if toggle_button.get_active () is True: #toggle to show DrawingArea
            x+=50
            y+=50
        # other than that it simply sits there doing nothing 
    

    现在,关于断开 "draw" 信号.. 将 cr 的最后一个渲染副本放在缓冲区中的某个位置,当您准备再次触发它时,再次连接,但这次不要从 "draw" 信号中获取 cairo *cr参数。使用副本

    【讨论】:

    • 将 cr 的最后一个渲染副本放在缓冲区中的某个位置 - 我正在研究这个并意识到也许这就是我必须做的 - 请参阅我刚刚发布的编辑:我需要逻辑吗... 它包含您建议的选项。我会通过将旧线路参数保存在某个容器中来做到这一点。实际的应用程序比我这里提到的更复杂,需要更多的参数,但它是可行的。但是应该有更简单的方法......如果没有人想出更好的方法,你会得到积分。
    • 很高兴为您提供帮助,祝您项目顺利。如果有更简单的方法,并且你找到了,请告诉我
    • 只询问切换按钮是否处于活动状态 - 该按钮不在处理渲染的类的范围内。它必须是在渲染类中设置的状态信息,或者是对该类的函数调用中的参数——可以在控制器类的中心函数中设置的东西,对所有潜在相关的前端成员都是可见和可调用的。将渲染逻辑绑定到前端的特定组件不是一个好主意:紧密耦合。
    【解决方案2】:

    我通过编写一个控制on_draw() 事件行为方式的小型状态机解决了这个问题:

    on_draw() 触发时代表不同信号状态的枚举:

    enum class Draw_Signals
    {
        DS_RedrawSignal, DS_HoldSignal, DS_ClearSignal, DS_End
    };
    
    DS_RedrawSignal 
    

    表示根据最近的应用程序活动重新绘制新的添加/更改。

    DS_HoldSignal 
    

    意味着保持当前的图形状态,但不做任何新的事情。

    DS_ClearSignal 
    

    表示完全清除绘图区域并重新开始。

    MyDrawingArea 类有一个成员:

    Draw_Signals mDSignal { Draw_Signals::DS_HoldSignal };

    这个值是由触发on_draw()的方法从我的应用程序中使用“setter”方法设置的。

    在不深入所有细节的情况下,我的MyDrawingArea::on_draw() 现在看起来像这样:

    bool MyDrawingArea::on_draw( const Cairo::RefPtr<Cairo::Context>& cr )
        {
    
            get_window( )->freeze_updates( );
    
            switch ( mDSignal )
              {
                case Draw_Signals::DS_RedrawSignal:
                  {
                    DrawNewLine( cr );//draws new line and caches it for use when RedrawOldLine() is called.
                    break;
                  }
    
                case Draw_Signals::DS_HoldSignal:
                  {
                    RedrawOldLine( cr );
                    break;
                  }
    
                case Draw_Signals::DS_ClearSignal:
                  {
                    clearDrawArea( );
                  }
    
              }
    
            get_window( )->thaw_updates( );
    
            mDSignal = Draw_Signals::DS_HoldSignal;
    
            return true;
    
        }
    

    这里的关键是默认状态始终是Draw_Signals::DS_HoldSignal,,它只是重绘以前的内容,而新的绘图只有在应用程序明确发出信号时才会发生。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-07-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-31
      • 1970-01-01
      • 2021-10-28
      • 1970-01-01
      相关资源
      最近更新 更多