how can I modify my parameter without writing whole line again? - python-3.x

Hi I'm trying some method overwriting but I dont know how to do it. I have this class called RectCreator
class RectCreator:
def __init__(self, location_x, location_y, width, height, ):
self.location_x = location_x
self.location_y = location_y
self.width = width
self.height = height
def creating_rect(self, display, color):
creating_rect = pygame.Rect(self.location_x, self.location_y, self.width, self.height)
drawing_rect = pygame.draw.rect(display, color, creating_rect, border_radius=20)
return creating_rect, drawing_rect
this class is in a file. I imported the file in my main.py and I'm using the class like this:
button_1 = RectCreator(350, 350, 100, 100)
btn_1 = button_1.creating_rect(display_surface, blue)
Now here is what I want to do. I dont know how to change the color of the btn_1 without writing all the line again like this:
btn_1 = button_1.creating_rect(display_surface, green) ---------------> I DONT WANT TO WRITE THAT
I tried to add a color method to the class and put that method in the method that uses color.
def color(self, color):
return color
def creating_rect(self, display):
creating_rect = pygame.Rect(self.location_x, self.location_y, self.width, self.height)
drawing_rect = pygame.draw.rect(display, self.color(), creating_rect, border_radius=20)
return creating_rect, drawing_rect
That was my solution but self.color() asks me for a parameter. All I want to do is:
btn_1 = button_1.creating_rect(display_surface, blue)
**output : a blue box that display on my app**
btn_1.change_color(green)
**output : now that blue box turned to green box**

Once you draw something on the screen (like a rect), it's there to stay and won't go away until you draw something new at the same position. E.g. if you set the pixel of the screen at position 5, 12 to the color green, you can't magically change the color at that position without interacting with the screen surface again.
So if you draw a blue rect and now want a green rect, you have to call pygame.draw.rect again. Changing a random variable is not enough.
What you could do is add a color attribute to your class and use that to change the color of the rect that you have to draw every frame anyway (exceptions may exist):
class RectCreator:
def __init__(self, location_x, location_y, width, height, color='blue'):
self.rect = pygame.Rect((location_x, location_y, width, height))
self.color = color
def draw(self, display):
pygame.draw.rect(display, self.color, self.rect, border_radius=20)
Then, create an instance and in your main loop, keep calling draw. You can then change the color simply by setting the color attribute
import random
...
btn_1 = RectCreator(350, 350, 100, 100)
...
while True:
for e in pygame.event.get():
if e.type == pygame.KEYDOWN:
btn_1.color = random.choice(('blue', 'green', 'red', 'yellow'))
btn_1.draw(display_surface)
...
pygame.display.flip()

Related

How to render the text like a all_sprites.update() but all_texts.update()?

I have a programm where i have a lot of text objects.
And i want to render it all by 1 command like a sprite.
For example:
all_sprites = pygame.sprite.Group()
sprite1 = mySprite([args])
sprite2 = mySprite([args])
all_sprite.add(sprite1, sprite2)
while True:
all_sprites.update(([width, height]))
in the Class mySprite() we have a def update() what working with calling by Class Group()
I want to doo this operation not with sprites but with text. Can i did that by pygame instrumental? Or i want make this class (like a class Group()) by myself? If the second option, then how i can make it
A rendered text is simply a pygame.Surface object. All you need to do is set the image attribute of the pygame.sprite.Sprite object with the rendered text surface and the rect attribute with the position of the text.
import pygame
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 100)
class Text(pygame.sprite.Sprite):
def __init__(self, font, text, color, center_pos):
super().__init__()
self.image = font.render(text, True, color)
self.rect = self.image.get_rect(center = center_pos)
all_text = pygame.sprite.Group()
all_text.add(Text(font, "Hello", "white", (200, 150)))
all_text.add(Text(font, "World!", "white", (200, 250)))
run = True
while run:
clock.tick(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window.fill(0)
all_text.draw(window)
pygame.display.flip()
pygame.quit()
exit()

First Dialogue Box

I'm in my early days of pygame coding, but here is the piece I am trying to figure out right now, which is to draw the dialogue box, then the text on top of it. When ran, I see the displayed dialogue box, but no text.
import pygame
from pygame.locals import *
import os
pygame.init()
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
screen = pygame.display.set_mode([SCREEN_WIDTH, SCREEN_HEIGHT])
class Text(pygame.sprite.Sprite):
def __init__(self, text):
# Call the parent class (Sprite) constructor
pygame.sprite.Sprite.__init__(self)
os.chdir(r"<my directory to the dialogue box>.png")
self.surf = pygame.image.load("spikey_box.png").convert()
os.chdir(r"<my directory to my font>")
self.font = pygame.font.Font("final_fantasy_36_font.ttf", 12)
# set up dialogue box sprite
self.rect = self.surf.get_rect()
self.rect.center = (400, 500)
screen.blit(self.surf, self.rect)
# for text
self.textSurf = self.font.render(text, True, (255, 255, 255))
self.textRect = self.textSurf.get_rect()
self.textRect.center = (400, 500)
screen.blit(self.textSurf, self.textRect)
test_message = Text("Hello, world!")
running = True
clock = pygame.time.Clock()
while running:
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
running = False
elif event.type == QUIT:
running = False
pressed_keys = pygame.key.get_pressed()
screen.fill((255, 255, 255))
screen.blit(test_message.surf, test_message.rect)
pygame.display.flip()
clock.tick(30)
pygame.quit()
I have some sample code that I was able to adapt a little and make work for another piece I'm working on that I tried to use for inspiration to build the class:
def text_objects(text, font):
textSurface = font.render(text, True, (0, 0, 0))
return textSurface, textSurface.get_rect()
def message_display(text):
largeText = pygame.font.Font('freesansbold.ttf', 20)
TextSurf, TextRect = text_objects(text, largeText)
TextRect.center = ((SCREEN_WIDTH//2), (SCREEN_HEIGHT//2))
screen.blit(TextSurf, TextRect)
The set of two functions were defined outside of the game loop, and worked fine when called from within the game loop as {message_display(f-string)}. I respectfully request guidance to learn how to ask the right question to figure out how to make the class implementation work. I want to build on it to the point that I can call the dialogue window and allow the player to scroll back over dialogue already given in that instanced conversation.
Thank you in advance!
When you make a PyGame Sprite based on pygame.sprite.Sprite, you must define two variables as a minimum:
sprite.image - used to hold the sprite bitmap
sprite.rect - defines the location and size of the .image
Your Text Sprite does not appear to be creating the .image so it wont work as a normal sprite. But since you directly blit() the Text.surf to the display, you've dodged this issue for now.
The code is not writing the text image on top of the background dialogue. It's writing it directly to the screen in the sprite __init__(). Once the sprite is constructed, this screen update is lost.
Probably you need something like:
class Text(pygame.sprite.Sprite):
def __init__(self, text):
# Call the parent class (Sprite) constructor
pygame.sprite.Sprite.__init__(self)
os.chdir(r"<my directory to the dialogue box>.png")
self.image = pygame.image.load("spikey_box.png").convert()
os.chdir(r"<my directory to my font>")
# Draw the text, centred on the background
self.font = pygame.font.Font("final_fantasy_36_font.ttf", 12)
text = self.font.render(text, True, (255, 255, 255))
centre_x = ( self.rect.width - text.get_width() ) // 2
centre_y = ( self.rect.height - text.get_height() ) // 2
self.image.blit( text, ( centre_x, centre_y ) )
# Set the rect
self.rect = self.image.get_rect()
self.rect.center = (400, 500)
Which loads the dialogue background image, and then rendered the text centred into that box so there is just a single image.
Since surf has been renamed to image, the main loop needs a tweak:
...
screen.fill((255, 255, 255))
screen.blit(test_message.image, test_message.rect)
pygame.display.flip()
clock.tick(30)
pygame.quit()
But really the code should use the PyGame Sprite functions for drawing, rather than accessing the internal Surface directly.
Thank you #Kingsley for your input, and you're definitely right, and helped point me in the right direction. I found that the issue was how I was instantiating the class object, and performing an odd call in the game loop that didn't really make sense. I was trying to blit a blit. I restructured my class object, and it now works perfectly!
class Text(pygame.sprite.Sprite):
def __init__(self):
# Call the parent class (Sprite) constructor
pygame.sprite.Sprite.__init__(self)
os.chdir(r"C:\Users\mcbri\Documents\Python\master\Resources\pv1\pv1_objects")
self.surf = pygame.image.load("spikey_box.png").convert() #for box
os.chdir(r"C:\Users\mcbri\Documents\Python\master\Resources\fonts")
self.font = pygame.font.Font("final_fantasy_36_font.ttf", 12)
self.rect = self.surf.get_rect()
self.rect.center = (400, 500)
def pop_message(self, text):
screen.blit(self.surf, self.rect)
self.textSurf = self.font.render(text, True, (255, 255, 255))
self.textRect = self.textSurf.get_rect()
self.textRect.center = (400, 500)
screen.blit(self.textSurf, self.textRect)
...in loop:
elif event.type == QUIT:
running = False
pressed_keys = pygame.key.get_pressed()
screen.fill((255, 255, 255))
test_message.pop_message("Hello, world!")
pygame.display.flip()
clock.tick(30)
Thank you so much for the fast input!

How to make Canvas widget color darker compared to the original color?

I have this canvas widget with a green rectangle:
canvas = tk.Canvas(root)
canvas.create_rectangle(0,0,50,100, fill='green')
I want to darken it. So:
canvas.itemconfig(1, fill=?????)
The issue is, what should I put in the ????? aka the 'darker' color of the original color.
Sure, I can just find a hex for a darker shade of green or something like that, but the point is: How can I find the darkened the widget based on the original color?
I don't necessarily need to find the darker version of the color if there is something like: canvas.itemconfig(1, darkness=-100).
Here is a simple example of how to use a function to lower the color based on a RGB tuple that is converted to hex. We start out with a lightish green and with each button press it gets darker. This is a simple example but I am sure you can adapt it for your needs.
import tkinter as tk
root = tk.Tk()
cv = (110, 160, 50)
canvas = tk.Canvas(root)
rect = canvas.create_rectangle(0,0,50,100, fill="#%02x%02x%02x" % cv)
canvas.pack()
def darker():
global cv
greater = True
cv = (cv[0]- 10, cv[1] - 10, cv[2] - 10)
for item in cv:
if item < 0:
greater = False
if greater == True:
canvas.itemconfig(rect, fill = "#%02x%02x%02x" % cv)
tk.Button(root, text="Darker", command=darker).pack()
root.mainloop()
Or you can do this using the current preferred concatenation with format():
import tkinter as tk
root = tk.Tk()
cv = (110, 160, 50)
canvas = tk.Canvas(root)
rect = canvas.create_rectangle(0,0,50,100, fill = "#{}".format("".join("{:02X}".format(a) for a in cv)))
canvas.pack()
def darker():
global cv
greater = True
cv = (cv[0]- 10, cv[1] - 10, cv[2] - 10)
for item in cv:
if item < 0:
greater = False
if greater == True:
canvas.itemconfig(rect, fill = "#{}".format("".join("{:02X}".format(a) for a in cv)))
tk.Button(root, text="Darker", command=darker).pack()
root.mainloop()

Issue with fitInView of QGraphicsView when ItemIgnoresTransformations is on

While trying to implement a scene where item sizes do not change but distances between items get magnified I encountered this problem with the following code which draws a rectangle and the text "A". Now if I set the flag ItemIgnoresTransformations on the rectangle item, zooming in causes the rectangle to vanish (click and drag mouse button around the rectangle). But that does not happen in case of the text. Also, the new viewport area set by fitInView is very different for I asked for:
import sys
from PyQt4 import QtCore, QtGui
class GV(QtGui.QGraphicsView):
def __init__(self, *args, **kwargs):
QtGui.QGraphicsView.__init__(self, *args, **kwargs)
def mousePressEvent(self, event):
pos = QtCore.QPoint(event.pos())
self.startPos = pos
def mouseReleaseEvent(self, event):
pos = QtCore.QPoint(event.pos())
self.endPos = pos
rect = QtCore.QRect(self.startPos, self.endPos)
sceneRect = self.mapToScene(rect).boundingRect()
print 'Selected area: viewport coordinate:', rect, \
', scene coordinate:', sceneRect
self.fitInView(sceneRect)
print 'new viewport in scene coordinates:', \
self.mapToScene(self.viewport().geometry()).boundingRect()
class Scene(QtGui.QGraphicsScene):
def __init__(self, *args, **kwargs):
QtGui.QGraphicsScene.__init__(self, *args, **kwargs)
self.itemA = QtGui.QGraphicsSimpleTextItem('A')
self.itemA.setPos(20, 20)
self.itemA.setFlag(QtGui.QGraphicsItem.ItemIgnoresTransformations, True)
self.addItem(self.itemA)
self.itemB = QtGui.QGraphicsRectItem(30, 50, 20, 20)
self.addItem(self.itemB)
self.itemB.setFlag(QtGui.QGraphicsItem.ItemIgnoresTransformations, True)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
widget = QtGui.QMainWindow()
scene = Scene()
view = GV(scene, widget)
widget.setCentralWidget(view)
widget.show()
app.exec_()
Any explanations will be appreciated!
In fact, the rectangle does not vanish. But it moves around "strangely".
self.itemB = QtGui.QGraphicsRectItem(30, 50, 20, 20)
This line may not be what you want. This creates an item and puts a rectangle/square starting from (30, 50) in local coordinates. Then you add this to the scene. This gives you an item anchored at (0, 0), spans up to (50, 70) but draws a rectangle only in the bottom right 20x20.
When you set ItemIgnoresTransformations, item can't do its regular transformations in case of a zoom. Scene zooms in, for item to ignore this transformation, it kind of "shrinks" itself. But it's still anchored at (0, 0) and the rectangle is at the bottom-right, so the drawn rectangle moves toward the upper-left.
Solution is rather simple. Don't create your rectangle in local coordinates, i.e. your rectangle should start from (0, 0) and you should position it explicitly. Which translates to this:
self.itemB = QtGui.QGraphicsRectItem(0, 0, 20, 20)
self.itemB.setPos(30, 50)

tkinter: dragging widgets

I'd like to make a drag-n-drop function for a widget. The code is this:
from tkinter import *
root = Tk()
root.config(background = "red", width = 500, height = 500)
root.title("root")
def frameDrag(event):
frame.place(x = event.x , y = event.y)
frame = Frame(root, width = 60, height = 30)
frame.place(x=0, y=0)
frame.bind("<B1-Motion>", frameDrag)
root.mainloop()
Basically, it should place the widget to the location you move your mouse to. Instead, the widget jumps around all over the window.
Any ideas how to fix this?
It is jumping all over the place because you are telling it to as shown by:
def frameDrag(event):
print event.x, event.y
frame.place(x = event.x , y = event.y)
Better to use a canvas widget and better to use B1-Click and B1-Release events and compute the delta. Look for the widget demo that comes along with Tkinter.

Resources