Re: Help with two issues, buttons and second class object
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
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
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
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
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
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
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): se