How does the ship object know to get displayed centred at the bottom of the main screen surface? - python-3.x

These are the relevant source files:
1. ship.py
#!/usr/bin/python3
import pygame
class Ship(object):
"""A class to manage the ship."""
def __init__(self, ai_game):
"""Initialize the ship and set its starting position."""
self.screen = ai_game.screen
self.screen_rect = ai_game.screen.get_rect()
# Load the ship image and get its rect.
self.image = pygame.image.load('images/ship.bmp')
self.rect = self.image.get_rect()
# Start each new ship at the bottom center of the screen.
self.rect.midbottom = self.screen_rect.midbottom
def blitme(self):
"""Draw the ship at its current location."""
self.screen.blit(self.image, self.rect)
2. alien_invasion.py
#!/usr/bin/python3
import pygame
from sys import exit
from settings import Settings
from ship import Ship
class AlienInvasion(object):
"""Overall class to manage game assets and behavior."""
def __init__(self):
"""Initialize the game, and create game resources."""
pygame.init()
self.settings = Settings()
self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height))
pygame.display.set_caption("Alien Invasion")
self.ship = Ship(self)
def run_game(self):
"""Start the main loop for the game"""
while True:
# Watch for keyboard and mouse events. ** The Event Loop **
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
# Redraw the screen during each pass through the loop
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
# Make the most recently drawn screen visible.
pygame.display.flip()
if __name__ == '__main__':
# Make a game instance, and run the game.
ai = AlienInvasion()
ai.run_game()
Running alien_invasion.py produces the following output...
...which is the desired output. I just don't understand how the ship's rect is positioned (correctly) centered at the bottom when the Ship.blitme() method takes parameters image and rect (and not rect.midbottom). To my understanding, this doesn't use the self.rect.midbottom attribute anywhere to draw the image on screen after defining it.
TIA!

When you create an instance of the ship class, you move its rect to the bottom middle of the screen with self.rect.midbottom = self.screen_rect.midbottom. This moves rect.x and rect.y as well, so when you blit the ship with the rect, it is already positioned right

Related

How to resize a window from the edges after adding the property QtCore.Qt.FramelessWindowHint

Good night.
I have seen some programs with new borderless designs and still you can make use of resizing.
At the moment I know that to remove the borders of a pyqt program we use:
QtCore.Qt.FramelessWindowHint
And that to change the size of a window use QSizeGrip.
But how can we resize a window without borders?
This is the code that I use to remove the border of a window but after that I have not found information on how to do it in pyqt5.
I hope you can help me with an example of how to solve this problem
from PyQt5.QtWidgets import QMainWindow,QApplication
from PyQt5 import QtCore
class Main(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
app = QApplication([])
m = Main()
m.show()
m.resize(800,600)
app.exec_()
If you use a QMainWindow you can add a QStatusBar (which automatically adds a QSizeGrip) just by calling statusBar():
This function creates and returns an empty status bar if the status bar does not exist.
Otherwise, you can manually add grips, and their interaction is done automatically based on their position. In the following example I'm adding 4 grips, one for each corner, and then I move them each time the window is resized.
class Main(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
self.gripSize = 16
self.grips = []
for i in range(4):
grip = QSizeGrip(self)
grip.resize(self.gripSize, self.gripSize)
self.grips.append(grip)
def resizeEvent(self, event):
QMainWindow.resizeEvent(self, event)
rect = self.rect()
# top left grip doesn't need to be moved...
# top right
self.grips[1].move(rect.right() - self.gripSize, 0)
# bottom right
self.grips[2].move(
rect.right() - self.gripSize, rect.bottom() - self.gripSize)
# bottom left
self.grips[3].move(0, rect.bottom() - self.gripSize)
UPDATE
Based on comments, also side-resizing is required. To do so a good solution is to create a custom widget that behaves similarly to QSizeGrip, but for vertical/horizontal resizing only.
For better implementation I changed the code above, used a gripSize to construct an "inner" rectangle and, based on it, change the geometry of all widgets, for both corners and sides.
Here you can see the "outer" rectangle and the "inner" rectangle used for geometry computations:
Then you can create all geometries, for QSizeGrip widgets (in light blue):
And for custom side widgets:
from PyQt5 import QtCore, QtGui, QtWidgets
class SideGrip(QtWidgets.QWidget):
def __init__(self, parent, edge):
QtWidgets.QWidget.__init__(self, parent)
if edge == QtCore.Qt.LeftEdge:
self.setCursor(QtCore.Qt.SizeHorCursor)
self.resizeFunc = self.resizeLeft
elif edge == QtCore.Qt.TopEdge:
self.setCursor(QtCore.Qt.SizeVerCursor)
self.resizeFunc = self.resizeTop
elif edge == QtCore.Qt.RightEdge:
self.setCursor(QtCore.Qt.SizeHorCursor)
self.resizeFunc = self.resizeRight
else:
self.setCursor(QtCore.Qt.SizeVerCursor)
self.resizeFunc = self.resizeBottom
self.mousePos = None
def resizeLeft(self, delta):
window = self.window()
width = max(window.minimumWidth(), window.width() - delta.x())
geo = window.geometry()
geo.setLeft(geo.right() - width)
window.setGeometry(geo)
def resizeTop(self, delta):
window = self.window()
height = max(window.minimumHeight(), window.height() - delta.y())
geo = window.geometry()
geo.setTop(geo.bottom() - height)
window.setGeometry(geo)
def resizeRight(self, delta):
window = self.window()
width = max(window.minimumWidth(), window.width() + delta.x())
window.resize(width, window.height())
def resizeBottom(self, delta):
window = self.window()
height = max(window.minimumHeight(), window.height() + delta.y())
window.resize(window.width(), height)
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
self.mousePos = event.pos()
def mouseMoveEvent(self, event):
if self.mousePos is not None:
delta = event.pos() - self.mousePos
self.resizeFunc(delta)
def mouseReleaseEvent(self, event):
self.mousePos = None
class Main(QtWidgets.QMainWindow):
_gripSize = 8
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
self.sideGrips = [
SideGrip(self, QtCore.Qt.LeftEdge),
SideGrip(self, QtCore.Qt.TopEdge),
SideGrip(self, QtCore.Qt.RightEdge),
SideGrip(self, QtCore.Qt.BottomEdge),
]
# corner grips should be "on top" of everything, otherwise the side grips
# will take precedence on mouse events, so we are adding them *after*;
# alternatively, widget.raise_() can be used
self.cornerGrips = [QtWidgets.QSizeGrip(self) for i in range(4)]
#property
def gripSize(self):
return self._gripSize
def setGripSize(self, size):
if size == self._gripSize:
return
self._gripSize = max(2, size)
self.updateGrips()
def updateGrips(self):
self.setContentsMargins(*[self.gripSize] * 4)
outRect = self.rect()
# an "inner" rect used for reference to set the geometries of size grips
inRect = outRect.adjusted(self.gripSize, self.gripSize,
-self.gripSize, -self.gripSize)
# top left
self.cornerGrips[0].setGeometry(
QtCore.QRect(outRect.topLeft(), inRect.topLeft()))
# top right
self.cornerGrips[1].setGeometry(
QtCore.QRect(outRect.topRight(), inRect.topRight()).normalized())
# bottom right
self.cornerGrips[2].setGeometry(
QtCore.QRect(inRect.bottomRight(), outRect.bottomRight()))
# bottom left
self.cornerGrips[3].setGeometry(
QtCore.QRect(outRect.bottomLeft(), inRect.bottomLeft()).normalized())
# left edge
self.sideGrips[0].setGeometry(
0, inRect.top(), self.gripSize, inRect.height())
# top edge
self.sideGrips[1].setGeometry(
inRect.left(), 0, inRect.width(), self.gripSize)
# right edge
self.sideGrips[2].setGeometry(
inRect.left() + inRect.width(),
inRect.top(), self.gripSize, inRect.height())
# bottom edge
self.sideGrips[3].setGeometry(
self.gripSize, inRect.top() + inRect.height(),
inRect.width(), self.gripSize)
def resizeEvent(self, event):
QtWidgets.QMainWindow.resizeEvent(self, event)
self.updateGrips()
app = QtWidgets.QApplication([])
m = Main()
m.show()
m.resize(240, 160)
app.exec_()
to hide the QSizeGrip on the corners where they shouldn't be showing, you can just change the background color of the QSizeGrip to camouflage them to the background. add this to each of the corners of musicamante's answer:
self.cornerGrips[0].setStyleSheet("""
background-color: transparent;
""")

Encountering a Syntax Error while attempting to blit an image in Python 3.8

I am encountering a syntax error after I blit an image on my display window. I made a separate module where inside of it, I created a class that would manage all the aspects(position, behaviour) of the image. I loaded the image and fetched its rect and finally I drew the image in it's desired position. The file had no errors and so I shifted to the main file that managed game assets and behaviours. In the main file, I imported the class that managed the image. Then, I made a call(after filling the background) to draw the image so it appears on top of the background. It gave me the error
line 46
self.ship.blitme()
^
SyntaxError: invalid syntax
Here's the code snippet to the image class
import pygame
class Ship:
"""A class to manage the ship."""
def __init__(self, ai_game):
"""Initialize the ship and set its starting position."""
self.screen = ai_game.screen
self.screen_rect = ai_game.screen.get_rect()
# Load the ship image and get its rect.
self.image = pygame.image.load('images/ship.bmp')
self.rect = self.image.get_rect()
# Start each new ship at the bottom center of the screen.
self.rect.midbottom = self.screen_rect.midbottom
def blitme(self):
"""Draw the ship at its current location."""
self.screen.blit(self.image, self.rect)
Here's the main class for managing game assets and behaviour
import sys
import pygame
from settings import Settings
from ship import Ship
class AlienInvasion:
"""Overall class to manage game assets and behavior."""
def __init__(self):
"""Initialize the game, and create game resources."""
pygame.init()
self.settings = Settings()
self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height))
pygame.display.set_caption("Alien Invasion")
# Set the background color.
self.bg_color = (230, 230, 230)
self.ship = Ship(self)
def run_game(self):
"""Start the main loop for the game."""
while True:
self._check_events()
self._update_events()
# Redraw the screen during each pass through the loop.
def _check_events(self):
# Respond for keyboard and mouse events
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
def _update_events(self):
"""Update images on the screen, and flip to the new screen."""
self.screen.fill((self.settings.bg_color)
self.ship.blitme()
# Make the most recently drawn screen visible.
pygame.display.flip()
if __name__ == '__main__':
# Make a game instance, and run the game.
ai = AlienInvasion()
ai.run_game()
You forgot a closing bracket in line 45, main.py:
self.screen.fill((self.settings.bg_color) ) # <-- this one
Unfortunately, Python often marks a line beneath the wrong one.

Display image based on a output in one window (Python)

I have an implementation of k-NN algorithm in Python, that returns the class label of an input. What I need is to show an image assigned to the class label in one window while output is coming (by refreshing the window). Problem is, I am not very experienced in GUI programming, thus I need some resources and assistance to start with. What libraries, books and tutorials could you recommend? Pieces of code would be appreciated too.
There are many libraries allow you add images to your program such as Turtle, PIL.ImagTk and Canvas and you can even use Pygame to a best performance.
PIL.ImagTk and Canvas:
from Tkinter import *
from PIL import ImageTk
backgroundImage = PhotoImage("image path (gif/PPM)")
canvas = Canvas(width = 200, height = 200, bg = 'blue')
canvas.pack(expand = YES, fill = BOTH)
image = ImageTk.PhotoImage(file = "C:/Python27/programas/zimages/gato.png")
canvas.create_image(10, 10, image = image, anchor = NW)
mainloop()
Turtle:
import turtle
screen = turtle.Screen()
# click the image icon in the top right of the code window to see
# which images are available in this trinket
image = "rocketship.png"
# add the shape first then set the turtle shape
screen.addshape(image)
turtle.shape(image)
screen.bgcolor("lightblue")
move_speed = 10
turn_speed = 10
# these defs control the movement of our "turtle"
def forward():
turtle.forward(move_speed)
def backward():
turtle.backward(move_speed)
def left():
turtle.left(turn_speed)
def right():
turtle.right(turn_speed)
turtle.penup()
turtle.speed(0)
turtle.home()
# now associate the defs from above with certain keyboard events
screen.onkey(forward, "Up")
screen.onkey(backward, "Down")
screen.onkey(left, "Left")
screen.onkey(right, "Right")
screen.listen()
Now let’s change the background
import turtle
screen = turtle.Screen()
# this assures that the size of the screen will always be 400x400 ...
screen.setup(400, 400)
# ... which is the same size as our image
# now set the background to our space image
screen.bgpic("space.jpg")
# Or, set the shape of a turtle
screen.addshape("rocketship.png")
turtle.shape("rocketship.png")
move_speed = 10
turn_speed = 10
# these defs control the movement of our "turtle"
def forward():
turtle.forward(move_speed)
def backward():
turtle.backward(move_speed)
def left():
turtle.left(turn_speed)
def right():
turtle.right(turn_speed)
turtle.penup()
turtle.speed(0)
turtle.home()
# now associate the defs from above with certain keyboard events
screen.onkey(forward, "Up")
screen.onkey(backward, "Down")
screen.onkey(left, "Left")
screen.onkey(right, "Right")
screen.listen()
for Turtle:
http://blog.trinket.io/using-images-in-turtle-programs/

Pyqt4: How to correct QGraphicsItem position?

I'm trying to draw a path on a QGraphicsView. However, the position seems not right. The first point(red) is (0,0), which is supposed to be at the top-left corner. How do I move the drawing to the right position?
from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import QPointF as qpf
import sys
from PyQt4.QtGui import QPainterPath
data= [qpf(0,0),qpf(40,30),qpf(30,60),qpf(70,90),qpf(20,120),qpf(60,150)]
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.view = View(self)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.view)
class View(QtGui.QGraphicsView):
def __init__(self, parent):
QtGui.QGraphicsView.__init__(self, parent)
self.setScene(QtGui.QGraphicsScene(self))
item = QtGui.QGraphicsRectItem(data[0].x()-2,data[0].y()-2,4,4)
item.setBrush(QtCore.Qt.red)
self.scene().addItem(item)
self.path = path = QPainterPath(data[0])
for d in data[1:]:
path.lineTo(d)
item = QtGui.QGraphicsPathItem(path)
self.scene().addItem(item)
def mouseReleaseEvent(self, event):
pos = event.pos()
rect = QtCore.QRectF(pos.x()-2, pos.y()-2,4,4)
item = QtGui.QGraphicsRectItem(rect)
self.scene().addItem(item)
if self.path.intersects(rect):
print 'on line'
else:
print 'Not belong to line (%d, %d)' % (pos.x(), pos.y())
QtGui.QGraphicsView.mouseReleaseEvent(self, event)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Window()
window.resize(400, 400)
window.show()
sys.exit(app.exec_())
This behavior is because you draw using QGraphicsScene and QGraphicsView. This is because view and scene have some automatism, which are very convenient, normally. The scene coordinated can be completely different from the view (which is in pixels).
From the docs: "After you call show(), the view will by default scroll to the center of the scene and display any items that are visible at this point." show() is called implicitly in your case, I believe after you add an item to the scene.
I can think of two possibilities to get what you want:
1) change the view onto your scene, so that the scene-coordinate (0, 0) is in the upper left corner of your view.
2) Do NOT use QGraphicsScene and QGraphicsView, but just draw on a widget from its paint event as shown for example here. This means your dimensions are all in pixels, i.e. the coordinates of your points are pixels. And no automatism that might confuse you is done.
You would want to use self.setAlignment(QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) in your view class constructor, that will make 0,0 of the scene coordinates the same as 0,0 in view coordinates.AlignmentFlag
I would also suggest pos = self.mapToScene(event.pos()) in the mouseReleaseEvent. that way if you zoom in or scroll the scene, the click will happen in the right place in the view. QGraphicsView.mapFromScene

Pygame play a sound when click on certain spot on GUI

I'm an amateur, very inexperience programmer. I've been working on an art project that I am programming using Pygame. I've hit a road block, ad can't figure out how to do what I need it to do.
I need it to play a specific sound when clicking on a specific place on the GUI. For example, when you click on the red button, it plays an audio file that says "red"
I also need it to be able to play sounds with clicking and dragging on the canvas part.
I hope this is enough detail. Thanks for the help!
import pygame, sys, time, random
from pygame.locals import *
# set up pygame
pygame.init()
pygame.mixer.init
pygame.mixer.get_init
bgimg="GUIsmall.gif"
inst="instructionssm.gif"
white=(255,255,255)
screen=pygame.display.set_mode((800,600), pygame.RESIZABLE)
screen.fill(white)
bg=pygame.image.load(bgimg)
instrimg=pygame.image.load(inst)
screen.blit(bg, (0,0))
pygame.display.flip()
red=pygame.mixer.Sound("red.mp3")
while True:
for event in pygame.event.get():
if event.type==pygame.QUIT:
raise SystemExit
elif event.type==pygame.MOUSEBUTTONDOWN:
red.play(0,0,0)
I think you should have a class button and a collection of buttons:
class Button:
__init__(self, name, position, image_file, sound_file):
self.name = name
self.image = pygame.image.load(image_file)
self.sound = pygame.mixer.Sound(sound_file)
self.position = position
self.rect = pygame.Rect(position, self.image.get_size())
buttons = []
buttons.add( Button("red", (0,0), "red.png", "red.mp3") )
...
Then you can use it in the main loop:
while True:
for event in pygame.event.get():
if event.type==pygame.QUIT:
raise SystemExit
elif event.type==pygame.MOUSEBUTTONDOWN:
for b in buttons:
if b.rect.collidepoint(event.pos):
b.sound.play()

Resources