【Python】用GTK实现多文档窗体弹出对话框

一、用python实现

下面是使用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 的包来安装适当的绑定。

二、用C实现

以下是一个简单的示例代码,它使用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库来实现的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库以使上面的代码正确编译,具体链接方式取决于使用的编译工具链和操作系统。

你可能感兴趣的:(编程,#,C语言,#,python,python,开发语言)