绑定事件到自定义 wxButton

时间:2021-05-26 10:55:53

标签: c++ events graphics wxwidgets

我正在用 C++ wxWidgets 编写一个应用程序。我正在尝试创建一个自定义按钮类,但有两个问题:

  1. 我将 wxEVT_COMMAND_BUTTON_CLICKED 绑定到按钮以在单击时执行函数,但它似乎没有被调用
  2. 它周围有一个可怕的灰色边框,我无法删除(即使使用 wxBORDER_NONE 也无法删除)

My custom buttons

代码:

gridButton.h

#pragma once
#include "wx/wx.h"

enum gbSTYLE {
    NUM,
    OP,
    EXTRA
};

class gridButton : public wxWindow
{
    bool pressed;
    bool hovered;
    wxString text;

    static const int width = 50, height = 50;
    
    wxFont* gbFont = new wxFont(20, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, 
        wxFONTWEIGHT_LIGHT, false, "Lato");
    // RICORDATI DI CANCELLARE font_Button DALLA CLASSE MAIN QUANDO HAI FINITO

    wxBrush* background;
    wxColour* textColor;

    wxBrush* hoveredBackground;
    wxColour* hoveredTextColor;

    wxBrush* pressedBackground;
    wxColour* pressedTextColor;

public:
    gridButton(wxFrame* parent, wxWindowID id, wxString text, gbSTYLE style);
    ~gridButton();

    void applyStyle(gbSTYLE style);

    void paintEvent(wxPaintEvent& evt);
    void paintNow();
    void render(wxDC& dc);

    void mouseMoved(wxMouseEvent& evt);
    void mouseDown(wxMouseEvent& evt);
    void mouseWheelMoved(wxMouseEvent& evt);
    void mouseReleased(wxMouseEvent& evt);
    void rightClick(wxMouseEvent& evt);
    void mouseLeftWindow(wxMouseEvent& evt);
    void mouseEnterWindow(wxMouseEvent& evt);
    void keyPressed(wxKeyEvent& evt);
    void keyReleased(wxKeyEvent& evt);

    DECLARE_EVENT_TABLE()
};

gridButton.cpp

#include "gridButton.h"


BEGIN_EVENT_TABLE(gridButton, wxWindow)

    EVT_MOTION(gridButton::mouseMoved)
    EVT_LEFT_DOWN(gridButton::mouseDown)
    EVT_LEFT_UP(gridButton::mouseReleased)
    EVT_RIGHT_DOWN(gridButton::rightClick)
    EVT_ENTER_WINDOW(gridButton::mouseEnterWindow)
    EVT_LEAVE_WINDOW(gridButton::mouseLeftWindow)
    EVT_KEY_DOWN(gridButton::keyPressed)
    EVT_KEY_UP(gridButton::keyReleased)
    EVT_MOUSEWHEEL(gridButton::mouseWheelMoved)

    EVT_PAINT(gridButton::paintEvent)

END_EVENT_TABLE()



// funzione costruttrice (wxFULL_REPAINT_ON_RESIZE per evitare un glitch grafico quando si ridimensiona la finestra)
gridButton::gridButton(wxFrame* parent, wxWindowID id, wxString text, gbSTYLE style)
    : wxWindow(parent, id, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE) 
{
    SetMinSize(wxSize(width, height));
    this->text = text;
    
    hovered = false;
    pressed = false;

    applyStyle(style);
}

// funzione distruttrice
gridButton::~gridButton()
{
    delete gbFont;

    delete background;
    delete textColor;

    delete hoveredBackground;
    delete hoveredTextColor;

    delete pressedBackground;
    delete pressedTextColor;
}

void gridButton::applyStyle(gbSTYLE style)
{
    switch (style)
    {
    default:
        background = new wxBrush(wxColour(255, 255, 255), wxBRUSHSTYLE_SOLID);
        textColor = new wxColour(25, 25, 25);

        hoveredBackground = new wxBrush(wxColour(125, 125, 125), wxBRUSHSTYLE_SOLID);
        hoveredTextColor = new wxColour(185, 185, 185);

        pressedBackground = new wxBrush(wxColour(25, 25, 25), wxBRUSHSTYLE_SOLID);
        pressedTextColor = new wxColour(200, 200, 200);
        break;

    case NUM:
        background = new wxBrush(wxColour(98, 101, 138), wxBRUSHSTYLE_SOLID);
        textColor = new wxColour(190, 197, 200);

        hoveredBackground = new wxBrush(wxColour(66, 69, 96), wxBRUSHSTYLE_SOLID);
        hoveredTextColor = new wxColour(36, 38, 52);

        pressedBackground = new wxBrush(wxColour(16, 17, 23), wxBRUSHSTYLE_SOLID);
        pressedTextColor = new wxColour(128, 134, 192);
        break;
    }
}


//chiamato da EVT_PAINT ogni frame
void gridButton::paintEvent(wxPaintEvent& evt)
{
    wxPaintDC dc(this);
    render(dc);
}
//eseguire un render quando si vuole
void gridButton::paintNow()
{
    wxClientDC dc(this);
    render(dc);
}

//renderizzazione, qui si mette la grafica
void gridButton::render(wxDC& dc)
{
    int w = this->GetSize().GetWidth();
    int h = this->GetSize().GetHeight();

    if (pressed) {
        dc.SetBrush(*pressedBackground);
        dc.SetTextForeground(*pressedTextColor);
    }
    else if (hovered) {
        dc.SetBrush(*hoveredBackground);
        dc.SetTextForeground(*hoveredTextColor);
    }
    else {
        dc.SetBrush(*background);
        dc.SetTextForeground(*textColor);
    }

    dc.DrawRectangle(0, 0, w, h);

    dc.SetFont(*gbFont);
    wxSize textSize = GetTextExtent(text);
    dc.DrawText(text, w / 2 - (textSize.GetWidth()), h / 2 - (textSize.GetHeight()));
}

void gridButton::mouseDown(wxMouseEvent& evt)
{
    pressed = true;
    paintNow();
}
void gridButton::mouseReleased(wxMouseEvent& evt)
{
    pressed = false;
    paintNow();
}
void gridButton::mouseEnterWindow(wxMouseEvent& evt)
{
    hovered = true;
    paintNow();
}
void gridButton::mouseLeftWindow(wxMouseEvent& evt)
{
    hovered = false;
    pressed = false;
    paintNow();
}

// eventi inutilizzati
void gridButton::mouseMoved(wxMouseEvent& evt) {}
void gridButton::mouseWheelMoved(wxMouseEvent& evt) {}
void gridButton::rightClick(wxMouseEvent& evt) {}
void gridButton::keyPressed(wxKeyEvent& evt) {}
void gridButton::keyReleased(wxKeyEvent& evt) {}

这就是我布局网格的方式(现在看起来很糟糕,因为我仍在处理按钮本身):

#include "main.h"


void Main::AddButtons()
{
    int w = 5, h = 4;
    MainGrid = new wxGridSizer(w, h, 0, 0);


        gridButton* test = new gridButton(this, wxID_ANY, "(", NUM);
        MainGrid->Add(test, 0, wxEXPAND);

    AddOpButton(4);

    for (int i = 0; i < 2; i++) MainGrid->AddSpacer(1);

    for (int i = 7; i < 10; i++) AddNumButton(i);
    AddOpButton(3);
    for (int i = 4; i < 7; i++) AddNumButton(i);
    AddOpButton(2);
    for (int i = 1; i < 4; i++) AddNumButton(i);
    AddOpButton(1);
    MainGrid->AddSpacer(1);
    AddNumButton(0);
    
    //dot
    wxButton* dot = new wxButton(this, wxID_ANY, ".");
    dot->SetFont(*font_Button);
    dot->SetBackgroundColour(wxColour(200, 200, 200));
    dot->SetForegroundColour(wxColour(125, 125, 125));
    dot->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &Main::OnDotClicked, this);
    MainGrid->Add(dot, 0, wxEXPAND);

    AddOpButton(0);

    MainSizer->Add(MainGrid, 10, wxEXPAND);
}

void Main::AddNumButton(int n)
{
    gridButton* btn = new gridButton(this, BUTTON_NUM + n, wxString(std::to_string(n)), NUM);
    btn->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &Main::OnNumClicked, this);

    MainGrid->Add(btn, 0, wxEXPAND);
}

void Main::AddOpButton(int n)
{
    std::string s(1, ops[n]);

    gridButton* btn = new gridButton(this, BUTTON_OP + n, wxString(s), OP);
    btn->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &Main::OnOpClicked, this);

    MainGrid->Add(btn, 0, wxEXPAND);
}

它应该触发的事件:

void Main::OnNumClicked(wxCommandEvent& evt)
{
    int id = evt.GetId() - BUTTON_NUM;
    DoNumClick(id);
    evt.Skip();
}

void Main::DoNumClick(int id)
{
    char c = '0' + id;
    char exp[EXPBUFFER];
    strcpy(exp, MainText->GetValue());


    if (strlen(exp) == 1 && exp[0] == '0')
    {
        exp[0] = c;
        MainText->SetLabel(exp);
    }
    else
        MainText->AppendText(std::to_string(id));
}

我是 wxWidgets 的新手,不胜感激。

1 个答案:

答案 0 :(得分:1)

我认为额外的灰色边框只是网格中剩余的额外像素。要去除剩余的像素,您可以改用 flex grid sizer,并使所有行和列都灵活。

void Main::AddButtons()
{
    MainGrid = new wxFlexGridSizer(w, h, 0, 0);

    for ( int i = 0 ; i < w ; ++ i)
    {
        MainGrid->AddGrowableRow(i);
    }
    for ( int j = 0 ; j < h ; ++j )
    {
        MainGrid->AddGrowableCol(j);
    }
...

要去掉按钮本身周围的矩形,而不是绘制矩形,您可以设置背景画笔并调用Clear() 方法。 Clear() 方法使用背景画笔完全绘制窗口。

void gridButton::render(wxDC& dc)
{
    int w = this->GetSize().GetWidth();
    int h = this->GetSize().GetHeight();

    if (pressed) {
        dc.SetBackground(*pressedBackground);

        dc.SetTextForeground(*pressedTextColor);
    }
    else if (hovered) {
        dc.SetBackground(*hoveredBackground);
        dc.SetTextForeground(*hoveredTextColor);
    }
    else {
        dc.SetBackground(*background);
        dc.SetTextForeground(*textColor);
    }

    dc.Clear();

您之前看到的边框是用默认笔绘制的矩形的边缘。

我有一个小小的建议。我会存储颜色而不是画笔本身。每个画笔都是一个 gdi 对象,在 Windows 上,gdi objects available 的数量是有限制的。

例如,如果您已经存储了pressedBackgroundColor,您可以像这样在堆栈上创建一个画笔:

    if (pressed) {
        wxBrush temp(*pressedBackgroundColor));
        dc.SetBackground(temp);

或者你可以像这样隐式创建画笔:

    if (pressed) {
        dc.SetBackground(*pressedBackgroundColor);

这无疑是一个轻微的反优化,因为必须在每次调用 render 方法时创建画笔。因此,如果您不担心 Windows 上的 gdi 限制,请随时忽略这一点。


回答第一个问题。您需要为自定义按钮手动生成按钮按下。最简单的方法是使用 mouseReleased 方法。要生成按钮事件,该方法需要执行以下操作:

void gridButton::mouseReleased(wxMouseEvent& evt)
{
    pressed = false;
    paintNow();

    // Create a button event and send it for processing.
    wxCommandEvent event(wxEVT_BUTTON);
    event.SetId(GetId());
    event.SetEventObject(this);
    // Set any other data needed here.

    ProcessWindowEvent(event);
}

为了避免与原生按钮混淆,您还可以使用 wxDECLARE_EVENTwxDEFINE_EVENT macros 创建自己的事件类型。 documentation 中有更多详细信息。

相关问题