I am making a game, with pygame, and i want it to be that when the mouse hovers over my text, the box gets shaded.
here is what I have so far:
in the event-handling loop:
(tuples in CheckToUnshadeBoxes are pairs of text-surfaces and their boxes (from get_rect method))
elif event.type == MOUSEMOTION:
CheckToShadeBoxes(event, LERect, LoadRect, PlayRect)
CheckToUnshadeBoxes(event, (LERect, LESurf),
(LoadRect, LoadSurf), (PlayRect, PlaySurf))
here is CheckToShadeBoxes:
def CheckToShadeBoxes(event, *args):
'''
shade the box the mouse is over
'''
for rect in args:
s = pg.Surface((rect.width, rect.height))
s.set_alpha(50)
print(rect.x, rect.y)
s.fill(COLOURS['gray'])
x, y = event.pos
if rect.collidepoint(x, y):
SURFACE.blit(s, (rect.x, rect.y))
and CheckToUnshadeBoxes:
def CheckToUnshadeBoxes(event, *args):
''' if the mouse moves out of the box, the box will become unshaded.'''
for (rect, TextSurf) in args:
x, y = event.pos
if not rect.collidepoint(x, y):
SURFACE.blit(TextSurf, (rect.x, rect.y))
This works fine! except that when I move the mouse inside of the box, the box will continue to get darker and darker until you cant even see the text! I know it is a small detail, but it has been bugging me for a long time and I don't know how to fix it.
by the way, if it is not evident enough, COLOURS is a dictionary with string keys and RGB tuple values, and SURFACE is my main drawing surface.
If you have any questions about my code, or anything I have done just comment!
If the rects you are passing are your own derived class, then I recommend making a .is_shaded class variable and checking to make sure the variable is false before shading it.
If these rects aren't your own class extending another, then I recommend you make one, as it would make it much simpler
The problem with your code is that you keep adding in dark rectangles when the mouse is hovered over. No wonder it gets darker and darker.
All that is needed is for your code to be shuffled around a little.
Inside your event handling, you should call a function to check if it should shade the text. This should return a Boolean value. Store this in a variable. Most of the code from the CheckToShadeBoxes function will do the trick.
In your render section, you should render just one surface on top of your text, based on whether the Boolean value is true or not. The code that creates a gray surface in the CheckToShadeBoxes function will work but make sure it is only one surface. Don't create a new surface in every iteration. Define it outside once and blit it to the screen inside the loop.
This should fix your problem!
I hope this answer helps you! If you have any further questions please feel free to post a comment below!
Related
So my intent here is to just create a button where the player clicks on the start color to begin, then moves the mouse to the end color. It's an avoider game, where you avoid colors that are not on the path and not the start or end color.
I'm trying to have the mouse hover over the start color (it's green) and click on it to start the game. I was testing it out by setting the position of the cursor, but that didn't seem to work.
I've tried several different methods to get the color. Including drawing a whole extra button, but I couldn't get that where I wanted it to be. It was also not really what I needed.
Here is my code so far. I'm missing a function that would allow the cursor to register what color is underneath it.
At the moment, when running the game it doesn't crash, but it doesn't register what color is below the cursor and what happens when you click on it.
for event in pygame.event.get():
if mousepressed:
color = (102, 255, 102)
screen.set_at(pygame.mouse.get_pos(), color)
if color and event.type == pygame.MOUSEBUTTONDOWN:
pygame.mouse.set_pos(102, 255)
else:
if not color and event.type == pygame.MOUSEBUTTONDOWN:
pygame.mouse.set_pos(100,100)
Thanks for the help! Really appreciate it.
The color of a pixel from a pygame.Surface at a certain position can be get by .get_at(). Use this method, with the Surface object, which is associated to the display (screen):
color = screen.get_at(pygame.mouse.get_pos())
.set_at() changes to color of the pixel on the Surface, at the specified position.
In this context, set_at means to set a color at a position on the Surface and get_at means to get a color at a position from the Surface.
I'm working on an image annotation tool using Tkinter. A rectangular bounding box is following the cursor and is placed on the image by clicking. Now I need to be able to resize the bounding box and I'd like to do it in a similar fashion as it's done in Photoshop, holding down a button and depending on where the mouse is moved, the brush changes in size. Is that possible in Tkinter?
I've come up with this, where I wanted to add the pointer distance traveled in each direction to the rectangles size:
self.canvas.bind('<Alt_L>', self.resize)
self.previousx = 0
self.previousy = 0
def resize(self, event):
x = self.canvas.canvasx(event.x) # get coordinates of the event on the canvas
y = self.canvas.canvasy(event.y)
self.rect_size[0] += (self.previousx - x) # width
self.rect_size[1] += (self.previousy - y) # height
self.motion(event)
self.previousx = self.canvas.canvasx(event.x)
self.previousy = self.canvas.canvasy(event.y)
It kind of works too but
the previous coordinates need to be initialized with a different key first which is very annoying and
when the cursor - and with it the rectangle too - changes position it is very hard to tell if the rectangle is already the proper size.
How can I keep the cursor at the same spot and still get the mouse movements?
Edit:
Bryan in the comments was right, you can't move the mouse without also moving the on-screen cursor, which is true at least for my purposes. The solution was very simple, while resizing the bounding box I stopped updating it's position, so while the mouse was still moving, the rectangle did not.
Bryan in the comments was right, you can't move the mouse without also moving the on-screen cursor, which is true at least for my purposes. The solution was very simple, while resizing the bounding box I stopped updating it's position, so while the mouse was still moving, the rectangle did not.
I've made a program which draws an oval on click (mouse click=start point, mouse release= end point) as shown in code below, and I'd like to add if condition: when the shift key is pressed midst drawing, it would equalize the coordinates and therefore as a result, a circle (or perfect oval if you wish) will be drawn.
from tkinter import *
def draw(event):
if str(event.type)=='ButtonPress':
canvas.old_coords=event.x,event.y
elif str(event.type)=='ButtonRelease':
x,y=event.x,event.y
x1,y1=canvas.old_coords
canvas.create_oval(x,y,x1,y1)
canvas=Canvas()
canvas.pack()
canvas.bind('<B1-Motion>',draw)
canvas.bind('<ButtonPress-1>',draw)
canvas.bind('<ButtonRelease-1>',draw)
How could I possibly take in account pressed shift and then draw a circle?
So, I found a Python module called keyboard and I solved my problem using it, adding this condition:
if keyboard.is_pressed('shift'):
if y>y1: y=y1+abs(x-x1)
else: y=y1-abs(x-x1)
which changes the end coordinates and later draws circle accordingly
I'm currently writing up some GUI code for a small project I'm working on and I've come to the point where I need to implement scroll bars and their associated containers. For ease of execution, I would love to be able to draw all elements within the "scroll box" (the window that the scroll bar will affect) to a separate surface from my main display surface. The separate surface would then be cropped as need be and then drawn to the display surface in the render loop. I'm having trouble getting this to work, however.
In the draw() method of my ScrollBox class, I have the following code.
def draw(self):
self.subSurface.blit(self.image, (x, y))
#subSurface is, naturally, a Surface, and image is a pygame.Image; x and y are whatever
self.displaySurface.blit(self.subSurface, (x,y))
As with all drawable GUI elements in my code, draw() is called every pass through the main render loop. What the above code gives me is the default filled-in black Rect and self.image is not displayed in any capacity. I tried replacing the first line with
pygame.draw.rect(self.subSurface, color, rect)
but it yielded the same results. From my reading up on other Pygame GUI libraries, it seems what I want to do is possible but I don't think I'm executing it properly. How do I attach other sources/surfaces to subSurface and then have subSurface be drawn (with the sources attached) by displaySurface?
Any help would be much appreciated. Thanks.
For people visiting this question in the future:
Remember that the dest argument for Surface.blit() is relative to the upper-left corner of the destination surface. So if you're assembling an image on a subsurface, remember to use coordinates relative to the top-left corner of the object you're assembling, rather than absolute display coordinates.
So to assemble a scrollbar and draw it somewhere:
class ScrollBar:
# ... code ...
def render(self, display, x, y):
self.subSurface.blit(self.handle_image, (0, self.handle_pos))
self.subSurface.blit(self.upbtn_image, (0, 0))
self.subSurface.blit(self.dnbtn_image, (0, self.height - self.btn_height))
# ... other rendering operations
display.blit(self.subSurface, (x, y))
Adjust all numbers and variable names to taste, but you get the idea. Notice that all the scrollbar elements are positioned in "scrollbar-local" coordinates, with only the final blit to the display surface positioned in screen/application coordinates.
I am creating a simple mp3 player and my first task was to create a simple button that a user could press. I created a class called Button which handled this behavior and detects if a user has clicked it and then changes color. I am now trying to have a default text that the button displays and another string (pres_string) which will be displayed if the button is being pressed.
The only problem is my background surface seems to be in the wrong place and is drawing over any changes I have made.
Here is my code:
http://pastebin.com/Nh3yy01X
As you can see I've commented out the lines I described and tried it with basic variables in the main function just to test what was going wrong.
Thanks for any help.
(Feel free to change the title of the question, I wasn't sure what most accuratelydescribed my problem)
Clear the surface every loop
def draw(self):
# clear screen."
self.screen.fill( self.color_bg )
# first draw background
# Then buttons
# then any extra top level text
# update
pygame.display.flip()
tip: For colors, you can call pygame.Color() with human-names like red ( gray20 and gray80 have a nice contrast, to use for bg and text. )
from pygame import Color
text = Color('gray20')
Your button, psuedocode. Fix: moved color as an instance member.
class Button(object):
def __init__(self, text, rect=None):
self.color_bg = Color("gray20")
self.color_text = color("gray80")
if rect is None: rect = Rect(0,0,1,1)
self.rect = rect
self._render()
def _render(self):
# draw button and .render() font, cache to surface for later.
self.surface_cached = self.surface.copy()
# render text
#if size changes, save rect.size of cached surface , mantaining location
self.rect.size = cache.get_rect.size
def draw(self):
# draw cached surface
screen.blit( self.surface_cached, self.rect)
For testClick use Rect.collidepoint http://www.pygame.org/docs/ref/rect.html#Rect.collidepoint
2D bitmap-based computer graphics are like drawing or painting - you put the new ink on top of whatever was there already. So your background must be the first thing you draw each time.