简体   繁体   中英

Implement multithreading in Python Zelle graphics

I am creating a program which opens a world map in a window using Zelle's graphics.py. It has one function which draws dots on the map, and another function which undraws those dots after they are on the screen for 1 second (which are stored in a list after being drawn). I want these functions to work concurrently, but when the addDots() function is called in a thread it won't draw the dot in the window, it just stalls. Here is the module which I run:

import thread
import threading
import time
import random
import sys
sys.path.append('..')
from Display import map
import tester
import datetime


dots = list(())

def deleteDots():
    while 1==1:
        tF = datetime.datetime.now()
        a = 0
        for i in range(len(dots)):
            tD = tF - dots[i-a][2]
            tD = int(str(tD)[5:7])
            if tD >= 1:
                map.deletePoint(dots[i-a][0],dots[i-a][1])
                dots.pop(i-a)
                a = a+1
def addDots():
    oldResponseCount = tester.getResponseCount()
    oldResponseCount = int(str(oldResponseCount))
    while 1==1:
        print(oldResponseCount)
        newResponseCount = tester.getResponseCount()
        newResponseCount = int(str(newResponseCount))
        print(newResponseCount)

       if(newResponseCount != oldResponseCount):
            difference = newResponseCount - oldResponseCount

            for i in range(difference):
                lat = random.randint(-90,90)
                long = random.randint(-180,180)
                map.drawPoint(lat,long)
                tI = datetime.datetime.now()
                dots.append([lat,long,tI])

       oldResponseCount = newResponseCount

if __name__ == '__main__':
    threading.Thread(target=addDots).start()
    threading.Thread(target=deleteDots).start()

And here is the map module which draws the map on a graphics window and contains the functions to plot and delete a point:

from graphics import *
import math
import images
size = 0.6
Circles = list(())
win = GraphWin("My Window", 1920*size, 1080*size)
win.setBackground('blue')
images.test(size)
myImage = Image(Point(960*size,540*size), "../Display/temp.gif")

myImage.draw(win)
import time

def drawPoint(lat,long):
    x = int(long*5.3+960)*size
    y = int(lat*(-5.92)+540)*size
    pt = Point(x,y)
    cir = Circle(pt,5)
    cir.setFill(color_rgb(255,0,0))
    Circles.append([cir,x,y])
    cir.draw(win)

def deletePoint(lat,long): 
    x = int(long*5.3+960)*size
    y = int(lat*(-5.92)+540)*size
    for c in Circles:
        if c[1]==x and c[2]==y:
            c[0].undraw()

How should I go about doing this?

There are a couple of issues that have to be addressed. First, any graphics.py commands that invoke tkinter (ie commands that cause something to be drawn/undrawn) must be issued by the primary (main) thread. So we need the secondary threads to communicate drawing requests to the primary thread.

Second, you have both your secondary threads modifying the Circles and dots lists -- you need to syncronize (lock) access to these lists so that only one thread at a time can modify or iterate them.

Below is my rework of your code as an example. I've eliminated map and tester routines as I'm just putting dots up on a window with one thread and deleting them after they are a second old from another thread:

from threading import Thread, Lock
from queue import Queue  # use for thread-safe communications
from random import randint
import time

from graphics import *

def drawPoint(lat, long):
    x = int(long * 5.3 + 960)
    y = int(lat * -5.92 + 540)

    point = Point(x, y)
    circle = Circle(point, 5)
    circle.setFill(color_rgb(255, 0, 0))

    circles_lock.acquire()
    circles.append(circle)
    circles_lock.release()

    actions.put((circle.draw, win))

def deletePoint(lat, long):
    global circles

    x = int(long * 5.3 + 960)
    y = int(lat * -5.92 + 540)

    keep_circles = []

    circles_lock.acquire()
    for circle in circles:
        center = circle.getCenter()

        if center.getX() == x and center.getY() == y:
            actions.put((circle.undraw,))
        else:
            keep_circles.append(circle)

    circles = keep_circles
    circles_lock.release()


def deleteDots():
    global dots

    while True:
        keep_dots = []

        dots_lock.acquire()
        now = time.time()

        for dot in dots:
            lat, long, then = dot

            if now - then >= 1.0:
                deletePoint(lat, long)
            else:
                keep_dots.append(dot)

        dots = keep_dots
        dots_lock.release()

        time.sleep(0.5)

def addDots():
    while True:
        lat = randint(-90, 90)
        long = randint(-180, 180)

        drawPoint(lat, long)

        dots_lock.acquire()
        dots.append((lat, long, time.time()))
        dots_lock.release()

        time.sleep(0.25)

win = GraphWin("My Window", 1920, 1080)

circles = []
circles_lock = Lock()

dots = []
dots_lock = Lock()

actions = Queue()

Thread(target=addDots, daemon=True).start()
Thread(target=deleteDots, daemon=True).start()

while True:
    if not actions.empty():
        action, *arguments = actions.get()
        action(*arguments)

    time.sleep(0.125)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM