将事件绑定到自定义 wxButton [英] Binding event to a custom wxButton

查看:54
本文介绍了将事件绑定到自定义 wxButton的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm writing an app in C++ wxWidgets. I'm trying to create a custom button class, although there are two problems:

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

代码:

gridButton.h

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

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) {}

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

This is how I layout the grid (it looks bad right now because I'm still working on the buttons themselves):

#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 的新手,不胜感激.

I'm new to wxWidgets and any help is appreciated.

推荐答案

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

I think the extra grey border is just the extra pixels that are leftover from the grid. To get rid of the leftover pixels, you can use a flex grid sizer instead and make all the rows and columns flexible.

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() 方法使用背景画笔完全绘制窗口.

To get rid of the rectangle around the buttons themselves, instead of drawing a rectangle, you can set a background brush and call the Clear() method. The Clear() method completely paints the window with the background brush.

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();

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

The border you were seeing before was do to the edges of the rectangles being drawn with the default pen.

我有一个小小的建议.我会存储颜色而不是画笔本身.每个画笔都是一个 gdi 对象,在 Windows 上,gdi 对象可用.

I have one small recommendation. I would store the colors instead of the brushes themselves. Each brush is a gdi object and on windows there's a limit to the number of gdi objects available.

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

For example, if you have stored the pressedBackgroundColor, you can create a brush on the stack like this:

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

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

or you can let the brush be created implicitly like this:

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

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

This is admittedly a slight deoptimization since brushes have to be created in each call to the render method. So feel free to ignore this if you aren't concerned about the gdi limit on windows.

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

To answer the first question. You'll need to manually generate the button press for your custom button. The easiest way to do this is in the mouseReleased method. To generate the button event, the method needs do something like this:

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 .文档中有更多详细信息.

In order to avoid confusion with native buttons, you can also create your own event types with the wxDECLARE_EVENT and wxDEFINE_EVENT macros. There is more detail in the documentation.

这篇关于将事件绑定到自定义 wxButton的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆