在CodeBolcks下wxSmith的C++编程教程——访问表单中的组件

0.前言

欢迎来到 wxSmith 教程页面!wxSmith 与 Code::Blocks、wxWidgets 和 C++ 编译器相结合,为您提供一种所见即所得的方式来创建具有图形用户界面 (GUI) 的应用程序。该组合形成了一个用于快速应用程序开发 (RAD) 的工具,可在 Linux、Mac OS X 和 Windows 上运行。当您工作时,您会在屏幕上看到您正在设计的表单;他们看着你,就像他们看着你的程序的用户一样。
这些教程假设您是Code::Blocks 或 wxWidgets 的初学者,事实上,它们也是学习它们的好教程;假设您对 C++ 有基本的了解,您能够看懂教程中的C++实例代码。
这套教程的其他文章可以从以下文章中找到:

在CodeBolcks下wxSmith的C++编程教程——wxSmith教程目录(序言) - lexyao - 博客园

编写这篇文章参照了Code::Blocks 用户文档中wxSmith教程的以下文章:

WxSmith tutorial: Accessing items in resource - Code::Blocks

在这篇文章中你将看到以下内容:

  1. 访问表单中的组件
  2. 设置表单
  3. 通过成员函数访问组件
  4. 更改 wxStaticText 中的标签
  5. 从 wxTextCtrl 读取值
  6. 更改 wxGauge 的值
  7. 使用 wxSlider 中的值更改字体大小
  8. 更改组件的颜色
  9. 更多信息
  10. 结束语

1.访问表单中的组件

我们在前面的教程中讲了使用wxSmith构建GUI界面的知识,从这一篇教程开始我们将讲述访问GUI界面中的组件的方法。
我们构建GUI界面的目的是什么?可以肯定地说,构建GUI界面是为了让我们的应用程序有一个用户友好型的界面。什么是用户友好型?相信你从字面上也有猜测,想更深入的了解也可以在网上搜索一下,我不在这里解释是因为这不是我要讲的重点。我们要关注的是怎么与GUI界面上的组件通信。
GUI界面设计的再好,如果什么也做不了也就失去了意义,所以与界面中的组件通信是更重要的事情。
在这一篇教程中,我将向您展示一些基础知识 —— 例如,如何从文本框中读取数据、更改标签,以及一些更高级的事情,例如在应用程序运行时更改颜色和字体。
像往常一样,我们将从一个空应用程序开始,项目名称为tu06。您应该不会有任何问题 —— 所有说明都在第一个教程中。

2.设置表单

我们已经构建了名为tu06的空项目,现在我们向主框架中添加一些组件,后面的操作将会从这些组件开始。添加组件后tu06frame传给你了下面截图的样子:

image

在这个表单中,有一个 wxBoxSizer,其中有一个 wxPanel,在面板上,有一个具有 2 列的 wxFlexGridSizer,两列都是可增长的(要实现这一点,请在 sizer 的 Growable Cols 属性中输入“0,1”(不带引号),指示列 0 和 1)。
在这里示例中使用wxGridSizer要比wxFlexGridSizer更方便,wxGridSizer的单元格大小都是相同的,也就是说它的列不用设置也会增长。
以下表格显示了窗口中哪些组件以及位置:

wxStaticText
wxButton
wxTextCtrl
wxButton
wxGauge
wxButton
wxStaticText
wxSlider
wxStaticText
wxButton

所有控件及其后面的 wxPanel 都选中了它们的 Expand 属性。

以下是我们想要让按钮执行的作:

  • 第一行的按钮将更改其左侧静态文本中的文本。
  • 第二行中的按钮将读取用户在其左侧的 TextCtrl 中写入的任何文本,并在消息框中显示该文本。
  • 第三行的按钮会将仪表向左推进仪表左侧的十分之一,从而模拟典型的进度条。
  • 移动第四行中的滑块将更改其左侧静态文本中字体的大小。
  • 单击底行中的按钮将允许用户更改其左侧的 StaticText 控件中的文本颜色。

 

3.通过成员函数访问组件

如果在编辑器中将项目添加到表单中,在大多数情况下,wxSmith 会将一个新成员变量添加到表单的 C++ 类中。所有这些成员都列在头文件中,在我们现在的例子中是文件 tu06Main.h。它们介于 wxSmith 的特殊注释之间://(*Declarations... 和 //*)该列表应如下所示:
...
class tu06Frame: public wxFrame
{
    ...

        //(*Declarations(tu06Frame)
        wxSlider* Slider1;
        wxButton* Button4;
        wxStaticText* StaticText2;
        wxButton* Button1;
        wxGauge* Gauge1;
        wxStaticText* StaticText1;
        wxStaticText* StaticText3;
        wxButton* Button2;
        wxButton* Button3;
        wxStatusBar* StatusBar1;
        wxTextCtrl* TextCtrl1;
        //*)
    ...
};
...

具有此类成员变量的每个组件还将具有以下属性:

  • Var name - 所用变量的名称
  • Is member - 切换是否应通过类成员变量访问此项目
因此,如果您想更改用于访问该项目的名称,可以修改Var name 属性的值成为你希望的名称。
wxSmith 对变量名称施加了一些限制。最明显的是,每个变量名都必须是有效的 C++ 标识符,因此不能使用特殊字符甚至空格。另一个限制是变量名称必须是唯一的。
如果名称无效,wxSmith 将自动将其替换为符合条件的名称。

4.更改 wxStaticText 中的标签

以前我们做过的是设计时从属性浏览器中更改组件的标签,在这里我们将通过编写代码让用户可以在运行时改变组件的标签。
让我们做一个基本练习。当我们单击“Change label”按钮时,我们希望程序将第一个 wxStaticText 控件的标签更改为“Label changed”(你也可以尝试改为“标签已更改”)。为此,请双击按钮以生成事件处理程序并将代码更改为以下内容:
void tu06Frame::OnButton1Click(wxCommandEvent& event)
{
    StaticText1->SetLabel(_("Label changed"));
    Layout();
}

在函数体的第一行中,我们使用 SetLabel() 函数来更改标签的文本。由于文本的长度会发生变化,因此我们必须重新计算位置,这是通过调用 Layout() 函数来完成的。
静态项目(用户无法更改的项目)通常有两个函数:GetLabel 和 SetLabel,用于读取和写入该项目呈现的内容。

您可能想知道为什么我们对字符串使用了一些奇怪的符号:

_("Label changed")

而不是简单的

"Label changed"

这种方法有两个优点:

  • 通过在字符串周围添加 _(...),我们为翻译过程准备应用程序。wxWidgets 可以帮助开发多语言应用程序。进行翻译时,wxWidgets 将自动在源语言和目标语言的等效字符串数据库中搜索短语“Label changed”的翻译。
  • wxWidgets 可以提供两个版本:支持 Unicode 和不支持 Unicode(ANSI 构建)。对于 Unicode 版本,我们必须使用 Unicode 字符串:L"标签已更改",对于 ANSI 版本,我们必须使用标准字符串表示法:"标签已更改"。使用 _(...) 宏可确保无论使用什么 wxWidgets 版本,它都将始终生成正确的代码。

字符串宏有一个替代版本,其工作方式类似于 _(..),以 _T(...) 或 wxT(...) 的形式编写。它的工作原理类似于 _( ) 的,但字符串永远不会被翻译。

我在Code::Blocks 25.03中测试时,如果想将标签改为中文,需要使用(_T(“标签已更改”))或(wxT(“标签已更改”))才能正常显示,使用(_(“标签已更改”))看到的是乱码。我们没有尝试在i18n多语言应用程序开发时怎么解决这个问题,有兴趣的可以去探索。

5.从 wxTextCtrl 读取值

现在让我们阅读用户编写的内容。为此,我们将使用 wxTextCtrl(使用变量 TextCtrl1),并使用标准消息框显示文本。让我们双击“Read text”按钮并将代码更改为以下内容:

void tu06Frame::OnButton2Click(wxCommandEvent& event)
{
    wxString Text = TextCtrl1->GetValue();
    wxMessageBox(_("User entered text:\n") + Text);
}

在第一行中,我们从 TextCtrl 读取值并将其存储在名为 Text 且类型为 wxString 的变量中。wxString 是字符串类的 wxWidgets 实现,该库将其用作字符串表示的基础。
在第二行中,我们调用 wxMessageBox 函数,该函数显示一个标准消息框,就像它是一个模式对话框一样。
通常,提供用户输入的内容的项有两个成员函数:GetValue 和 SetValue。您可以使用它们来读取和写入此类项目的内容。

6.更改 wxGauge 的值

现在让我们将组件值的读取和写入结合在一个函数中。为此,我们将使用 wxGauge。我们将使用它的两个成员函数:GetValue 和 SetValue。我之前写过,这些函数通常存在于用户可以输入某些值的项目中。好吧,该规则并非没有例外,正如我们现在在为第三个按钮编写处理程序时所看到的那样。双击按钮并填写处理程序,如下所示:

void tu06Frame::OnButton3Click(wxCommandEvent& event)
{
    int NewValue = Gauge1->GetValue()+10;
    if ( NewValue > 100 ) NewValue = 0;
    Gauge1->SetValue(NewValue);
}

在第一行中,我们通过读取wxGauge当前进度值并向其添加 10 来生成一个新的进度值。在第二行中,我们防止该值超出范围。(默认情况下,wxGauge 的范围设置为 0-100。这些值可以在属性浏览器中更改:最小值是0,最大值可以通过Range属性更改。)在第三行中,我们将新值写入wxGauge。

7.使用 wxSlider 中的值更改字体大小

现在让我们做一些更高级的事情。在我们的资源中,第四行是一个 wxSlider(变量名Slider1)和一个wxStaticText(变量名StaticText2)。我们将在用鼠标拖动wxSlider的滑块时改变wxStaticText的标签字体大小。我们必须分两步更新字体大小:

  • 只要用户拖动滑块,我们就应该只更改字体大小
  • 当用户完成拖动后,我们应该布局窗口,因为文本的大小会发生变化

wxSlider 提供了两个事件,可以准确地用于这个目的。第一个是EVT_COMMAND_SCROLL_THUMB_TRACK,在拖动滑块时触发。第二个是EVT_COMMAND_SCROLL_THUMB_RELEASE,当用户完成拖动时触发。

因为我们使用的事件不是滑块的默认事件,所以无法像使用按钮一样双击滑块为处理程序创建一个框架。我们必须通过属性浏览器添加这两个处理程序。您可以通过单击浏览器顶部的按钮在编辑标准属性和编辑事件之间切换:

image 

切换到事件后,找到所需的事件,然后从下拉列表中选择“--Add new handler--”。

下面是EVT_COMMAND_SCROLL_THUMB_TRACK处理程序的代码:

void tu06Frame::OnSlider1CmdScrollThumbTrack(wxScrollEvent& event)
{
    wxFont Font = StaticText2->GetFont();
    Font.SetPointSize( Slider1->GetValue() );
    StaticText2->SetFont(Font);
}

在这里,我们使用 GetFont() 函数获取 StaticText 控件使用的字体,使用 SetPointSize() 更改字体的大小,并使用 SetFont() 函数写回字体。GetFont() 和 SetFont() 可用于大多数组件。

现在让我们编写EVT_COMMAND_SCROLL_THUMB_RELEASE代码:

void tu06Frame::OnSlider1CmdScrollThumbRelease(wxScrollEvent& event)
{
    Layout();
    GetSizer()->SetSizeHints(this);
}

在第一行中,我们将项目的位置调整为新的字体大小,就像在 wxStaticText 中更改标签一样。在第二行中,我们重新计算窗口的最小大小,以确保所有项目都有足够的空间。

(使用 Panel1->GetSizer()->SetSizeHints(this);对于上述语句中的第二行,其中 Panel1 是框架中基础面板的名称。原始代码不是将大小调整为新的所需大小,而是将大小调整为原始默认大小。)

在前面我们提到管理添加的组件时“使用wxGridSizer要比wxFlexGridSizer更方便”,但在这里却会体现出二者的不同。当运行时将wxStaticText 的字体变大,释放鼠标后:

  • 使用wxFlexGridSizer时窗口中StaticText2所在的行和列变大以适应StaticText2新的大小。
  • 使用wxGridSizer时窗口中所有的组件都会变大,这是因为wxGridSizer的所有单元格始终保持同样的大小。

8.更改组件的颜色

在本教程中,我们要做的最后一件事是更改某些项目的颜色。与字体一样,我们将更改“更改”按钮左侧的标签。由于我们将使用此按钮的标准事件,因此我们可以通过双击它来添加事件处理程序。这是代码:

void tu06Frame::OnButton4Click(wxCommandEvent& event)
{
    wxColour OldColour = StaticText3->GetForegroundColour();
    wxColour NewColour = wxGetColourFromUser(this,OldColour);

    if ( NewColour.IsOk() )
    {
        StaticText3->SetForegroundColour(NewColour);
        Refresh();
    }
}

(wxWidets 起源于爱丁堡,因此使用一些古色古香的拼写,可以追溯到盎格鲁-诺曼时代。一开始,我们将当前颜色读入变量 OldColour。在下一行中,我们调用 wxGetColourFromUser 函数,该函数打开一个对话框,用户可以在其中选择颜色。

最后,我们使用 SetForegroundColor 函数设置新颜色。设置后需要刷新才能更改颜色。

但是,如果您尝试编译此代码,您将收到一条错误消息,告诉您 wxGetColourFromUser() 是一个未知函数。由于我们手动放入它,因此我们也必须手动放入它的标头。因此,打开 tu06Main.h 文件并添加 #include < wx/colordlg.h>。

使用上面提供的代码我们在win11系统上使用Code::Blocks 25.03+wxWidets 3.3.1构建并运行是成功的,但原英文版教程的作者提到了找不到 IsOk()的问题并提供了替代的解决方法。不知道你的系统中有没有遇到这样的问题?
我相信原文作者不会无的放矢,或许是后来的wxWidets版本弥补了这个缺陷也未可知。不管是什么原因,我觉得还是把这个方法列出来比较好,即使这个代码中不需要,至少他也提供了一种解决问题的思路。以下是附加的说明和代码:


使用 NewColour.IsOk() 调用是因为如果用户取消颜色选择,它将返回 false。 但是在 Ubuntu Linux 11.10 安装的 wxWidgets 上,wxColour 类没有 IsOk() 功能,因此实际上您必须删除此调用。根据 wxWidgets 文档,wxColour 类确实具有 IsOk() 成员函数,但没有找到。

修复:为了让前面的处理程序中的代码工作,只需将其替换为:

wxColour OldColour = StaticText3->GetForegroundColour();
wxColourData dc;
dc.SetColour(OldColour);
wxColourDialog clrdlg(this,&dc);

if ( clrdlg.ShowModal() == wxID_OK )
{
   StaticText3->SetForegroundColour(clrdlg.GetColourData().GetColour());
   Refresh();
}

 

9.结束语

在这一篇教程中我们讲述了给组件添加事件响应程序的方法,也涉及到wxWidets更深层次的内容。在这里讲述的是基本的方法,就像是我们学习语言时掌握了几个单词和简单的语句,算是入门了。将来掌握了更多的知识就可以写出完整的文章。

posted @ 2025-09-25 11:21  lexyao  阅读(22)  评论(0)    收藏  举报