Help with two issues, buttons and second class object

Peter Otten __peter__ at web.de
Thu Nov 24 09:53:26 EST 2016


Thomas Grops via Python-list wrote:

> Hi I have created some code, which moves a rectangle around and when it
> hits the edge it picks a random new direction. It does this by the count
> function within my class. I am wanting to create a button to randomly
> change count but I my class seems to be getting errors.
> 
> I also wanted to create a second class object tank2 but it doesn't seem to
> move when I create it with tank2.Tank(x,y,vx,vy) and call tank1.move()
> 
> Can anyone help me many thanks.

It may take some time to get it, but don't use time.sleep() in a tkinter 
script!

>     def right(self):
>         canvas.move(self.id,+5,0)#move right
>         
>         #reposition x,vx,y,vy values
>         self.x+=5
>         self.vx+=5
> 
>         #Update canvas
>         canvas.update()
>         time.sleep(0.1)

When you have to repeat the same code with small variations (the 
canvas.move() arguments) make these variations arguments of the function.

>     def move(self):
>         
>         # Loop for steps in movement
>         for t in range(1, 10000):
>             #Move direction depending on count value
>             if self.count==0:
>                 self.left()

So 0 means "left" and 6 means "down and right". If a value's meaning is not 
obvious use a name instead

LEFT = 42 # also have a look at the enum module in the stdlib

if self.count == LEFT:
    self.left()

>             if self.count==1:
>                 self.right()
>             if self.count==2:
>                 self.up()
>             if self.count==3:
>                 self.down()
>             if self.count==4:
>                 self.upLeft()
>             if self.count==5:
>                 self.upRight()
>             if self.count==6:
>                 self.downRight()
>             if self.count==7:
>                 self.downLeft()

That's quite a lot of if-s. It might be better to use a table.

>             #Left border
>             if self.x <= 0:
>                 #banned directions
>                 excludedNumbers = [0,4,7]
>                 #define random integer to be selected
>                 randomNumber = random.randint(0,8)
>                 #nested while loop so that the banned directions are not
>                 #selected
>                 while randomNumber in excludedNumbers:
>                     randomNumber = random.randint(0,8)
>                 #feed allowed random direction back to the count
>                 self.count=randomNumber
> 
>             #Right border
>             elif self.vx >= 1000:
>                 #banned directions
>                 excludedNumbers = [1,5,6]
>                 #define random integer to be selected
>                 randomNumber = random.randint(0,8)
>                 #nested while loop so that the banned directions are not
>                 #selected
>                 while randomNumber in excludedNumbers:
>                     randomNumber = random.randint(0,8)
>                 #feed allowed random direction back to the count

Instead of repeating your code with copy-and-past make a helper function 
like the randx() posted by Larry Hudson.

By the way, randint(min, max) may return max so there are 9 possible 
outcomes while you handle only 8. To be consistent with Python's half-open 
ranges use 

random.randrange(8) # may return 0, 1, 2, 3, 4, 5, 6, 7, but not 8

Below is what became of your code when I messed with it. Don't copy it, try 
to pick up ideas, and have fun!

import tkinter as tk
import random

#Canvas size
WIDTH = 1000
HEIGHT = 700

BUTTONS_PER_ROW = 6

MRIGHT = 0
MLEFT = 1
MUP = 2
MDOWN = 3
MLEFTUP = 4
MRIGHTUP = 5
MRIGHTDOWN = 6
MLEFTDOWN = 7


class Tank():
    def __init__(self, root, canvas, name, x, y, width, height, color):
        self.root = root
        self.canvas = canvas
        self.color = color
        self.name = name
        self.__life = 10
        self.speed = 1
        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.direction = MRIGHT
        self.id = canvas.create_rectangle(
            x, y, self.right, self.bottom, fill=color
        )
        self.moves = [
            (5, 0),    # right
            (-5, 0),   # left
            (0, -2),   # up
            (0, 2),    # down
            (-1, -1),  # left-up
            (1, -1),   # right-up
            (1, 1),    # right-down
            (-1, 1)    # left-down
        ]

    @property
    def right(self):
        return self.x + self.width

    @property
    def bottom(self):
        return self.y + self.height

    def attack(self):
        print('ouch!')
        self.__life -= 1

    def checkLife(self):
        if self.__life <= 0:
            print('dead')
        else:
            print(str(self.__life) + " life left")

    def medic(self):
        self.__life += 5

    def move_tank(self, dx, dy):
        self.x += dx
        self.y += dy
        self.canvas.move(self.id, dx, dy)

    def move(self):
        self.move_tank(*self.moves[self.direction])

        # If a boundary has been crossed, pick a direction randomly
        directions = set(range(8))
        if self.x <= 0:
            directions -= {MLEFT, MLEFTUP, MLEFTDOWN}
        elif self.right >= WIDTH:
            directions -= {MRIGHT, MRIGHTUP, MRIGHTDOWN}

        if self.y <= 0:
            directions -= {MUP, MLEFTUP, MRIGHTUP}
        elif self.bottom >= HEIGHT:
            directions -= {MDOWN, MLEFTDOWN, MRIGHTDOWN}
        assert directions, "nowhere to go!"

        if len(directions) < 8:
            print(self, "hit wall, changing direction")
            self.direction = random.choice(list(directions))

        self.root.after(10, self.move)

    def changeDirection(self):
        print(self, "changing direction on user request")
        self.direction = random.randrange(8)

    def __str__(self):
        return self.name


def random_color():
    colors = [
        random.randrange(128),
        random.randrange(128, 256),
        random.randrange(128)
    ]
    random.shuffle(colors)
    return "#{:02x}{:02x}{:02x}".format(*colors)


def main():
    root = tk.Tk()

    canvas = tk.Canvas(root, width=WIDTH, height=HEIGHT, bg='khaki')
    canvas.grid(row=0, column=0, columnspan=BUTTONS_PER_ROW)

    tanks = [
        Tank(
            root, canvas, "Tank #{}".format(i),
            x, 650, 40, 40,
            random_color())
        for i, x in enumerate(range(0, WIDTH, 80), 1)
    ]
    for i, tank in enumerate(tanks):
        tank.move()
        button = tk.Button(
            root,
            text=tank.name,
            command=tank.changeDirection,
            bg=tank.color
            )
        button.grid(row=1+i // BUTTONS_PER_ROW, column=i % BUTTONS_PER_ROW)

    root.mainloop()

if __name__ == "__main__":
    main()

PS:

>     #life, speed, starting position, vectors, size of tank
>     def __init__(self, x, y, vx, vy):

Python has docstrings. Use them!





More information about the Python-list mailing list