python - Updating a TKinter GUI from a multiprocessing calculation -


i'm creating gui python simulator. gui provides tools set simulation , run it. while simulation running want pass progress information gui , have displayed on label in simulation_frame. because simulations need run multiprocessing, i'm using queue pass updated information gui.

the way have set up, running simulations blocks tk mainloop since need able close pool @ end of call. i'm calling update_idletasks() force gui update progress information.

this seems me inelegant , potentially risky solution. moreover, while works in ubuntu, not seem work in windows xp--the window goes blank after second or of running. may able make work in windows calling update() rather update_idletasks(), seems worse me.

is there better solution?

the relevant code:

sims = [] queues = [] svars = [] names = [] = 0 manager = mp.manager() config in self.configs:     name, file, num = config.get()     j = 0     _ in range(num):         #progress monitor label         q = manager.queue()         s_var = stringvar()         label = label(self.sim_frame, textvariable = s_var, bg = "white")         s_var.set("%d: not started"%i)         label.grid(row = i, column = 0, sticky = w+n)         self.sim_labels.append(label)         queues.append(q)         svars.append(s_var)         names.append("%s-%d"%(name, j))         sims.append(("%s-%d"%(name, j),file, data, verbose, q))         += 1         j += 1 self.update()  # progress tracking pretty hacky.  pool = mp.pool(parallel) num_sims = len(sims) #start simulating tracker = pool.map_async(run_1_sim,sims) while not tracker.ready():     pass     in range(num_sims):         q = queues[i]         try:             gen = q.get(timeout = .001)             # if sim has updated, update label             #print gen             svars[i].set(gen)             self.update()         except empty:             pass # results of map, if necessary tracker.get()      def update(self):         """         redraws         """         self.master.update_idletasks()  def run_1_sim(args):     """     runs 1 simulation specified args, output updates supplied     pipe every generation     """     name,config,data, verbose, q = args     sim = simulation(config, name=name, data = data)     generation = 0     q.put(sim.name + ": 0")     try:         while sim.run(verbose=verbose, log=true, generations = sim_step):             generation += sim_step             q.put(sim.name + ": " + str(generation))     except exception err:         print err 

this may or may not helpful you, possible make tkinter thread-safe ensuring code , methods executed on particular thread root instantiated on. 1 project experimented concept can found on over python cookbook recipe 577633 (directory pruner 2). code below comes lines 76 - 253 , easy extend widgets.


primary thread-safety support

# import several gui libraries. import tkinter.ttk import tkinter.filedialog import tkinter.messagebox  # import other needed modules. import queue import _thread import operator  ################################################################################  class affinityloop:      "restricts code execution thread instance created on."      __slots__ = '__action', '__thread'      def __init__(self):         "initialize affinityloop job queue , thread identity."         self.__action = queue.queue()         self.__thread = _thread.get_ident()      def run(self, func, *args, **keywords):         "run function on creating thread , return result."         if _thread.get_ident() == self.__thread:             self.__run_jobs()             return func(*args, **keywords)         else:             job = self.__job(func, args, keywords)             self.__action.put_nowait(job)             return job.result      def __run_jobs(self):         "run pending jobs in job queue."         while not self.__action.empty():             job = self.__action.get_nowait()             job.execute()      ########################################################################      class __job:          "store information run job @ later time."          __slots__ = ('__func', '__args', '__keywords',                      '__error', '__mutex', '__value')          def __init__(self, func, args, keywords):             "initialize job's info , ready execution."             self.__func = func             self.__args = args             self.__keywords = keywords             self.__error = false             self.__mutex = _thread.allocate_lock()             self.__mutex.acquire()          def execute(self):             "run job, store error, , return sender."             try:                 self.__value = self.__func(*self.__args, **self.__keywords)             except exception error:                 self.__error = true                 self.__value = error             self.__mutex.release()          @property         def result(self):             "return execution result or raise error."             self.__mutex.acquire()             if self.__error:                 raise self.__value             return self.__value  ################################################################################  class _threadsafe:      "create thread-safe gui class safe cross-threaded calls."      root = tkinter.tk      def __init__(self, master=none, *args, **keywords):         "initialize thread-safe wrapper around gui base class."         if master none:             if self.base not self.root:                 raise valueerror('widget must have master!')             self.__job = affinityloop() # use affinity() if not break.             self.__schedule(self.__initialize, *args, **keywords)         else:             self.master = master             self.__job = master.__job             self.__schedule(self.__initialize, master, *args, **keywords)      def __initialize(self, *args, **keywords):         "delegate instance creation later time if necessary."         self.__obj = self.base(*args, **keywords)      ########################################################################      # provide framework delaying method execution when needed.      def __schedule(self, *args, **keywords):         "schedule execution of method till later if necessary."         return self.__job.run(self.__run, *args, **keywords)      @classmethod     def __run(cls, func, *args, **keywords):         "execute function after converting arguments."         args = tuple(cls.unwrap(i) in args)         keywords = dict((k, cls.unwrap(v)) k, v in keywords.items())         return func(*args, **keywords)      @staticmethod     def unwrap(obj):         "unpack inner objects wrapped _threadsafe instances."         return obj.__obj if isinstance(obj, _threadsafe) else obj      ########################################################################      # allow access , manipulation of wrapped instance's settings.      def __getitem__(self, key):         "get configuration option underlying object."         return self.__schedule(operator.getitem, self, key)      def __setitem__(self, key, value):         "set configuration option on underlying object."         return self.__schedule(operator.setitem, self, key, value)      ########################################################################      # create attribute proxies methods , allow execution.      def __getattr__(self, name):         "create requested attribute , return cached result."         attr = self.__attr(self.__callback, (name,))         setattr(self, name, attr)         return attr      def __callback(self, path, *args, **keywords):         "schedule execution of named method attribute proxy."         return self.__schedule(self.__method, path, *args, **keywords)      def __method(self, path, *args, **keywords):         "extract method , run provided arguments."         method = self.__obj         name in path:             method = getattr(method, name)         return method(*args, **keywords)      ########################################################################      class __attr:          "save attribute's name , wait execution."          __slots__ = '__callback', '__path'          def __init__(self, callback, path):             "initialize proxy callback , method path."             self.__callback = callback             self.__path = path          def __call__(self, *args, **keywords):             "run known method given arguments."             return self.__callback(self.__path, *args, **keywords)          def __getattr__(self, name):             "generate proxy object sub-attribute."             if name in {'__func__', '__name__'}:                 # hack "tkinter.__init__.misc._register" method.                 raise attributeerror('this not real method!')             return self.__class__(self.__callback, self.__path + (name,))  ################################################################################  # provide thread-safe classes used tkinter.  class tk(_threadsafe): base = tkinter.tk class frame(_threadsafe): base = tkinter.ttk.frame class button(_threadsafe): base = tkinter.ttk.button class entry(_threadsafe): base = tkinter.ttk.entry class progressbar(_threadsafe): base = tkinter.ttk.progressbar class treeview(_threadsafe): base = tkinter.ttk.treeview class scrollbar(_threadsafe): base = tkinter.ttk.scrollbar class sizegrip(_threadsafe): base = tkinter.ttk.sizegrip class menu(_threadsafe): base = tkinter.menu class directory(_threadsafe): base = tkinter.filedialog.directory class message(_threadsafe): base = tkinter.messagebox.message 

if read rest of application, find built widgets defined _threadsafe variants used seeing in other tkinter applications. method calls come in various threads, automatically held until becomes possible execute calls on creating thread. note how mainloop replaced way of lines 291 - 298 , 326 - 336.


notice nodefaltroot & main_loop calls

@classmethod def main(cls):     "create application containing single trimdirview widget."     tkinter.nodefaultroot()     root = cls.create_application_root()     cls.attach_window_icon(root, icon)     view = cls.setup_class_instance(root)     cls.main_loop(root) 

main_loop allows threads execute

@staticmethod def main_loop(root):     "process gui events according tkinter's settings."     target = time.clock()     while true:         try:             root.update()         except tkinter.tclerror:             break         target += tkinter._tkinter.getbusywaitinterval() / 1000         time.sleep(max(target - time.clock(), 0)) 


Comments

Popular posts from this blog

c# - SharpSVN - How to get the previous revision? -

c++ - Is it possible to compile a VST on linux? -

url - Querystring manipulation of email Address in PHP -