[Python] How to make the vertical scrollbar go down with any new inputs or outputs in...

Discussão em 'Python' iniciado por Stack, Maio 27, 2025.

  1. Stack

    Stack Membro Participativo

    I have this code:

    import pygame
    import pygame_gui
    import sys
    import requests
    import difflib
    import re
    from datetime import datetime
    import appointments_lib as appt
    import psutil
    import GPUtil
    import cpuinfo
    import platform

    # INIT PYGAME
    pygame.init()
    WIDTH, HEIGHT = 800, 600
    screen = pygame.display.set_mode((WIDTH, HEIGHT))
    pygame.display.set_caption("JARVIS")
    clock = pygame.time.Clock()

    # PYGAME GUI SETUP
    manager = pygame_gui.UIManager((WIDTH, HEIGHT))

    # UI ELEMENTS
    terminal = pygame_gui.elements.UITextBox(
    html_text="",
    relative_rect=pygame.Rect((10, 10), (780, 550)),
    manager=manager
    )

    input_line = pygame_gui.elements.UITextEntryLine(
    relative_rect=pygame.Rect((10, 555), (780, 40)),
    manager=manager
    )
    input_line.set_text("")

    # COLOR PALETTE
    colors = {
    "default": "#00FFFF",
    "success": "#00FF00",
    "error": "#FF3C38",
    "info": "#FFB627",
    "user": "#32CD32",
    }

    # APPOINTMENTS STORAGE
    appointments = appt.load_appointments()
    pending_cancel = None
    pending_command = None

    # NATURAL LANGUAGE SUPPORT
    natural_command_aliases = {
    "status": ["system status", "how's everything running", "status report", "are all systems nominal"],
    "shutdown": ["shut down", "power off", "turn off jarvis", "exit"],
    "help": ["what can you do", "show help", "list commands", "command list", "how can you help"],
    "weather report": ["what's the weather", "current weather", "how's the weather", "weather right now", "tell me the weather"],
    "check schedule": ["show my schedule", "do I have any appointments", "what’s on my calendar", "list appointments", "check agenda"],
    "check schedule today": ["what's on my schedule today", "do i have any appointments today", "show me today's schedule", "what do i have today", "anything planned today", "appointments today", "my schedule today"],
    }

    #STATUS OF PC
    def get_system_status_report():
    info = cpuinfo.get_cpu_info()
    cpu_name = info.get('brand_raw', 'Unknown CPU')
    cores = psutil.cpu_count(logical=False)
    threads = psutil.cpu_count(logical=True)
    cpu_usage = psutil.cpu_percent(interval=1)

    svmem = psutil.virtual_memory()
    ram_total = svmem.total / (1024 ** 3)
    ram_used = svmem.used / (1024 ** 3)
    ram_percent = svmem.percent

    try:
    gpus = GPUtil.getGPUs()
    gpu_reports = []
    for gpu in gpus:
    gpu_reports.append(
    f"GPU: {gpu.name} | Load: {gpu.load*100:.1f}% | "
    f"VRAM: {gpu.memoryUsed:.1f}/{gpu.memoryTotal:.1f} MB | Temp: {gpu.temperature}°C"
    )
    except Exception as e:
    gpu_reports = ["GPU: Unable to retrieve GPU information."]

    try:
    temps = psutil.sensors_temperatures()
    cpu_temp = None
    for key in temps:
    if "coretemp" in key or "cpu_thermal" in key:
    for entry in temps[key]:
    if "Package" in entry.label or entry.label == "":
    cpu_temp = entry.current
    break
    if cpu_temp:
    break
    temp_line = f"CPU Temperature: {cpu_temp:.1f}°C" if cpu_temp else "CPU Temperature: Unavailable"
    except Exception:
    temp_line = "CPU Temperature: Unavailable"

    system = platform.system()
    machine = platform.machine()
    os_version = platform.version()

    return [
    f"System: {system} {machine} | OS Version: {os_version}",
    f"CPU: {cpu_name} | Cores: {cores} | Threads: {threads} | Usage: {cpu_usage:.1f}%",
    temp_line,
    f"RAM Usage: {ram_used:.1f}/{ram_total:.1f} GB ({ram_percent}%)",
    *gpu_reports,
    "All systems operational, sir."
    ]

    # LOCATION
    def get_location():
    try:
    response = requests.get("https://ipinfo.io/json")
    data = response.json()
    city = data.get("city")
    loc = data.get("loc")
    lat, lon = map(float, loc.split(",")) if loc else (0.0, 0.0)
    return city, lat, lon
    except Exception as e:
    print("Location error:", e)
    return None, 0.0, 0.0

    # WEATHER

    def weather_code_to_desc(code):
    mapping = {
    0: "clear sky", 1: "mainly clear", 2: "partly cloudy", 3: "overcast",
    45: "fog", 48: "depositing rime fog", 51: "light drizzle", 53: "moderate drizzle",
    55: "dense drizzle", 56: "light freezing drizzle", 57: "dense freezing drizzle",
    61: "light rain", 63: "moderate rain", 65: "heavy rain",
    66: "light freezing rain", 67: "heavy freezing rain",
    71: "light snow", 73: "moderate snow", 75: "heavy snow", 77: "snow grains",
    80: "light rain showers", 81: "moderate rain showers", 82: "violent rain showers",
    85: "light snow showers", 86: "heavy snow showers",
    95: "thunderstorm", 96: "thunderstorm with slight hail", 99: "thunderstorm with heavy hail",
    }
    return mapping.get(code, "unknown weather")

    def get_weather(lat, lon):
    try:
    url = f"https://api.open-meteo.com/v1/forecast?latitude={lat}&longitude={lon}&current_weather=true"
    response = requests.get(url)
    data = response.json()
    if "current_weather" not in data:
    return "The weather data is currently unavailable, sir."

    weather = data["current_weather"]
    temperature = weather["temperature"]
    windspeed = weather["windspeed"]
    weather_code = weather["weathercode"]
    desc = weather_code_to_desc(weather_code)

    return (
    f"the current weather conditions indicate {desc} "
    f"with a temperature of {temperature:.1f} degrees Celsius. "
    f"Wind speed is measured at {windspeed:.1f} kilometers per hour."
    )
    except Exception as e:
    print("Weather error:", e)
    return "The weather service is currently unavailable, sir."

    # NORMALIZATION

    def normalize(text):
    return re.sub(r'[^\w\s]', '', text).lower()

    # TIME & GREETING

    time = datetime.now()
    greeting_time = "evening"
    if 6 <= time.hour < 12:
    greeting_time = "morning"
    elif 12 <= time.hour < 18:
    greeting_time = "afternoon"

    city, lat, lon = get_location()
    if city:
    weather_report = get_weather(lat, lon)
    else:
    city = "your area"
    weather_report = "The weather data is currently unavailable, sir."

    def build_greeting():
    today = appt.get_today_date_str()
    appts_today = appointments.get(today, [])
    greet_lines = [
    f"Good {greeting_time} sir!",
    f"It's {time.hour:02}:{time.minute:02}, {weather_report}"
    ]
    if appts_today:
    greet_lines.append("You have the following appointments today:")
    for appt_item in appts_today:
    greet_lines.append(f"- {appt_item}")
    else:
    greet_lines.append("You have no appointments scheduled for today.")
    return greet_lines

    # COMMAND MAP
    command_map = {
    "status": "status",
    "shutdown": "shutdown",
    "help": "help",
    "weather report": "weather_rep",
    "check schedule": "check_schedule",
    "check schedule today": "check_schedule_today",
    }

    # Add natural aliases to command_map
    def extend_command_map():
    for base_cmd, aliases in natural_command_aliases.items():
    for phrase in aliases:
    if phrase not in command_map:
    command_map[phrase] = command_map[base_cmd]

    extend_command_map()

    # TEXT CONTENT
    init_anim = ["Initializing core systems" + "." * i for i in range(4)] + [
    "Checking location.", "Checking location..", f"Location: {city}", '<font color="#00FF00">DONE!</font>'
    ]
    startup = [
    "Power levels optimal.", "Neural net operational.", "Connection secure.", "Initialization complete.",
    '<font color="#00FF00">J.A.R.V.I.S is now online!</font>'
    ]
    sorry = ["No need to apologize sir."]
    oweu_welcome = ["Don't worry sir, I am happy to help."]
    welcome = ["You're very welcome sir! I am at your service. If I can provide assistance, please ask."]
    greeting = build_greeting()
    status = get_system_status_report()
    shutdown = ["SHUTTING DOWN..."]
    help = [
    "AVAILABLE COMMANDS:",
    "STATUS OF ALL SYSTEMS - 'status'",
    "SHUTDOWN AI SYSTEM - 'shutdown'",
    "SHOW ALL AVAILABLE COMMANDS - 'help'",
    "SHOW WEATHER REPORT - 'weather report'",
    "ADD APPOINTMENT - 'add appointment YYYY-MM-DD description'",
    "CANCEL APPOINTMENT - 'cancel appointment YYYY-MM-DD description'",
    "VIEW SCHEDULE - 'check schedule'",
    "VIEW TODAY'S SCHEDULE - 'check schedule today'",
    ]
    weather_rep = [f"{weather_report}".capitalize()]
    error = ["ERROR - COMMAND UNKNOWN"]
    schedule_lines = []

    displayed = []
    last_update = 0
    defdelay = 2000
    index = 0
    phase = "init"

    def show_text(text, delay=defdelay, stage="", loop=False, color="default"):
    global current_time, last_update, index, phase, displayed
    color_hex = colors.get(color, colors["default"])

    if index == 0:
    input_line.disable()

    if current_time - last_update > delay and index < len(text):
    line = f'<font color="{color_hex}">{text[index]}</font>'
    if loop:
    terminal.set_text(line)
    else:
    displayed.append(line)
    terminal.set_text("<br>".join(displayed[-100:]))

    index += 1
    last_update = current_time
    input_line.set_text("")

    if index >= len(text):
    index = 0
    phase = f"{stage}"
    input_line.enable()
    input_line.focus()

    # MAIN LOOP
    run = True
    while run:
    time_delta = clock.tick(60) / 1000
    current_time = pygame.time.get_ticks()

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

    if event.type == pygame_gui.UI_TEXT_ENTRY_FINISHED and event.ui_element == input_line and event.text != "":
    user_command = event.text.strip()
    displayed.append(f'<font color="{colors["user"]}">> {user_command}</font>')
    cmd = normalize(user_command)

    if pending_cancel:
    if cmd in ["yes", "y"]:
    date_str, appt_text = pending_cancel
    if appt.cancel_appointment(appointments, date_str, appt_text):
    displayed.append(f'<font color="{colors["success"]}">Appointment on {date_str} cancelled.</font>')
    else:
    displayed.append(f'<font color="{colors["error"]}">No matching appointment found for {date_str}.</font>')
    pending_cancel = None
    elif cmd in ["no", "n"]:
    displayed.append(f'<font color="{colors["error"]}">Cancellation aborted.</font>')
    pending_cancel = None
    else:
    displayed.append(f'<font color="{colors["default"]}">Please respond with only yes or no, sir.</font>')
    terminal.set_text("<br>".join(displayed[-100:]))
    input_line.set_text("")
    continue

    elif pending_command:
    if cmd in ["yes", "y"]:
    phase = command_map[pending_command]
    pending_command = None
    elif cmd in ["no", "n"]:
    displayed.append(f'<font color="{colors["error"]}">Command aborted.</font>')
    pending_command = None
    else:
    displayed.append(f'<font color="{colors["default"]}">Please respond with only yes or no, sir.</font>')
    terminal.set_text("<br>".join(displayed[-100:]))
    input_line.set_text("")
    continue

    elif cmd.startswith("add appointment"):
    parts = user_command.split(' ', 3)
    if len(parts) < 4:
    displayed.append(f'<font color="{colors["error"]}">Invalid format. Use: add appointment YYYY-MM-DD description</font>')
    else:
    date_str = parts[2]
    appt_text = parts[3]
    try:
    datetime.strptime(date_str, "%Y-%m-%d")
    appt.add_appointment(appointments, date_str, appt_text)
    displayed.append(f'<font color="{colors["success"]}">Appointment added for {date_str}.</font>')
    except ValueError:
    displayed.append(f'<font color="{colors["error"]}">Invalid date format. Use YYYY-MM-DD.</font>')

    elif cmd.startswith("cancel appointment"):
    parts = user_command.split(' ', 3)
    if len(parts) < 4:
    displayed.append(f'<font color="{colors["error"]}">Invalid format. Use: cancel appointment YYYY-MM-DD description</font>')
    else:
    date_str = parts[2]
    appt_text = parts[3]
    if date_str in appointments:
    best_match = difflib.get_close_matches(appt_text, appointments[date_str], n=1, cutoff=0.6)
    if best_match:
    displayed.append(f'<font color="{colors["default"]}">Did you mean to cancel "{best_match[0]}" on {date_str}?</font>')
    pending_cancel = (date_str, best_match[0])
    else:
    displayed.append(f'<font color="{colors["error"]}">No similar appointment found on {date_str}.</font>')
    else:
    displayed.append(f'<font color="{colors["error"]}">No appointments found on {date_str}.</font>')

    elif cmd.startswith(("thank", "i thank", "i appreciate", "appreciate", "much obliged")):
    phase = "welcome"
    elif cmd.startswith(("i owe you", "i'm in debt", "i am in debt", "i will make it up", "i'll make it up", "im in debt")):
    phase = "oweu_welcome"
    elif cmd.startswith(("sorry", "apologies", "i apologize", "pardon", "i'm sorry", "i sorry")):
    phase = "sorry"

    elif cmd in command_map:
    phase = command_map[cmd]
    else:
    match = difflib.get_close_matches(cmd, command_map.keys(), n=1, cutoff=0.6)
    if match:
    displayed.append(f'<font color="{colors["default"]}">Did you mean "{match[0]}"?</font>')
    pending_command = match[0]
    else:
    phase = "error"

    terminal.set_text("<br>".join(displayed[-100:]))
    input_line.set_text("")

    manager.process_events(event)

    # PHASE-BASED RESPONSE
    if phase == "init":
    show_text(init_anim, 350, "startup", True, "default")
    elif phase == "startup":
    show_text(startup, defdelay, "greet", True, "default")
    elif phase == "greet":
    greeting = build_greeting()
    show_text(greeting, color="default")
    elif phase == "error":
    show_text(error, 0, color="error")
    elif phase == "status":
    show_text(status, color="default")
    elif phase == "help":
    show_text(help, 0, color="info")
    elif phase == "shutdown":
    show_text(shutdown, color="default")
    run = False
    elif phase == "weather_rep":
    show_text(weather_rep, color="default")
    elif phase == "check_schedule":
    schedule_lines = appt.get_schedule_lines(appointments)
    show_text(schedule_lines, color="default")
    elif phase == "check_schedule_today":
    today = appt.get_today_date_str()
    schedule_lines = appointments.get(today, []) or ["You have no appointments scheduled for today, sir."]
    show_text(["Today's appointments:"] + [f"- {a}" for a in schedule_lines] if schedule_lines else schedule_lines, color="default")
    elif phase == "welcome":
    show_text(welcome, color="default")
    elif phase == "oweu_welcome":
    show_text(oweu_welcome, color="default")
    elif phase == "sorry":
    show_text(sorry, color="default")

    screen.fill((0, 0, 0))
    manager.draw_ui(screen)
    manager.update(time_delta)
    pygame.display.update()

    pygame.time.wait(700)
    pygame.quit()
    sys.exit()


    And when the terminal gets crowded and the scroll bar appears, it's default position is to stay up, and only moves down when the user moves it downwards. But if it receives any input or output, it moves back up.

    I have already tried using chat gpt and copilot from VSC to fix this issue, I needed this ready asap and I'm new with pygame_gui, so I don't really know how to fix this issue. I wanted the scrollbar to stay permanently down, when it receives inputs and outputs, and only move up if the user chooses to (either by scrolling or dragging the scrollbar)

    Continue reading...

Compartilhe esta Página