Re: Help with two issues, buttons and second class object

2016-11-25 Thread Peter Otten
Thomas Grops via Python-list wrote:

> Also I am struggling to understand:
> 
>def move_tank(self, dx, dy):
> self.x += dx
> self.y += dy
> self.canvas.move(self.id, dx, dy)
> 
> Where does the dx and dy values get input?

To find the place where the move_tank() method is invoked hit the search 
button or key of your text editor. In this case you'll find

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

so move_tank() is invoked by the move() method. But what the heck is

*self.moves[self.direction]

? From the fact that the script runs without error you can conclude that it 
resolves to two integer values. Let's try in the interactive interpreter
(the script is called tanks2, so that's what I import):

$ python3
Python 3.4.3 (default, Nov 17 2016, 01:08:31) 
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import tkinter
>>> import tanks2
>>> root = tkinter.Tk()
>>> canvas = tkinter.Canvas(root, width=100, height=100)
>>> canvas.pack()
>>> tank = tanks2.Tank(root, canvas, "example", 10, 10, 10, 10, "red")
>>> tank.direction
0

That's the value determining the direction into which the tank is supposed 
to move (what you called "count" in your script).

>>> tank.moves
[(5, 0), (-5, 0), (0, -2), (0, 2), (-1, -1), (1, -1), (1, 1), (-1, 1)]

That's the list of speed vectors I set up in the initialiser (the 
Tank.__init__() method)

>>> tank.moves[tank.direction]
(5, 0)

So move_tank() is supposed to move the tank 5 pixels to the right and 0 
pixels down. For this invocation

self.move_tank(*self.moves[self.direction])

is equivalent to

self.move_tank(*(5, 0))

The leading star tells python to treat the elements of the tuple as if they 
were passed individually to the function or method. The actual dx and dy are 
then 5 and 0:

self.move_tank(5, 0)

Now let's move our little red tank:

>>> tank.x, tank.y
(10, 10)
>>> tank.move_tank(3, 7)
>>> tank.x, tank.y
(13, 17)
>>> tank.move_tank(tank.moves[tanks2.MRIGHTDOWN])
Traceback (most recent call last):
  File "", line 1, in 
TypeError: move_tank() missing 1 required positional argument: 'dy'

Oops, I forgot the leading *. Second attempt:

>>> tank.move_tank(*tank.moves[tanks2.MRIGHTDOWN])
>>> tank.x, tank.y
(14, 18)



-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Help with two issues, buttons and second class object

2016-11-25 Thread Peter Otten
Thomas Grops via Python-list wrote:

> Peter, in your code what does that self.root = root mean in the __init__
> function of the class

In your original script you used global variables to access the tkinter.Tk() 
object and the canvas. A simplified example:

import tkinter

class Tank:
def make_button(self):
tkinter.Button(root).pack()

root = tkinter.Tk() # you used the name "main" instead of "root"
tank = Tank()
tank.make_button()
root.mainloop()

Programmers with a little experience tend to avoid globals, so let's put the 
script code above into a function:

import tkinter

class Tank:
def make_button(self):
tkinter.Button(root).pack()

def main():
root = tkinter.Tk() # you used the name "main" instead of "root"
tank = Tank()
tank.make_button()
root.mainloop()

main()

When you run the above you'll get a NameError. Because make_button() 
accesses root which is no longer global you have to find a way to pass it to 
the tank instance. One way is to change the make_button() method and pass it 
along as an argument:

class Tank:
def make_button(self, root):
tkinter.Button(root).pack()

def main():
root = tkinter.Tk() 
tank = Tank()
tank.make_button(root)
root.mainloop()

The other way (the one I used) is to make it an attribute of the tank 
instance:

class Tank:
def __init__(self, root):
self.root = root
def make_button(self):
tkinter.Button(self.root).pack()

def main():
root = tkinter.Tk() 
tank = Tank(root)
tank.make_button()
root.mainloop()

Now you can access root as self.root in every method of the Tank class. 
There is no advantage in this case, but when root is referred in more than 
one place, either in the class or by the code invoking methods of the class, 
it will be more convenient.

An important advantage of using an explicit argument instead of a global is 
that different instances can have different values for root. For a Tank that 
might be battlefields

def main():
root = tkinter.Tk()
other = tkinter.Toplevel()

for battlefield in root, other:
tank = Tank(battlefield)
tank.make_button()

root.mainloop()

Note that this requires no change of the Tank class.

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Help with two issues, buttons and second class object

2016-11-25 Thread Thomas Grops via Python-list
Also I am struggling to understand:

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

Where does the dx and dy values get input?
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Help with two issues, buttons and second class object

2016-11-25 Thread Thomas Grops via Python-list
Peter, in your code what does that self.root = root mean in the __init__ 
function of the class
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Help with two issues, buttons and second class object

2016-11-24 Thread Larry Hudson via Python-list

On 11/24/2016 06:53 AM, Peter Otten wrote:

Thomas Grops via Python-list wrote:

[snip...]

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



About my (Larry Hudson) randx() example code:

I used randint() in it because that was what the OP was using.  I didn't want to bother with the 
side issue of randint() vs randrange() since it was just meant as example code to be adapted. 
If I had written it for my own use I would have used randrange() for the consistency that you 
are describing.


Not an excuse — just a comment.   ;-)

--
 -=- Larry -=-
--
https://mail.python.org/mailman/listinfo/python-list


Re: Help with two issues, buttons and second class object

2016-11-24 Thread Thomas Grops via Python-list
Wow thankyou that code is really good, I had no programming knowledge until 2 
months ago, I enjoy your descriptions it is really helpful for me. I like to 
understand what the code does before using it myself or a variant of it.

Will tweak bits tonight the project is in tomorrow. This code is just a side to 
the one I completed and will be handing in so im just doing this for fun and 
learning but the past months but as I learn new things I keep wanting to 
rebuild my program. I will be adding obstacles into the code too and eventually 
enemies, that explains the commented out code for attack and medic.

I will upload the code when I am done with it or get stuck again to see what 
your feedback is :D
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Help with two issues, buttons and second class object

2016-11-24 Thread Peter Otten
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, 1):
> #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):

Help with two issues, buttons and second class object

2016-11-24 Thread Thomas Grops via Python-list
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.

from tkinter import *
import time
import random

#Properties for the tank
class Tank():



#life, speed, starting position, vectors, size of tank
def __init__(self, x, y, vx, vy):
self.__life=10
#self.canvas=canvas
self.speed = 1
self.x = x
self.y = y
self.vx = vx
self.vy = vy
self.count=0
#self.w = tank.width()
#self.h = tank.height()
self.id = canvas.create_rectangle(x,y,vx,vy, fill='green')
#Create left button and call function to print left
'''buttonL=Button(canvas,text='Change Direction', 
command=self.changeDirection())
buttonL.pack(side=BOTTOM, anchor=SW)'''
#print(self.x)
#tank attacked
def attack(self):
print('ouch!')
self.__life -= 1

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

#respawn at starting point
#def respawn(self):


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

#move directions
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)

def left(self):
canvas.move(self.id,-5,0)#move left

#reposition x,vx,y,vy values
self.x+=-5
self.vx+=-5

#Update canvas
canvas.update()
time.sleep(0.1)

def up(self):
canvas.move(self.id,0,-2)#move up

#reposition x,vx,y,vy values
self.y+=-2
self.vy+=-2

#Update canvas
canvas.update()
time.sleep(0.1)

def down(self):
canvas.move(self.id,0,+2)#move down

#reposition x,vx,y,vy values
self.y+=2
self.vy+=2

#Update canvas
canvas.update()
time.sleep(0.1)

def upLeft(self):
canvas.move(self.id,-1,-1)#move upLeft

#reposition x,vx,y,vy values
self.y+=-1
self.vy+=-1
self.x+=-1
self.vx+=-1

#Update canvas
canvas.update()
time.sleep(0.1)

def upRight(self):
canvas.move(self.id,+1,-1)#move upRight

#reposition x,vx,y,vy values
self.y+=-1
self.vx+=1
self.vy+=-1
self.x+=1

#Update canvas
canvas.update()
time.sleep(0.1)

def downLeft(self):
canvas.move(self.id,-1,+1)#move downLeft

#reposition x,vx,y,vy values
self.x+=-1
self.vx+=-1
self.y+=1
self.vy+=1

#Update canvas
canvas.update()
time.sleep(0.1)

def downRight(self):
#move downRight
canvas.move(self.id,+1,+1)

#reposition x,vx,y,vy values
self.x+=1
self.vx+=1
self.y+=1
self.vy+=1

#Update canvas
canvas.update()
time.sleep(0.1)



def count(self,count):
#Count triggers direction of movement
self.count = count
print (count)



#movement
def move(self):

# Loop for steps in movement
for t in range(1, 1):
#Move direction depending on count value
if self.count==0:
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()

 # If a boundary has been crossed, pick a direction randomly

#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: