Making Minecraft Using Ursina & Python! (1/19/25 blog post)

Ursina is an easy-to-use, open source 3D game engine that can run better than Pygame. Since Pygame has some issues with frame-rate, poor performance, and is not the best when making games with 3D capability, I won’t be using it (for this project). This is 3D games-wise, though. Pygame is still better and MUCH easier to use when making simpler looking games. Ursina only needs a few lines of code to really make a game, and is beginner friendly when making a 3D program. However, like Pygame, it does have some bugs that come with it such as player model issues, blend files unable to load, and collider issues when making a player model. Unfortunately, Ursina isn’t available for MacOS (you can use Panda3D though, which is available for MacOS and Windows).

Besides these issues, Ursina is quite powerful and a good option for a 3D game engine for beginners. Now, let’s get started.

INTRO TO URSINA

First and possibly the most obvious step is to install Ursina.

  • Open your terminal in whatever code editor you use and type in “pip install Ursina”
pip install Ursina
  • We’re now able to start coding. first import all objects of Ursina using the * character.
from ursina import *
  • Now, we must create and instance of the game. You can name this whatever you’d like, but for this it will be named “app” (I know, so creative).
app = Ursina()

*Make sure to add parenthesis at the end or the code will yell at you ( I learnt this the hard way numerous times).

  • Now, lets run it. This will create the interface where the game will be displayed.
from ursina import *
app = Ursina()
app.run()

* If you thought that it would look like something such as unity or unreal engine, remember that we are using Python, not C# or C++ lol.

  • The astronomically microscopic numbers on the top right-hand corner of the screen shows the frame rate, the number of entities on the screen, and the number of collisions.
  • Lets add an entity, which is a base object for anything in Ursina. This can be anything, like a square, a circle, a cube, etc… Anything you see on the screen will count as an entity. It will be automatically added to the game, rather than calling it in a loop. An entity doesn’t need a color (it will default to white if no color is stated).
test_square = Entity(model='circle', color=color.blue)
  • This will create circle at the center of the screen when we run the program.
  • Let’s change it to a different shape.
test_square = Entity(model='quad', color=color.blue) 
  • This will change the circle into a square which it known as a “quad” in Ursina.

* You are able to add 3D objects like cubes, but since we’re looking at only one side at the moment, it’s kind of redundant to call a 3D object at the moment. We’ll get to that later, though.

  • You can also scale the entity by passing a number for the x and y axies.
test_square = Entity(model='quad', color=color.blue, scale = (1, 4))
  • In this instance, the 1 is how wide the entity will be, and the 4 is how long.
  • You can also change the position of the entity. It uses the same way we changed the scale of the entity, but this time we use “position” (or “pos” if you’re too lazy to write the whole word out).
test_square = Entity(model='quad', color=color.blue, scale = (1, 4), position = (5, 1))
  • This will move the square up 1 unit and right 5 units.
  • It should be obvious, but the center of the screen is the origin point (0,0). Making the y-axis positive or negative will scale or move the entity up or down, same with the x-axis (I know this is obvious but this 3D engine is interesting and fun to mess around in).
  • Now, lets make a function to move the entity. You will simply call the object you want to move in the function, the axis you wish to change, and how fast it will move.
def update():
    test_square.x -= 1
  • This will move the entity to the left at a speed of 1. Now, that may sound slow, but watch this:
Don’t blink or you’ll miss it lol
  • Now obviously we need to slow it down, so we’ll use “time.dt”. This will be multiplied by the speed at which the object is going, and will adjust to any frame-rate.
def update():
    test_square.x -= 1 * time.dt
  • It will now look like this:

*Note to self: going to add more in the future!

1/10/25 BLog Post (Happy New Year!)

Attempting to finish up the Drum Kit in Python!!!!

What it does so far…

ADDING A PLAY/PAUSE BUTTON

First, create the button in the main loop, and place it on the screen.

play_pause = py.draw.rect(screen, grey, [50, HEIGHT - 150, 200, 100], 0, 5)
    play_text = label_font.render('Play/Pause', True, white)
    screen.blit(play_text, (70, HEIGHT - 130))

After that, create a new variable called “mediumFont” as in indicator if the program is playing or not. This will be connected to “play_text2”.

 if playing:
        play_text2 = mediumFont.render('Playing', True, darkGrey)
    else:
        play_text2 = mediumFont.render('Paused', True, darkGrey)
        screen.blit(play_text2, (70, HEIGHT - 100))

The “play_text2” will have “Playing” or “Pausing” shown on screen when the “Play/Pause” button is pressed.

So… we made the button show up on screen, but we haven’t made it actually work yet.

Down into where the buttons pressed on your keyboard are called, make a new event type of ‘MOUSEBUTTONUP”. This will make the program able to play and pause when the button is clicked.

if event.type == py.MOUSEBUTTONUP:
            if play_pause.collidepoint(event.pos):
                if playing:
                    playing = False
                elif not playing:
                    playing = True

RESULTS:

11/8/24 Blog Post

MAKING (still) A BEAT MAKER IN PYTHON WHOO!!!!

FULL CODE(so far)

import pygame as py

from pygame import mixer
py.init()

WIDTH, HEIGHT = 1400, 800

black = (0,0,0)
white = (255,255,255)
grey = (128, 128, 128)
green = (0, 255, 0)
gold = (212, 175, 55)
blue = (0, 255, 255)


screen = py.display.set_mode([WIDTH, HEIGHT])
py.display.set_caption("Beat Maker")
label_font = py.font.Font('Roboto-Bold.ttf', 32)

FPS = 60
timer = py.time.Clock()
beats = 8
instrumnents = 6
boxes = []
clicked = [[-1 for _ in range(beats)] for _ in range(instrumnents)]
bpm = 240
playing = True
active_length = 0
active_beat = 1
beat_changed = True

# ---------Sounds---------#
hi_hat = mixer.Sound('sounds\hi hat.WAV')
snare = mixer.Sound('sounds\snare.WAV')
clap = mixer.Sound('sounds\clap.wav')
kick = mixer.Sound('sounds\kick.WAV')
crash = mixer.Sound('sounds\crash.wav')
tom = mixer.Sound('sounds\\tom.WAV')
py.mixer.set_num_channels(instrumnents * 3)
# ------------------------#


def play_notes():
    '''plays the sound of the instrument'''
    for i in range(len(clicked)):
        if clicked[i][active_beat] == 1:
            if  i == 0:
                hi_hat.play()
            if  i == 1:
                snare.play()
            if  i == 2:
                kick.play()
            if  i == 3:
                crash.play()
            if  i == 4:
                clap.play()
            if  i == 5:
                tom.play()


def draw_grid(clicks, beat):
    # Making the boxes for the interface
    left_box = py.draw.rect(screen, grey, [0, 0, 200, HEIGHT - 200], 5)
    bottom_box = py.draw.rect(screen, grey, [0, HEIGHT - 200, WIDTH, 200], 5)
    boxes = []
    colors = [grey, white, grey]
    # Making the names of all the instruments
    hi_hat_text = label_font.render('Hi-hat', True, white)
    screen.blit(hi_hat_text, (30, 30))
    snare_text = label_font.render('Snare', True, white)
    screen.blit(snare_text, (30, 130))
    kick_text = label_font.render('Kick', True, white)
    screen.blit(kick_text, (30, 230))
    crash_text = label_font.render('Crash', True, white)
    screen.blit(crash_text, (30, 330))
    clap_text = label_font.render('Clap', True, white)
    screen.blit(clap_text, (30, 430))
    floor_tom_text = label_font.render('Floor Tom', True, white)
    screen.blit(floor_tom_text, (30, 530))
    # draw some lines between the instrument names
    for i in range(instrumnents):
        py.draw.line(screen, grey, (0, (i * 100) + 100), (200, (i * 100) + 100), 5)
    # check how many boxes there are for each instrument
    for i in range(beats):
        for j in range(instrumnents):
            if clicks[j][i] == -1:
                color = grey
            else:
                color = green
                # Making a tri-color effect with rects
            rect = py.draw.rect(screen, color, 
            [i * ((WIDTH - 200) // beats) + 205, (j * 100) + 5, ((WIDTH - 200) // beats) - 10, 
            ((HEIGHT- 200)// instrumnents) - 10], 0, 3)
            # Blank/ NOT colorful rect
            py.draw.rect(screen, gold, 
            [i * ((WIDTH - 200) // beats) + 200, (j * 100), ((WIDTH - 200) // beats), 
             ((HEIGHT- 200)// instrumnents)], 5, 5)
            py.draw.rect(screen, black, 
            [i * ((WIDTH - 200) // beats) + 200, (j * 100), ((WIDTH - 200) // beats), 
             ((HEIGHT- 200)// instrumnents)], 2, 5)
            boxes.append((rect, (i, j)))
        active = py.draw.rect(screen, blue, [beat * ((WIDTH-200)//beats) + 200, 0, 
                ((WIDTH-200)//beats), instrumnents*100], 5, 3)
    return boxes


# main loop
run = True
while run:
    timer.tick(FPS)
    screen.fill(black)
    boxes = draw_grid(clicked, active_beat)
    if beat_changed:
        play_notes()
        beat_changed = False

    for event in py.event.get():
        if event.type == py.QUIT:
            run = False
        if event.type == py.MOUSEBUTTONDOWN:
            for i in range(len(boxes)):
                if boxes[i][0].collidepoint(event.pos):
                    coords = boxes[i][1]
                    clicked[coords[1]][coords[0]] *= -1

    beat_length = 3600 // bpm

    if playing:
       if active_length < beat_length:
           active_length += 1
       else:
           active_length = 0
           if active_beat < beats - 1:
               active_beat += 1
               beat_changed = True
           else:
               active_beat  = 0
               beat_changed = True

    py.display.flip()
py.quit()

What it does so far:

Adding later: Different kits, a menu to select the kits, bpm change, and more.

def play_notes does as the name says… it plays the notes.

def draw_grid just makes the interface and puts them on the screen. It shows the names and the initial design of the program.

def draw_grid:

def draw_grid(clicks, beat):
    # Making the boxes for the interface
    left_box = py.draw.rect(screen, grey, [0, 0, 200, HEIGHT - 200], 5)
    bottom_box = py.draw.rect(screen, grey, [0, HEIGHT - 200, WIDTH, 200], 5)
    boxes = []
    colors = [grey, white, grey]
    # Making the names of all the instruments
    hi_hat_text = label_font.render('Hi-hat', True, white)
    screen.blit(hi_hat_text, (30, 30))
    snare_text = label_font.render('Snare', True, white)
    screen.blit(snare_text, (30, 130))
    kick_text = label_font.render('Kick', True, white)
    screen.blit(kick_text, (30, 230))
    crash_text = label_font.render('Crash', True, white)
    screen.blit(crash_text, (30, 330))
    clap_text = label_font.render('Clap', True, white)
    screen.blit(clap_text, (30, 430))
    floor_tom_text = label_font.render('Floor Tom', True, white)
    screen.blit(floor_tom_text, (30, 530))
    # draw some lines between the instrument names
    for i in range(instrumnents):
        py.draw.line(screen, grey, (0, (i * 100) + 100), (200, (i * 100) + 100), 5)
    # check how many boxes there are for each instrument
    for i in range(beats):
        for j in range(instrumnents):
            if clicks[j][i] == -1:
                color = grey
            else:
                color = green
                # Making a tri-color effect with rects
            rect = py.draw.rect(screen, color, 
            [i * ((WIDTH - 200) // beats) + 205, (j * 100) + 5, ((WIDTH - 200) // beats) - 10, 
            ((HEIGHT- 200)// instrumnents) - 10], 0, 3)
            # Blank/ NOT colorful rect
            py.draw.rect(screen, gold, 
            [i * ((WIDTH - 200) // beats) + 200, (j * 100), ((WIDTH - 200) // beats), 
             ((HEIGHT- 200)// instrumnents)], 5, 5)
            py.draw.rect(screen, black, 
            [i * ((WIDTH - 200) // beats) + 200, (j * 100), ((WIDTH - 200) // beats), 
             ((HEIGHT- 200)// instrumnents)], 2, 5)
            boxes.append((rect, (i, j)))
        active = py.draw.rect(screen, blue, [beat * ((WIDTH-200)//beats) + 200, 0, 
                ((WIDTH-200)//beats), instrumnents*100], 5, 3)
    return boxes

Voxel Engine in Python and OpenGL(Blog Post 10/4/24)

MAKING THE OPENGL WINDOW

To install OpenGL and other modules:

pip install PyOpenGL
pip install pygame moderngl PyGLM numba

First import the common modules for the program in “settings.py”.

from numba import njit
import numpy as np
import glm
import math

Put in the respective background color and window resolution in “settings.py”.

# Window resolution
WIN_RES = glm.vec2(1600, 900)

# Colors
BG_COLOR = glm.vec3(0.1, 0.16, 0.25)

Import modules in “main.py” and create a class of VoxelEngine.

from settings import *
import moderngl as mgl
import pygame as pg
import sys

class VoxelEngine:

class VoxelEngine:
    def __init__(self):
        pg.init()
        pg.display.gl_set_attribute(pg.GL_CONTEXT_MAJOR_VERSION, 3)
        pg.display.gl_set_attribute(pg.GL_CONTEXT_MINOR_VERSION, 3)
        pg.display.gl_set_attribute(pg.GL_CONTEXT_PROFILE_MASK, pg.GL_CONTEXT_PROFILE_CORE)
        pg.display.gl_set_attribute(pg.GL_DEPTH_SIZE, 24)

        pg.display.set_mode(WIN_RES, flags=pg.OPENGL | pg.DOUBLEBUF)
        self.ctx = mgl.create_context()

        self.ctx.enable(flags=mgl.DEPTH_TEST | mgl.CULL_FACE | mgl.BLEND)
        self.ctx.gc_mode = 'auto'

        self.clock = pg.time.Clock()
        self.delta_time = 0
        self.time = 0

        self.is_running = True

    def update(self):
        self.delta_time = self.clock.tick()
        self.time = pg.time.get_ticks() * 0.001
        pg.display.set_caption(f'{self.clock.get_fps() :.0f}')

    def render(self):
        self.ctx.clear(color=BG_COLOR)
        pg.display.flip()

    def handle_events(self):
        for event in pg.event.get():
            if event.type == pg.QUIT or (event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE):
                self.is_running = False

    def run(self):
        while self.is_running:
            self.handle_events()
            self.update()
            self.render()
        pg.quit()
        sys.exit()

if __name__ == '__main__':
    app = VoxelEngine()
    app.run()

(this will get larger overtime)

Define the attributes for the program (in “def __init__”).

pg.init()
        pg.display.gl_set_attribute(pg.GL_CONTEXT_MAJOR_VERSION, 3)
        pg.display.gl_set_attribute(pg.GL_CONTEXT_MINOR_VERSION, 3)
        pg.display.gl_set_attribute(pg.GL_CONTEXT_PROFILE_MASK, pg.GL_CONTEXT_PROFILE_CORE)

Set the window resolution and create OpenGL context

  pg.display.set_mode(WIN_RES, flags=pg.OPENGL | pg.DOUBLEBUF)
        self.ctx = mgl.create_context()

Enable the context and set the garbage collection mode(gc_mode) to ‘auto’

self.ctx.enable(flags=mgl.DEPTH_TEST | mgl.CULL_FACE | mgl.BLEND)
        self.ctx.gc_mode = 'auto'

After that, set a time variable to keep the window on the screen a variable to run things in the main function

self.clock = pg.time.Clock()
        self.delta_time = 0
        self.time = 0

        self.is_running = True

In “def update()”, set the delta time and update its tick for every frame, then put it as a caption on the top left-hand corner of the screen.

self.delta_time = self.clock.tick()
        self.time = pg.time.get_ticks() * 0.001
        pg.display.set_caption(f'{self.clock.get_fps() :.0f}')

Plaster the background color from “settings.py” and update it to the screen in “def render()”

 self.ctx.clear(color=BG_COLOR)
        pg.display.flip()

In “def handle_events()”, make a for loop that checks if the player has clicked the “x” on the window or the down arrow key and the “escape” key (Esc) and will turn “is_running” to be false, which closes the window.

def handle_events(self):
        for event in pg.event.get():
            if event.type == pg.QUIT or (event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE):
                self.is_running = False

Initialize all functions in “def run()”.

def run(self):
        while self.is_running:
            self.handle_events()
            self.update()
            self.render()
        pg.quit()
        sys.exit()

and this too for the code to actually work 🙂

if __name__ == '__main__':
    app = VoxelEngine()
    app.run()

RESULT:

W/O the background color:

WITH the background:

I’m now working on the initial setup 😀

9/36/24 Blog Post

making 2048 in Python.

SETTING UP THE SCREEN

Made a blank pygame screen.

Made it using the WINDOW constant variable.

WINDOW = py.display.set_mode((WIDTH, HEIGHT))

The tuple (WIDTH, HEIGHT) are also constants set at 800 by 800.

WIDTH, HEIGHT = 800, 800

DRAWING THE WINDOW

Made a function of draw to fill the window with the background color (BG_COLOR).

Outside draw()

BG_COLOR = (205, 192, 180)

Used the variable to fill the window color and then displaying it on the window.

def draw()

def draw(window):
    window.fill(BG_COLOR)
    draw_grid(window)
    py.display.update()

Added draw() to the main loop

Main Loop:

def main(window):
    clock = py.time.Clock()
    run = True
    while run:
        clock.tick(FPS)
 
        for event in py.event.get():
            if event.type == py.QUIT:
                run = False
                break
        draw(window)
            
    py.quit()

RESULT:

9/15/24 Blog Post

MADE COLLISION AND GRAVITY FOR THE JETPACK TO WORK!!!!!

Added a y velocity and gravity for the player and jetpack.

y_vel = 0 

grav = 0.2

made a if statement for the jetpack to work

if not pause:
        if booster:
            y_vel -= grav
            #makes player go up when pressing button
        else: y_vel += grav

        if (colliding[0] and y_vel > 0) or (colliding[1] and y_vel < 0):
            y_vel = 0
        #makes player go down when not pressing button
        player_y += y_vel

if the player presses the key down, the y vel will become negative, causing the player to go up (- gravity). if the player lets go of the key, the y vel will become positive, causing the player to go down (+ gravity).

colliding is a function to check_colliding. colliding[0] is the top of the game’s screen, and colliding[1] is the bottom. when the player goes up. check_colliding will check if the player has reached the top, and will add collision. Same thing will happen to the bottom of the screen.

check_colliding function

def check_colliding():
    coll = [False, False]
    if player.colliderect(bottom_plat):
        coll[0] = True
    elif player.colliderect(top_plat):
        coll[1] = True
    return coll

RESULT.

9/5/24 Blog post: Jetpack joyride

FULL code (so far)

#Jetpack Joyride in Python

import random
import pygame as py

py.init()

WIDTH = 1000
HEIGHT = 600

screen = py.display.set_mode([WIDTH, HEIGHT])
surface = py.Surface((WIDTH, HEIGHT), py.SRCALPHA)
py.display.set_caption("Jetpack Joyride Remake")
FPS = 60
timer = py.time.Clock()
font = py.font.Font('freesansbold.ttf', 32)
BG_COLOR = (128, 128, 128)
lines = [0, WIDTH//4, 2* WIDTH//4, 3* WIDTH//4]
game_speed = 2
pause = False
init_y = HEIGHT - 130
player_y = init_y
booster = False
counter = 0


#Drawing the background screen
def draw_screen(line_list):
    screen.fill("black")
    py.draw.rect(surface, (BG_COLOR[0], BG_COLOR[1], BG_COLOR[2], 50), [0, 0, WIDTH, HEIGHT])
    screen.blit(surface, (0, 0))
    top_rect = py.draw.rect(screen, 'gray', [0, 0, WIDTH, 50])
    bottom_rect = py.draw.rect(screen, 'gray', [0, HEIGHT - 50, WIDTH, 50])

    #making the infinite lines on the screen
    for i in range(len(line_list)):
        py.draw.line(screen, 'black', (line_list[i], 0), (line_list[i], 50), 3)
        py.draw.line(screen, 'black', (line_list[i], HEIGHT - 50), (line_list[i], HEIGHT), 3)
        if not pause:
            line_list[i] -= game_speed
        if line_list[i] < 0:
            line_list[i] = WIDTH
    return line_list, top_rect, bottom_rect


#Dawing the player and animated states
def draw_player():
    player_hitbox = py.rect.Rect((120, player_y + 10), (25, 60)) # Player' hitbox
    if player_y < init_y or pause:
        if booster:
            py.draw.ellipse(screen, 'red', [100, player_y + 50, 20, 30])# overall body of the flame
            py.draw.ellipse(screen, 'orange', [105, player_y + 50, 10, 30])# detail
            py.draw.ellipse(screen, 'yellow', [110, player_y + 50, 5, 30])# also detail
        py.draw.rect(screen,'#ffca69', [128, player_y + 60, 10, 20], 0, 3)#ligher leg
        py.draw.rect(screen,'#ffa600', [130, player_y + 60, 10, 20], 0, 3)#darker leg
    else:
        #drawing the animated legs
        if counter < 10:
            py.draw.line(screen, '#ffca69', [128, player_y + 60], [140, player_y + 80], 10)#lighter leg
            py.draw.line(screen, '#ffa600', [130, player_y + 60], [120, player_y + 80], 10)#darker leg
        elif 10 <= counter < 20:
            py.draw.rect(screen,'#ffca69', [128, player_y + 60, 10, 20], 0, 3)#ligher leg
            py.draw.rect(screen,'#ffa600', [130, player_y + 60, 10, 20], 0, 3)#darker leg
        elif 20 <= counter < 20:
            py.draw.line(screen, '#ffca69', [128, player_y + 60], [120, player_y + 80], 10)#lighter leg
            py.draw.line(screen, '#ffa600', [130, player_y + 60], [140, player_y + 80], 10)#darker leg
        else:
            py.draw.rect(screen,'#ffca69', [128, player_y + 60, 10, 20], 0, 3)#ligher leg
            py.draw.rect(screen,'#ffa600', [130, player_y + 60, 10, 20], 0, 3)#darker leg

    # Player's body        
    py.draw.rect(screen, 'white', [100, player_y + 20, 20, 30], 0, 5) # Player's jetpack
    py.draw.ellipse(screen, '#ffa600', [120, player_y + 20, 20, 50]) # Player's main body
    py.draw.circle(screen, '#ffa600', (135, player_y + 15), 10)# Player's head
    py.draw.circle(screen, 'black', (138, player_y + 12), 3)# Player's head
    return player_hitbox

# Main loop
run = True
while run:
    timer.tick(FPS)
    if counter < 40:
        counter += 1
    else: 
        counter = 0
    lines, top_plat, bottom_plat = draw_screen(lines)
    player = draw_player()

    for event in py.event.get():
        if event.type == py.QUIT:
            run = False

    py.display.flip()
py.quit()

DRAWING THE BACKGROUND SCREEN (and making the lines)!!!

“BG_COLOR” is the gray rectangle at the top and bottom of the screen evenly separated by the lines. They are then made to be transparent where the player and obstacles are going to be placed.

8/29/24 Blog post

I didn’t actually try to do a project today but I did watch a few videos on how to make a FPS 3D game in Unity. It explained the basics on how to make the player rig, and the different components to make the player rig be able to be controlled. It also explained the basic movement for the player rig, how to connect them to VS code in Unity, and rigging components together to make the player rig moveable.

That’s the farthest I got since I decided it would be a splendid idea and go do other errands… I promise to actually work on something next Friday :D.

Calculator in JS

HTML:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    
    <div id="calculator">
        <input id="display" readonly>
        <div id="keys">
            <button onclick="appendToDisplay('+')" class="operator-btn">+</button>
            <button onclick="appendToDisplay('7')">7</button>
            <button onclick="appendToDisplay('8')">8</button>
            <button onclick="appendToDisplay('9')">9</button>
            <button onclick="appendToDisplay('-')" class="operator-btn">-</button>
            <button onclick="appendToDisplay('4')">4</button>
            <button onclick="appendToDisplay('5')">5</button>
            <button onclick="appendToDisplay('6')">6</button>
            <button onclick="appendToDisplay('*')" class="operator-btn">*</button>
            <button onclick="appendToDisplay('1')">1</button>
            <button onclick="appendToDisplay('2')">2</button>
            <button onclick="appendToDisplay('3')">3</button>
            <button onclick="appendToDisplay('/')" class="operator-btn">/</button>
            <button onclick="appendToDisplay('0')">0</button>
            <button onclick="appendToDisplay('.')">.</button>
            <button onclick="calculate()">=</button>
            <button onclick="clearDisplay()" class="operator-btn">C</button>
        </div>
    </div>

    <script src="index.js"></script>
</body>
</html>

CSS:

body{
    margin: 0;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    background-color: hsl(0, 0%, 90%);

}
#calculator{
    font-family: Arial, sans-serif;
    background-color: hsl(0, 0%, 15%);
    border-radius: 15px;
    max-width: 500px;
    overflow: hidden;
}
#display{
    width: 100%;
    padding: 20px;
    font-size: 5rem;
    text-align: left;
    border: none;
    background-color: hsl(0, 0%, 20%);
}
#keys{
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 10px;
    padding: 25px;
}
button{
    width: 100px;
    height: 100px;
    border-radius: 50px;
    border: none;
    background-color: hsl(0, 0%, 30%);
    color: white;
    font-size: 3rem;
    font-weight: bold;
    cursor: pointer;
}
button:hover{
    background-color: hsl(0, 0%, 40%);

}
button:active{
    background-color: hsl(0, 0%, 60%);

}
.operator-btn{
    background-color: hsl(198, 83%, 47%);

}
.operator-btn:hover{
    background-color: hsl(198, 83%, 67%);

}
.operator-btn:active{
    background-color: hsl(198, 83%, 87%);

}

I don’t have JS yet because I’ve not finished it :/

Created the display screen and buttons

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    
    <div id="calculator">
        <input id="display" readonly>
        <div id="keys">
            <button onclick="appendToDisplay('+')" class="operator-btn">+</button>
            <button onclick="appendToDisplay('7')">7</button>
            <button onclick="appendToDisplay('8')">8</button>
            <button onclick="appendToDisplay('9')">9</button>
            <button onclick="appendToDisplay('-')" class="operator-btn">-</button>
            <button onclick="appendToDisplay('4')">4</button>
            <button onclick="appendToDisplay('5')">5</button>
            <button onclick="appendToDisplay('6')">6</button>
            <button onclick="appendToDisplay('*')" class="operator-btn">*</button>
            <button onclick="appendToDisplay('1')">1</button>
            <button onclick="appendToDisplay('2')">2</button>
            <button onclick="appendToDisplay('3')">3</button>
            <button onclick="appendToDisplay('/')" class="operator-btn">/</button>
            <button onclick="appendToDisplay('0')">0</button>
            <button onclick="appendToDisplay('.')">.</button>
            <button onclick="calculate()">=</button>
            <button onclick="clearDisplay()" class="operator-btn">C</button>
        </div>
    </div>

    <script src="index.js"></script>
</body>
</html>

result:

wow so cool!!!!!

and the buttons

okay not so cool…

Made the buttons less gross using Styles.css

styles.css

#keys{
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 10px;
    padding: 25px;
}
button{
    width: 100px;
    height: 100px;
    border-radius: 50px;
    border: none;
    background-color: hsl(0, 0%, 30%);
    color: white;
    font-size: 3rem;
    font-weight: bold;
    cursor: pointer;
}

#keys is the id of the buttons. it changes the size and spaces the buttons have. button customizes the buttons to make them look better.

result:

The buttons

Display screen (I just changed the color and made it larger)

Code of the display screen:

#display{
    width: 100%;
    padding: 20px;
    font-size: 5rem;
    text-align: left;
    border: none;
    background-color: hsl(0, 0%, 20%);
}

Made the calculator center in the document.

styles.css

body{
    margin: 0;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    background-color: hsl(0, 0%, 90%);

}

before:

after:

the screen is darker b/c i changed the background color.

Added button hovering and clicking indications(?)(and color)

styles.css

body{
    margin: 0;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    background-color: hsl(0, 0%, 90%);

}
#calculator{
    font-family: Arial, sans-serif;
    background-color: hsl(0, 0%, 15%);
    border-radius: 15px;
    max-width: 500px;
    overflow: hidden;
}
#display{
    width: 100%;
    padding: 20px;
    font-size: 5rem;
    text-align: left;
    border: none;
    background-color: hsl(0, 0%, 20%);
}
#keys{
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 10px;
    padding: 25px;
}
button{
    width: 100px;
    height: 100px;
    border-radius: 50px;
    border: none;
    background-color: hsl(0, 0%, 30%);
    color: white;
    font-size: 3rem;
    font-weight: bold;
    cursor: pointer;
}
button:hover{
    background-color: hsl(0, 0%, 40%);

}
button:active{
    background-color: hsl(0, 0%, 60%);

}
.operator-btn{
    background-color: hsl(198, 83%, 47%);

}
.operator-btn:hover{
    background-color: hsl(198, 83%, 67%);

}
.operator-btn:active{
    background-color: hsl(198, 83%, 87%);

}

button hovering

button clicking

COLORED OPERATOR BUTTONS AYAYAYAYA

I did a miniscule bit of JS, but it’s not an ABSURD amount.

(Kinda) Small Weather App project

main.py

get_weather() gets the weather info from the OpenWeatherApp Api website.

def get_weather(city):
    '''gets weather info from OpenWeatherMap API'''
    API_key = "***********************"
    url = f"https://api.openweathermap.org/data/2.5/weather?q={city}&appid={API_key}"
    res = requests.get(url)
    
    if res.status_code == 404:
        messagebox.showerror("Error", "City not found")
        return None
    
    # Parse response JSON to get weather info
    weather = res.json()
    icon_id = weather['weather'][0]['icon']
    temperature = weather['main']['temp'] - 273.15

it also gets the temperature and icon to make it into readable text.

search() allows the user to search for the current weather of a city.

mian.py

def search():
    '''Searches the current weather of a city'''
    city = city_entry.get()
    result = get_weather(city)
    if result is None:
        return

The rest is the set up for the GUI.

window = ttkbootstrap.Window(themename="morph")
window.title("Weather App")
window.geometry("400x400")

# Entry widget - enter city name
city_entry = ttkbootstrap.Entry(window, font="Helvetica, 18")
city_entry.pack(pady=10)

# Button widget - search for weather information
search_button = ttkbootstrap.Button(window, text="Search", 
command=search, bootstyle="warning")
search_button.pack(pady=10)

# label widget - show country/city name
location_label = tk.Label(window, font="Helvetica, 25")
location_label.pack(pady=20)

# Label widget - show weather icon
icon_label = tk.Label(window)
icon_label.pack()

# Label widget - show the temperature
temperature_label = tk.Label(window, font="Helvetica, 20")
temperature_label.pack()

# Label widget - show weather desc.
desc_label = tk.Label(window, font="Helvetica, 20")
desc_label.pack()

window.mainloop()

That’s really all I did… 1. because I started a little late and 2. it took too long for me to get an account for the API key