【问题标题】:wxpython: how to access a window when a dialog is openwxpython:打开对话框时如何访问窗口
【发布时间】:2019-04-04 23:52:14
【问题描述】:

我知道当 ShowModal() 时对话框的父级应该是不可访问的。但是,我的程序需要提供对单独窗口的访问权限,以便用户可以查看该窗口上的信息来决定在对话框上单击哪个按钮。就像在下面的程序中一样,当“对话框”打开时,我希望能够访问“不同的框架”。我怎样才能做到这一点?

import wx

class MyFrame(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent, -1, "Different Frame", size=(300, 300))
        panel = wx.Panel(self)
        sizer = wx.BoxSizer(wx.VERTICAL)
        btn = wx.Button(panel, -1, "open frame")
        self.Bind(wx.EVT_BUTTON, self.OnOpenFrame, id=btn.GetId())
        sizer.Add(btn)
        text = "This is a line.\n" * 100
        txtCtrl = wx.TextCtrl(panel, -1, text, style=wx.TE_MULTILINE,
                              size=(200,200))
        sizer.Add(txtCtrl)
        panel.SetSizer(sizer)
        self.Centre()
        self.Show()

    def OnOpenFrame(self, evt):
        ParentFrame()

class ParentFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, title="Parent Frame",size=(500, 500))
        panel = wx.Panel(self)
        btn = wx.Button(panel, -1, "open dialog")
        self.Bind(wx.EVT_BUTTON, self.OnOpenDialog, id=btn.GetId())
        self.Centre()
        self.Show()

    def OnOpenDialog(self, event):
        dlg1 = MyDialog(self)
        val = dlg1.ShowModal()

        dlg2 = MyDialog(self, val)
        dlg2.ShowModal()

class MyDialog(wx.Dialog): 
    def __init__(self, parent, prevAnsw=None): 
        wx.Dialog.__init__(self, parent, -1, title = "Dialog") 
        panel = wx.Panel(self)
        sizer = wx.BoxSizer(wx.VERTICAL)
        if prevAnsw is not None:
            sizer.Add(wx.StaticText(
                panel, -1, "Answer from previous Dialog: %s" % prevAnsw))
        sizer.Add(wx.StaticText(panel, -1, "Try to scroll in Different Frame to "
                                "see if it is blocked"))
        btn1 = wx.Button(panel, -1, "Pass 1")
        self.Bind(wx.EVT_BUTTON, self.OnPass1, id=btn1.GetId())
        sizer.Add(btn1)

        btn2 = wx.Button(panel, -1, "Pass 2")
        self.Bind(wx.EVT_BUTTON, self.OnPass2, id=btn2.GetId())
        sizer.Add(btn2)
        panel.SetSizer(sizer)

    def OnPass1(self, evt):
        self.EndModal(1)

    def OnPass2(self, evt):
        self.EndModal(2)

app = wx.App(0)
frame = MyFrame(None)
frame.Show()
app.MainLoop()

【问题讨论】:

  • 您不必以模态方式显示对话框。你可以给他们看……
  • @MikeDriscoll:我知道我可以 Show() 他们。但是我需要很多对话框来逐步显示,其中每一步都是由上一步的结果决定的。只是 Show() 对话框,它与框架没有什么不同。
  • 对我来说这听起来像是一个巫师。 wxPython 有一个向导小部件 - wxpython.org/Phoenix/docs/html/wx.adv.Wizard.html
  • @MikeDriscoll 我的直觉是巫师天生就是模态的。
  • 它们是wx.Dialog 的子类,但我很确定如果你愿意,你可以以非模态方式使用Show

标签: dialog wxpython accessibility


【解决方案1】:

简单的答案是不要将其设为dialog
下面是一个相当不雅的伪对话,因为它在动作发生之前不会返回。它通过在等待事件发生的while 循环中调用Yield 来保持访问其他窗口的能力。
由于其他窗口是“活动的”,一旦每个进程完成,激活此进程的按钮必须禁用并重新启用,所以它真的有点像黑客,但它应该为你指明实现目标的方向你想要。
也许它可以自己写成一个 ChainDialog 模块,但我认为它很可能只是复制 Mike Driscoll 提到的 Wizard 模块。

import wx
import time

class MyFrame(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent, -1, "Reference Frame", size=(300, 300))
        panel = wx.Panel(self)
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.btn = wx.Button(panel, -1, "open frame")
        self.Bind(wx.EVT_BUTTON, self.OnOpenFrame, id=self.btn.GetId())
        sizer.Add(self.btn)
        text = "This is a line.\n" * 100
        txtCtrl = wx.TextCtrl(panel, -1, text, style=wx.TE_MULTILINE,
                              size=(200,200))
        sizer.Add(txtCtrl)
        panel.SetSizer(sizer)
        self.Centre()
        self.Show()

    def OnOpenFrame(self, evt):
        DialogRequest(self)

class DialogRequest(wx.Frame):
    def __init__(self,parent):
        wx.Frame.__init__(self, parent, -1, title="Dialog Request Frame",size=(500, 500))
        self.parent = parent
        #Disable the "open frame" button
        self.parent.btn.Enable(False)

        #Use a dictionary to store chained results
        #Allows for rewriting of results if "Previous" button used
        self.stored_results = {}

        self.dlg_chain_no = 0
        self.dlg_prev_value = 0
        panel = wx.Panel(self)
        self.btn = wx.Button(panel, -1, "open dialog")
        self.Bind(wx.EVT_BUTTON, self.OnOpenDialog, id=self.btn.GetId())
        self.Bind(wx.EVT_CLOSE, self.OnClose)
        self.Centre()
        self.Show()

    def OnOpenDialog(self, event):
        self.btn.Enable(False) #Disable open dialog

        #For test purposes there will be 4 chained dialogs
        #result -1 is a cancelled dialog
        #
        while self.dlg_chain_no != 4 and self.dlg_chain_no != -1:
            dlg = ChainDialog(self, self.dlg_chain_no)
            result = ChainDialog.Action(dlg)
            if result.chain_no == -1: # dialog Cancelled zero down stored values
                self.dlg_chain_no = 0
                self.stored_results = {}
                print("Dialog chain Cancelled")
                break
            self.dlg_chain_no = result.chain_no
            self.stored_results[result.chain_no] = result.result
            self.dlg_prev_value = result.result

        self.dlg_chain_no = 0
        self.dlg_prev_value = 0
        print("dialog values", self.stored_results)
        self.stored_results = {}

        self.btn.Enable(True) #Re-enable open dialog

    def OnClose(self, event):
        #Re-enable the "open frame" button
        self.parent.btn.Enable(True)
        event.Skip()

class ChainDialog(wx.Frame):
    def __init__(self, parent, chain_no):
        wx.Frame.__init__(self, parent, -1, title = "Dialog "+str(chain_no))
        self.chain_no = self.saved_chain_no = chain_no
        self.result = None
        self.parent = parent
        panel = wx.Panel(self)
        sizer = wx.BoxSizer(wx.VERTICAL)
        if parent.dlg_prev_value != 0:
            sizer.Add(wx.StaticText(
                panel, -1, "Answer from previous Dialog: %s" % parent.dlg_prev_value))
        sizer.Add(wx.StaticText(panel, -1, "Try to scroll in Different Frame to "
                                "see if it is blocked"))
        btn1 = wx.Button(panel, -1, "Step "+str(self.chain_no))
        btn2 = wx.Button(panel, -1, "Previous")
        btn3 = wx.Button(panel, -1, "Cancel")
        self.Bind(wx.EVT_BUTTON, self.OnStep, id=btn1.GetId())
        self.Bind(wx.EVT_BUTTON, self.OnPrev, id=btn2.GetId())
        self.Bind(wx.EVT_BUTTON, self.OnCancel, id=btn3.GetId())
        sizer.Add(btn1)
        sizer.Add(btn2)
        sizer.Add(btn3)
        panel.SetSizer(sizer)
        self.Show()

    def Action(self):
        #Keep this dialog frame open until an action has occurred.
        #The action will be from OnStep, OnPrev or OnCancel
        #Use Yield to allow other frames to be viewed
        #This pseudo-dialog returns self of relevance chain_no and result
        while self.chain_no == self.saved_chain_no:
            time.sleep(0.2)
            wx.GetApp().Yield()
        self.Close()
        return self

    def OnStep(self, evt):
        self.chain_no += 1
        #Return some relevant value or other
        self.result = int(time.time())

    def OnPrev(self, evt):
        self.chain_no -= 1
        #Return some relevant value or other (I'm just using time)
        self.result = int(time.time())

    def OnCancel(self, evt):
        self.chain_no = -1

app = wx.App()
frame = MyFrame(None)
frame.Show()
app.MainLoop()

【讨论】:

  • 如果不显示为对话框,它就像一个框架。我显示 2 个对话框只是为了显示 2 个任务,后者需要等待前者在它开始之前完成。我真的需要对话框的工作方式,但仍然需要一种方法来查看不相关的窗口(“不同的框架”)。
  • @HuongOrchid 查看我的改编答案,这绝不是完成的文章,但可能会有所帮助
  • 我有一种方法可以解决这个问题,即将 OnOpenDialog() 拆分为 2 个函数并仅阻止父框架。我稍后会发布它。但是您的回答似乎比我的更能阻止父框架。但是,它太复杂了,无法立即理解。当我明白我会接受它作为我问题的解决方案时。谢谢。
【解决方案2】:

来自萨克森的罗尔夫的回答很有趣,所以我把它作为我的解决方案。对于这个问题,我还有另一个更简单的答案,我将函数 OnOpenDialog() 拆分,以便它可以在 ParentFrame 中等待。这个想法实际上也来自萨克森卷的第一个答案。我添加了当它的 MyDialogs 打开而其他不受影响(MyFrame 或不同的 Frame)时 ParentFrame 被阻止的选项。这个答案不是很干净但很简单,我不必对代码进行太多更改。

import wx

class NonrelatedFrame(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent, -1, "Non-related Frame", size=(300, 300))
        panel = wx.Panel(self)
        sizer = wx.BoxSizer(wx.VERTICAL)
        btn = wx.Button(panel, -1, "open frame")
        self.Bind(wx.EVT_BUTTON, self.OnOpenFrame, id=btn.GetId())
        sizer.Add(btn)
        text = "This is a line.\n" * 100
        txtCtrl = wx.TextCtrl(panel, -1, text, style=wx.TE_MULTILINE,
                              size=(200,200))
        sizer.Add(txtCtrl)
        panel.SetSizer(sizer)
        self.Centre()
        self.Show()

    def OnOpenFrame(self, evt):
        ParentFrame()

class ParentFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, title="Parent Frame",size=(500, 500))
        self.passActivate = None
        panel = wx.Panel(self)
        btn = wx.Button(panel, -1, "open dialog")
        self.Bind(wx.EVT_BUTTON, self.OnOpenDialog, id=btn.GetId())
        self.Centre()
        self.Show()
        self.Bind(wx.EVT_ACTIVATE, self.OnActivate)

    def OnActivate(self, evt):
        # use self.passActivate and OnActivate() to help make user not
        # be able to access anything on this form
        if self.passActivate is not None:
            self.passActivate.Raise()

    def OnOpenDialog(self, event):
        dlg1 = MyDialog(self, func2call=self.OnOpenDialog2)
        dlg1.Show()

    def OnOpenDialog2(self, val):
        dlg2 = MyDialog(self, prevAnsw=val, lastDialog=True)
        dlg2.Show()

class MyDialog(wx.Dialog): 
    def __init__(self, parent, prevAnsw=None, lastDialog=False, func2call=None): 
        wx.Dialog.__init__(self, parent, -1, title = "Dialog", style=wx.CAPTION)
        self.parent = parent
        self.parent.passActivate = self
        self.lastDialog = lastDialog
        self.func2call = func2call
        panel = wx.Panel(self)
        sizer = wx.BoxSizer(wx.VERTICAL)
        if prevAnsw is not None:
            sizer.Add(wx.StaticText(
                panel, -1, "Answer from previous Dialog: %s" % prevAnsw))
        sizer.Add(wx.StaticText(panel, -1, "Try to scroll in Non-related Frame to "
                                "see if it is blocked"))
        btn1 = wx.Button(panel, -1, "Pass 1")
        self.Bind(wx.EVT_BUTTON, self.OnPass1, id=btn1.GetId())
        sizer.Add(btn1)

        btn2 = wx.Button(panel, -1, "Pass 2")
        self.Bind(wx.EVT_BUTTON, self.OnPass2, id=btn2.GetId())
        sizer.Add(btn2)
        panel.SetSizer(sizer)

    def OnPass1(self, evt):
        if not self.lastDialog:
            self.func2call(1)
        else:
            self.parent.passActivate = None
        self.Close()


    def OnPass2(self, evt):
        if not self.lastDialog:
            self.func2call(2)
        else:
            self.parent.passActivate = None
        self.Close()


app = wx.App(0)
frame = NonrelatedFrame(None)
frame.Show()
app.MainLoop()

【讨论】:

  • 我不认为你的passActivate 正在做你认为它正在做的事情。您仍然可以同时访问 open frameopen dialog 按钮。
  • 我试过这个。我可以访问“打开框架”按钮但不能访问“打开对话框”按钮,因为当任何对话框打开时,对父框架的任何访问都将传递给对话框。当对话框打开时阻止父框架并保持非相关框架畅通正是我想要的。如果我需要像您一样禁用“打开框架”按钮,这不是问题。但是,我更喜欢您的解决方案,因为我可以更轻松地处理不同的场景。非常感谢。
猜你喜欢
  • 2023-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多