There are a lot of these question already, but I can't figure out what's wrong. compounding the problem the code is spread out over a class. I'm trying to create a scrolling panel. This is class slot which displays some phrase, and acts as a linked list to allow growth (and hopefully compact code)
code:
variables: the_game: instance of module pygame, font: instance of module pygame.font, phrase: string being pushed, self.bitmap: Surface object of slot to be drawn to screen, num: number indicating slot number, because they are supposed to be in sequence.
class slot(object):
def __init__(self,x,y,num,dimx,dimy,the_game,font):
...
self.wordnum = convertnum(num)+". "
self.phrase=""
self.bitmap = the_game.Surface((dimx,dimy))
self.updatesprite(the_game,font)
function updatesprite for clearing and re-blitting whatever phrase the slot is supposed to hold when changed:
def updatesprite(self,the_game,font):
self.bitmap.fill((240,240,240))
phrase = self.wordnum
if(self.has_phrase==True):
phrase+=self.phrase
text = font.render(phrase,1,(0,0,255))
the_game.Surface.blit(text,self.bitmap,[self.x+2, self.y+2])
...
for moving back a phrase or sentence onto the next slot when another one is "pushed" on a previous slot
def mypush(self,num,phrase,the_game,font):
if (num > self.num):
if (self.has_next==False):
self.addmore(num-self.num,the_game,font)
self.next.mypush(num,phrase,the_game,font)
self.updatesprite(the_game,font)
elif (num == self.num):
if (self.has_phrase==True):
self.mypush(self.num+1,self.phrase,the_game,font)
else:
self.has_phrase=True
self.phrase=phrase
self.updatesprite(the_game,font)
function for setting the phrase of a slot:
def setphrase(self,phrase,realx,realy,virty,realdim,mousex,mousey,the_game,font):
...
if (self.has_phrase==True):
self.mypush(self.num+1,self.phrase,the_game,font)
self.has_phrase=True
self.phrase=phrase
self.updatesprite(the_game,font)
and then the method for drawing to the screen:
def mydraw(self,the_game,scrn,realx,realy,virty,realdim):
...
holder = self.bitmap.subsurface(0,self.e_y-virty,self.dim_x,self.dim_y-(self.e_y-virty))
scrn.blit(holder,[self.x,0])
if(self.has_next==True):
self.next.mydraw(the_game,scrn,realx,realy,virty,realdim)
code that was abbreviated out is for determining if the user clicked on a slot, determining screen coordinates from scrolling coordinates, etc.
The reason for it being written like this is because: a) I expect there to be quite a lot of slots potentially for one panel. I figured drawing to a large bitmap and grabbing what I need wouldn't be smart, or scalable. b) individual slots may be deleted
I can make examples work, but not this work. I can see the actual bitmap: self.bitmap, being drawn to the screen but no string. IDK whats going on
updated updatesprite method:
def updatesprite(self,the_game,font):
self.bitmap.fill((240,240,240))
phrase = self.wordnum
if(self.has_phrase==True):
phrase+=self.phrase
text = font.render(phrase,1,(0,0,255))
**self.bitmap.blit(text,[1, 1])** #compare to original
heh
Related
I'm currently trying to make 3d graphics using Turtle.
I have a list of objects that have inherited the Turtle class and each one has an attribute frontFactor - this value is the distance of the turtle to the camera.
How can I make the turtles closer to the camera render on top of the other turtles behind it?
My current code trying to do this:
finished = []
highestIndex = 0
print("FRAME")
for object in objects:
for i in range(len(object.particles)):
curHighest = 0
for j in range(len(object.particles)):
if object.particles[j].frontFactor > curHighest and (not (j in finished)):
curHighest = object.particles[j].frontFactor
highestIndex = j
object.particles[highestIndex].move(0,0,0)
print(object.particles[highestIndex].frontFactor)
finished.append(highestIndex)
where objects has an attribute of particles which is a list,
and every item in this list has a value frontFactor that determines the distance to the virtual camera.
This function goes through each particle on the screen (currently i only have one object), starting from the particles farthest away from the camera and ending at the particles nearest the camera.
This is what I want to happen,
but this is what it looks like instead. (darker particles are further away)
I've heard of a rule which states "last to move is on top" although I don't know how to implement that in this situation.
I am new to Python and I have a program that has 2 classes, one is essentially a rectangle and the other is essentially a circle. I am drawing them using the Canvas in Tkinter in the way below:
def draw(self):
self.canvas.delete("all")
self.rect.draw(self.canvas)
self.ball.draw(self.canvas)
The Ball class has its location variables and diameter variables and the Rect class has its location and dimension variables.
I am wondering how I detect the collision between these two "shapes". I know that one was is to treat the Ball as a square and do basic rectangle collision but I would like to know how to be precise.
I was also wondering if there was anything similar in Python to the way shape collision can be done in Java. In my Java game I use the following code to detect collision between any 2 Shapes:
public boolean collisionCheck(Shape a, Shape b) {
Area aA = new Area(a);
Area aB = new Area(b);
aA.intersect(aB);
return !aA.isEmpty();
}
Is there anything similar to this simple solution in Python?
And if not how would I go about circle-rectangle collision in Python?
Thank you for any help
I managed to figure this out using a method that Tkinter's Canvas object has. Every time something is drawn using Canvas it is given an ID. So as long as you record that ID somewhere you can use the find_overlapping method that the Canvas object has.
Say you have an object, in my case a custom Platform object, that keeps the ID stored in a variable. I did so like this:
def draw_platform(self, canvas): #Located in the Platform Class
self.ID = canvas.create_rectangle(self.x, self.y, self.x+self.w, self.y+self.h)
Now I can use this ID to determine if there are any object overlapping it.
def check_collision(self, plat, other_plat):
result = self.canvas.find_overlapping(plat.x, plat.y, plat.x + plat.w, plat.y + plat.h)
for i in result:
if i == other_plat.ID:
return True
return False
result returns a tuple of the ID's that are located within the rectangle bounds entered into find_overlapping. You can than loop through the tuple to see if any of the ID's match your other shape.
This works great for Rectangle-Rectangle collision and Circle-Rectangle collision.
I'm creating a Memory Matching game for a final project for my compsci class.
As of now, I have the cards all appear on the GUI window once you hit start game.
Once start game is hit, all the cards with their face value shows with the back of the card immediately. (So you never actually see the card fronts)
My reasoning for doing this is so that when you click on a card, I'll simply undraw the back card image instead of "Flipping" anything.
However, I keep getting errors, and when I don't get errors, it simply results in my program saying "Not Responding" and makes me have to restart the Shell.
Here are some little bits and pieces of my code which I think pertain to my issue:
for firstcard in range(6):
firstrow = self.deck.dealCard()
randomsuite = firstrow.getSuite()
randomrank = firstrow.getRank()
#Real image with face value of card
realCard = Image(Point(xpos,ypos),"playingcards/"+ (randomsuite) + str(randomrank) +".gif")
realCard.draw(win)
#Red Back of card to cover image
firstcard = Image(Point(xpos,ypos),"playingcards/b1fv.gif")
firstcard.draw(win)
#pushes the card to the right a bit
xpos += 100
while not Quitbutton.isClicked(p):
#Start Game
if StartGameButton.isClicked(p) and player == True:
if first == True:
Game.gameboard(win,150,125)
firstcard = Image(Point(xpos,ypos),"playingcards/b1fv.gif")
first = False
if StartGameButton.isClicked(p):
p = win.getMouse()
if firstcard.isClicked(p):
firstcard.undraw()
p = win.getMouse()
As of now you can see I have in my code if firstcard.isClicked(p):. However, this doesn't actually work because my isClicked function is a part of a Button Class and therefore, only works when dealing with Buttons and not images. So I'm not entirely sure what to do to select a single card and how to show that that card has been clicked and therefore should be undrawn
So basically, I have text which is typed out character by character. with the code:
text = "test"
delta = 40
delay = 0
for i in range(len(text) + 1):
s = test_string[:i]
update_text = lambda s=s: canvas.itemconfigure(variable, text=s)
canvas.after(delay, update_text)
delay += delta
This is all inside of a function, lets call: def NewEvent(). What I want to do is create a text button with the text "Skip" which changes delta to a lower number, thus speeding up the animation upon click. I cant seem to figure it out, normally when you make text clickable, it has something along the lines of:
skipbutton = canvas.create_text((400,100), activefill="Medium Turquoise", text="Skip", fill="White", font=('Arial', 30), tags="skip")
canvas.tag_bind('skip', '<ButtonPress-1>', function)
The problem is, it needs to stay within the same function. So I thought of creating an if statement similar like this:
if delta is 40 and skip is ____:
delta = 10
However, I dont know what would come after- (skip is) for it to work, or even if this would work at all... Any help would be appreciated.
You are doing animation in a way that makes your problem very difficult to solve. The problem is that you are scheduling all of the frames of animation before you display the first frame. In order to change the speed you would have to cancel all of the pending jobs and recreate new jobs. This is not the proper way to do animation in Tkinter.
A better solution is to only have a single job active at one time. You do this by having a function that displays one frame of animation and then schedules itself to run again in the future.
The general structure looks like this:
def animate():
<draw one frame of animation>
if <there are more frames>:
root.after(delay, animate)
In your case, each "frame" is simply adding one character to a character string, and your condition at the end is to simply check to see if there are more characters.
A simple implementation is to pass a string into the animate function, have it pull the first character off of the string and append it to the display.
For example:
def update_text(text):
char = text[0]
remainder = text[1:]
current_string = canvas.itemcget(variable, "text")
new_string = current_string + char
canvas.itemconfigure(variable, text=new_string)
if len(remainder) > 0:
canvas.after(delay, update_text, remainder)
To start the animation, give it the string you want to display:
update_text("Hello, world")
This this function depends on a global variable for the delay, writing a function to speed the animation up or slow it down only requires that you modify this delay:
def speedup():
global delay
delay = int(delay/2)
You can call it from a button quite easily:
tk.Button(root, text="Speed up!", command=speedup)
Lets say I have created a canvas in a Tkinter app.
In the canvas, I have drawn several rectangles.
What I want in essence, is to know in which rectangle the user has clicked, that is my main concern, but this is what I have tried and what I think might be the solution.
This is my layout:
for x in range(1,6):
for y in range(1,7):
tableNumber = y + 6*(x-1)
w.create_rectangle((table.xSeparation + table.width) * y - table.width,
(table.ySeparation + table.height) * x - table.height,
(table.xSeparation + table.width)*y,
(table.ySeparation + table.height) * x,
fill="brown", tags="table" + str(tableNumber))
w.tag_bind("table" + str(tableNumber), '<Button-1>', do)
So this just creates rectangles in a rows and columns layout.
Each rectangle has a specific tag in the form of table1,table2 to table30.
When I click on the rectangle, the do function executes.
Let's say do was something like:
def do(event):
print "click"
Not I get a "click" in the console for every click in the rectangle.
I would like to be able to get the tag of the clicked rectangle to be able to do something like this:
def do(event):
print str(theTagOfTheClickedRectangle)
So I checked on what events I can call which are:
widget
x, y
x_root, y_root
char
keysym
keycode
num
width, height
type
None of the seem to pin point what rectangle I have clicked.
I thought about adding more parameters to the do function here:
w.tag_bind("table" + str(tableNumber), '<Button-1>', do(event, tag))
But that doesn't seem to work fine, but maybe I'm not doing the do right:
do(event, tag): #this doesn't work at all!!
print event.x
print tag
I hope the question is clear, if I got the tag in the same manner as I can get the 'x' and 'y' of the event, that would be very comfortable.
The only actual solution I can now think of, is working out the math of 'event.x' and 'event.y' and calculate, based on the coordinates of the click, on which rectangle the user has clicked, this seems overcomplicated for what I'm trying to do, though I've done it before and it obviously works.
I hope the question is clear, other wise please ask for any clarification.
Note that I'm not tied to any of this code, rather, I'm looking for a solution for this problem, the most efficient will work even if means not working with tags, or another type of widget or whatever is the easier.
You can use something like this to reference the clicked-on object:
event.widget.find_withtag("current")
The tag "current" is special and represents the top-most object under the mouse.
w.tag_bind("table" + str(tableNumber), '<Button-1>', do(event, tag))
should be changed to
tag = "table" + str(tableNumber)
callback = lambda event, tag=tag: do(event, tag)
w.tag_bind(tag, '<Button-1>', callback)
The lambda creates a function with the tag name as the default value of the second argument. Tkinter calls that function with just one argument, the event, and the second argument uses the default value.