Быстро перерисовывается окно Tkinter - Что быстрее? - PullRequest
1 голос
/ 29 декабря 2010

Я пытаюсь создать симулятор микросреды в Python, и до сих пор у меня есть генератор фрактальных карт и простой класс обнаружения столкновений с движущимися «сущностями». Сущности работали хорошо, когда холст был разреженным, но теперь, когда он должен перерисовывать карту с каждым обновлением, он работает медленно. Я использую create_rectangle для визуализации матрицы объектов поверх матрицы карты (она называется «плитки»).

Я пытался использовать pygame и несколько других модулей, но я не могу заставить их работать с любой версией python (даже более старой и 32-разрядной), я думаю, это потому, что я использую Windows 7 64- немного, они не кажутся совместимыми.

В любом случае, есть ли способ быстрее визуализировать эти две матрицы, чтобы я мог получить более высокий FPS?

Большое спасибо, вот мой код (вы можете перемещать белый объект с помощью клавиш со стрелками !!!):

from Tkinter import *
from random import *
from time import *

root = Tk()

w = Canvas(root, width = 640, height=640)
w.pack()

entities = []
running = True

## Size of squares, in pixels
size = 5

## Make tiles a 128x128 array
tiles = []
for i in range(128):
    tiles.append(128*[0])


## IMPORTANT: all entities must have .x and .y attributes!

class Entity:

    def __init__(self):
        self.setup()
        entities.append(self)

    def setup(self, x=0,y=0, name='Undefined', color='pink'):
        self.x = x
        self.y = y
        self.name = name
        self.color = color

    def checkCollsn(self, try_x , try_y):
        for i in entities:
            if i is not self and try_x == i.x and try_y == i.y:
                print i.name, 'is in the way of', self.name, '!'
                return False
        else:
            ## print 'All clear!'
            return True


    def maintain(self):

        for i in entities:
            if i is not self:
                ## Detect anything in immediate proximity:
                dx = abs(i.x - self.x)
                dy = abs(i.y - self.y)
                if (dx == 1 or dx == 0) and (dy == 1 or dy== 0):
                    print self.name, 'has detected', i.name
                    ## TODO: Then what?

        ## TODO: Add other 'maintainance checks' here.


class Mobile(Entity):  ## Mobile is a subclass of Entity, which can move!  
    def up(self):
        if self.y < 1:
            print 'Upper bound!'
        else:
            if self.checkCollsn(self.x, self.y-1):
                self.y -= 1

    def down(self):
        if self.y > 127:
            print 'Lower bound!'
        else:
            if self.checkCollsn(self.x, self.y+1):
                self.y += 1

    def left(self):
        if self.x < 1:
            print 'Leftmost bound!'
        else:
            if self.checkCollsn(self.x-1, self.y):
                self.x -= 1

    def right(self):
        if self.x > 127:
            print 'Rightmost bound!'
        else:
            if self.checkCollsn(self.x+1, self.y):
                self.x += 1


def fractalGen(iterations=5, chanceWater=0.5):
    for i in range(iterations):
        if i == 0:
            ## Generate random 16x16 blocks
            blockNo = 8
            blockLen = 16
            randomize = True
            fractalize = False
        else:
            randomize = False
            fractalize = True

        if i == 1:
            blockNo = 16
            blockLen = 8

        if i == 2:
            blockNo = 32
            blockLen = 4

        if i == 3:
            blockNo = 64
            blockLen = 2

        if i == 4:
            blockNo = 128
            blockLen = 1

        if i > 4:
            print 'Excessive i value! Was', i

        for yBlock in range(blockNo):
            ## If it stays red, something's screwy!
            blockVal = 0

            for xBlock in range(blockNo):
                if randomize:
                    ## print 'i:',i,'Randomizing.'
                    r = random()
                    if r <= chanceWater:
                        blockVal = 4
                    else: blockVal = 3

                if fractalize:
                    land = 0
                    water = 0
                    ## First, assess surrounding % water of all valid surrounding blocks.
                    ## Remember, blocks are now higher res than previous iteration!


                    xRange = [0]  ## This paragraph makes sure we're staying within
                    yRange = [0]  ## the range of the matrix.

                    if xBlock != 0:
                        xRange.append(-1)
                    if xBlock != blockNo-1:
                        xRange.append(1)

                    if yBlock != 0:
                        yRange.append(-1)
                    if yBlock != blockNo-1:
                        yRange.append(1)                    


                    for ySign in yRange:
                        yCheck = (yBlock+ySign)*blockLen
                        for xSign in xRange:
                            xCheck = (xBlock+xSign)*blockLen
                            if xSign ==0 and ySign == 0:
                                selfVal = tiles[yCheck][xCheck]
                                ## print 'Self is', selfVal
                            else:
                                if tiles[yCheck][xCheck] == 4:
                                    ## print 'Water at', xCheck, yCheck
                                    water += 1
                                if tiles[yCheck][xCheck] == 3:
                                    land += 1
                                    ## print 'Land at', xCheck, yCheck
                    percentWater = water/float(land+water)
                    ##print percentWater
                    if selfVal == 4: #If self is water, oppSurr is % land
                        oppSurr = 1 - percentWater
                    if selfVal == 3: #If self is land, oppSurr is % water
                        oppSurr = percentWater

                    r = randint(0,25)/100.0
                    ##print oppSurr, r, oppSurr + r

                    if oppSurr + r >= 0.625:
                    ## If surroundings + random > 50%, switch self type!
                        if selfVal == 4:
                            blockVal = 3
                            ## print 'To land'
                        elif selfVal == 3:
                            blockVal = 4
                            ## print 'To water'
                    ## else: print 'Not switched, remains', selfVal
                    else: blockVal = selfVal


                    ## NB: Must assign blockVal in all cases, otherwise
                    ## you get the last value of blockVal carried over!

                for y in range(yBlock*blockLen,(yBlock+1)*blockLen):
                    for x in range(xBlock*blockLen,(xBlock+1)*blockLen):
                        tiles[y][x] = blockVal


def drawWorld():

    w.delete(ALL)

    x=0
    y=0

    for i in tiles:
        for j in i:
            color = 'gray'
            if j == 0: tColor = 'red'
            if j == 1: tColor = 'orange'
            if j == 2: tColor = 'yellow'
            if j == 3: tColor = 'green'
            if j == 4: tColor = 'blue'
            if j == 5: tColor = 'purple'

            w.create_rectangle(x,y,x+size,y+size,fill=tColor)
            x += size
        x = 0
        y += size

    for i in entities:
        w.create_rectangle(i.x*size, i.y*size, i.x*size+size, i.y*size+size, fill=i.color)


    w.update()


fractalGen(5,.4)
drawWorld()

def keyPress(event):
    if event.keysym == 'Up':
        p.up()

    if event.keysym == 'Down':
        p.down()

    if event.keysym == 'Left':
        p.left()

    if event.keysym == 'Right':
        p.right()


def moveRand(ent):
    cmd = randint(0,3) 
    if cmd == 0: ent.up()
    if cmd == 1: ent.down()
    if cmd == 2: ent.left()
    if cmd == 3: ent.right()

## Player:
p = Mobile()
p.setup(0,5,'Man', 'white')

## Boulder:
b = Entity()
b.setup(10,15,'Boulder','black')

## Flower:
f = Entity()
f.setup(5,5,'Flower', 'red')

## Elf:
elf = Mobile()
elf.setup(10,10,'Elf','green')

interval = 0.2

while running:
        moveRand(elf)
        root.bind_all('<Key>', keyPress)

        for i in entities:
            i.maintain()

        drawWorld()

Ответы [ 3 ]

1 голос
/ 06 января 2011

Несколько вещей, вероятно, могут быть сделаны для улучшения FPS вашего кода:

  1. Избавьтесь от w.update () и используйте обычный цикл обработки событий через mainloop ()
  2. Избавьтесь от w.delete (ALL) и повторно используйте блоки (просто переместите их вместо удаления и заново создайте их все)
  3. Используйте метод canvas find () для обнаружения столкновений вместо проверки только в коде Python
  4. Переписать в Tcl, чтобы вы могли использовать многопоточность Tcl вместо урезанных потоков CPython (полезно только если у вас более одного CPU / core);
0 голосов
/ 05 августа 2011

некоторые минимальные изменения:

from Tkinter import *
from random import *
from time import *

root = Tk()

w = Canvas(root, width = 640, height=640, bd=0, highlightthickness=0)
w.pack()

entities = []
running = True

## Size of squares, in pixels
size = 5

## Make tiles a 128x128 array
tiles = []
for i in range(128):
    tiles.append(128*[0])


## IMPORTANT: all entities must have .x and .y attributes!

class Entity:

    def __init__(self):
        self.setup()
        entities.append(self)

    def setup(self, x=0,y=0, name='Undefined', color='pink'):
        self.x = x
        self.y = y
        self.name = name
        self.color = color

    def checkCollsn(self, try_x , try_y):
        for i in entities:
            if i is not self and try_x == i.x and try_y == i.y:
                #print i.name, 'is in the way of', self.name, '!'
                return False
        else:
            ## print 'All clear!'
            return True


    def maintain(self):
        for i in entities:
            if i is not self:
                ## Detect anything in immediate proximity:
                dx = abs(i.x - self.x)
                dy = abs(i.y - self.y)
                if (dx == 1 or dx == 0) and (dy == 1 or dy== 0):
                    pass
                    #print self.name, 'has detected', i.name
                    ## TODO: Then what?

        ## TODO: Add other 'maintainance checks' here.


class Mobile(Entity):  ## Mobile is a subclass of Entity, which can move!  
    def up(self):
        if self.y < 1:
            self.y = 0
        else:
            if self.checkCollsn(self.x, self.y-1):
                self.y -= 1

    def down(self):
        if self.y > 127:
            self.y = 127
        else:
            if self.checkCollsn(self.x, self.y+1):
                self.y += 1

    def left(self):
        if self.x < 1:
            self.x = 0
        else:
            if self.checkCollsn(self.x-1, self.y):
                self.x -= 1

    def right(self):
        if self.x > 127:
            self.x = 127
        else:
            if self.checkCollsn(self.x+1, self.y):
                self.x += 1


def fractalGen(iterations=5, chanceWater=0.5):
    for i in range(iterations):
        if i == 0:
            ## Generate random 16x16 blocks
            blockNo = 8
            blockLen = 16
            randomize = True
            fractalize = False
        else:
            randomize = False
            fractalize = True

        if i == 1:
            blockNo = 16
            blockLen = 8

        if i == 2:
            blockNo = 32
            blockLen = 4

        if i == 3:
            blockNo = 64
            blockLen = 2

        if i == 4:
            blockNo = 128
            blockLen = 1

        if i > 4:
            print 'Excessive i value! Was', i

        for yBlock in range(blockNo):
            ## If it stays red, something's screwy!
            blockVal = 0

            for xBlock in range(blockNo):
                if randomize:
                    ## print 'i:',i,'Randomizing.'
                    r = random()
                    if r <= chanceWater:
                        blockVal = 4
                    else: blockVal = 3

                if fractalize:
                    land = 0
                    water = 0
                    ## First, assess surrounding % water of all valid surrounding blocks.
                    ## Remember, blocks are now higher res than previous iteration!


                    xRange = [0]  ## This paragraph makes sure we're staying within
                    yRange = [0]  ## the range of the matrix.

                    if xBlock != 0:
                        xRange.append(-1)
                    if xBlock != blockNo-1:
                        xRange.append(1)

                    if yBlock != 0:
                        yRange.append(-1)
                    if yBlock != blockNo-1:
                        yRange.append(1)                    


                    for ySign in yRange:
                        yCheck = (yBlock+ySign)*blockLen
                        for xSign in xRange:
                            xCheck = (xBlock+xSign)*blockLen
                            if xSign ==0 and ySign == 0:
                                selfVal = tiles[yCheck][xCheck]
                                ## print 'Self is', selfVal
                            else:
                                if tiles[yCheck][xCheck] == 4:
                                    ## print 'Water at', xCheck, yCheck
                                    water += 1
                                if tiles[yCheck][xCheck] == 3:
                                    land += 1
                                    ## print 'Land at', xCheck, yCheck
                    percentWater = water/float(land+water)
                    ##print percentWater
                    if selfVal == 4: #If self is water, oppSurr is % land
                        oppSurr = 1 - percentWater
                    if selfVal == 3: #If self is land, oppSurr is % water
                        oppSurr = percentWater

                    r = randint(0,25)/100.0
                    ##print oppSurr, r, oppSurr + r

                    if oppSurr + r >= 0.625:
                    ## If surroundings + random > 50%, switch self type!
                        if selfVal == 4:
                            blockVal = 3
                            ## print 'To land'
                        elif selfVal == 3:
                            blockVal = 4
                            ## print 'To water'
                    ## else: print 'Not switched, remains', selfVal
                    else: blockVal = selfVal


                    ## NB: Must assign blockVal in all cases, otherwise
                    ## you get the last value of blockVal carried over!

                for y in range(yBlock*blockLen,(yBlock+1)*blockLen):
                    for x in range(xBlock*blockLen,(xBlock+1)*blockLen):
                        tiles[y][x] = blockVal

def drawWorld():

    x=0
    y=0

    for i in tiles:
        for j in i:
            color = 'gray'
            if j == 0: tColor = 'red'
            if j == 1: tColor = 'orange'
            if j == 2: tColor = 'yellow'
            if j == 3: tColor = 'green'
            if j == 4: tColor = 'blue'
            if j == 5: tColor = 'purple'

            w.create_rectangle(x,y,x+size,y+size,fill=tColor)
            x += size

        x = 0
        y += size
    w.update()

def draw_entities():
    w.delete('ent')
    for i in entities:
        w.create_rectangle(i.x*size, i.y*size, i.x*size+size, i.y*size+size, fill=i.color, tags='ent')

def moveRand(ent):
    cmd = randint(0,3) 
    if cmd == 0: ent.up()
    if cmd == 1: ent.down()
    if cmd == 2: ent.left()
    if cmd == 3: ent.right()

def keyPress(event):
    if event.keysym == 'Up':
        p.up()

    if event.keysym == 'Down':
        p.down()

    if event.keysym == 'Left':
        p.left()

    if event.keysym == 'Right':
        p.right()

fractalGen(5,.4)
drawWorld()

## Player:
p = Mobile()
p.setup(0,5,'Man', 'white')

## Boulder:
b = Entity()
b.setup(10,15,'Boulder','black')

## Flower:
f = Entity()
f.setup(5,5,'Flower', 'red')

## Elf:
elf = Mobile()
elf.setup(10,10,'Elf','pink')

##interval = 0.2 #?

draw_entities()

root.bind_all('<Key>', keyPress)

try:
    while running:
        moveRand(elf)
        for i in entities:
            i.maintain()
        draw_entities()
        w.update()
except TclError:
    pass
0 голосов
/ 29 декабря 2010

Скорее всего, вы достигли предела Tk - он не предназначен для анимации и других модных шагов.

Лучшим вариантом будет оптимизированная библиотека, такая как PyGame. Не должно быть причины, по которой он не будет работать на Win 7 64bit - просто убедитесь, что у вас есть Python, который соответствует PyGame (вероятно, 32bit Python).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...