diff --git a/CMakeLists.txt b/CMakeLists.txt index a652205..61a9522 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,9 +16,9 @@ find_package(wxWidgets REQUIRED COMPONENTS core base) include(${wxWidgets_USE_FILE}) add_executable(wxWidgetStudy main.cpp) +add_executable(wxEvent event_01_exe.cpp src/event_01.cpp) target_link_libraries(wxWidgetStudy PRIVATE ${wxWidgets_LIBRARIES}) +target_link_libraries(wxEvent PRIVATE ${wxWidgets_LIBRARIES}) -set_target_properties(wxWidgetStudy - PROPERTIES - WIN32_EXECUTABLE TRUE -) \ No newline at end of file +set_target_properties(wxWidgetStudy PROPERTIES WIN32_EXECUTABLE TRUE) +set_target_properties(wxEvent PROPERTIES WIN32_EXECUTABLE TRUE) \ No newline at end of file diff --git a/event_01_exe.cpp b/event_01_exe.cpp new file mode 100644 index 0000000..cc1b091 --- /dev/null +++ b/event_01_exe.cpp @@ -0,0 +1,4 @@ +#include "src/event_01.h" + +IMPLEMENT_APP(MyApp) +DECLARE_APP(MyApp) \ No newline at end of file diff --git a/main.cpp b/main.cpp index da79d5b..c0e836a 100644 --- a/main.cpp +++ b/main.cpp @@ -1,20 +1,57 @@ -// Name: minimal.cpp -// Purpose: Minimal wxWidgets sample -// Author: Julian Smart +/* + 下面大概的描述一下整个程序的执行过程: + 1. 依照系统平台的不同,不同的main函数或者winmain函数或者其它类似的函数被调用(这个函数是由 + wxWidgets提供的,而不是由应用程序提供的).wxWidgets 初始化它自己的数据结构并且创建一个MyApp的 + 实例. + 2. wxWidgets调用MyApp::OnInit函数, 这个函数会创建一个MyFrame的实例. + 3. MyFrame的构造函数通过它的基类wxFrame的构造函数创建一个窗口,然后给这个窗口增加图标,菜单栏 + 和状态栏. + 4. MyApp::OnInit函数显示主窗口并且返回真. + 5. wxWidgets开始事件循环,等待事件发生并且将事件分发给相应的处理过程. + 就目前我们所知道的,应用程序会在以下情况下退出:主窗口被关闭,用户选择退出菜单或者系统按钮和系统菜 + 单中的关闭选项(这些系统菜单和系统按钮在不同的系统中就往往千差万别了)。 + +*/ + #include "wx/wx.h" +/* + 每一个wxWidgets程序都需要定义一个wxApp类的子类,并且需要并且只能构造一个这个类的实例,这个实例控 + 制着整个程序的执行。你的这个继承自 wxApp的子类至少需要定义一个OnInit函数,当wxWidgets准备好运行 + 你写的代码的时候,它将会调用这个函数(和一个典型的Win32程序中的main函数或者WinMain函数类似)。 +*/ + // 定义应用程序类 class MyApp : public wxApp { public: - // 这个函数将会在程序启动的时候被调用 +/* + 在这个OnInit函数中,你通常应该创建至少一个窗口,对传入的命令行参数进行解析,为应用程序进行数据设置 + 和其它的一些初始化的操作.如果这个函数返回真,wxWidgets将开始事件循环用来处理用户输入并且在必要的情 + 况下处理这些输入。如果OnInit函数返回假, wxWidgets将会释放它内部已经分配的资源,然后结束整个程序的 + 运行。 +*/ virtual bool OnInit(); + +/* + 另外,大多数的应用程序类还应该重载一个 OnExit 函数,以便在任何时候程序退出时,执行一下清理和资源回收 + 的动作。需要注意的是,这个函数只有在 OnInit 函数返回真的时候才会被执行 +*/ }; -// 定义主窗口类 + + +/* + 一个Frame窗口是一个可以容纳别的窗口的顶级窗口,通常拥有一个标题栏和一个菜单栏。 +*/ class MyFrame : public wxFrame { public: // 主窗口类的构造函数 MyFrame(const wxString& title); - // 事件处理函数 + +/* +事件处理函数 + 事件处理函数在MyFrame类中不是虚函数。如果不是虚函数,他们是怎样被调用的呢?答 + 案就在下面的事件表里: +*/ void OnQuit(wxCommandEvent& event); void OnAbout(wxCommandEvent& event); @@ -23,22 +60,54 @@ private: DECLARE_EVENT_TABLE() }; -// 有了这一行就可以使用 MyApp& wxGetApp()了 -DECLARE_APP(MyApp) -// 告诉wxWidgets主应用程序是哪个类 +/* + 如果没有实现这个类 DECLARE_APP,wxWidgets就不知道怎样创建一个新的应用程序对象。这个宏除了上述的功能以外,还会 + 检查编译应用程序使用的库文件是否和当前的库文件的版本相匹配,如果没有这种检查,由此而产生的一些运行 + 期的错误可能很难被查出原因。 +*/ IMPLEMENT_APP(MyApp) +/* + 当wxWidgets创建这个MyApp类的实例的时候,会将创建的结果赋值给一个全局变量wxTheApp.你当然可以在 + 你的程序中使用这个变量,但是你可能不得不一遍又一遍的进行从wxApp到MyApp的类型强制转换。增加下面 + 的这一行声明以后,你就可以调用wxGetApp()函数,这个函数会返回一个到这个MyApp实例的引用,这样用起 + 来就方便多了。 + + 即使没有声明DECLARE_APP,你仍然可以不用进行类型强制转化就直接对wxTheApp变量调用wxApp的方法.这可 + 以避免在所有的头文件中包含MyApp的头文件,对于那些库文件而不是应用程序的代码来说也更有意义,而且还 + 可以缩短编译的时间。 + +*/ +DECLARE_APP(MyApp) + // 初始化程序 bool MyApp::OnInit() { - // 创建主窗口 + +/* +创建主窗口 + wxT 宏:是让你的代码兼容Unicode模式,这个宏和另外一个_T宏的作用是完全一样的,使用这个宏也不会带来运行期的性能损失。 + (你可能还会遇到另外一个类似的"_()"标记,这个标记是用来告诉 wxWidgets将其中的字符串翻译成其它语言的版本)。 +*/ MyFrame* frame = new MyFrame(wxT("Minimal wxWidgets App")); // 显示主窗口 frame->Show(true); // 开始事件处理循环 return true; } -// MyFrame类的事件表 + +/* +事件表 + 所谓事件表,是一组位于类的实现文件(.cpp文件)中的宏,用来告诉wxWidgets来自用户或者其它地方的事件 + 应该怎样和类的成员函数对应起来。 + + 这里的EVT_MENU宏只是很多中事件宏的其中之一,事件宏的作用是告诉wxWidgets哪种事件应该被关联到哪个成员函数 + + 例如:wxID_EXIT 和 wxID_ABOUT 为菜单项的事件。wxID_ABOUT和wxID_EXIT是wxWidgets + 预定义的宏,通常你应该通过枚举,常量或者预编译宏的方式定义你自己的标识。 + + 用此方法定义的时间表是一种静态的事件表,它不可以在运行期改变,也可以在运行期改变的动态事件表(在其他示例)。 +*/ BEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_MENU(wxID_ABOUT, MyFrame::OnAbout) EVT_MENU(wxID_EXIT, MyFrame::OnQuit) @@ -48,14 +117,41 @@ void MyFrame::OnAbout(wxCommandEvent& event) { wxString msg; msg.Printf(wxT("Hello and welcome to %s"), wxVERSION_STRING); + + // 四个参数分别代表消息内容,标题,窗口类型以及父窗口。 wxMessageBox(msg, wxT("About Minimal"), wxOK | wxICON_INFORMATION, this); } + void MyFrame::OnQuit(wxCommandEvent& event) { - // 释放主窗口 +/* + wxFrame类的 Close 函数并不直接关闭 frame窗口,而是产生一个 wxEVT_CLOSE_WINDOW 事件,这个事件默 + 认的处理函数调用 wxWindow::Destroy 函数释放了 frame 窗口。 + 用户还可以通过别的方法关掉应用程序,比如通过点击标题栏上的关闭按钮或者是通过系统菜单中的关闭菜单, + 在这种情况下,OnQuit 函数是怎样被调用的呢?事实上,在这种情况,OnQuit 函数并没有被调用。这时, + wxWidgets会通过 Close 函数(象OnQuit中的那样),给 frame 窗口发送一个 wxEVT_CLOSE_WINDOW 事件, + 这个事件默认的处理函数会释放掉 frame 窗口。在你的应用程序中,可以通过重载这个处理函数来增加改变这种 + 默认的行为,比如:如果你想问一问你的用户是不是真的要关闭窗口。 +*/ Close(); } + +/* + 这个构造函数首先调用它的基类(wxFrame)的构造函数,使用的参数是父窗口(还没有父窗口,所以用NULL), + 窗口标识(wxID_ANY标识让 wxWidgets自己选择一个)和标题。这个基类的构造函数才真正创建了一个窗口的实 + 例。除了这样的调用方法,还有另外一种方法是直接在构造函数里面显式调用基类默认的构造函数,然后调用 + wxFrame::Create函数来创建一个frame窗口的实例。 + 小图片或者是图标在所有的平台上都可以用XPM格式来表示。XPM文件其实是一个ASCII编码的完全符合C++语 + 法的文本文件,所以可以直接用C++的方式包含到代码中(译者注:显然这样的包含方式在分发软件的时候是不 + 需要分发这个图片文件的)。SetIcon那一行代码使用 mondrian_xpm 变量在堆栈上创建了一个图标(这个 + mondrian变量是在mondrian.xpm文件里定义的)。然后将这个图标和 frame窗口关联在一起。 + 接下来创建了菜单条。增加菜单项的Append函数的三个参数的意义分别为:菜单项标识,菜单上的文本以及一 + 个稍微长一些的帮助字符串。这个帮助字符串会自动在菜单项被高亮显示的时候自动显示在状态栏上。菜单上的 + 文本中由"&"符号前导的字符将成为菜单的快捷操作符,在实际的显示中用下划线表示。而"\t"符号则前导一个全 + 局的快捷键,这个快捷键甚至可以在菜单项没有显示的时候触发菜单功能。 + 这个构造函数所做的最后一件事是创建一个由两个区域组成的状态条并且在状态条的第一个区域写上欢迎的字样。 +*/ MyFrame::MyFrame(const wxString& title) : wxFrame(NULL, wxID_ANY, title) { // 设置窗口图标 diff --git a/src/event_01.cpp b/src/event_01.cpp new file mode 100644 index 0000000..7871ee6 --- /dev/null +++ b/src/event_01.cpp @@ -0,0 +1,46 @@ +#include "event_01.h" + +BEGIN_EVENT_TABLE(MyFrame, wxFrame) +EVT_MENU(wxID_ABOUT, MyFrame::OnAbout) +EVT_MENU(wxID_EXIT, MyFrame::OnQuit) +END_EVENT_TABLE() + +bool MyApp::OnInit() +{ + MyFrame* frame = new MyFrame(wxT("Minimal wxWidgets App")); + frame->Show(true); + return true; +} + +void MyFrame::OnAbout(wxCommandEvent& event) +{ + wxString msg; + msg.Printf(wxT("Hello and welcome to %s"), wxVERSION_STRING); + wxMessageBox(msg, wxT("About Minimal"), wxOK | wxICON_INFORMATION, this); +} + +void MyFrame::OnQuit(wxCommandEvent& event) +{ + Close(); +} + +MyFrame::MyFrame(const wxString& title) : wxFrame(NULL, wxID_ANY, title) +{ + // 设置窗口图标 + // SetIcon(wxIcon(mondrian_xpm)); + // 创建菜单条 + wxMenu* fileMenu = new wxMenu; + // 添加“关于”菜单项 + wxMenu* helpMenu = new wxMenu; + helpMenu->Append(wxID_ABOUT, wxT("&About...\tF1"), + wxT("Show about dialog")); + fileMenu->Append(wxID_EXIT, wxT("E&xit\tAlt-X"), wxT("Quit this program")); + // 将菜单项添加到菜单条中 + wxMenuBar* menuBar = new wxMenuBar(); + menuBar->Append(fileMenu, wxT("&File")); + menuBar->Append(helpMenu, wxT("&Help")); + // ...然后将菜单条放置在主窗口上 + SetMenuBar(menuBar); + CreateStatusBar(2); + SetStatusText(wxT("Welcome to wxWidgets!")); +} \ No newline at end of file diff --git a/src/event_01.h b/src/event_01.h new file mode 100644 index 0000000..cb0df39 --- /dev/null +++ b/src/event_01.h @@ -0,0 +1,31 @@ +#ifndef EVENT_CHAPTER_ONE +#define EVENT_CHAPTER_ONE + +#include "wx/wx.h" + +/* + 应用程序一直停留在一个循环中,等待着来自用户或者其他地方 + (比如窗口刷新或网络连接)的事件,一旦收到某种事件,应用程序就将其扔给处理这个事件的函数。虽然看上 + 去不同的窗口是同时被刷新的,但实际上,绝大多数的GUI程序都是单线程的,因此窗口的刷新是依次按顺序进 + 行的。如果由于某种意外你的电脑变得很慢导致窗口刷新的过程变的很明显,你就会注意到这一点。 + 不同的GUI编程架构用不同的方法将它内部的事件处理机制展现给程序开发者。对于wxWidgets来说,事件表机 + 制是最主要的方法。 +*/ + +class MyApp : public wxApp { +public: + virtual bool OnInit(); +}; + +class MyFrame : public wxFrame { +public: + MyFrame(const wxString& title); + void OnQuit(wxCommandEvent& event); + void OnAbout(wxCommandEvent& event); + +private: + DECLARE_EVENT_TABLE() +}; + + +#endif \ No newline at end of file