2015-06-20 64 views
6

To zdarza się tylko na Linuksie (również w systemie OS X, nie można przetestować atm), działa poprawnie w systemie Windows.wx.ProgressDialog powodujący błąd seg i/lub błąd GTK_IS_WINDOW podczas niszczenia

Mam wx.ProgressDialog, który jest spawn z głównym wątku. Wysyłam pracę do innego wątku i okresowo oddzwoni do funkcji wywołania zwrotnego w głównym wątku, która zaktualizuje ProgressDialog lub, pod koniec pracy, zniszczy go. Jednak mam ciekawą wiadomość na Linuksie, kiedy to nastąpi:

(python:12728): Gtk-CRITICAL **: IA__gtk_window_set_modal: assertion 'GTK_IS_WINDOW (window)' failed

Okno ma blisko, ale gdy próbuję go tarło ponownie wygląda na to, że już prawie zakończone. Czasami komunikat o błędzie seg również występuje po tym komunikacie.

Próbowałem zasymulować z uproszczoną wersję tutaj.

import wxversion 
wxversion.select("2.8") 
import wx 
import sys 
import threading 

MAX_COUNT = 100 

## This class is in a different area of the codebase and 
class WorkerThread(threading.Thread): 
    def __init__(self, callback): 
     threading.Thread.__init__(self) 
     self.callback = callback 

    def run(self): 
     # simulate work done. IRL, this calls another function in another 
     # area of the codebase. This function would generate an XML document, 
     # which loops through a list of items and creates a set of elements for 
     # each item, calling back after each item. Here, we simply set up a for 
     # loop and simulate work with wx.MilliSleep 
     for i in xrange(MAX_COUNT): 
      print i 
      wx.MilliSleep(30) 
      wx.CallAfter(self.callback, i) 

     # Send done signal to GUI 
     wx.CallAfter(self.callback, -1) 

class Frame(wx.Frame): 
    def __init__(self, title): 
     wx.Frame.__init__(self, None, title=title, pos=(150,150), size=(350,200)) 

     panel = wx.Panel(self) 
     box = wx.BoxSizer(wx.VERTICAL) 

     m_btn = wx.Button(panel, wx.ID_ANY, "Run Stuff") 
     m_btn.Bind(wx.EVT_BUTTON, self.OnRunButton) 
     box.Add(m_btn, 0, wx.ALL, 10) 

     panel.SetSizer(box) 
     panel.Layout() 

    def OnRunButton(self, event): 
     self.progressDialog = wx.ProgressDialog("Doing work", 
          "Doing Work", 
          maximum=MAX_COUNT, parent=self, 
          style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME) 
     self.worker(self.threadCallback) 
     self.progressDialog.ShowModal() 

    def worker(self, callback): 
     # This bit is in another part of the codebase originally. In the test, 
     # I could have added it to OnRunButton, but I wanted function calls to 
     # be similar between test and actual code 
     thread = WorkerThread(callback) 
     thread.start() 

    def threadCallback(self, info): 
     # We update based on position, or destroy if we get a -1 
     if info == -1: 
      self.progressDialog.Destroy() 
     else: 
      self.progressDialog.Update(info) 

app = wx.App(redirect=False) 
top = Frame("ProgressDialog Test") 
top.Show() 
app.MainLoop() 

(możemy wybrać 2.8, ale idealnie dowolny fix powinien działać zarówno w 2,8 i 3,0 I rzeczywiście nie była w stanie przetestuj go w wersji 3.0 w Linuksie z powodu złej wersji 3.0)

To dobrze ilustruje problem: działa dobrze w systemie Windows, ale wykreśla błąd, gdy próbuje zniszczyć okno dialogowe postępu. Jednak nie mogę uzyskać przykładu pokazującego, że próbowałem szukać rozwiązań. Czytałem, że może to wynikać z faktu, że wątek roboczy kończy się zbyt szybko, a zatem pozostawia GUI z wiadomościami w jego kolejce. Nie jestem pewien, czy całkowicie to rozumiem (nigdy nie zrozumiałem kwestii wydajności i komunikatów itp.), Ale wierzę, że oznacza to, że gdy pracownik ma 100%, ProgressDialog (wolniejszy) może być tylko na poziomie 75% i nadal ma dodatkowe 25% wiadomości, których można użyć do "aktualizacji" GUI, ale zamiast tego zostanie zniszczony.

Chciałbym wyjaśnienia, jeśli dobrze rozumiem, czy nie.

Poza tym uważam, że .Hide() działa jak dzieło, ale chciałbym je zniszczyć, ponieważ jest to właściwe.

Bez względu na to, jakakolwiek pomoc byłaby bardzo doceniana. =)

Odpowiedz

1

Próbowałem już twój kod, również wiele modyfikacji próbowano przezwyciężyć ten problem, ale nie powiodło się. W każdym razie, ja stworzył poniższy skrypt wxPython aby spełnić swój cel, patrz poniżej:

import wxversion 
wxversion.select("2.8") # version 3.0 works, too. 
import wx 
import sys 
import threading 
import time 

MAX_COUNT = 200 

class WorkerThread(threading.Thread): 
    def __init__(self, target, countNum): 
     threading.Thread.__init__(self, target = target) 
     self.setDaemon(True) 
     self.cnt = countNum 
     self.target = target 
     self.pb = self.target.pb 

    def run(self): 
     for i in xrange(self.cnt): 
      print i+1 
      wx.MilliSleep(50) 
      wx.CallAfter(self.pb.SetValue, i+1) 

     wx.CallAfter(self.target.MakeModal, False) 
     wx.CallAfter(self.target.Close) 

class ProgressBarFrame(wx.Frame): 
    def __init__(self, parent, title, range = 100) : 
     wx.Frame.__init__(self, parent = parent, title = title) 
     self.range = range 
     self.createProgressbar() 
     self.SetMinSize((400, 10)) 
     self.Centre() 
     self.Show() 
     self.t0 = time.time() 
     self.elapsed_time_timer.Start(1000) 

    def createProgressbar(self): 
     self.pb  = wx.Gauge(self) 
     self.pb.SetRange(range = self.range) 

     self.elapsed_time_st = wx.StaticText(self, label = 'Elapsed Time:') 
     self.elapsed_time_val = wx.StaticText(self, label = '00:00:00') 

     vbox_main = wx.BoxSizer(wx.VERTICAL) 
     hbox_time = wx.BoxSizer(wx.HORIZONTAL) 
     hbox_time.Add(self.elapsed_time_st, 0, wx.ALIGN_LEFT | wx.EXPAND | wx.ALL, 5) 
     hbox_time.Add(self.elapsed_time_val, 0, wx.ALIGN_LEFT | wx.EXPAND | wx.ALL, 5) 
     vbox_main.Add(self.pb, 0, wx.EXPAND | wx.ALL, 5) 
     vbox_main.Add(hbox_time, 0, wx.EXPAND | wx.ALL, 5) 

     self.SetSizerAndFit(vbox_main) 

     self.elapsed_time_timer = wx.Timer(self) 
     self.Bind(wx.EVT_TIMER, self.onTickTimer, self.elapsed_time_timer) 

    def onTickTimer(self, event): 
     fmt='%H:%M:%S' 
     self.elapsed_time_val.SetLabel(time.strftime(fmt, time.gmtime(time.time()-self.t0))) 

class Frame(wx.Frame): 
    def __init__(self, title): 
     wx.Frame.__init__(self, None, title=title, pos=(150,150), size=(350,200)) 

     panel = wx.Panel(self) 
     box = wx.BoxSizer(wx.VERTICAL) 

     m_btn = wx.Button(panel, wx.ID_ANY, "Run Stuff") 
     self.Bind(wx.EVT_BUTTON, self.OnRunButton, m_btn) 
     box.Add(m_btn, 0, wx.ALL, 10) 

     panel.SetSizer(box) 

    def OnRunButton(self, event): 
     self.progressbar = ProgressBarFrame(self, 'Working Processing', MAX_COUNT) 
     self.progressbar.MakeModal(True) 
     worker = WorkerThread(self.progressbar, MAX_COUNT) 
     worker.start() 

app = wx.App(redirect=False) 
top = Frame("ProgressDialog Test") 
top.Show() 
app.MainLoop() 

Używam wx.Gauge robić to, co robi wx.ProgressDialog, a także dodatkową wx.Timer pokazać upływ czasu. Metoda MakeModal() służy do naśladowania efektu ShowModal, który jest domyślnym stylem, który pokazuje Dialog, nie zapomnij zwolnić stanu modalnego przez MakeModal(False) lub ramka zostanie zamrożona. Możesz dodać więcej rzeczy w klasie ProgressBarFrame.

Myślę, że błąd związany z błędem segmentu może wynikać z wywoływania zdarzeń, zwłaszcza jeśli chodzi o problem z numerem multithreading, może być dokładnie sprawdzony w klasie wx.ProgressDialog, by pokazać jakąś wskazówkę.

Screenshot of progressbar demo