Kivy - 弹出窗口 A 显示在弹出窗口 B 之上

Kivy - popup A shows over popup B

问题

我有一个循环,在每个段落中都显示一个弹出窗口。我将其称为 popup_A。在循环中有一个条件,当满足时它会同时触发另一个弹出窗口和线程中的一个方法。我将第二个弹出窗口称为 popup_B。问题是它确实显示了 popup_B,但紧接着 popup_A 显示在 [=108= 之上,完全覆盖了它。为了更好地了解流程:

def myFun:
    if condition1:
        method1
    if condition2:
        method2
    if condition3:
        show popup_B
        thread method3
    thread popup_A

def popup_A:
    do something
    display message_A
    call myFun
   
def popup_B:
    display message_B

代码 循环涉及的方法:

def goForward(self):
        if  self.header == "someTask":
            if "tasks" in self.data:    # check if the request has "tasks" in the body
                if self.counter < len(self.data["tasks"]):  # check the counter
                    self.code = self.data["tasks"][self.counter].get("code")
                    action = self.data["tasks"][self.counter].get("actionDescription")
                    
                    if "myCondition" in str(action):
                            
                        #set the popup structure
                        self.popup_B = ActivityBox(self)
                        self.popup_B.open()
                        
                        # run the method in a thread
                        t1 = threading.Thread(target = timeConsumingMethod)
                        t1.start()
                    
                    # dismiss the popup ActivityBox     
                    self.popup_B.dismiss()

                    # call popup_A in thread
                    t3 = threading.Thread(target = self.popup_A)
                    t3.start()
                    
def do(self):
    self.counter = self.counter + 1
    self.popup.dismiss()
    self.goForward()

def cancel(self):
    self.counter = self.counter + 1
    self.popup.dismiss()
    self.goForward()

def popup_A(self):
    self.popup = MessageBox(self)
    self.popup.open()

Builder.load_string() 中的 ActivityBox 和 MessageBox 弹出结构:

<MessageBox>:
    size_hint: 1, .7
    auto_dismiss: False
    title: "MessageBoxTitle"
    title_align: "center"
    title_size: 30

    BoxLayout:
        orientation: "vertical"
        Label:
            font_size: '30sp'
            text: "MessageBoxLabel"
        BoxLayout:
            orientation: "horizontal"
            spacing: 10
            size_hint: 1, .5
            Button:
                font_size: 50
                background_color: 0,204,0,1
                text: "CONFIRM"
                on_press:
                    self.disabled = True
                    self.background_color = 0,255,0,1
                    app.do()
                    root.dismiss()
            Button:
                font_size: 50
                background_color: 204,0,0,1
                text: "CANCEL"
                on_press:
                    self.background_color = 255,0,0,1
                    app.cancel()
                    root.dismiss()

<ActivityBox>:
    size_hint: 1, .7
    auto_dismiss: False
    title: "ActivityBoxTitle"
    title_align: "center"
    title_size: 30

    BoxLayout:
        orientation: "vertical"
        Label:
            font_size: '30sp'
            text: "ActivityBoxLabel"
        BoxLayout:
            orientation: "horizontal"
            spacing: 10
            size_hint: 1, .5

代码说明 主循环的组件是 goForward 和 popup_A。随着循环的每一段, popup_A 出现,在线程中调用。然后 popup_A 回调 goForward。如果满足 goForward 中的条件“工作”,则显示“进行中的工作”popup_B。 popup_B 运行 与线程中的方法一起使用,否则 Kivy 不会显示弹出窗口(锁定 GUI)。

结果

到目前为止我已经尝试过:

  1. 运行 popup_B in a thread t1 = threading.Thread(target = self.popup.open): popup_A 覆盖 popup_B.
  2. 使用线程.join():popup_A出现但popup_B不出现,.join()忽略它。
  3. 运行 popup_B 和 timeConsumingMethod 一起在一个线程中: popup_A 出现但 popup_B 没有出现。
  4. 运行 timeConsumingMethod 作为进程:popup_A 出现但 popup_B 不出现,程序挂起。
  5. 使用 mutex = threading.Lock() 锁定带有 popup_B 的线程:popup_A 出现在 popup_B 之上。 GUI 也会出现乱码。
  6. 运行 popup_A 不在一个线程中 运行 popup_B 和 timeConsumingMethod 一起在一个线程中: popup_A 出现popup_B.

问题

popup_A 只有在线程中的方法完成并且 popup_B 被关闭后才能显示。如何防止 popup_A 覆盖 popup_B?

我查看了下面的帖子,但没有找到解决方案。

--- 更新 20200715 -------------------------------- --------------

在代码中,我在 popup_B 中将弹出窗口重命名为“进行中”,在 popup_A[=87 中重命名弹出窗口 2 =] 以便更好地理解。

--- 更新 20200716 -------------------------------- --------------

我使用 Clock.schedule_once 修改了代码 step2(线程 popup_Astep3(线程 timeConsumingMethodpopup_B ). popup_B 上升但 popup_A 覆盖它直到最后一个 popup_A 被按钮关闭。等待 timeConsumingMethod 在没有 [=26= 的情况下完成] 启动,我使用 while loop。代码如下:

def goForward(self):
        if  self.header == "someTask":
            if "tasks" in self.data:    # check if the request has "tasks" in the body
                if self.counter < len(self.data["tasks"]):  # check the counter
                    self.code = self.data["tasks"][self.counter].get("code")
                    action = self.data["tasks"][self.counter].get("actionDescription")
                    
                    if "myCondition" in str(action):
                        self.returnStatus = 1 # set status  
                        #the two rows below give the same result
                        #self.popup_B = ActivityBox(self)
                        #self.popup_B.open()

                        Clock.schedule_once(self.step3)
                        
                        # run the method in a thread
                        t1 = threading.Thread(target = self.step1)
                        t1.start()

                        while self.returnStatus != 0:
                            time.sleep(1)
                    
                    Cloch.schedule_once(self.step2)

def step1(self):
    ts1 = threading.Thread(target = self.timeConsumingMethod)
    ts1.start()
    ts1.join()
    self.returnStatus = 0 # change status when over
    return(self.returnStatus)

def step2(self, *args):
    ts2 = threading.Thread(target = self.popup_A)
    ts2.start()

def step3(self, *args):     
    #set the popup structure
    self.popup = ActivityBox(self)
    self.popup.open()
    
def popup_A(self):
    self.popup = MessageBox(self)
    t3 = threading.Thread(target = self.popup.open)
    t3.start()

def do(self):
    self.counter = self.counter + 1
    self.popup.dismiss()
    self.goForward()

def cancel(self):
    self.counter = self.counter + 1
    self.popup.dismiss()
    exit()
  1. Correct way to implement loading popup in kivy app

让 popup_A 在耗时线程完成之前不弹出的一种方法是在耗时线程结束时调用 popup_A。尝试将 goForward() 方法分成两部分。第一部分几乎没有变化:

def goForward(self):
        if  self.header == "someTask":
            if "tasks" in self.data:    # check if the request has "tasks" in the body
                if self.counter < len(self.data["tasks"]):  # check the counter
                    self.code = self.data["tasks"][self.counter].get("code")
                    action = self.data["tasks"][self.counter].get("actionDescription")
                    
                    if "myCondition" in str(action):
                            
                        #set the popup structure
                        self.popup_B = ActivityBox(self)
                        self.popup_B.open()
                        
                        # run method in a thread
                        t1 = threading.Thread(target = self.timeConsumingMethod)
                        t1.start()

我已将 timeConsumingMethod() 移至 class,因此需要在 Thread 中调用 self.timeConsumingMethod

然后将其余的旧goForward()方法放在一个单独的方法中(我称之为step2()):

def step2(self, *args):
    # dismiss the popup ActivityBox
    self.popup_B.dismiss()

    # call popup_A in thread
    t3 = threading.Thread(target=self.popup_A)
    t3.start()

然后在timeConsumingMethod()中,完成后调用step2()

def timeConsumingMethod(self):
    time.sleep(5)
    Clock.schedule_once(self.step2)

解决方案

我已经使用线程解决了弹出窗口重叠的问题。我想问题出在共享内存和解释器锁上。

代码

我修改了代码并将 threading.Thread 添加到绑定到弹出窗口按钮的方法 do(self)cancel(self) 中。 step1 在线程中运行 timeConsumingMethodstep2 调用在线程中运行的 popup_A,step3 在线程中运行 popup_B一个线程。这样 popup_A 确实显示并在 popup_B 出现时被解雇。

def goForward(self):
        if  self.header == "someTask":
            if "tasks" in self.data:    # check if the request has "tasks" in the body
                if self.counter < len(self.data):   # check the counter
                    if "myCondition":
                        self.step3()
                        self.step1()
                    self.popup_A()

def step1(self):
    self.popup_A.dismiss()  # dismiss the popup A first
    ts1 = threading.Thread(target = self.timeConsumingMethod)
    ts1.start()
    ts1.join()
    self.popup_B.dismiss()  # dismiss the popup B at the end

def step2(self, *args):
    self.popup_A()

def step3(self, *args):     
    #set the popup structure
    self.popup_B = ActivityBox(self)
    ts3 = threading.Thread(target = self.popup_B.open)
    ts3.start()
    ts3.join()
    
def popup_A(self):
    self.popup_A = MessageBox(self)
    t3 = threading.Thread(target = self.popup_A.open)
    t3.start()

def do(self):
    self.counter = self.counter + 1
    self.popup_A.dismiss()  # dismiss the popup A first
    td = threading.Thread(target = self.goForward)
    td.start()

def cancel(self):
    self.counter = self.counter + 1
    self.popup_A.dismiss()  # dismiss the popup A first
    tc = threading.Thread(target = self.goForward)
    tc.start()