#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
. Мой будет удобен в вашем случае…