tkinter animation speed on windows and linux - python-3.x

I was trying to create simple 2d game using tkinter, but faced with interesting problem: animation speed is quite different on various computers.
To test this, I've create script, that measures time of animation
import tkinter as tk
import datetime
root = tk.Tk()
can = tk.Canvas(height=500, width=1000)
can.pack()
rect = can.create_rectangle(0, 240, 20, 260, fil='#5F6A6A')
def act():
global rect, can
pos = can.coords(rect)
if pos[2] < 1000:
can.move(rect, 5, 0)
can.update()
can.after(1)
act()
def key_down(key):
t = datetime.datetime.now()
act()
print(datetime.datetime.now() - t)
can.bind("<Button-1>", key_down)
root.mainloop()
and get these results:
i3-7100u ubuntu 20.04 laptop python3.8.5 - 0.5 seconds
i3-7100u windows 10 laptop python3.9.4 - 3 seconds
i3-6006u ubuntu 20.10 laptop python3.9.x - 0.5 seconds
i3-6006u windows 10 laptop python3.8.x - 3 seconds
i5-7200u windows 10 laptop python3.6.x - 3 seconds
i5-8400 windows 10 desktop python3.9.x - 3 seconds
fx-9830p windows 10 laptop python3.8.x - 0.5 seconds
tkinter vesrion is the same - 8.6
How can be it fixed or at least explained?

tkinter.Canvas.after should be used like so:
def act():
global rect, can
pos = can.coords(rect)
if pos[2] < 1000:
can.move(rect, 5, 0)
can.update()
can.after(1, act)
The after method is not like time.sleep. Rather than recursively calling the function, the above code schedules it to be called later, so this will break your timing code.
If you want to time it again, try this:
def act():
global rect, can, t
pos = can.coords(rect)
if pos[2] < 1000:
can.move(rect, 5, 0)
can.update()
can.after(1, act)
else:
print(datetime.datetime.now() - t)
def key_down(key):
global t
t = datetime.datetime.now()
act()
This may still take different amounts of time on different machines. This difference can be caused by a variety of things like CPU speed, the implementation of tkinter for your OS etc. The difference can be reduced by increasing the delay between iterations: tkinter.Canvas.after takes a time in milliseconds, so a delay of 16 can still give over 60 frames per seconds.
If keeping the animation speed constant is important, I would recommend you use delta time in your motion calculations rather than assuming a constant frame rate.

AS you can see from your data it doesent matter which python version. It appears that ubuntu systems can help process python easier. However, im pretty sure its just the processer or how much ram the computer has.

Related

How to Fully Utilize CPU cores for skopt.forest_minimize

So I have the following code for running skopt.forest_minimize(), but the biggest challenge I am facing right now is that it is taking upwards of days to finish running even just 2 iterations.
SPACE = [skopt.space.Integer(4, max_neighbour, name='n_neighbors', prior='log-uniform'),
skopt.space.Integer(6, 10, name='nr_cubes', prior='uniform'),
skopt.space.Categorical(overlap_cat, name='overlap_perc')]
#skopt.utils.use_named_args(SPACE)
def objective(**params):
score, scomp = tune_clustering(X_cont=X_cont, df=df, pl_brewer=pl_brewer, **params)
if score == 0:
print('saving new scomp')
with open(scomp_file, 'w') as filehandle:
json.dump(scomp, filehandle, default = json_default)
return score
results = skopt.forest_minimize(objective, SPACE, n_calls=1, n_initial_points=1, callback=[scoring])
Is it possible to optimize the following code so that it can compute faster? I noticed that it was barely making use of my CPU, highest CPU utilized is about 30% (it's i7 9th gen with
8 cores).
Also a question while I'm at it, is it possible to utilize a GPU for these computational tasks? I have a 3050 that I can use.

question about combining def function() and PWM duty_ns() in micropython

as a Micro python beginner, I combined few codes found on different forums in order to achieve a higher resolution for ESC signal control. The code will generate from a MIN 1000000 nanoseconds to a MAX 2000000 nanoseconds' pulse but I could only do 100 in increments. My code is kind of messy. Sorry if that hurts your eyes. My question is, does it represent an actual 100ns of resolution? and what's the trick to make it in increments of 1.(Not sure whether is even necessary, but I still hope someone can share some wisdom.)
from machine import Pin, PWM, ADC
from time import sleep
MIN=10000
MAX=20000
class setPin(PWM):
def __init__(self, pin: Pin):
super().__init__(pin)
def duty(self,d):
super().duty_ns(d*100)
print(d*100)
pot = ADC(0)
esc = setPin(Pin(7))
esc.freq(500)
esc.duty(MIN) # arming ESC at 1000 us.
sleep(1)
def map(x, in_min, in_max, out_min, out_max):
return int((x - in_min)*(out_max - out_min)/(in_max - in_min) + out_min)
while True:
pot_val = pot.read_u16()
pulse_ns = map(pot_val, 256, 65535, 10000, 20000)
if pot_val<300: # makes ESC more stable at startup.
esc.duty(MIN)
sleep(0.1)
if pot_val>65300: # gives less tolerance when reaching MAX.
esc.duty(MAX)
sleep(0.1)
else:
esc.duty(pulse_ns) # generates 1000000ns to 2000000ns of pulse.
sleep(0.1)
try to change
esc.freq(500) => esc.freq(250)
x=3600
print (map(3600,256,65535,10000,20000)*100)

Overwrite GPS coordinates in Image Exif using Python 3.6

I am trying to transform image geotags so that images and ground control points lie in the same coordinate system inside my software (Pix4D mapper).
The answer here says:
Exif data is standardized, and GPS data must be encoded using
geographical coordinates (minutes, seconds, etc) described above
instead of a fraction. Unless it's encoded in that format in the exif
tag, it won't stick.
Here is my code:
import os, piexif, pyproj
from PIL import Image
img = Image.open(os.path.join(dirPath,fn))
exif_dict = piexif.load(img.info['exif'])
breite = exif_dict['GPS'][piexif.GPSIFD.GPSLatitude]
lange = exif_dict['GPS'][piexif.GPSIFD.GPSLongitude]
breite = breite[0][0] / breite[0][1] + breite[1][0] / (breite[1][1] * 60) + breite[2][0] / (breite[2][1] * 3600)
lange = lange[0][0] / lange[0][1] + lange[1][0] / (lange[1][1] * 60) + lange[2][0] / (lange[2][1] * 3600)
print(breite) #48.81368778730952
print(lange) #9.954511162420633
x, y = pyproj.transform(wgs84, gk3, lange, breite) #from WGS84 to GaussKrüger zone 3
print(x) #3570178.732528623
print(y) #5408908.20172699
exif_dict['GPS'][piexif.GPSIFD.GPSLatitude] = [ ( (int)(round(y,6) * 1000000), 1000000 ), (0, 1), (0, 1) ]
exif_bytes = piexif.dump(exif_dict) #error here
img.save(os.path.join(outPath,fn), "jpeg", exif=exif_bytes)
I am getting struct.error: argument out of range in the dump method. The original GPSInfo tag looks like: {0: b'\x02\x03\x00\x00', 1: 'N', 2: ((48, 1), (48, 1), (3449322402, 70000000)), 3: 'E', 4: ((9, 1), (57, 1), (1136812930, 70000000)), 5: b'\x00', 6: (3659, 10)}
I am guessing I have to offset the values and encode them properly before writing, but have no idea what is to be done.
It looks like you are already using PIL and Python 3.x, not sure if you want to continue using piexif but either way, you may find it easier to convert the degrees, minutes, and seconds into decimal first. It looks like you are trying to do that already but putting it in a separate function may be clearer and account for direction reference.
Here's an example:
def get_decimal_from_dms(dms, ref):
degrees = dms[0][0] / dms[0][1]
minutes = dms[1][0] / dms[1][1] / 60.0
seconds = dms[2][0] / dms[2][1] / 3600.0
if ref in ['S', 'W']:
degrees = -degrees
minutes = -minutes
seconds = -seconds
return round(degrees + minutes + seconds, 5)
def get_coordinates(geotags):
lat = get_decimal_from_dms(geotags['GPSLatitude'], geotags['GPSLatitudeRef'])
lon = get_decimal_from_dms(geotags['GPSLongitude'], geotags['GPSLongitudeRef'])
return (lat,lon)
The geotags in this example is a dictionary with the GPSTAGS as keys instead of the numeric codes for readability. You can find more detail and the complete example from this blog post: Getting Started with Geocoding Exif Image Metadata in Python 3
After much hemming & hawing I reached the pages of py3exiv2 image metadata manipulation library. One will find exhaustive lists of the metadata tags as one reads through but here is the list of EXIF tags just to save few clicks.
It runs smoothly on Linux and provides many opportunities to edit image-headers. The documentation is also quite clear. I recommend this as a solution and am interested to know if it solves everyone else's problems as well.

DPI aware screen capture

I used AutoIt's _ScreenCapture_Capture() before without any trouble. But on Windows 10 with a 4k resolution screen I'm using DPI scaling at 200%. _ScreenCapture_capture() now gets something 2x zoomed in and half the location I asked for.
For instance :
_ScreenCapture_Capture("c:\a.bmp", 100, 100, 200, 200)
; Path, X1, Y1, X2, Y2
Does not return a 100x100 pixel square of the screen (at 100,100 to 200,200). It returns a 100x100 pixel square (at 50,50 to 100,100) instead.
I did find a solution :
DllCall("User32.dll", "bool", "SetProcessDPIAware")
However this screws up the GUI looks. So I then found this code:
GUISetFont(8.5 * _GDIPlus_GraphicsGetDPIRatio()[0])
Func _GDIPlus_GraphicsGetDPIRatio($iDPIDef = 96)
Local $aResults[2] = [1, 1]
_GDIPlus_Startup()
Local $hGfx = _GDIPlus_GraphicsCreateFromHWND(0)
If #error Then Return SetError(1, #extended, $aResults)
#forcedef $__g_hGDIPDll, $ghGDIPDll
$aResult = DllCall($__g_hGDIPDll, "int", "GdipGetDpiX", "handle", $hGfx, "float*", 0)
If #error Then Return SetError(2, #extended, $aResults)
Local $iDPI = $aResult[2]
Local $aresults[2] = [$iDPIDef / $iDPI, $iDPI / $iDPIDef]
_GDIPlus_GraphicsDispose($hGfx)
_GDIPlus_Shutdown()
Return $aresults
EndFunc ;==>_GDIPlus_GraphicsGetDPIRatio
Which works great for the GUI but doesn't help for _ScreenCapture_Capture() calls. Seems I can either have a good looking- or a functioning program, not both.
How could I combine these two solutions to get both a good GUI and a functioning program? If I knew how to do the opposite of :
DllCall("User32.dll", "bool", "SetProcessDPIAware")
Then when I need to capture a screen portion I could turn on compatibility, then immediately turn it off when done capturing. But I don't know how to 'UNsetprocessdpiaware'.
If you are using Windows 10 A update (10.0.14393) you can spin up a separate thread and make the thread alone Per Monitor DPI aware, and leave your main GUI as dpi unaware.
See
SetThreadDpiAwarenessContext function
If you are building using a version of visual studio before 2015 you can declare a pointer to the function prototype
DPI_AWARENESS_CONTEXT WINAPI SetThreadDpiAwarenessContext(
_In_ DPI_AWARENESS_CONTEXT dpiContext
);
and use GetProcAddress to test for the presence of the function before you call it. Anything in that thread will give you raw physical information if you used
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE
Note that this function is not available on Windows 10 10.0.10586. You must test for its availability.

How to communicate through a serial port with python?

I am creating a Solar Array Simulator with python to simulate the voltage and current being created with different angles and intensities of sunlight as a satellite orbits around the earth. I have a very simple program that outputs the voltage and current just with the angle (no orbiting parameters yet). However, I need it to communicate the outputs generated with a E4350B model solar array simulator through a serial port, and I don't know where to start. I have installed pip and used that to install PySerial but do not know what to do from there. How do I communicate the voltage and amp outputs to the simulator through COM ports? Here is what I have for my program that runs the simulator.
from math import sin,radians,pi
import time
'''Needed information:
Voc (per one cell) = 2,680mV
Vmp (per one cell = 2,325mV
Isc (per one cell) = 453mA
Imp (per one cell) = 434mA
angular velocity = .05d/s'''
#Timer eclipse set for total eclipse tim ein seconds
timeEclipse = 1800
#total eclipse time = 30 mins
#Timer sun set for how many seconds it takes to change one degree
timeSun = 20
#Total sun exposer time = 60 mins
#Find the Vmp, Voc, Imp, and Isc from Beta angles 0 - 180
def Exposure():
tSun = timeSun
for x in range(0,181):
angle = sin(radians(x))
Voc = 2680 * angle
Vmp = 2325 * angle
#Amps are going to be a function of voltage
Isc = 453 * angle
Imp = 434 * angle
#
print('At angle ',x,' Vmp = ',Vmp,
'mV, Voc = ',Voc,'mV, Isc = ',Isc,'mA, and Imp = ',Imp,'mA')
time.sleep(tSun)
#Simulate time during eclipse
#Outputs nothing
def Eclipse():
tEclipse = timeEclipse
time.sleep(tEclipse)
#Run loop through Exposure and eclipse
def Run():
run = True
while(run):
Exposure()
Eclipse()
P.S. For anybody who dabbles in a little bit of physics, I need a way to find the current as a function of the voltage at every angle.

Resources