前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >python gui中线程的调用

python gui中线程的调用

作者头像
用户5760343
发布2022-05-13 10:36:38
3360
发布2022-05-13 10:36:38
举报
文章被收录于专栏:sktj

""" ################################################################################# System-wide thread interface utilities for GUIs.

Implements a single thread callback queue and checker timer loop shared by all the windows in a program; worker threads queue their exit and progress actions to be run in the main thread; this doesn't block the GUI - it just spawns operations and manages and dispatches exits and progress; worker threads can overlap freely with the main thread, and with other workers.

Using a queue of callback functions and arguments is more useful than a simple data queue if there can be many kinds of threads running at the same time - each kind may have different implied exit actions.

Because GUI API is not completely thread-safe, instead of calling GUI update callbacks directly after thread main action, place them on a shared queue, to be run from a timer loop in the main thread, not a child thread; this also makes GUI update points less random and unpredictable; requires threads to be split into main action, exit actions, and progress action.

Assumes threaded action raises an exception on failure, and has a 'progress' callback argument if it supports progress updates; also assumes callbacks are either short-lived or update as they run, and that queue will contain callback functions (or other callables) for use in a GUI app - requires a widget in order to schedule and catch 'after' event loop callbacks; to use this model in non-GUI contexts, could use simple thread timer instead. ################################################################################# """

run even if no threads # in standard lib now

try: # raise ImportError to import _thread as thread # run with GUI blocking except ImportError: # if threads not available import _dummy_thread as thread # same interface, no threads

shared cross-process queue

named in shared global scope, lives in shared object memory

import queue, sys threadQueue = queue.Queue(maxsize=0) # infinite size

#################################################################################

IN MAIN THREAD - periodically check thread completions queue; run implied GUI

actions on queue in this main GUI thread; one consumer (GUI), and multiple

producers (load, del, send); a simple list may suffice too: list.append and

pop atomic?; 4E: runs at most N actions per timer event: looping through all

queued callbacks on each timer event may block GUI indefinitely, but running

only one can take a long time or consume CPU for timer events (e.g., progress);

assumes callback is either short-lived or updates display as it runs: after a

callback run, the code here reschedules and returns to event loop and updates;

because this perpetual loop runs in main thread, does not stop program exit;

#################################################################################

def threadChecker(widget, delayMsecs=100, perEvent=1): # 10x/sec, 1/timer for i in range(perEvent): # pass to set speed try: (callback, args) = threadQueue.get(block=False) # run <= N callbacks except queue.Empty: break # anything ready? else: callback(*args) # run callback here

代码语言:javascript
复制
widget.after(delayMsecs,                                 # reset timer event
    lambda: threadChecker(widget, delayMsecs, perEvent)) # back to event loop

#################################################################################

IN A NEW THREAD - run action, manage thread queue puts for exits and progress;

run action with args now, later run on* calls with context; calls added to

queue here are dispatched in main thread only, to avoid parallel GUI updates;

allows action to be fully ignorant of use in a thread here; avoids running

callbacks in thread directly: may update GUI in thread, since passed func in

shared memory called in thread; progress callback just adds callback to queue

with passed args; don't update in-progress counters here: not finished till

exit actions taken off queue and dispatched in main thread by threadChecker;

#################################################################################

def threaded(action, args, context, onExit, onFail, onProgress): try: if not onProgress: # wait for action in this thread action(args) # assume raises exception if fails else: def progress(any): threadQueue.put((onProgress, any + context)) action(progress=progress, *args) except: threadQueue.put((onFail, (sys.exc_info(), ) + context)) else: threadQueue.put((onExit, context))

def startThread(action, args, context, onExit, onFail, onProgress=None): thread.start_new_thread( threaded, (action, args, context, onExit, onFail, onProgress))

#################################################################################

a thread-safe counter or flag: useful to avoid operation overlap if threads

update other shared state beyond that managed by the thread callback queue

#################################################################################

class ThreadCounter: def init(self): self.count = 0 self.mutex = thread.allocate_lock() # or use Threading.semaphore def incr(self): self.mutex.acquire() # or with self.mutex: self.count += 1 self.mutex.release() def decr(self): self.mutex.acquire() self.count -= 1 self.mutex.release() def len(self): return self.count # True/False if used as a flag

#################################################################################

self-test code: split thread action into main, exits, progress

#################################################################################

if name == 'main': # self-test code when run import time # or PP4E.Gui.Tour.scrolledtext from tkinter.scrolledtext import ScrolledText

代码语言:javascript
复制
def onEvent(i):                             # code that spawns thread
    myname = 'thread-%s' % i
    startThread(
        action     = threadaction,
        args       = (i, 3),
        context    = (myname,),
        onExit     = threadexit,
        onFail     = threadfail,
        onProgress = threadprogress)

# thread's main action
def threadaction(id, reps, progress):       # what the thread does
    for i in range(reps):
        time.sleep(1)
        if progress: progress(i)            # progress callback: queued
    if id % 2 == 1: raise Exception         # odd numbered: fail

# thread exit/progress callbacks: dispatched off queue in main thread
def threadexit(myname):
    text.insert('end', '%s\texit\n' % myname)
    text.see('end')

def threadfail(exc_info, myname):
    text.insert('end', '%s\tfail\t%s\n' % (myname, exc_info[0]))
    text.see('end')

def threadprogress(count, myname):
    text.insert('end', '%s\tprog\t%s\n' % (myname, count))
    text.see('end')
    text.update()   # works here: run in main thread

# make enclosing GUI and start timer loop in main thread
# spawn batch of worker threads on each mouse click: may overlap

text = ScrolledText()
text.pack()
threadChecker(text)                 # start thread loop in main thread
text.bind('<Button-1>',             # 3.x need list for map, range ok
          lambda event: list(map(onEvent, range(6))) )
text.mainloop()                     # pop-up window, enter tk event loop

tests thread callback queue, but uses class bound methods for action and callbacks

import time from tkinter.scrolledtext import ScrolledText

class MyGUI: def init(self, reps=3): self.reps = reps # uses default Tk root self.text = ScrolledText() # save widget as state self.text.pack() threadChecker(self.text) # start thread check loop self.text.bind('<Button-1>', # 3.x need list for map, range ok lambda event: list(map(self.onEvent, range(6))))

代码语言:javascript
复制
def onEvent(self, i):  # code that spawns thread
    myname = 'thread-%s' % i
    startThread(
        action=self.threadaction,
        args=(i,),
        context=(myname,),
        onExit=self.threadexit,
        onFail=self.threadfail,
        onProgress=self.threadprogress)

# thread's main action
def threadaction(self, id, progress):  # what the thread does
    for i in range(self.reps):  # access to object state here
        time.sleep(1)
        if progress: progress(i)  # progress callback: queued
    if id % 2 == 1: raise Exception  # odd numbered: fail

# thread callbacks: dispatched off queue in main thread
def threadexit(self, myname):
    self.text.insert('end', '%s\texit\n' % myname)
    self.text.see('end')

def threadfail(self, exc_info, myname):  # have access to self state
    self.text.insert('end', '%s\tfail\t%s\n' % (myname, exc_info[0]))
    self.text.see('end')

def threadprogress(self, count, myname):
    self.text.insert('end', '%s\tprog\t%s\n' % (myname, count))
    self.text.see('end')
    self.text.update()  # works here: run in main thread

if name == 'main': MyGUI().text.mainloop()

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-05-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • run even if no threads # in standard lib now
  • shared cross-process queue
  • named in shared global scope, lives in shared object memory
  • IN MAIN THREAD - periodically check thread completions queue; run implied GUI
  • actions on queue in this main GUI thread; one consumer (GUI), and multiple
  • producers (load, del, send); a simple list may suffice too: list.append and
  • pop atomic?; 4E: runs at most N actions per timer event: looping through all
  • queued callbacks on each timer event may block GUI indefinitely, but running
  • only one can take a long time or consume CPU for timer events (e.g., progress);
  • assumes callback is either short-lived or updates display as it runs: after a
  • callback run, the code here reschedules and returns to event loop and updates;
  • because this perpetual loop runs in main thread, does not stop program exit;
  • IN A NEW THREAD - run action, manage thread queue puts for exits and progress;
  • run action with args now, later run on* calls with context; calls added to
  • queue here are dispatched in main thread only, to avoid parallel GUI updates;
  • allows action to be fully ignorant of use in a thread here; avoids running
  • callbacks in thread directly: may update GUI in thread, since passed func in
  • shared memory called in thread; progress callback just adds callback to queue
  • with passed args; don't update in-progress counters here: not finished till
  • exit actions taken off queue and dispatched in main thread by threadChecker;
  • a thread-safe counter or flag: useful to avoid operation overlap if threads
  • update other shared state beyond that managed by the thread callback queue
  • self-test code: split thread action into main, exits, progress
  • tests thread callback queue, but uses class bound methods for action and callbacks
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档