向导的替代方案

3 投票
5 回答
2939 浏览
提问于 2025-04-11 09:34

我正在制作一个程序,完美地符合向导的概念;用户可以通过步骤来创建一个游戏角色。

不过,我发现向导的局限性让设计“优雅”的逻辑流程变得困难。例如,因为向导的所有页面都是同时初始化的,所以我不能把一个页面输入的值直接用到下一个页面。我必须在每个页面上放一个按钮,以便从前一个页面获取值,而不能简单地让字段自动填充。

我考虑过不使用向导的替代方案。我觉得最好的主意是在一个面板上放一些按钮,这些按钮可以改变另一个面板上的信息,比如使用一个分割窗口。

但是,我在wxPython中找不到如何动态改变面板的文档。目前找到的资料都比较静态,所以才会使用向导。甚至《wxPython in Action》这本书也没有提到这个。

有没有关于制作“动态面板”或更好管理向导的教程呢?

5 个回答

0

我觉得应该完全去掉向导功能。它们是我用过的最让人不爽的东西。

需要用到向导的应用,通常是因为用户需要点击“下一步”,但其实这可能是一个可以用更好的用户界面来解决的问题。与其弹出一个让人烦的“下一步”按钮,不如这样做:

直接显示一个页面。当用户在页面上输入信息时,可以根据输入的内容来调整页面的长度。如果你的应用需要处理一些事情才能继续,而且处理后无法撤回,那就创建一个新页面,或者禁用当前页面的某些部分。当你不再需要用户输入,或者应用完成时,可以显示一个按钮,或者启用一个已经存在的按钮。

我不是说所有的功能都要在浏览器里实现。可以简单做一个可以滚动的容器,里面放按钮和标签,呈现成一个平面的列表。

好处是:用户只需点击一个标签,你也可以把所有的处理放在填写页面的最后。

1

wxPython的示例中有一个“动态”向导的例子。这个向导的页面可以通过重写GetNext()和GetPrev()方法来动态显示。这展示了基本的技巧;你可以在这个基础上扩展,添加和删除页面,实时更换页面,甚至动态调整页面的顺序。

不过,这个向导类只是为了方便使用。你可以对它进行修改,或者自己实现一个新的。现在比较流行的一种风格是使用基于HTML的展示;如果你的应用程序只在Windows上运行,可以使用wxHtml控件或者IEHtmlWindow控件来模拟这种效果。

5

这里有一个简单的例子。这样你可以让你的“向导”像一个有限状态机一样工作,其中每个状态就是不同的页面,这些页面根据需要进行初始化。同时,页面之间的数据是可以共享的。

import wx
import wx.lib.newevent


(PageChangeEvent, EVT_PAGE_CHANGE) = wx.lib.newevent.NewEvent()


class Data:
    foo = None
    bar = None


class Page1(wx.Panel):
    def __init__(self, parent, data):
        wx.Panel.__init__(self, parent)
        self.parent = parent
        self.data = data

        sizer = wx.BoxSizer(wx.VERTICAL)
        self.SetSizer(sizer)
        label = wx.StaticText(self, label="Page 1 - foo")
        self.foo = wx.TextCtrl(self)
        goto_page2 = wx.Button(self, label="Go to page 2")

        for c in (label, self.foo, goto_page2):
            sizer.Add(c, 0, wx.TOP, 5)

        goto_page2.Bind(wx.EVT_BUTTON, self.OnPage2)

    def OnPage2(self, event):
        self.data.foo = self.foo.Value
        wx.PostEvent(self.parent, PageChangeEvent(page=Page2))


class Page2(wx.Panel):
    def __init__(self, parent, data):
        wx.Panel.__init__(self, parent)
        self.parent = parent
        self.data = data

        sizer = wx.BoxSizer(wx.VERTICAL)
        self.SetSizer(sizer)
        label = wx.StaticText(self, label="Page 2 - bar")
        self.bar = wx.TextCtrl(self)
        goto_finish = wx.Button(self, label="Finish")

        for c in (label, self.bar, goto_finish):
            sizer.Add(c, 0, wx.TOP, 5)

        goto_finish.Bind(wx.EVT_BUTTON, self.OnFinish)

    def OnFinish(self, event):
        self.data.bar = self.bar.Value
        wx.PostEvent(self.parent, PageChangeEvent(page=finish))


def finish(parent, data):
    wx.MessageBox("foo = %s\nbar = %s" % (data.foo, data.bar))
    wx.GetApp().ExitMainLoop()


class Test(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None)
        self.data = Data()
        self.current_page = None

        self.Bind(EVT_PAGE_CHANGE, self.OnPageChange)
        wx.PostEvent(self, PageChangeEvent(page=Page1))

    def OnPageChange(self, event):
        page = event.page(self, self.data)
        if page == None:
            return
        if self.current_page:
            self.current_page.Destroy()
        self.current_page = page
        page.Layout()
        page.Fit()
        page.Refresh()


app = wx.PySimpleApp()
app.TopWindow = Test()
app.TopWindow.Show()
app.MainLoop()

撰写回答