Работа над интерфейсом приложения-портфеля Beeware для Android с движком в стиле пары друзей — это тога. Я пытаюсь сделать некоторые изображения кликабельными. Значки кнопок недостаточно большие, и я не нашел способа сделать их больше. Кроме того, я не нашел способа наслаивания кнопок и изображений, который мы также исследовали, но теперь я считаю его невозможным, что заставило меня обратиться к последнему средству: веб-просмотру (это то, чего мы хотим как можно меньше). Пока что это работает (стиль не полностью отсортирован, но на данный момент это не такая уж большая проблема. В настоящее время, когда я нажимаю изображение в веб-просмотре, оно отправляет все нормально, но появляется веб-страница, недоступная в течение доли секунды. Чтобы кнопка работала, мне пришлось изменить URL-адрес в javascript на URL-адрес, который не существует, а затем сбросить его обратно на правильный.
Python:
все в классе jsApp можно игнорировать, если в этом нет необходимости. Любые советы о том, как я могу:
1.получить полноразмерные значки на кнопках
2.накладывать изображения поверх кнопок
3.сделать недоступную веб-страницу Android, чтобы она не появлялась/быстрее перезагрузить правильный URL-адрес.
Конечная цель кнопок — сохранить имя пользователя с именем охотника или скрывающегося перед ним и сохранять изображение выделенным до тех пор, пока не начнется игра (не здесь, поскольку не моя проблема) или была нажата другая кнопка (я подожду, пока одна кнопка не заработает полностью, прежде чем разбираться с этим.
Работа над интерфейсом приложения-портфеля Beeware для Android с движком в стиле пары друзей — это тога. Я пытаюсь сделать некоторые изображения кликабельными. Значки кнопок недостаточно большие, и я не нашел способа сделать их больше. Кроме того, я не нашел способа наслаивания кнопок и изображений, который мы также исследовали, но теперь я считаю его невозможным, что заставило меня обратиться к последнему средству: веб-просмотру (это то, чего мы хотим как можно меньше). Пока что это работает (стиль не полностью отсортирован, но на данный момент это не такая уж большая проблема. В настоящее время, когда я нажимаю изображение в веб-просмотре, оно отправляет все нормально, но появляется веб-страница, недоступная в течение доли секунды. Чтобы кнопка работала, мне пришлось изменить URL-адрес в javascript на URL-адрес, который не существует, а затем сбросить его обратно на правильный. Python: [code]assert 1 + 1 == 2
# --- IMPORTS ---
from typing import Callable import httpx # type:ignore import toga # type:ignore from toga.style import Pack # type:ignore from toga.style.pack import COLUMN, ROW # type:ignore from toga.widgets import webview # type: ignore import asyncio import pathlib import threading import os from socketserver import TCPServer import json from http.server import SimpleHTTPRequestHandler, HTTPServer from datetime import datetime import time
class jsApp(toga.App): def __init__(self, *args, **kwargs): self.permission_granted = False super().__init__(*args, **kwargs) self.locationList:dict = {} # used to store location data if player is a hunter self.gui = GUI(self)
# -- ONSTART -- # gets perms on startup
async def on_start(self): print("Location backend:", type(self.location)) print("Onstart:") granted = await self.location.request_permission() if not granted: print("You denied location. Please enable in settings") self.permission_granted = False return if granted: print("Permission granted!, getting current location") self.permission_granted = True
asyncio.create_task(self.on_start()) # gets perms for location date
async def on_running(self): # wait for permission_granted (avoid race) timeout = 10.0 waited = 0.0 while not getattr(self, "permission_granted", False) and waited < timeout: await asyncio.sleep(0.1) waited += 0.1 if not self.permission_granted: print("Permission not granted before on_running finished waiting; skipping location calls") else: print("Before location grab") asyncio.create_task(self.getCurrentLocation()) print("After location grab")
if localPlayerId[1] == "i": # i for hider asyncio.create_task(self.sleepyTime(10, self.serverUpdateLocation, True)) elif localPlayerId[1] == "u": # u for hunter asyncio.create_task(self.sleepyTime(recieveLocationEvery, self.serverSendLocations, True)) else: print("Error: Something updated the ID")
# begins getting location every few seconds asyncio.create_task(self.sleepyTime(getLocationEvery, self.getCurrentLocation, True)) self.main_window.content = self.gui.startupScreen() self.main_window.show()
# POST update-location async def serverUpdateLocation(self): print(localLat) print(localLong) print("Post request attempted") # remove this for final build try: async with httpx.AsyncClient(verify = False) as client: postAttempt = await client.post(currentIp + "/update-location", json={ "playerId": localPlayerId, "lat": localLat, "long": localLong }) self.updateStatusLabel(f"Server responds with: {postAttempt.json()}") except Exception as e: self.updateStatusLabel(f"Error within serverUpdateLocation: {e}")
# POST caught async def serverCaught(self, id): print("caught") try: async with httpx.AsyncClient(verify = False) as client: postAttempt = await client.post(currentIp + "/caught", json={ "playerID": id }) print(f"caught: server responds with {postAttempt.json()}") except Exception as e: print("error within serverCaught:", e)
# GET send-locations async def serverSendLocations(self): print("Get request attempted") # remove this for final build try: async with httpx.AsyncClient(verify = False) as client: getAttempt = await client.get(currentIp + "/send-locations") self.locationList = getAttempt.json() try: # self.rebuild_hunter_markers() # self.refreshMarkers() pass except Exception: pass
print(getAttempt.json()) # remove this for final build self.updateStatusLabel(f"Location: {getAttempt.json()}") except Exception as e: self.updateStatusLabel(f"Error: {e}") print(f"Error within updateStatusLabel: {e}") # here
# GET waiting async def waitingRoom(self): print("waiting room") try: async with httpx.AsyncClient(verify = False) as client: while True: print("waiting attempt") getAttempt = await client.get(currentIp + "/waiting") print(getAttempt) if getAttempt.json().get("status") == "started": print("game started") await self.gameLoop() return await asyncio.sleep(1) except Exception as e: print("failed to connect to waiting room:", e)
# -- ASYNC --
async def sleepyTime(self, zzz : float, function : Callable = None, repeat : bool = False): if function is None: # ??? None -> not await asyncio.sleep(zzz) if repeat: while True: await asyncio.sleep(zzz) await function() else: if function is None: await asyncio.sleep(zzz) else: await asyncio.sleep(zzz) await function(zzz)
def updateStatusLabel(self, text:str): if hasattr(self.main_window, "statusLabel"): self.gui.statusLabel.text = text print(text)
async def endTimeGrab(self): """ Assumes time is in the format HHMM """ global endTime endTime = self.gui.timeInput.value print("end time grabbed:", endTime)
# sends a ping and checks perms # bug in here where an error is thrown if location is missed async def getCurrentLocation(self,*args, **kwargs):# try: print("Get current location ran") # remove for final build service = self.location loc = await service.current_location() if loc is not None: print("current location might be:", loc) print(loc.lat, loc.lng) global localLat global localLong localLat = loc.lat localLong = loc.lng else: print("Failed to get location") except Exception as e: print("Error within getCurrentLocation: ", e)
while True: try: await self.serverCaught(oldId) print("server connection sucessful") break except Exception as e: print("failed to connect to server:", e) asyncio.sleep(1)
print("gameloop 2") await self.gameLoop()
async def waitingRoomBackend(self): print("waiting room logic worked") # basically this needs to run the waiting room logic await self.waitingRoom() # this did have some more stuff in it
async def gameLoop(self): # phew this is a long time in the making # logic for the game print("gameloop ran")
async def hiderTimerLoop(): while True: self.hiderTimer() await asyncio.sleep(1)
async def hunter(): print("hunterscreen") # this never runs # self.main_window.content = self.gui.hunterScreen() # self.main_window.show() # self.show_hunter_map() # self.refreshMarkers() global recieveLocationEvery self.init_hunter_map() await hiderTimerLoop() await self.sleepyTime(recieveLocationEvery, self.refresh_hunter_markers, True)
global localPlayerId if localPlayerId[1] == "i": # i for hider hider() elif localPlayerId[1] == "u": # u for hunter print("hunter") await hunter() else: print("?????")
# -- AI CODE --
def init_hunter_map(self): import toga #type:ignore
print("Initializing hunter map...")
# Step 1: Start local server if not already running if not hasattr(self, "local_port"): self.start_local_server()
async def refresh_hunter_markers(self): global currentIp """Fetch latest locations and update markers on the map.""" try: async with httpx.AsyncClient() as client: response = await client.get(f"{currentIp}/send-locations") if response.status_code == 200: locations = response.json() js = f"refreshMarkers({json.dumps(locations)});" self.webview.evaluate_javascript(js) else: print("Failed to fetch locations:", response.status_code) except Exception as e: print("Error refreshing hunter markers:", e)
def start_local_server(self): def run_server(): # Try a list of candidate directories candidates = [] try: candidates.append(str(self.paths.app / "resources")) except Exception: pass # add working dir fallback candidates.append(os.getcwd()) chosen = None for c in candidates: try: if os.path.isdir(c): os.chdir(c) chosen = c break except Exception: pass if chosen is None: print("No valid resources directory found; local server won't run.") return
with TCPServer(("127.0.0.1", 0), SimpleHTTPRequestHandler) as httpd: self.local_port = httpd.server_address[1] print(f"Local server running on port {self.local_port}, serving {chosen}") httpd.serve_forever()
[/code] все в классе jsApp можно игнорировать, если в этом нет необходимости. Любые советы о том, как я могу: 1.получить полноразмерные значки на кнопках 2.накладывать изображения поверх кнопок 3.сделать недоступную веб-страницу Android, чтобы она не появлялась/быстрее перезагрузить правильный URL-адрес. Конечная цель кнопок — сохранить имя пользователя с именем охотника или скрывающегося перед ним и сохранять изображение выделенным до тех пор, пока не начнется игра (не здесь, поскольку не моя проблема) или была нажата другая кнопка (я подожду, пока одна кнопка не заработает полностью, прежде чем разбираться с этим.