I am writing a Tetris program with PyGame, and came across a funny problem.
Before I ask the question, here is the pseudo-code:
while True:
# In this part, the human controls the block to go left, right, or speed down
if a key is pressed and the block isnt touching the floor:
if the key is K-left:
move piece left one step
if the key is K-right:
move piece right one step
if the key is K-down:
move piece down one step
# This part of the code makes the piece fall by itself
if the block isnt touching the floor:
move block down one step
# This part makes the while loop wait 0.4 seconds so that the block does not move
# down so quickly
wait 0.4 seconds
The problem is that, because of the "wait 0.4 seconds" part of the code, the part that the human controls can only move every 0.4 seconds. I would like it so that the block moves as fast as the human can press the key, while at the same time, the block dropping every 0.4 seconds. How could I arrange the code so that it will do that? Thanks!
The main problem I see here is that you are limiting your framerate using a wait of 0.4 seconds.
You should not limit framerate, but instead, you should limit how fast your block falls.
If I remember well, there was a formula you could use to do just that. It was based on the amout of time elapsed since the last frame. It looked like:
fraction of a second elapsed since last frame * distance you want your block to move in a second
This way, you can keep your mainloop intact, and the move processing will happen at every frame.
You could also do...
...
# This part of the code makes the piece fall by itself
if the block isn't touching the floor and
the block hasn't automatically moved in the last 0.4 seconds:
move block down one step
...
Just realize you'll be doing a lot of polling if the user hasn't struck any keys.
You may try asking gamedev.stackexchange.com instead. Check the site for Game Loops, and check out other example pygame projects to see how they're doing it. Having a good game loop is essential and will take care of things for you such as user inputs and a consistent frame rate.
Edit: https://gamedev.stackexchange.com/questions/651/tips-for-writing-the-main-game-loop
When doing games you should always try to do something like this:
while not finished:
events = get_events() # get the user input
# update the world based on the time that elapsed and the events
world.update(events, dt)
word.draw() # render the world
sleep(1/30s) # go to next frame
The sleep time should be variable so it takes into consideration the amount of time spend drawing and calculating the world updates.
The world update method would look something like this:
def update(self, events, dt):
self.move(events) # interpret user action
self.elapsed += dt
if self.elapsed > ADVANCE_TIME:
self.piece.advance()
self.elapsed = 0
The other way of implementing this (so you dont redraw too much) is to have events fired when the user orders a piece to be moved or when ADVANCE_TIME time passes. In each event handler you would then update the world and redraw.
This is assuming you want the pieces to move one step at a time and not continuous. In any case, the change for continuous movement is pretty trivial.
Related
I just started using Psychopy in order to create my first adaptive staircase experiment.
I tried to set up the experiment by using the Builder interface. The loop type I'm using is the staircase, not the interleaved staircase.
In the experiment, I would like to change the contrast of the image according to the participants response.
I've already designed the experiment so far that I can present the start stimulus to the participants, when the program runs. Also, the participant can respond. But the problem is, that my stimulus does not change at all after a participant responded. I've tried so many things to fix that, starting from inserting every possible stimulus manually, coding it according to the tutorial of Yentl de Kloe, but nothing is working - the simulus remains unchanged which leads to the result that the experiment runs forever, if I dont cancel it manually.
Is there anyone, who can tell me a simple (for a beginner understandable), but detailed solution how to solve this problem within the Psychopy Builder?
Thank you in advance!
Experimental Structure
Staircase Loop
I am trying to solve the following problem. I have a code where there is an event and it is connected to a time in seconds. I am attaching this to video so I need a lead and a lag time. I am able to create the lead time, but for some of the events, the lag time is correlated to when the next event occurs.
For example, a shot has a lead time of 3 and a lag time of 7. However, for zone time, I need to create a lead time of 3 and a lag time of next occurrence of the title 'whistle'.
Im using Python VLC to build a custom playback app in pyqt. I have painted a nice custom slider to track along with the video, but hit a bit of an annoying problem.
No matter how often I tell my slider to update, it's quite glitchy (jumping every 1/4 second or so) and looks choppy (just the timeline, not the video).
Digging into it, I learned that
media_player.get_position()
Has quite a low polling rate. It returns the same value quite often then jumps a large amount the next time it gives a new value.
So right now I ran some test metrics and found it tends to update every 0.25-0.3 seconds. So now I have a system that basicay stores the last value and last system time a new value came in, and the last jump-distance in returned values and does some basic math with those things to fake proper linear timeline data between polls to make a very smooth timeline slider.
The problem is this assumes my value of every 0.25-0.3 seconds is consistent across machines, hardware, frame rates of videos etc.
Does anyone know of a better fix?
Maybe a way to increase the poll rate of VLC to give me better data to begin with - or some better math to handle smoothing?
Thanks
Using get_position() returns a value between 0.0 and 1.0, essentially a percentage of the current position measured against the total running time.
Instead you can use get_time() which returns the current position in 1000ths of a second.
i.e.
print (self.player.get_time()/1000) would print the current position in seconds.
You could also register a callback for the vlc event EventType.MediaPlayerTimeChanged, as mentioned in the other answer given by #mtz
i.e.
Where self.player is defined as:
self.Instance = vlc.Instance()
self.player = self.Instance.media_player_new()
Then:
self.vlc_event_manager = self.player.event_manager()
self.vlc_event_manager.event_attach(vlc.EventType.MediaPlayerTimeChanged, self.media_time_changed)
def media_time_changed(self,event):
print (event.u.new_time/1000)
print (self.player.get_time()/1000)
print (self.player.get_position())
Try using the libvlc_MediaPlayerPositionChanged or libvlc_MediaPlayerTimeChanged mediaplayer events instead.
https://www.videolan.org/developers/vlc/doc/doxygen/html/group__libvlc__event.html#gga284c010ecde8abca7d3f262392f62fc6a4b6dc42c4bc2b5a29474ade1025c951d
I'm relatively new to Python and I'm attempting to develop a function that operates more or less like a parking sensor in a car.
That is: the code receives some sensor input data and in real-time a beeping sound is created of which the wait period between two beeps is inversely related to the magnitude of the signal (i.e., when close the beeps are close together and vice versa).
I've attempted to create a while loop that includes winsound.beep() and a pause. The pause duration will be calculated using the sensor input. However, even when run as a few lines of simple code the while loop seems asynchronous and the pauses aren't evenly spaced apart.
I attempted to put the code in a separate thread but this did not solve the problem.
I'll include the code here to just perform a repeated beeping sound, not taking any input yet to troubleshoot from there.
from time import sleep
import winsound
while True:
winsound.Beep(1500,100)
sleep(0.05)
I expect the output to be a consistent beep spaced 0.05s apart, but the actual output is asynchronous beeping with the pauses varying with time.
Try
from time import sleep
import winsound
while True:
winsound.Beep("SystemExit", winsound.SND_ALIAS)
sleep(0.05)
Edit :
Try this
from time import sleep
import winsound
while True:
frequency = 10 # change it based on sensor input
duration = 1000 # keep this constant
winsound.Beep(frequency,duration)
sleep(duration) # i'm assuming beep is not blocking so this delay keep the program waiting until beep duration is completed
Beep Takes the parameters frequency and duration.
So instead of changing the sleep duration, you can change the frequency to match the magnitude of signal.
First off, sorry for this lenghty text. I'm new to python and matplotlib, so please bear with me.
As a followup to this question I found the way of generating the grid to be quite time consuming on a Raspberry Pi using web2py. I have a csv file with round about 12k lines looking like this:
1;1.0679759979248047;0.0;147.0;0.0;;{'FHR1': 'US', 'FHR2': 'INOP', 'MHR': 'INOP'};69;good;;;;1455891539.502167
The thing is, that reading those 12k lines with numpy.genfromtxt already takes 30 something seconds. Populating the chart then (without the fancy grids) took another 30 seconds, just using columns 1, 3 and 7 of that csv. But after adding the solution time exploded to 170 seconds. So now I have to figure out what to do to reduce time consumption to somewhere under a minute.
My first thought is to eliminate the csv - I'm the one reading the data anyway, so I could skip that by either keeping the data in memory or by just writing it into the plot right away. And that's what I did, with a (in my mind) simple test layout and using the pdf backend. I chose to write the data into the chart every time I get them and save the chart once the transmission is done. I thought that should work fine, well it doesn't. I keep getting ludicrous errors:
RuntimeError: RRuleLocator estimated to generate 9178327 ticks from 0001-01-01 15:20:31.883239+00:00 to 0001-04-17 20:52:39.779205+00:00: exceeds Locator.MAXTICKS * 2 (6000000)
And believe me, I keep increasing those maxticks with every test run to top the number the error message says. Its ridiculous because that message is just for 60 seconds of data, and I want to go somewhere near 24 hours of data. I would either like the RRuleLocator to stop estimating or to just shut up and wait for the data to end. I really don't think I can make an MCVE out of this, but I can carve out the details I'm most likely messing up.
First off, I got some classes set up, so no globals. To simplify I have a communications class, that reads the serial port at one second intervals. This is running fine and up till yesterday wrote whatever came in on the serial port into a csv. Now I wanted to see if I could populate the chart while getting the data, and just save it, once I'm done. So for testing I added this to my .py
import matplotlib
matplotlib.use('PDF') # I want a PDF in the end
from matplotlib import dates
import matplotlib.pyplot as plt
import numpy as np
from numpy import genfromtxt
Then some members to the communication class, that come from the charting part, mainly above mentioned solution. I initialize them in the classes init
self.fig = None
self.ctg = None
self.toco = None
then I have this method I call, once I feel the data I'm receiving is in correct form/state so that the chart may be prepared for populating with data
def prepchart(self):
# how often to show xticklabels and repeat yticklabels:
print('prepchart')
xtickinterval = 5
hfmt = dates.DateFormatter('%H:%M:%S')
self.fig = plt.figure()
self.ctg = self.fig.add_subplot(2, 1, 1) # two rows, one column, first plot
plt.ylim(50, 210)
self.toco = self.fig.add_subplot(2, 1, 2)
plt.ylim(0, 100)
# Set the minor ticks to every 30 seconds
minloc = dates.SecondLocator(bysecond=[0,30])
minloc.MAXTICKS = 3000000
self.ctg.xaxis.set_minor_locator(minloc)
# self.ctg.xaxis.set_minor_locator(dates.MinuteLocator())
self.ctg.xaxis.set_major_formatter(hfmt)
self.toco.xaxis.set_minor_locator(dates.MinuteLocator())
self.toco.xaxis.set_major_formatter(hfmt)
# actg.xaxis.set_ticks(rotation=45)
plt.xticks(rotation=45)
Then every so often once I have data I want to plot I'll do this in my data processing method
self.ctg.plot_date(timevalue, heartrate, '-')
self.toco.plot_date(timevalue, toco, '-')
finally once no more data is sent (this can be after one minute or 24 hours) I'll call
def handleCTG(self):
self.fig.savefig('/home/pi/test.pdf')
In conclusion: Am I going at this completely wrong or is there just an error in my code? And is this really a way to reduce waiting time for the chart to be generated?
OK, so here's the deal. Obviously web2py runs a pretty tight ship. Meaning that there are not so many threads floating around, and it sure wont start a new thread for my little chart creation. I sort of noticed this, when I followed CPU usage on my Raspis taskmanager and only ever saw something around 25%. Now the Raspberry Pi has 4 kernels... go do the math. First I ran my script outside of web2py on my Raspi and, lo and behold, the entire thing including csv-reading and chart rendering only takes 20s. From there on (inspired by How to run a task outside web2py and retrieve the output) it's a piece of cake: use the well documented subprocess to call a new python with this script and done. So thanks to anyone who gave this some thought.