[Python] Hilo general

PiradoIV

#1560 Lo guay es que si pillas el concepto, luego te da igual en qué lenguaje lo estés resolviendo.

2 1 respuesta
B

#1561 Ya lo decían en airbag... lo importante es el concepto.

2
CaNaRy_r00lz

Una pregunta, para trabajar con python y demás, se requiere un portatil potente? lo pregunto desde la total ignorancia, no se si cuando tienes un proyecto grande necesita potencia o con un i3 vas que te matas.
Lo pregunto pk estoy planteandome pillarme un portatil solo para esto, y poder irme a donde sea que ahora mismo uso el sobremesa

1 respuesta
Kaiserlau

#1563 Supongo que depende de lo que hagas y a que te dediques necesitaras algo mas o menos potente o algo mas o menos especifico (no me dedico a esto). Pero de entrada para mi imprescindible debería de ser algo totalmente compatible con linux para trabajar en nativo con él, instalar, funcionar y listo, sin mierdas de drivers o cosas especificas sin soporte por culpa de la marca y blabla (hoy casi todo portátil no da problemas, pero mejor asegurarse antes de la compra), En cuanto a características de hard algo de Ram y una Cpu normalucha de ahora que ya traen una buena cantidad de hilos.

3 2 respuestas
B

CPython tiene el llamo GIL (Global Interpreter Lock), no me preocuparía a día de hoy por el número de procesadores. Vamos, python es el lenguaje... todo varía de que interprete uses. Que en el 99% de los casos será CPython.

Pero claro, seguramente quieras usar Dockers, tener el navegador abierto, pasarle un debugger, etc.. etc... píllate entre 8 o más de RAM (mejor si verificas que la ram del portatil sea ampliable), la CPU pues lo dicho... obviamente cuantos más ciclos pueda hacer pues mejor.

Respecto a lo que comenta #1564 tienes marcas dedicadas... una conocida y que se hace en España es: https://slimbook.es/
Tienes otras como https://system76.com/ ... los Dell XPS tenían su fama de ser compatibles también.... y otros tantos que habrá que desconozco y/o no me acuerdo xD

1 1 respuesta
CaNaRy_r00lz

#1564 #1565 Gracias a ambos, si he visto los XPS pero no quiero gastarme tanto la verdad, he visto los huawei y para tenerlo como hobby creo que van bien con un i7 y 16gb de ram, pero tengo que mirar mas, que basicamente quiero para pillarlo e irme por ahi y estar tranquilo fuera de casa

1 respuesta
eondev

#1566 una mierda así te sobra, no te ralles con pcs caros xD

1
16 días después
-Entropy-

Hola. Estoy modificando una app de un juego que está escrita en Python. En un punto del código creo una condición para reproducir un sonido con Soundplay. Dicho código está dentro de una sección en la que el juego comprueba constantemente la condición una y otra vez para, si se cumple, reproducir el sonido inmediatamente. El problema es que a veces se me reproduce el sonido de forma muy seguida e incluso solapándose varias reproducciones. Necesitaría una manera de que el código no reproduzca el sonido a menos que hayan pasado unos segundos desde la última reproducción. No tengo ni idea de Python, así que estuve mirando ayudas y probé algunas cosas. Por ejemplo, probé con Sleep, pero me pausa todo el juego (en vez del hilo en cuestión). También probé a cambiar block = false por true, pero entonces el sonido no se reproduce. En fin, alguna idea. Os pego el trozo de código.


if Angle > Drift and carKMH > MinKMH:

    if not sound_played:

        playsound(SoundFile, block = False)

        sound_played = True

        return

else:

    sound_played = False

    return
2 respuestas
gonya707

#1568 Por lo que veo ese playsound es parte de un paquete homonimo que solo contiene esa funcion, asi que en principio no hay manera de saber de manera asincrona si el sonido se sigue reproduciendo o no, y el block no puedes cambiarlo porque precisamente es lo que permite que el sonido se haga asincronamente cuando sea necesario.

Algo que puedes hacer si sabes que el sonido dura x segundos es poner un threading.Timer con ese tiempo que cambie una hipotetica variable is_currently_playing de true a false y añadir esa variable a las condiciones que usas para reproducir el sonido

if Angle > Drift and carKMH > MinKMH and not is_currently_playing:
	playsound(SoundFile, block=False)
	is_currently_playing = True

	t = threading.Timer(seconds, allow_sound)
	t.start()

	return

para esto tendras que poner la funcion que yo he llamado allow_sound en algun sitio, algo asi:

def allow_sound():
	is_currently_playing = false
1 1 respuesta
Slowbro

#1568 Sin tener ni idea de que framework/motor usas, posiblemente funcione con un bucle de eventos y por lo tanto en la API de lo que estés usando habrá a un sleep (no el del sistema operativo) que permita que no evalues esa función/sección hasta que pase ese tiempo sin parar todo el juego.

La otra es que tengas algún contador, o que hagas un control de tiempo a mano. Por ejemplo, una vez se reproduzca el sonido puedes guardar el tick del sistema en una variable, y en la rama del else comprobar si desde la ultima vez que lo has guardado ha pasado tiempo suficiente. Es cutre pero debería funcionar.

Perdona si está fatal la explicación. Si no te vale de nada, intento escribir un ejemplo tonto de como lo haría.

Edit: Si no tienes problemas con usar threads, la solución de @gonya707 está mu bien.

1 respuesta
CaNaRy_r00lz

#1569 Pregunta tonta, esto "threading.Timer" te lo has sacado de la manga o es algo que viene en python? pk dentro de mi ignorancia mientras iba leyendo lo que habia puesto, estaba pensando en un timer, pero npi como se podria incluir eso, y luego te veo pones lo que entiendo es un timer no?

3 respuestas
gonya707

#1571 es una de las librerias que vienen con python

https://docs.python.org/3/library/threading.html

1
Apo_powa

#1571 es una librería built-in de Python para paralelizar procesos

1
Lolerpopler

#1571 Tu idea iba bien encaminada, el problema cuando tienes ejecucion en paralelo es, como sabe un hilo o un proceso lo que esta pasando en otro?

1 respuesta
CaNaRy_r00lz

#1574 No si cada vez que pienso que empiezo a entender Python aparece algo que me rompe el culo, pero me gusta ver los problemas pensar teoricamente como los solucionaria y ver como realmente se solucionan xD

1 respuesta
Lolerpopler

#1575 no si tu idea estaba bien. Sin haberte mirado la libreria y saber que esta usando procesos en parelelo hubieses ido en buen camino

-Entropy-

Muchas gracias por la ayuda. Luego cuando llegue a casa probaré esas soluciones a ver si hay suerte. Algunas de las cosas que me preguntáis me suenan a chino, porque apenas se nada de programación (algo de AutoHotkey, si es que eso cuenta). Efecticamente el script llama a algunas funciones de otro fichero playsoundDRS.py (os pego el código) con cosas como éstas al principio del script. También parece que llama a una carpeta pydubDRS.

from playsoundDRS import playsound
from pydubDRS.pydub import AudioSegment

El programa es una app de Assetto Corsa. La he modificado con código de otra app para que haga lo de reproducir el sonido.

playsoundDRS.py:


class PlaysoundException(Exception):
    pass

def _playsoundWin(sound, block = True):
    '''
    Utilizes windll.winmm. Tested and known to work with MP3 and WAVE on
    Windows 7 with Python 2.7. Probably works with more file formats.
    Probably works on Windows XP thru Windows 10. Probably works with all
    versions of Python.

Inspired by (but not copied from) Michael Gundlach <gundlach@gmail.com>'s mp3play:
https://github.com/michaelgundlach/mp3play

I never would have tried using windll.winmm without seeing his code.
'''
from ctypes import c_buffer, windll
from random import random
from time   import sleep
from sys    import getfilesystemencoding

def winCommand(*command):
    buf = c_buffer(255)
    command = ' '.join(command).encode(getfilesystemencoding())
    errorCode = int(windll.winmm.mciSendStringA(command, buf, 254, 0))
    if errorCode:
        errorBuffer = c_buffer(255)
        windll.winmm.mciGetErrorStringA(errorCode, errorBuffer, 254)
        exceptionMessage = ('\n    Error ' + str(errorCode) + ' for command:'
                            '\n        ' + command.decode() +
                            '\n    ' + errorBuffer.value.decode())
        raise PlaysoundException(exceptionMessage)
    return buf.value

alias = 'playsound_' + str(random())
winCommand('open "' + sound + '" alias', alias)
winCommand('set', alias, 'time format milliseconds')
durationInMS = winCommand('status', alias, 'length')
winCommand('play', alias, 'from 0 to', durationInMS.decode())

if block:
    sleep(float(durationInMS) / 1000.0)

def _playsoundOSX(sound, block = True):
    '''
    Utilizes AppKit.NSSound. Tested and known to work with MP3 and WAVE on
    OS X 10.11 with Python 2.7. Probably works with anything QuickTime supports.
    Probably works on OS X 10.5 and newer. Probably works with all versions of
    Python.

Inspired by (but not copied from) Aaron's Stack Overflow answer here:
http://stackoverflow.com/a/34568298/901641

I never would have tried using AppKit.NSSound without seeing his code.
'''
from AppKit     import NSSound
from Foundation import NSURL
from time       import sleep

if '://' not in sound:
    if not sound.startswith('/'):
        from os import getcwd
        sound = getcwd() + '/' + sound
    sound = 'file://' + sound
url   = NSURL.URLWithString_(sound)
nssound = NSSound.alloc().initWithContentsOfURL_byReference_(url, True)
if not nssound:
    raise IOError('Unable to load sound named: ' + sound)
nssound.play()

if block:
    sleep(nssound.duration())

def _playsoundNix(sound, block=True):
    """Play a sound using GStreamer.

Inspired by this:
https://gstreamer.freedesktop.org/documentation/tutorials/playback/playbin-usage.html
"""
if not block:
    raise NotImplementedError(
        "block=False cannot be used on this platform yet")

# pathname2url escapes non-URL-safe characters
import os
try:
    from urllib.request import pathname2url
except ImportError:
    # python 2
    from urllib import pathname2url

import gi
gi.require_version('Gst', '1.0')
from gi.repository import Gst

Gst.init(None)

playbin = Gst.ElementFactory.make('playbin', 'playbin')
if sound.startswith(('http://', 'https://')):
    playbin.props.uri = sound
else:
    playbin.props.uri = 'file://' + pathname2url(os.path.abspath(sound))

set_result = playbin.set_state(Gst.State.PLAYING)
if set_result != Gst.StateChangeReturn.ASYNC:
    raise PlaysoundException(
        "playbin.set_state returned " + repr(set_result))

# FIXME: use some other bus method than poll() with block=False
# https://lazka.github.io/pgi-docs/#Gst-1.0/classes/Bus.html
bus = playbin.get_bus()
bus.poll(Gst.MessageType.EOS, Gst.CLOCK_TIME_NONE)
playbin.set_state(Gst.State.NULL)


from platform import system
system = system()

if system == 'Windows':
    playsound = _playsoundWin
elif system == 'Darwin':
    playsound = _playsoundOSX
else:
    playsound = _playsoundNix

del system
-Entropy-

#1570 ¿Me podrías poner un ejemplo del control de tiempo en código, por favor?

He estado probando la otra solución con el threading pero no consigo que me funcione.

1 respuesta
Kaiserlau

Lo que yo veo en el codigo es que esta super hardcodeado todo el tema del reproductor. Por otro lado #1578 has visto que tienes en el código sleep(nssound.duration()) por ejemplo este?.

-Entropy-

Si pongo el block se me para el juego durante esos segundos.

1 respuesta
Slowbro

#1580

Es una solución cutre pero igual te ayuda o vale.


from datetime import datetime

...
TOUT = 10    # tiempo en segundos que para no volver a evaluar la condicion
tprev = datetime.min # Estoy asumiendo que esto lo puedes hacer fuera
...

tout_cond = (datetime.now() - tprev).total_seconds() >= TOUT 
if tout_cond and (Angle > Drift) and (carKMH > MinKMH):
        playsound(SoundFile, block = False)
        tprev = datetime.now()
...
return
1 respuesta
-Entropy-

#1581 Gracias, pero tampoco consigo que funcione (el sonido no se reproduce). He probado a poner las dos primeras líneas en la zona inicial del script y también en la zona final. Yo creo que el problema lo mismo viene del hecho de que la condición que tengo está dentro de un bloque que, según tengo entendido, es el que actualiza la información del juego cada frame. Es esto:

def acUpdate(deltaT):
global AngleLabel, Angle, PeakAngleLabel, PeakAngle, Timer, MinKMH, PeakKMHLabel, sound_played, offset
ac.setFontAlignment(AngleLabel, "center")
ac.setPosition(AngleLabel, 33, 3)
etc.

Si quieres te pongo todo el código completo del script tal y como lo tengo ahora (funciona, pero con el problema del solape de sonidos).

2 respuestas
Lolerpopler

#1582 Si se llama a cada frame tiene pinta de ser un problema de concurrencia. La funcion es llamada otra vez antes de que el valor sea cambiado a "True". Diria que tienes que buscar la manera de garantizar que la funcion no se puede acceder multiples veces, prueba con "lock" https://stackoverflow.com/questions/39145796/locking-a-method-in-python

Slowbro

#1582 Me pillas frio, pero ahora mismo lo que se me ocurre es que pongas algunas trazas para ver por donde está tirando el código (si no puedes debugearlo con tu IDE o lo que uses). Igual he metido la pata con algo.

Si pones el código/repo lo intento mirar, que estoy de vacaciones y pachucho :(

-Entropy-

¡Menudo castigo! Encima cada vez que al juego se el antoja por algún cambio que he hecho en el código me desabilita la app y tengo que borrar todo el contenido de su configuración en mis documentos y restaurar la copia de seguridad.

Pongo todo el código, a ver si sacáis algo en claro. La app básicamente lo que hace es mostrar un texto con el ángulo de derrape cuando se alcanza cierto giro, deslizamiento y velocidad. El cacho de código que yo he añadido es el que puse antes con la condición del soundplay.

He visto que ahí dentro hay también un Timer, que lo mismo se podría usar. He intentado varias cosas, un poco a lo tonto, porque no tengo ni idea de lo que hago, pero no funciona. El timer se encarga de cambiar una palabra por otra cada pocos segundos cuando el coche está parado.


import sys
import ac
import acsys
import math

from math import log
from playsounddrift import playsound
from pydubdrift.pydub import AudioSegment

sound_played = False

SoundFile = "apps/python/D_Angle/hotshot.wav"

#configure
MaxAngle = 130
MinKMH = 10
Drift = 20

#label
AngleLabel = 0

#field
Angle = 0
Timer = 0

def acMain(ac_version):
    global AngleLabel

# init Window
appWindow = ac.newApp("D_Angle")
ac.setSize(appWindow, 66, 30)
ac.setTitle(appWindow, "")
ac.drawBorder(appWindow, 0)
ac.setIconPosition(appWindow, 0, -10000)
ac.setBackgroundOpacity(appWindow, 0.3)

# label configure
AngleLabel = ac.addLabel(appWindow, "0°")
ac.setPosition(AngleLabel, 33, 3)
ac.setFontAlignment(AngleLabel, "center")
ac.setFontSize(AngleLabel, 18)
ac.setFontColor(AngleLabel, 1, 1, 1, 1)

def acUpdate(deltaT):
    global AngleLabel, Angle, Timer, MinKMH, sound_played, offset
    ac.setFontAlignment(AngleLabel, "center")
    ac.setPosition(AngleLabel, 33, 3)

# raw data
carKMH = ac.getCarState(0, acsys.CS.SpeedKMH)
fl, fr, rl, rr = ac.getCarState(0, acsys.CS.SlipAngle)
vx, vy, vz = ac.getCarState(0, acsys.CS.LocalVelocity)

# model data
Angle = getDriftAngle(rl, rr, vz)

# spin : over angle
if Angle > MaxAngle and carKMH > MinKMH:
    ac.setText(AngleLabel, "Spin")
    return

# spin : lower speed
if Angle > 60 and carKMH < MinKMH and vz > 0:
    ac.setText(AngleLabel, "Spin")
    return

# drifting
if carKMH > MinKMH:
    ac.setText(AngleLabel, "{:.0f}°".format(Angle))

# Wait anim
else:
    ac.setPosition(AngleLabel, 16, 3)
    Timer += deltaT
    ac.setFontAlignment(AngleLabel, "left")
    if int(Timer) % 3 == 0:
        ac.setText(AngleLabel, "Text1")
    if int(Timer) % 3 == 1:
        ac.setText(AngleLabel, "Text2")
    if int(Timer) % 3 == 2:
        ac.setText(AngleLabel, "Text3")

if Angle > Drift and carKMH > MinKMH:
    if not sound_played:
        playsound(SoundFile, block = False)
        sound_played = True
        return
else:
    sound_played = False
    return

def getDriftAngle(rl, rr, vz):
    rawAngle = math.fabs(round(rl+rr)/2)
    if vz <= 0:
        return 180 - rawAngle
    else:
        return rawAngle




8 días después
-Entropy-

Sigo liado con esto. ¿Me puede alguien decir por qué esto no funciona? He puesto

import time
start = time.time()

al principio del script y luego en la sección el final del timer para que se contabilice el tiempo. Una vez realizada la condición se resetea el timer. ¿Lo mismo es que la manera en la que he puesto que calcule el tiempo entre el principio y final no es correcta? Al menos no me falla la aplicación con este código pero no llega nunca a reproducir el sonido con esa condición. Una cosa que no entiendo de este lenguaje es cómo se organizan las condiciones, porque en AutoHotkey se enmarcan entre { } y si hay condiciones dentro de condiciones cada una lleva su bloque con { }, pero aquí en Python ni idea.

    if Angle > Drift and carKMH > MinKMH:
        end = time.time()
        if not sound_played and end - start > 10:
            playsound(SoundFile, block = False)
            sound_played = True
            start = time.time()
    else:
        sound_played = False
2 respuestas
hda

#1586 time.now()?

Edit: mete cada una de las cláusulas del if entre paréntesis. Ej: if (not sound_played) and (end - start > 10):

1 respuesta
gonya707

#1587 Con time.time() ya tiene los segundos desde el epoch, el problema no esta ahi, es cuestion de diseño.

#1586 prueba con esto.

Al principio del script:

import time
start = time.time()
sound_playing = False

Lo otro:

if Angle > Drift and carKMH > MinKMH:
    if not sound_playing:
        playsound(SoundFile, block = False)
        sound_playing = True
        start = time.time()
if time.time() - start > 10:
    sound_playing = False

Y a tu pregunta de como se organizan las condiciones, es cosa del tabulado. si despues de un if algo: las siguientes lineas van tabuladas, todo eso que este tabulado esta dentro del condicional, en el momento en el que vuelvas al nivel de tabulado de la linea con if algo: eso ya estara fuera

Kaiserlau

También se puede hacer con datetime

Import datetime

init = datetime.datetime.now()
finish = datetime.datetime.now()

result = (finish-init).total_seconds() # te saca los segundos de diferencia directamente

Decir que la resta te da un objeto de timedelta <class 'datetime.timedelta'> a lo que le puedes llamar a total_seconds directamente

-Entropy-

Muchas gracias a todos. Lamentablemente nada funciona. Creo que me rindo. Llevo demasiado tiempo invertido para esto y no merece la pena. Abandono.

3 respuestas

Usuarios habituales