下面是使用Python和PyGTK (gtk3) 创建一个窗口,具有一个菜单栏用于创建新文档以及弹出对话框的功能。为了让 Gtk.Notebook 的标签能够关闭,需要向每个标签页添加一个带有关闭按钮的标签。可以通过创建自定义的标签Widget,并在其中放置一个文本标签(Gtk.Label)和一个关闭按钮(Gtk.Button),实现这个功能。然后将这个自定义Widget添加为 notebook 的标签页。
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class MDIApplication(Gtk.Window):
def __init__(self):
super(MDIApplication, self).__init__(title='GTK MDI Application')
self.set_default_size(800, 600)
# 创建 VBox 和菜单
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
self.add(vbox)
menu_bar = Gtk.MenuBar()
file_menu = Gtk.Menu()
file_item = Gtk.MenuItem(label='File')
new_item = Gtk.MenuItem(label='New')
dialog_item = Gtk.MenuItem(label="Open Dialog")
file_item.set_submenu(file_menu)
file_menu.append(new_item)
file_menu.append(dialog_item)
menu_bar.append(file_item)
vbox.pack_start(menu_bar, False, False, 0)
# 创建 Notebook
self.notebook = Gtk.Notebook()
vbox.pack_start(self.notebook, True, True, 0)
# 信号连接
new_item.connect("activate", self.on_menu_new_activate)
dialog_item.connect("activate", self.on_menu_dialog_activate)
def create_tab_label(self, title):
label = Gtk.Label(label=title)
label.show()
close_image = Gtk.Image.new_from_icon_name("window-close", Gtk.IconSize.MENU)
button = Gtk.Button()
button.set_relief(Gtk.ReliefStyle.NONE)
button.set_focus_on_click(False)
button.add(close_image)
button.connect('clicked', self.on_close_tab, label)
button.show()
hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
hbox.pack_start(label, True, True, 0)
hbox.pack_start(button, False, False, 0)
hbox.show_all()
return hbox
def on_menu_new_activate(self, widget):
scrolled_window = Gtk.ScrolledWindow()
document_view = Gtk.TextView()
scrolled_window.add(document_view)
tab_label = self.create_tab_label('Untitled Document')
page_num = self.notebook.append_page(scrolled_window, tab_label)
self.notebook.set_current_page(page_num)
scrolled_window.show_all()
def on_menu_dialog_activate(self, widget):
dialog = Gtk.Dialog("Edit Text", self, Gtk.DialogFlags.MODAL, (
Gtk.STOCK_OK, Gtk.ResponseType.OK,
Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL
))
text_view = Gtk.TextView()
scroll_window = Gtk.ScrolledWindow()
scroll_window.add(text_view)
dialog.get_content_area().pack_start(scroll_window, True, True, 0)
dialog.show_all()
response = dialog.run()
if response == Gtk.ResponseType.OK:
buffer = text_view.get_buffer()
start_iter, end_iter = buffer.get_bounds()
text = buffer.get_text(start_iter, end_iter, True)
print("Text from dialog:", text) # 应用实际逻辑
dialog.destroy()
def on_close_tab(self, button, tab_label):
page_num = self.notebook.page_num(tab_label.get_parent())
self.notebook.remove_page(page_num)
def run(self):
self.connect("destroy", Gtk.main_quit)
self.show_all()
Gtk.main()
if __name__ == "__main__":
app = MDIApplication()
app.run()
create_tab_label
方法,它负责创建一个包含标签文本和关闭按钮的水平布局(Gtk.Box)。每当用户点击关闭按钮时,就会触发 on_close_tab
方法,并关闭对应的标签页。on_close_tab 方法通过查找传递过来的标签(实际上是点击的按钮的父级)在 notebook 中的页码,然后调用 remove_page
方法关闭标签页。
将代码保存为 .py
文件后,可以运行这个脚本来启动程序。这个程序会用 Python 运行,将看到:一个顶层窗口,带有创建新文档的菜单以及一个可以弹出编辑对话框的菜单项。
确保系统中已经安装了Python和PyGObject。如果尚未安装,可以使用以下pip命令来安装:
pip install PyGObject
或者如果使用的是 Linux 系统的包管理器,可以查找类似 python-gobject
或 python3-gobject
的包来安装适当的绑定。
以下是一个简单的示例代码,它使用GTK+ 3 (确保系统已安装GTK+ 3开发包):
#include
// 功能性的关闭按钮回调
void close_tab(GtkButton *button, GtkNotebook *notebook) {
gint page_num = gtk_notebook_page_num(notebook, gtk_widget_get_parent(GTK_WIDGET(button)));
printf("page_num:%d\n",page_num);
if (page_num != -1) {
// 页面存在,删除页面
gtk_notebook_remove_page(notebook, page_num);
}
}
// 创建一个可关闭的标签页
GtkWidget *create_tab_label(const gchar *title, GtkNotebook *notebook) {
// 创建一个新的水平箱容器
GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
// 创建一个标签并将其添加到箱中
GtkWidget *label = gtk_label_new(title);
gtk_container_add(GTK_CONTAINER(hbox), label);
// 创建关闭按钮
GtkWidget *close_button = gtk_button_new_from_icon_name("window-close", GTK_ICON_SIZE_MENU);
g_signal_connect(close_button, "clicked", G_CALLBACK(close_tab), notebook);
// 关闭按钮样式
GtkCssProvider *provider = gtk_css_provider_new();
gtk_css_provider_load_from_data(provider,
"button {"
"min-width: 0;"
"min-height: 0;"
"padding: 0;"
"margin: 0;"
"}", -1, NULL);
gtk_style_context_add_provider(gtk_widget_get_style_context(close_button),
GTK_STYLE_PROVIDER(provider),
GTK_STYLE_PROVIDER_PRIORITY_USER);
gtk_container_add(GTK_CONTAINER(hbox), close_button);
// 显示包含所需部件的容器
gtk_widget_show_all(hbox);
return hbox;
}
// 新的回调函数:当点击 "New" 菜单项时执行
void on_menu_new_activate(GtkMenuItem *item, gpointer user_data) {
GtkNotebook *notebook = GTK_NOTEBOOK(user_data);
GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
GtkWidget *document_view = gtk_text_view_new();
// 添加视图到滚动窗口
gtk_container_add(GTK_CONTAINER(scrolled_window), document_view);
// 创建可关闭的标签页
GtkWidget *tab_label = create_tab_label("Untitled Document", notebook);
// 将新的文档视图和标签加到 notebook,这次使用自定义的 create_tab_label 函数
gint page_num = gtk_notebook_append_page(notebook, scrolled_window, tab_label);
gtk_widget_show_all(scrolled_window);
// 向用户展示新选项卡
gtk_notebook_set_current_page(notebook, page_num);
}
void on_menu_dialog_activate(GtkMenuItem *item, gpointer user_data) {
// 创建对话框
GtkWidget *dialog = gtk_dialog_new_with_buttons("Edit Text", GTK_WINDOW(user_data),
GTK_DIALOG_MODAL,
"_OK", GTK_RESPONSE_OK,
"_Cancel", GTK_RESPONSE_CANCEL,
NULL);
// 创建一个文本编辑控件(text view)
GtkWidget *text_view = gtk_text_view_new();
// 创建一个滚动窗口容纳 text view
GtkWidget *scroll_window = gtk_scrolled_window_new(NULL, NULL);
gtk_container_add(GTK_CONTAINER(scroll_window), text_view);
// 将滚动窗口包含的 text view 控件添加到对话框中
gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), scroll_window, TRUE, TRUE, 0);
// 显示所有控件
gtk_widget_show_all(dialog);
// 运行对话框
gint response = gtk_dialog_run(GTK_DIALOG(dialog));
if (response == GTK_RESPONSE_OK) {
// 处理确认操作,例如从文本视图获取文本等
}
// 销毁对话框释放资源
gtk_widget_destroy(dialog);
}
int main(int argc, char *argv[]) {
GtkWidget *window;
GtkWidget *vbox;
GtkWidget *menu_bar;
GtkWidget *file_menu;
GtkWidget *file_item;
GtkWidget *new_item;
GtkWidget *notebook;
gtk_init(&argc, &argv);
// 窗口的基本设置
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "GTK MDI Application");
gtk_window_set_default_size(GTK_WINDOW(window), 800, 600); // 设置初始大小
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
// 创建箱子和菜单
vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
menu_bar = gtk_menu_bar_new();
file_menu = gtk_menu_new();
file_item = gtk_menu_item_new_with_label("File");
new_item = gtk_menu_item_new_with_label("New");
gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), new_item);
gtk_menu_item_set_submenu(GTK_MENU_ITEM(file_item), file_menu);
gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), file_item);
gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 0);
// 创建 notebook
notebook = gtk_notebook_new();
gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
gtk_container_add(GTK_CONTAINER(window), vbox);
// 创建弹出对话框菜单项并添加到文件菜单中
GtkWidget *dialog_item = gtk_menu_item_new_with_label("Open Dialog");
gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), dialog_item);
// 为 "Open Dialog" 菜单项的 "activate" 信号绑定回调函数
g_signal_connect(dialog_item, "activate", G_CALLBACK(on_menu_dialog_activate), (gpointer)window);
// 为 "New" 菜单项的 "activate" 信号绑定回调函数
g_signal_connect(new_item, "activate", G_CALLBACK(on_menu_new_activate), (gpointer)notebook);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
这段代码是一个使用GTK+ 3编写的C语言程序,它创建了一个带有多文档界面(MDI)以及菜单和对话框的窗口。它能够通过选项菜单新建文本编辑文档(New)以及打开一个对话框(Open Dialog)用于编辑文本。
1. 包含GTK库:首先,代码包括了GTK库的头文件,这是使用GTK+编写程序的基础。
#include
2. 定义回调函数:定义了两个回调函数,一个用于处理新建文档的请求,另一个用于打开编辑对话框。
on_menu_new_activate:当用户点击“New”菜单项时调用,创建一个带滚动条的文本编辑器,将其添加为笔记本组件的一页,并为其生成一个“Untitled Document”的标签。
gtk_notebook_append_page(notebook, scrolled_window, label);
on_menu_dialog_activate:当用户选择“Open Dialog”菜单项时调用,创建一个对话框,提供“OK”和“Cancel”按钮,并包含一个文本视图供用户编辑。
gtk_dialog_run(GTK_DIALOG(dialog));
3. 创建窗口和布局:在 main 函数中,创建了主窗口、布局盒子(vbox)、菜单栏(menu_bar)、文件菜单(file_menu)以及菜单项。
4. 菜单构建:构建了一个File菜单和两个菜单项New和Open Dialog。这些菜单和菜单项被添加到菜单栏中,并且菜单栏被放置在垂直布局盒里。
gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), new_item);
5. 创建Notebook控件:创建了一个notebook组件,这是GTK+中用于实现多文档界面(标签页式界面)的控件。
notebook = gtk_notebook_new();
6. 设置回调:将on_menu_new_activate和on_menu_dialog_activate两个回调函数分别关联到New菜单项和Open Dialog菜单项的"activate"信号上,当相应的菜单项被激活时,这些函数将会被调用。
7. 启动GTK:通过调用 gtk_widget_show_all(window) 将窗口和其中的所有组件都显示出来。接着,调用 gtk_main() 进入GTK+的主事件循环,等待用户操作。
程序运行时,会展现一个主窗口,其中包含一个菜单栏条有文件(File)菜单。当用户点击“New”菜单项时,程序将在notebook中创建一个新的标签页,带有一个可以编辑文本的滚动窗口。当用户点击“Open Dialog”时,会弹出一个对话框,里面包含一个文本视图,用户可以在其中输入或编辑文本。
8. 未能完成标签关闭,实现思路:
GTK+ 中的 GtkNotebook
小部件允许添加可关闭的标签页。要为 GtkNotebook
添加一个带有关闭按钮的标签页,需要创建一个包含标签文字和关闭按钮的水平箱(GtkHBox)作为标签页的标签。
这段代码中,我们定义了 create_tab_label
函数来创建一个带有文本和关闭按钮的标签。关闭按钮被连接到 close_tab
回调函数,该函数根据按钮的父控件找到页码,并从笔记本小部件中删除对应的页面。
对关闭按钮的样式进行了一点调整,使它看起来更小而且没有默认的内边距和外边距。这是使用 GTK CSS 简单的样式提供者做到的。
以下是一个使用wxWidgets库来实现的C++示例代码。确保已经安装了wxWidgets开发库,并设置了相关的编译器和链接器选项。
#include
#include
class MyFrame;
class MyApp : public wxApp
{
public:
virtual bool OnInit();
};
class MyFrame : public wxFrame
{
public:
MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size);
void OnNew(wxCommandEvent &event);
void OnDialog(wxCommandEvent &event);
private:
wxNotebook *m_notebook;
DECLARE_EVENT_TABLE()
};
enum
{
ID_New = 1,
ID_Dialog
};
// Event table for MyFrame
BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(ID_New, MyFrame::OnNew)
EVT_MENU(ID_Dialog, MyFrame::OnDialog)
END_EVENT_TABLE()
IMPLEMENT_APP(MyApp)
bool MyApp::OnInit()
{
MyFrame *frame = new MyFrame("wxWidgets MDI Application", wxDefaultPosition, wxSize(800, 600));
frame->Show(true);
return true;
}
MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size)
: wxFrame(NULL, wxID_ANY, title, pos, size)
{
// 创建菜单栏
wxMenuBar *menuBar = new wxMenuBar;
// 创建文件菜单
wxMenu *fileMenu = new wxMenu;
fileMenu->Append(ID_New, "&New\tCtrl-N", "Create a new document");
fileMenu->Append(ID_Dialog, "&Open Dialog\tCtrl-D", "Open a dialog box");
menuBar->Append(fileMenu, "&File");
// 设置菜单栏
SetMenuBar(menuBar);
// 创建笔记本控件
m_notebook = new wxNotebook(this, wxID_ANY);
// 设置框架大小
SetClientSize(size);
}
void MyFrame::OnNew(wxCommandEvent &event)
{
// 创建新的页面
wxPanel *newPage = new wxPanel(m_notebook, wxID_ANY);
wxTextCtrl *textCtrl = new wxTextCtrl(newPage, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE);
wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(textCtrl, 1, wxEXPAND | wxALL, 0);
newPage->SetSizer(sizer);
// 向笔记本添加页面
m_notebook->AddPage(newPage, "Untitled", true);
}
void MyFrame::OnDialog(wxCommandEvent &event)
{
// 创建对话框
wxDialog dialog(this, wxID_ANY, "Edit Text", wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE);
// 创建文本框
wxTextCtrl *textCtrl = new wxTextCtrl(&dialog, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE);
// 创建对话框内的布局
wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(textCtrl, 1, wxEXPAND | wxALL, 5);
dialog.SetSizer(sizer);
dialog.Fit();
// 显示对话框
if (dialog.ShowModal() == wxID_OK) {
// 处理文本等逻辑...
}
}
本示例创建了一个包含笔记本控件和菜单的主窗口。当用户选择“New”菜单项时,将创建一个新的笔记本页面。选择“Open Dialog”时将弹出一个对话框。未能实现标签的关闭。
需要链接wxWidgets库以使上面的代码正确编译,具体链接方式取决于使用的编译工具链和操作系统。