Skip to content

Commit

Permalink
Set screen resolution and FPS for current project only, filepicker
Browse files Browse the repository at this point in the history
  • Loading branch information
Secret-chest committed Feb 11, 2022
1 parent 735d7b6 commit b0a1231
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 31 deletions.
26 changes: 25 additions & 1 deletion config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
# manual: use the project file name defined in the "projectFileName" variable.
# interactive: use input().
# cmdline: use command argument.
# There will be a GUI, and it will be the default, but for now it's easier to test using manual mode.
# filechooser: graphical file chooser.
# For now, it's easier to test using manual mode, so it's the default.
projectLoadMethod: str = "manual"

# Project file name
Expand All @@ -21,6 +22,14 @@
# Set whether to extract the project assets on run.
extractOnProjectRun: bool = True

# Enable terminal output
# Set whether any output messages should be allowed.
enableTerminalOutput = True

# Enable debug messages
# Set whether debug messages (messages to stderr) should be allowed.
enableDebugMessages = True

# Max FPS
# Set maximum frame rate. Most projects won't break, but they
# might be too fast (or too slow) as they may rely on screen refresh.
Expand All @@ -38,3 +47,18 @@
# Again, most projects will break.
# Vanilla is false.
allowOffScreenSprites: bool = False


# "For this project" values
# Don't edit these. They will break Scratch2Python controls.
# Edit your defaults above instead. The values here inherit them.
projectMaxFPS = maxFPS
projectScreenWidth = screenWidth
projectScreenHeight = screenHeight
projectAllowOffScreenSprites = allowOffScreenSprites


# ConfigError class
# Error for invalid settings.
class ConfigError(Exception):
pass
123 changes: 107 additions & 16 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,14 @@
__version__ = "M12 (development version)"
__author__ = "Secret-chest"

from platform import system
import platform
import tkinter.simpledialog
from platform import system, platform
import os
import sys
from typing import Tuple

import config

if system() == "Linux":
OS = "linux"
Expand All @@ -34,12 +40,22 @@
else:
OS = "unknown"

print("Running on", OS)

if not config.enableTerminalOutput:
sys.stdout = open(os.devnull, "w")
sys.stderr = open(os.devnull, "w")
if not config.enableDebugMessages:
sys.stderr = open(os.devnull, "w")

print(f"Scratch2Python {__version__} running on {OS}")

if OS == "windows":
os.environ['path'] += r";cairolibs"
os.environ["path"] += r";cairolibs"

import config
if OS == "unknown":
print(f"Sorry, Scratch2Python does not recognize your OS. Your platform string is: {platform()}", file=sys.stderr)

sys.stdout = open(os.devnull, "w")
import io
import sb3Unpacker
from sb3Unpacker import *
Expand All @@ -50,18 +66,39 @@
import tkinter as tk
from pathlib import Path
from tkinter.messagebox import *
from tkinter.simpledialog import *
from tkinter import filedialog
from targetSprite import TargetSprite
import sys
sys.stdout = sys.__stdout__

if not config.enableTerminalOutput:
sys.stdout = open(os.devnull, "w")
sys.stderr = open(os.devnull, "w")
if not config.enableDebugMessages:
sys.stderr = open(os.devnull, "w")

# Start tkinter for showing some popups, and hide main window
mainWindow = tk.Tk()
mainWindow.withdraw()

if config.projectLoadMethod == "manual":
setProject = config.projectFileName
if config.projectLoadMethod == "cmdline":
elif config.projectLoadMethod == "cmdline":
try:
setProject = sys.argv[1]
except IndexError:
raise OSError("No project file name passed")
if config.projectLoadMethod == "interactive":
elif config.projectLoadMethod == "interactive":
setProject = input("Project file name: ")
elif config.projectLoadMethod == "filechooser":
fileTypes = [("Scratch 3 projects", ".sb3"), ("All files", ".*")]
setProject = filedialog.askopenfilename(parent=mainWindow,
initialdir=os.getcwd(),
title="Choose a project to load",
filetypes=fileTypes)
else:
sys.stderr = sys.__stderr__
raise config.ConfigError("Invalid setting: projectLoadMethod")

# Get project data and create sprites
targets, project = sb3Unpacker.sb3Unpack(setProject)
Expand All @@ -72,11 +109,7 @@
allSprites.add(sprite)
sprite.setXy(t.x, t.y)

# Start tkinter for showing some popups, and hide main window
wn = tk.Tk()
wn.withdraw()

# Start Pygame, load fonts and print a debug message
# Start pygame, load fonts and print a debug message
pygame.init()
font = pygame.font.SysFont(pygame.font.get_default_font(), 16)
fontXl = pygame.font.SysFont(pygame.font.get_default_font(), 36)
Expand All @@ -86,8 +119,8 @@
pausedWidth, pausedHeight = fontXl.size("Paused (Press F6 to resume)")

# Set player size and key delay
HEIGHT = config.screenHeight
WIDTH = config.screenWidth
HEIGHT = config.projectScreenHeight
WIDTH = config.projectScreenWidth
KEY_DELAY = 500

# Get project name and set icon
Expand Down Expand Up @@ -118,6 +151,40 @@

doScreenRefresh = False


# Define a dialog class for screen resolution
class SizeDialog(tkinter.simpledialog.Dialog):
def __init__(self, parent: tk.Misc | None, title):
super().__init__(parent, title)

def body(self, master) -> tuple[str, str]:
tk.Label(master, text="Width: ").grid(row=0)
tk.Label(master, text="Height: ").grid(row=1)

self.width = tk.Entry(master)
self.height = tk.Entry(master)

self.width.grid(row=0, column=1)
self.height.grid(row=1, column=1)

return self.width

def okPressed(self):
self.setWidth = self.width.get()
self.setHeight = self.height.get()
self.destroy()

def cancelPressed(self):
self.destroy()

def buttonbox(self):
self.okButton = tk.Button(self, text='OK', width=5, command=self.okPressed)
self.okButton.pack(side="left")
cancelButton = tk.Button(self, text='Cancel', width=5, command=self.cancelPressed)
cancelButton.pack(side="right")
self.bind("<Return>", lambda event: self.okPressed())
self.bind("<Escape>", lambda event: self.cancelPressed())

# Start project
toExecute = []
eventHandlers = []
Expand Down Expand Up @@ -164,6 +231,29 @@
project.extractall("assets")
if pygame.K_F6 in keys: # Pause
isPaused = not isPaused
if pygame.K_F7 in keys: # Set new FPS
# Open dialog
newFPS = askinteger(title="FPS", prompt="Enter new FPS")
if newFPS is not None:
print("FPS set to", newFPS)
config.projectMaxFPS = newFPS
if pygame.K_F8 in keys: # Set new screen resolution
try:
# Open special dialog
dialog = SizeDialog(mainWindow, title="Screen resolution")
config.projectScreenWidth = int(dialog.setWidth)
config.projectScreenHeight = int(dialog.setHeight)

# Redraw everything and recalculate sprite operations
display = pygame.display.set_mode([config.projectScreenWidth, config.projectScreenHeight])
HEIGHT = config.projectScreenHeight
WIDTH = config.projectScreenWidth
scratch.refreshScreenResolution()
for s in allSprites:
s.setXy(s.x, s.y)
print("Screen resolution set to", str(HEIGHT) + "x" + str(WIDTH))
except ValueError:
pass

display.fill((255, 255, 255))
if toExecute:
Expand All @@ -173,6 +263,7 @@
if not isPaused:
for e in eventHandlers:
if e.opcode == "event_whenkeypressed" and keys:
# TODO
# nextBlock = scratch.execute(block, block.target.sprite, keys)
# if nextBlock:
# if isinstance(nextBlock, list):
Expand Down Expand Up @@ -210,7 +301,7 @@
else:
display.blit(paused, (WIDTH // 2 - pausedWidth // 2, WIDTH // 2 - pausedHeight // 2))
pygame.display.flip()
wn.update()
mainWindow.update()
doScreenRefresh = False
clock.tick(config.maxFPS)
clock.tick(config.projectMaxFPS)
pygame.quit()
11 changes: 9 additions & 2 deletions sb3Unpacker.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,22 @@
The various files with classes are used by this program and the correct data is
set. Those are then used to build the project in main.py.
"""
# This is the Scratch2Python unpacker.
# The main file you probably want to run is located at main.py.
import zipfile as zf
import json
import config
import target, costume, sound, block, variable, monitor # , broadcast
from pathlib import Path
import io
import pygame
import scratch
import sys
import os


if not config.enableDebugMessages:
sys.stderr = open(os.devnull, "w")
if not config.enableTerminalOutput:
sys.stdout = open(os.devnull, "w")


def sb3Unpack(sb3):
Expand Down
22 changes: 18 additions & 4 deletions scratch.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,22 @@
Basically it emulates Scratch in Pygame, hence the name.
"""
import sys
import os
import random

import pygame.time
import cairosvg
import io

import config

HEIGHT = config.screenHeight
WIDTH = config.screenWidth

if not config.enableDebugMessages:
sys.stderr = open(os.devnull, "w")
if not config.enableTerminalOutput:
sys.stdout = open(os.devnull, "w")


HEIGHT = config.projectScreenHeight
WIDTH = config.projectScreenWidth

# Key maps to convert the key option in blocks to Pygame constants
KEY_MAPPING = {
Expand Down Expand Up @@ -110,6 +116,14 @@ def loadSvg(svgBytes):
return pygame.image.load(byteIo)


# Refresh screen resolution
def refreshScreenResolution():
global HEIGHT
global WIDTH
HEIGHT = config.projectScreenHeight
WIDTH = config.projectScreenWidth


# Run the given block object
def execute(block, s, keys=[]):
# Get block values
Expand Down
16 changes: 8 additions & 8 deletions targetSprite.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@ def __init__(self, target):
def setXy(self, x, y):
# Do sprite fencing
if not config.allowOffScreenSprites:
if x > config.screenWidth - config.screenWidth / 2:
x = config.screenWidth - config.screenWidth / 2
if x < config.screenWidth / 2 - config.screenWidth:
x = config.screenWidth / 2 - config.screenWidth
if y > config.screenHeight - config.screenHeight / 2:
y = config.screenHeight - config.screenHeight / 2
if y < config.screenHeight / 2 - config.screenHeight:
y = config.screenHeight / 2 - config.screenHeight
if x > scratch.WIDTH - scratch.WIDTH / 2:
x = scratch.WIDTH - scratch.WIDTH / 2
if x < scratch.WIDTH / 2 - scratch.WIDTH:
x = scratch.WIDTH / 2 - scratch.WIDTH
if y > scratch.HEIGHT - scratch.HEIGHT / 2:
y = scratch.HEIGHT - scratch.HEIGHT / 2
if y < scratch.HEIGHT / 2 - scratch.HEIGHT:
y = scratch.HEIGHT / 2 - scratch.HEIGHT
# Set X and Y
self.x = x + self.padX // 2
self.y = y - self.padY // 2
Expand Down

0 comments on commit b0a1231

Please sign in to comment.