wxpython: возможно ли остановить работающую программу, когда я нажму кнопку «Стоп»? - PullRequest
0 голосов
/ 22 декабря 2018

Предположим, у меня есть большая программа, выполнение которой занимает некоторое время, когда она запускается, я обнаружил, что какой-то параметр задан неправильно, поэтому я хочу остановить его.Вместо того, чтобы завершить программу полностью, я хочу, чтобы текущие вычисления были остановлены, но wx.Frame все еще отображается.Является ли это возможным?Ниже приведен пример кода:

import wx
import time

class Test(wx.Frame):

    def __init__(self, title):
        super().__init__(None, title=title)
        self.panel = wx.Panel(self)

        self.initUI()
        self.Centre()

    def initUI(self):
        vbox = wx.BoxSizer(wx.VERTICAL)
        button1 = wx.Button(self.panel, label="START Calculation")
        button1.Bind(wx.EVT_BUTTON, self.start_test)

        button2 = wx.Button(self.panel, label="STOP Calculation")
        button2.Bind(wx.EVT_BUTTON, self.stop_test)

        vbox.Add(button1, 0, wx.ALL, 5)
        vbox.Add(button2, 0, wx.ALL, 5)
        self.panel.SetSizer(vbox)

    def start_test(self, e):
        for i in range(20000):
            print("Program is running...")
            time.sleep(5)

    def stop_test(self, e):
        print("How do I stop the test when click this button?")


if __name__ == '__main__':
    app = wx.App()
    Test("A large program").Show()
    app.MainLoop()

enter image description here

Как показано в программе, когда я нажимаю кнопку запуска, программа запускается.Я надеюсь, что смогу остановить программу, но все равно останусь в окне приложения. На данный момент, когда я нажимаю кнопку запуска, кнопка остановки даже не активируется.Можно ли достичь цели, которую я хочу?

Ответы [ 3 ]

0 голосов
/ 22 декабря 2018

Я просто немного изменил ваш код.Я добавил поток для запуска метода вычисления, чтобы он не блокировал основной поток.

import wx
import time
import threading

class Test(wx.Frame):

    def __init__(self, title):
        super().__init__(None, title=title)
        self.panel = wx.Panel(self)

        self.initUI()
        self.Centre()

    def initUI(self):
        vbox = wx.BoxSizer(wx.VERTICAL)
        button1 = wx.Button(self.panel, label="START Calculation")
        button1.Bind(wx.EVT_BUTTON, self.start_test)

        button2 = wx.Button(self.panel, label="STOP Calculation")
        button2.Bind(wx.EVT_BUTTON, self.stop_test)

        vbox.Add(button1, 0, wx.ALL, 5)
        vbox.Add(button2, 0, wx.ALL, 5)
        self.panel.SetSizer(vbox)
        self.stop = False

    def create_thread(self, target):
        thread = threading.Thread(target=target)
        thread.daemon = True
        thread.start()

    def calculate(self):
        for i in range(20000):
            if self.stop:
                break
            print("Program is running...")
            time.sleep(5)
        self.stop = False

    def start_test(self, e):
        self.create_thread(self.calculate)

    def stop_test(self, e):
        print("The calculation has been stopped!")
        self.stop = True


if __name__ == '__main__':
    app = wx.App()
    Test("A large program").Show()
    app.MainLoop()

Если self.stop имеет значение True, он выпадает из цикла foor.Таким образом, вы можете начать и остановить вычисление, просто нажав две кнопки.

0 голосов
/ 23 декабря 2018

Некоторые люди, похоже, не одобряют этот метод, но я считаю его простым и полезным.
Используйте Yield, который на мгновение возвращает управление главному циклу.
Это позволяет основному циклу оставатьсят. е. ваша кнопка остановки может быть активирована между циклами цикла.

import wx
import time

class Test(wx.Frame):

    def __init__(self, title):
        super().__init__(None, title=title)
        self.panel = wx.Panel(self)

        self.initUI()
        self.Centre()

    def initUI(self):
        vbox = wx.BoxSizer(wx.VERTICAL)
        button1 = wx.Button(self.panel, label="START Calculation")
        button1.Bind(wx.EVT_BUTTON, self.start_test)

        button2 = wx.Button(self.panel, label="STOP Calculation")
        button2.Bind(wx.EVT_BUTTON, self.stop_test)

        vbox.Add(button1, 0, wx.ALL, 5)
        vbox.Add(button2, 0, wx.ALL, 5)
        self.panel.SetSizer(vbox)

    def start_test(self, e):
        self.running = 0
        loop = 0
        while self.running == 0:
            print("Program is running..."+str(loop))
            time.sleep(1)
            loop +=1
            wx.GetApp().Yield()

    def stop_test(self, e):
        print("Process has stopped")
        self.running = 1

if __name__ == '__main__':
    app = wx.App()
    Test("A large program").Show()
    app.MainLoop()
0 голосов
/ 22 декабря 2018

вы не можете сделать это с одним потоком.

Вы должны запустить цикл вычислений в другом потоке, и кнопка останова должна установить флаг, чтобы цикл остановился в какой-то момент (так как вы не можетепринудительно уничтожить поток: Есть ли способ уничтожить поток? . TL; DR? ответ: нет )

Примерно так:

import threading

...

  def run_code(self):
      for i in range(20000):
          print("Program is running...")
          time.sleep(5)
          if self.__stopped:
             break
      self.__stopped = True

  def start_test(self, e):
      if not self.__stopped:
         self.__stopped = False
         threading.Thread(target=run_code).start()

  def stop_test(self, e):
      self.__stopped = True

Теперь, когда вы нажимаете «Пуск», он запускает метод run_code в потоке, поэтому он уступает основному циклу, а кнопка «Стоп» активна.

Поскольку поток выполняетсяв другом методе вы можете поделиться атрибутом __stopped.

Будьте осторожны, время от времени вызывайте time.sleep() вызов (даже небольшой), потому что потоки не используют срезы времени в Python (из-заглобальная блокировка интерпретатора, которую реализует большинство версий Python).Работающий поток должен время от времени отдавать некоторый процессор главному потоку, иначе это не сработает.Ну, вы можете попробовать без предварительного просмотра, и посмотрите.

Также не используйте wx вызовы в потоках.Используйте wx.CallAfter для планирования wx-вызовов из потока, которые будут выполняться в основном потоке.

...