Как использовать event.value для другой функции вне основного цикла? [дубликат]

avatar
Leo Optimo II
6 августа 2021 в 21:56
175
1
0

Ниже приведен рабочий скелет моего фактического кода. Что я пытаюсь сделать, так это получить JOYAXISMOTION, event.value, y, чтобы я мог использовать его в другой функции вне цикла. Я уже определил переменную y.

import time
import sys
import pygame
import subprocess
import random


from pygame.locals import *
pygame.init()

y = 0

def get_percent_change(current, previous):
    if current == previous:
        return 0
    try:
        return ((float(current) - float(previous)) /abs(previous)) * 100.0
    except ZeroDivisionError:
        return float('inf')

def sendCommands():
    time.sleep(5)
    print('Hello World')

pygame.joystick.init()
joysticks = [pygame.joystick.Joystick(i) for i in range(pygame.joystick.get_count())]
for joystick in joysticks:
    print(joystick.get_name())

game_state = False
run = True

while run:
    for event in pygame.event.get():

        if event.type == JOYBUTTONDOWN:
            print(event)

            if game_state == False and event.button == 8:
                game_state = True

            if event.button == 4:
                game_state = False

        if event.type == JOYHATMOTION:
            if event.value[0] == 1 or event.value[0] == -1:
                game_state = False

        if event.type == JOYAXISMOTION:
            if event.axis == 4:
                current = event.value
                y = get_percent_change(current, -1.0001)
                print(y)

        if event.type == JOYDEVICEADDED:
            joysticks = [pygame.joystick.Joystick(i) for i in range(pygame.joystick.get_count())]
            for joystick in joysticks:
                print(joystick.get_name())
        if event.type == JOYDEVICEREMOVED:
            joysticks = [pygame.joystick.Joystick(i) for i in range(pygame.joystick.get_count())]


    if game_state == True:
        sendCommands()

pygame.quit()
sys.exit()

Основная проблема, с которой я столкнулся, это time.sleep(5), который требуется для sendCommands. Он блокирует скрипт во время сна. Я пробовал asyncio без успеха. Threading также очень сбивает меня с толку. Я также попробовал логику задержки в ответе на тесно связанный вопрос здесь, но это также блокирует запуск кода.

Позвольте мне порассуждать о том, как:

  1. Я могу запустить скрипт без блокировки
  2. и как я могу получить доступ и использовать event.value, y вне цикла
Источник
Ted Klein Bergman
6 августа 2021 в 22:26
0

Зачем вам функция sendCommands? Вы должны почти никогда использовать time.sleep в программе с графическим интерфейсом. Что ты пытаешься сделать? Скорее всего, вам следует опубликовать событие в очереди событий или создать таймер. И многопоточность почти никогда не является ответом при использовании pygame. Pygame делает за вас большую часть необходимой работы с потоками.

Ted Klein Bergman
6 августа 2021 в 22:32
0

И это распространенный источник ошибок, потому что у pygame есть особые требования к тому, когда и где вам разрешено использовать потоки.

Leo Optimo II
6 августа 2021 в 22:39
0

Я не создаю игру. Я просто использую pygame, чтобы использовать геймпад для управления скриптом. Полная функциональность находится внутри размещенного скелета, только функция sendCommands меняется на другую.

Ted Klein Bergman
6 августа 2021 в 22:49
0

Я про игры ничего не говорил. Каждый раз, когда вы используете pygame, вы должны регулярно обрабатывать события, как описано здесь. У вас не может быть ничего, что блокирует программу. В большинстве случаев в этом нет необходимости, так как вы можете управлять событиями, отправляя их в очередь событий или используя значения таймера для управления состояниями. Вот почему я спросил, для чего нужен sendCommands. Можно ли его обработать за разумное время (например, примерно за 1/30 секунды)? Или вы можете обрабатывать его дискретно (то есть сегментами)?

Ted Klein Bergman
6 августа 2021 в 22:53
0

Если вы уверены, что все вышеперечисленное — твердое «нет», тогда вам действительно нужно использовать потоки. Однако они сопровождаются огромным предупреждением: вам не следует обращаться к очереди событий или окну (и, возможно, к другим модулям). Честно говоря, я не уверен во всех требованиях. И вам также необходимо синхронизировать данные, что зависит от того, к каким данным фактически обращается функция sendCommands (например, глобальные переменные и т. д.).

Leo Optimo II
6 августа 2021 в 23:04
0

Тед, извини, я не обновил свой браузер достаточно быстро, чтобы увидеть твои вопросы :) - @Ted Klein Bergman Фактическая функция отправки - это подпроцесс, отправляющий команду в командную строку, и я хотел бы, чтобы она повторялась каждые 5 секунд. Я не могу понять, как запланировать повторяющиеся задачи, не блокируя сценарий! Это моя самая большая проблема.

Ответы (1)

avatar
user16038533
6 августа 2021 в 22:22
2

Чтобы использовать y вне цикла, вы можете объявить его глобальным, но лучшей альтернативой является создание функции для получения значения. Вы можете реализовать таймер для выполнения вашего подпроцесса каждые 5 секунд вместо блокировки всего потока. Следующий код печатает None каждые 5 секунд, так как у меня нет подключенных контроллеров, но я думаю, что он сработает для вас.

import time
import sys
import pygame
from pygame.locals import *

pygame.init()

def sendCommands(waitTime, y):
    global sendCommandsStart
    #count how many seconds have passed since sendCommandsStart
    #variable was last updated
    if time.time() - sendCommandsStart > waitTime:
        sendCommandsStart = time.time() #reset the timer
        print(y)#call your subpreocess here

def getJoyStickY(events):
     for event in events:
         if event.type == JOYAXISMOTION:
            if event.axis == 4:
                return get_percent_change(current, -1.0001)
     return None

pygame.joystick.init()
run = True

sendCommandsStart = time.time() #get the current time - its just a number (time since last epoh)

while run:
    allEvents = pygame.event.get()
    sendCommands(5, getJoyStickY(allEvents))

    
pygame.quit()
sys.exit()
    
Ted Klein Bergman
6 августа 2021 в 22:27
0

Однако это будет выполняться синхронно. Thread.join будет ждать завершения потока, поэтому вы не получите от этого никакой выгоды.

user16038533
6 августа 2021 в 22:35
0

@TedKleinBergman Спасибо, что сообщили мне. Я удалил это,

Leo Optimo II
6 августа 2021 в 22:36
0

Большое спасибо за ответ. Я был очень взволнован, чтобы попробовать это, но его запуск сразу же приводит к сбою IDE. Он ничего не печатает и в основном зависает. Вы знаете, почему это так? Я использую Atom (я не думаю, что это имеет большое значение) - еще раз спасибо :)

Ted Klein Bergman
6 августа 2021 в 22:38
0

@user16564162 user16564162 Это потому, что ОС думает, что программа зависает, потому что вы спите 5 секунд. Вы должны регулярно обрабатывать события. Текущий ответ - правильный способ сделать это.

Leo Optimo II
6 августа 2021 в 22:46
0

Ответ был отредактирован (спасибо!) - я проверяю, но я считаю, что опубликованный ответ должен работать!

user16038533
6 августа 2021 в 22:50
0

@ user16564162 Добро пожаловать. Мне любопытно, почему функция должна ждать 5 секунд.

Leo Optimo II
6 августа 2021 в 22:59
0

Я новичок в этом, так что, скорее всего, я делаю что-то не так. Однако запуск этого ничего не напечатает, и моя IDE перестанет отвечать, так как я не могу выйти из клавиатуры. Но это дает мне идею использовать функцию... так что это очень помогает.

Leo Optimo II
6 августа 2021 в 23:10
0

@user16038533 user16038533 настоящая функция — это подпроцесс, который нужно запускать каждые 5 секунд. Я все еще пытаюсь понять, как многократно запускать функцию, не блокируя остальную часть кода!

user16038533
6 августа 2021 в 23:24
0

@user16564162 user16564162 Вам не нужно останавливать весь поток, чтобы функция выполнялась каждые 5 секунд. Подождите, дайте мне отредактировать ответ.

Leo Optimo II
6 августа 2021 в 23:41
0

@user16564162 user16564162 Спасибо - если вы сможете понять, как запустить функцию sendCommands и при этом получить доступ к значению y (в моем исходном коде), это будет здорово. Поскольку это простой сценарий, я мог бы использовать глобальный y, поскольку, похоже, у меня возникают проблемы с использованием опубликованного кода (скорее всего, моя собственная ошибка пользователя).

user16038533
6 августа 2021 в 23:43
0

@ user16564162 Я не понимаю, что вы имеете в виду. В коде, который я разместил выше, у вас есть доступ к значению в любом месте. Просто вызовите функцию getJoyStickY, и вы получите значение.

Leo Optimo II
6 августа 2021 в 23:44
0

о niice - позвольте мне попробовать это !! - поражен тем, как вы можете делать так быстро то, что занимает у меня часы между исследованием и тестированием

Ted Klein Bergman
7 августа 2021 в 00:07
1

@user16564162 user16564162 Этот ответ — хороший способ сделать это вручную. Также есть pygame.time.set_timer, который будет публиковать событие каждые x миллисекунд. Таким образом, вы также можете определить событие RUN_COMMAND = pygame.USEREVENT и вызвать pygame.time.set_timer(RUN_COMMAND, 5000) в начале сценария. Это будет публиковать тип события RUN_COMMAND каждые 5 секунд в очереди событий (т. е. в pygame.event.get()).

Leo Optimo II
7 августа 2021 в 02:24
0

@Ted Klein Bergman Я тоже обязательно попробую. Я рад, что задал вопрос. Спасибо за редактирование вопроса для лучшей читабельности!