wxPython подпроцесс. ProgressDialog не работает в Windows

#windows #multithreading #user-interface #wxpython #subprocess

#Windows #многопоточность #пользовательский интерфейс #wxpython #подпроцесс

Вопрос:

У меня есть приложение с графическим интерфейсом, которое запускает некоторые команды с помощью подпроцесса, а затем показывает ход выполнения этих команд путем чтения из подпроцесса.Popen.стандартный вывод и использование wx.ProgressDialog. Я написал приложение под Linux, и оно работает там безупречно, но сейчас я провожу некоторое тестирование под Windows, и кажется, что попытка обновить диалоговое окно прогресса приводит к зависанию приложения. Нет сообщений об ошибках или чего-то еще, поэтому мне трудно понять, что происходит. Ниже приведен упрощенный код:

Подпроцесс запускается в отдельном потоке этим методом в основном потоке:

 def onOk(self,event):        
    """ Starts processing """
    self.infotxt.Clear()
    args = self.getArgs()
    self.stringholder = args['outfile']                

    if (args):                       
        cmd = self.buildCmd(args, True)

        if (cmd):
            # Make sure the output directory is writable.
            if not self.isWritable(args['outfile']):
                print "Cannot write to %s. Make sure you have write permission or select a different output directory." %os.path.dirname(args['outfile'])
            else:                
                try:
                    self.thread = threading.Thread(target=self.runCmd,args=(cmd,))
                    self.thread.setDaemon(True)
                    self.thread.start()                    
                except Exception:
                    sys.stderr.write('Error starting thread')
 

И вот метод runCmd:

 def runCmd(self, cmd):
    """ Runs a command line provided as a list of arguments """
    temp = []
    aborted = False
    dlg = None        
    for i in cmd:
        temp.extend(i.split(' '))

    # Use wx.MutexGuiEnter()/MutexGuiLeave() for anything that accesses GUI from another thread                       
    wx.MutexGuiEnter() 
    max = 100
    stl = wx.PD_CAN_ABORT | wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME | wx.PD_REMAINING_TIME
    dlg = wx.ProgressDialog("Please wait", "Processing...", maximum = max, parent = self.frame, style=stl)                               
    wx.MutexGuiLeave()             

    # This is for windows to not display the black command line window when executing the command
    if os.name == 'nt':
        si = subprocess.STARTUPINFO()
        si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
        si.wShowWindow = subprocess.SW_HIDE
    else:
        si = None        

    try:
        proc = subprocess.Popen(temp, shell=False, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)            
    except Exception:
        sys.stderr.write('Error executing a command. ')           

    # Progress dialog    
    count = 0

    while True: 
        line=proc.stdout.readline()
        count  = 1           

        wx.MutexGuiEnter()
        if dlg.Update(count) == (True, False):
            print line.rstrip()
            wx.MutexGuiLeave()
            if not line: break
        else:
            print "Processing cancelled."
            aborted = True
            wx.MutexGuiLeave()
            proc.kill()
            break 

    wx.MutexGuiEnter()
    dlg.Destroy()
    wx.GetApp().GetTopWindow().Raise()           
    wx.MutexGuiLeave() 

    if aborted:            
        if os.path.exists(self.stringholder):
            os.remove(self.stringholder)

    dlg.Destroy()
    proc.wait()
 

Опять же, это отлично работает в Linux, но зависает в Windows. Если я удалю dlg.Строка Update() также работает нормально. Вывод подпроцесса выводится в главном окне и отображается ProgressDialog, просто панель прогресса не перемещается. Чего мне не хватает?

Комментарии:

1. Было бы здорово, если бы вы могли сузить свою проблему до очень маленького приложения, которое мы можем запустить на наших компьютерах.

2. Я попытаюсь сделать это позже, если не смогу найти решение, однако вам может понадобиться какое-нибудь простое приложение на C для его тестирования. Команды, которые я выполняю в подпроцессе, были изменены, чтобы избежать какой-либо буферизации, поэтому proc.stdout.readline() получает вывод сразу после его создания. В противном случае вывод недоступен до завершения команды из-за реализации C stdout, которая использует полную буферизацию при записи в канал. Это можно решить в Linux с помощью ‘unbuffer’, но в Windows требуется внести некоторые изменения в код C. Я вернусь позже, если не смогу решить проблему с помощью wx.CallAfter.

Ответ №1:

Попробуйте не использовать wx.MutexGuiEnter и wx.MutexGuiLeave . Вы можете обрабатывать обновление графического интерфейса из другого потока, используя wx.CallAfter . Я никогда раньше не видел, чтобы кто-нибудь использовал эти мьютексы в приложении wx, и даже в руководствах или примерах использования потоков.

Комментарии:

1. Спасибо, это то, что я сейчас изучаю.

2. ХОРОШО. Кстати, есть удобный модуль для создания новых событий wx.lib.newevent . Мой будет удобен в вашем случае…