【问题标题】:animation with wxWidgets in erlang in linux and window environmentlinux和window环境中erlang中的wxWidgets动画
【发布时间】:2013-11-25 08:11:27
【问题描述】:

我用erlang中的wxWidget做了一个小程序,并成功让它在windows XP下运行良好。该程序根据内部事件对位图进行动画处理,并使用时基(无用户事件)对其进行刷新。我的问题是代码在 Linux Ubuntu (11.04) 下无法正常运行。

位图面板初始化如下:

fill_window(W,H,Frame,Z) ->
    MainSz = wxBoxSizer:new(?wxVERTICAL),
    Board = wxPanel:new(Frame),
    wxWindow:setSizer(Board,MainSz),

    PanSz = wxStaticBoxSizer:new(?wxVERTICAL, Board,[{label, "Le Monde"}]),
    KeySz = wxStaticBoxSizer:new(?wxVERTICAL, Board,[{label, "Controle"}]),
    KeyGrSz = wxGridSizer:new(2,3,2,2),

    Panel = wxPanel:new(Board, [{size,{W,H}}]),  

    wxSizer:addSpacer(MainSz,2),
    wxSizer:add(PanSz, Panel, [{flag, ?wxALL bor ?wxEXPAND}]), 
    wxSizer:add(MainSz, PanSz, [{proportion, 0}, {border, 4}, {flag, ?wxALL bor ?wxEXPAND}]), 
    wxSizer:addSpacer(MainSz,3),
    wxPanel:connect(Panel, paint, [callback]),
    wxPanel:connect(Panel, left_up, []),
    wxPanel:connect(Panel, right_down, []),
    wxPanel:connect(Panel, middle_down, []),

    wxSizer:addSpacer(MainSz,3),
    wxSizer:add(KeySz, KeyGrSz, [{flag, ?wxALL bor ?wxEXPAND}]), 
    [wxSizer:add(KeyGrSz, wxButton:new(Board,Id,[{label,Txt},{size,{80,20}}]), [{flag, ?wxALL}]) || {Id,Txt} <- ?BLIST],
    wxSizer:add(MainSz, KeySz, [{proportion, 0}, {border, 4}, {flag, ?wxALL}]), 
    wxSizer:addSpacer(MainSz,2),
    wxWindow:connect(Board, command_button_clicked),
    wxSizer:layout(MainSz),

    ClientDC = wxClientDC:new(Panel),
    Bitmap = wxBitmap:new(W,H),
    PM = wxPen:new(color(dead), [{width, 1}]),
    PV = wxPen:new(color(live), [{width, 1}]),
    BG  = wxBrush:new(color(background)),
    BM  = wxBrush:new(color(dead)),
    BV  = wxBrush:new(color(live)),

%% initialisation de l'image de départ
    MemoryDC = wxMemoryDC:new(Bitmap),
    wxDC:setBackground(MemoryDC,BG),
    wxDC:setBrush(MemoryDC,BG),
    PenTemp = wxPen:new(color(background), [{width, 1}]),
    wxDC:setPen(MemoryDC,PenTemp),
    wxDC:drawRectangle(MemoryDC, {0,0}, {W,H}),
    wxPen:destroy(PenTemp),
    wxDC:setPen(MemoryDC,PM),
    wxDC:setBrush(MemoryDC,BM),
    [cell(MemoryDC, {X,Y},Z) || X <- lists:seq(0,W-1), Y <- lists:seq(0,H-1)],
    redraw(ClientDC,Bitmap,W,H),

    wxWindow:refresh(Panel),    
    wxWindow:show(Frame),
    {Frame, Panel, Bitmap, ClientDC, PV, PM, BV, BM}.

color(live) -> {255,255,255};
color(dead) -> {80,80,80};
color(background) -> {0,0,40}.

redraw(DC, Bitmap, W, H) ->
    MemoryDC = wxMemoryDC:new(Bitmap),
    wxMemoryDC:destroy(MemoryDC).

进程从其他进程接收一些消息,告诉它绘制一个单元格。假设窗口会根据内部状态的值(refreshtoggle)自动刷新屏幕或将更改存储在列表中。它还接收一些“刷新”消息,告诉它执行图像修改的挂起列表并刷新屏幕。我已经使用同步事件来完成此操作,该事件更新待处理修改列表并在需要时通过调用刷新函数执行它:

handle_call(refresh, _From, #state{cellbuf=Cb,clientDC=ClientDC, bitmap=Bitmap, w=W, h=H,zoom=Z, penlive=PV, pendead=PM, brushlive=BV, brushdead=BM,panel=P} = State) ->
    do_refresh(Cb,ClientDC,Bitmap,W,H,Z,PV,PM,BV,BM,P),
    {reply, ok, State#state{cellbuf=[]}};
handle_call({setcell,Etat,Cell}, _From, #state{cellbuf=Cb,clientDC=ClientDC, bitmap=Bitmap, w=W, h=H,zoom=Z, penlive=PV, pendead=PM, brushlive=BV, brushdead=BM,panel=P,refreshtoggle=true} = State) ->
    do_refresh([{Etat,Cell}|Cb],ClientDC,Bitmap,W,H,Z,PV,PM,BV,BM,P),
    {reply, ok, State#state{cellbuf=[]}};
handle_call({setcell,Etat,Cell}, _From, #state{cellbuf=Cb} = State) ->
    {reply, ok, State#state{cellbuf=[{Etat,Cell}|Cb]}};
handle_call(What, _From, State) ->
    {stop, {call, What}, State}.

do_refresh(Cb,ClientDC,Bitmap,W,H,Z,PV,PM,BV,BM,P) ->
    wx:batch(fun ()  ->
        Cb1 = lists:reverse(Cb),
        MemoryDC = wxMemoryDC:new(Bitmap),
        [cell(MemoryDC,PV,BV,PM,BM,State,Cell,Z) || {State,Cell} <- Cb1],
        wxDC:blit(ClientDC, {0,0},{W,H},MemoryDC, {0,0}),
        wxMemoryDC:destroy(MemoryDC)
    end).

在 windows 中,这不会刷新屏幕,我必须添加一个同步绘制事件来更新屏幕:

handle_sync_event(#wx{event = #wxPaint{}}, _wxObj, #state{panel=Panel, bitmap=Bitmap, w=W, h=H}) ->
    DC = wxPaintDC:new(Panel),  
    wxPaintDC:destroy(DC),
    ok.

这个函数什么也不做,只是从面板创建一个 PaintDC 并立即销毁它。它看起来有点神奇,但这是我从 wxWidget 文档中了解到的(用户窗口可以隐藏或移动,但从不调整大小,所以我真的没有什么可重绘的)

正如我所说,这在 Windows XP 上运行良好,动画流畅,没有闪烁,但在 linux 上,屏幕永远不会更新。我可以看到位图更新了,因为如果我暂停动画,缩小窗口并重新打开它,新图像就在那里。

在避免闪烁并在不同平台上工作的情况下,实现这种动画的正确方法是什么?

[编辑 1]

这里是 PaintDC 文档的摘录,据说即使我不使用它,我也必须在 onPaint 事件期间构建和销毁 PaintDC。我需要这个事件,因为用户可能会隐藏、最小化或移动窗口(我知道这些事件会创建一个 onPaint 事件,我今晚会检查这个)。

Detailed Description

A wxPaintDC must be constructed if an application wishes to paint on the client area of a window from within an EVT_PAINT() event handler.

This should normally be constructed as a temporary stack object; don't store a wxPaintDC object. If you have an EVT_PAINT() handler, you must create a wxPaintDC object within it even if you don't actually use it.

Using wxPaintDC within your EVT_PAINT() handler is important because it automatically sets the clipping area to the damaged area of the window. Attempts to draw outside this area do not appear.

To draw on a window from outside your EVT_PAINT() handler, construct a wxClientDC object.

To draw on the whole window including decorations, construct a wxWindowDC object (Windows only).

A wxPaintDC object is initialized to use the same font and colours as the window it is associated with.

尽管如此,我会尝试在我的代码中重新插入一个 refresh(),如果我没记错的话,我会删除它,因为它没用。

【问题讨论】:

    标签: erlang wxwidgets


    【解决方案1】:

    我不太了解 Erlang 代码,但您绝对不会通过创建和销毁 wxPaintDC 来刷新屏幕。请改用Refresh()

    【讨论】:

    • 感谢您的回答,我在帖子末尾添加了一些评论。
    • 你必须 both 调用 Refresh() 以使 wxEVT_PAINT 出现,并在此事件的处理程序中创建 wxPaintDC
    • 我在window环境下试了一下,是可以的,条件是我把参数eraseBackground设置为false,不然画面闪烁。今晚我会试试我的 linux conf。
    • :o( 我修改了 do_refresh(..) 函数,添加了对 refresh(P,[{eraseBackground,false}]) 的显式调用(类似于 C++ 中的 P.refresh(false))但它根本不适用于 linux。
    • 我终于成功让它运行了 bun 我仍然对代码不满意。我需要清理它。完成后我会更新帖子。谢谢。
    猜你喜欢
    • 2019-12-28
    • 1970-01-01
    • 1970-01-01
    • 2012-12-15
    • 1970-01-01
    • 2011-10-25
    • 2014-10-05
    • 2021-10-03
    • 1970-01-01
    相关资源
    最近更新 更多