Let's report on the Tractor simulator (cabin tractor interface with Farming Simulator 17): Part 1 to see the global sketch.
All the PC, except Master, start at boot. The following launch.bat are linked in C:\Users\{USER}\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup directory (accessible with Startup:shell in execute menu).
launch.bat____________________________________________________________________________
@echo off
color 71
set PROC=ExeToLaunchWithoutExeExtension
set EXE=%PROC%.exe
title %PROC%
:RESTART
taskkill /F /IM %EXE% >NUL 2>&1
%EXE%
if %ERRORLEVEL% LSS 40 goto STOP
if %ERRORLEVEL% EQU 40 echo RESTART ... & goto RESTART
if %ERRORLEVEL% EQU 41 goto REBOOT
if %ERRORLEVEL% EQU 42 goto SHTDWN
exit
:REBOOT
echo REBOOT...
start shutdown /r /t 2
exit
:SHTDWN
echo SHUTDOWN ...
start shutdown /s /t 2
exit
:STOP
pause
____________________________________________________________________________________
This allows controlling the behavior of the PC depending on the program exit status (<40=pause, 40=relaunch program, 41=reboot PC, 42=shutdown PC).
And all the python scripts are compiled (minGW64) with nuitka with this batch file (just drag and drop the py file on this batch file):
compile.bat___________________________________________________________________________
@echo off
color 71
set PROCpy=%1
set PROC=%~n1
set PPTH=%~p1
cd %PPTH%
title Compiling %PROC% ...
echo Compiling %PROCpy% ...
set PATH=C:\minGW64\bin;%PATH%
nuitka --recurse-all --mingw --icon=%PROC%.ico --remove-output --plugin-enable=multiprocessing %PROCpy%
pause
____________________________________________________________________________________
1/ FS_Hat
It's named "FS_HAT" as it will use the info coming from a, b, c described below to monitor the Farming Simulator 17 Software.
This PC is connected to:
a/ Arduino DUE native port, considered as a keyboard by the PC
b/ Arduino DUE USB (serial) link to exchange data between PC Arduino
c/ BU0836A that simulates a joystick
1.1/ The Serial link with Arduino DUE and UNO
This function uses 2 queues, one to send the orders from the PC to the Arduino DUE, one to receive the data from the Arduino DUE to the PC.
defSerialLink.py_______________________________________________________________________
from defErrPrint import errPrint
from queue import Empty
# -----------------------------------------------------------------------------
#
def serialLink(serportspeed, eols, getData, putData, verbose):
""" Manage a serial link:
serportspeed : serial [Port, speed]
ex.: ['COM3', 115200], ['ttyUSB0', 115200]
eols : End Of Line of the System to communicate with
ex.: '\r\n' for arduino, '\n' for others
getData : get the data from the father process to send through link
a queue1.get_nowait in the father process
putData : send the received data from the link to the father process
a queue2.put_nowait in the father process
"""
from serial import Serial, SerialException
procName = 'Serial Link'
# ----- adapt the format for the father process
def putFmtData(msg):
putData(['SER', msg, 'serialLink'])
# ----- open link
serialOn = True
try:
ser = Serial(*serportspeed)
except:
errPrint ('Serial port {} not found'.format(serportspeed[0]))
serialOn = False
else:
print(' >> {} {} ({}): On'.format(procName, ser.name, ser.baudrate))
rems = '' # ---- remainning strings from the link (not ended with eols)
# ----------------------------------------------------------------- Loop
while serialOn:
try:
# ---- get (or not if Empty) an entry from father process
msg = getData()
except Empty:
# ---- if Empty, try to read if there is an entry from link
try:
if ser.in_waiting: # ---- incoming bytes waiting to be read
rcvs = rems + ser.read(ser.in_waiting).decode()
# ----- split with eols
r = rcvs.split(eols)
# ---- pop the last element (should be '' otherwise
# the beginning of another order)
rems = r.pop()
for s in r:
# ----- send the received data to the father process
if s: putFmtData(s)
if verbose: print('\treceived : ' + s)
except SerialException: serialOn = False
else:
# ----- if 'Exit' from the father process -> exit the function
if 'SerExit' in msg:
ser.close()
break
else:
# ----- otherwise, send the data through the link
try:
# ----- add eols to the string to send
ser.write((msg + eols).encode())
if verbose: print('\tsent : ' + msg)
except SerialException: serialOn = False
# ----- special info for the father process to warn Pb on serial link
if not serialOn: putFmtData('0serial')
print(' << {}: Off'.format(procName))
#
# -----------------------------------------------------------------------------
____________________________________________________________________________________
defTCPIP and defErrPrint are already described in a previous post.
Fs_Hat.py____________________________________________________________________________
# -*- coding: utf-8 -*-
""" PROCESS Fs_Hat
==============
"""
__author__ = 'Damien HALLEZ'
__version__ = '2.0'
__date__ = 'nov 2017'
from queue import Empty
from time import sleep, strftime, time
from multiprocessing import Process
from defErrPrint import errPrint
# ----- Lookup table to translate info coming from DUE to TCP
# ----- DUE orders coded with 3 letters to speed up the communication
outLU = { 'ACC' : 'Acc=' , \
'STW' : 'STW=' , \
'BRK' : 'Brake=', \
'RRL' : 'ReRedLght=', \
'BCN' : 'Beacon=', \
'LFD' : 'LghtFrDown=', \
'LRD' : 'LghtReDown=', \
'RBL' : 'RightBlinker=', \
'LBL' : 'LeftBlinker=', \
'WRN' : 'Warning=', \
'LFU' : 'LghtFrUp=', \
'LRU' : 'LghtReUp=' }
# -----------------------------------------------------------------------------
# Farming Simulator Hat
def gameManager(user, qData, sendSerial, putData):
""" Manage the Farming Simulator software:
user : user's name using the software (which use 'My Games' dir)
getData : get the data from the father process to send to FS
a queue1.get_nowait in the father process
sendSerial : send orders to the serial link
putData : send the received data to the father process
a queue2.put_nowait in the father process
"""
from PIL import Image
from os import path, listdir, remove, kill, chdir
from glob import glob as dirlist
from subprocess import call, check_output
from win32gui import FindWindow, ShowWindow, BringWindowToTop, \
SetForegroundWindow
from threading import Timer
procName = 'gameManager'
getData = qData.get
localPutData = qData.put_nowait
# ---------------------------------------------------- FS images management
scrShotDir = r'C:\Users\{}\Documents\My Games\FarmingSimulator2017\screenshots'.format(user)
scrShotFile = path.join(scrShotDir, 'fsScreen_20{}_*.png')
# ----- to speed the analyse, just pick a 2x14 pixels block from the image
imgBox = [[750, 370, 752, 384], [300, 950, 302, 964]]
outMsg = 'FSReboot'
def waitForImg(n):
""" wait for the image <n>;
n = 0 : enter game image
n = 1 : career image
"""
ok = True
# ----- purge directory
for file in listdir(scrShotDir): remove(path.join(scrShotDir, file))
cont = True
sleep(1 + 2*n) # longer for image 1
T0 = time()
while cont:
# ----- send Print Screen
sendSerial('kPSC')
# ----- time for FS to write the file
sleep(.1)
# ----- format of the file generated by FS when Print Screen
imgfile = sorted(dirlist(scrShotFile.format(strftime('%y_%m_%d'))))
if imgfile:
try:
# ----- read the screenshot image and crop a block
img = Image.open(imgfile.pop()).crop(imgBox[n])
except: pass
else:
# ----- verify if all the cropped area is green
cont = bool(img.getcolors()[0] != (28, (127, 192, 50)))
# ----- delete the exceeded screenshots files
for file in imgfile: remove(file)
if cont: sleep(.05)
# ----- if too long: FS is lost -> kill then reboot the game
if time() - T0 > 10:
sendSerial('sOk0')
out = check_output('tasklist /NH /FI "STATUS eq RUNNING" ' + \
'/FI "IMAGENAME eq FarmingSimulator2017Game.exe"')
for line in out.splitlines():
if b'FarmingSimulator2017Game' in line:
kill(int(line.split()[1]), 15)
print('\t! game lost')
ok = False
break
return ok
def getFSWindow():
""" Try during 5s to find the FS window
"""
for i in range(50):
h = FindWindow(0, "Farming Simulator 17")
if h: break
else: sleep(0.1)
return h
def setFSForeground(h):
""" put the window foreground
"""
try:
ShowWindow(h, 5)
BringWindowToTop(h)
SetForegroundWindow(h)
except: pass
print(' >> {}: On '.format(procName))
# ================================================================ launch Farming Simulator
# ----- go in the right directory to launch the software
chdir(r'C:\Program Files (x86)\Farming Simulator 2017')
# ----- call Farming Simulator
call(['FarmingSimulator2017.exe'])
# ----- wait installed (2 x 5 sec)
for i in range(2):
h = getFSWindow()
if h: break
tt = None
# ----- put it foreground to receive the corresponding orders
if h:
setFSForeground(h)
# ----- wait first image ...
if waitForImg(0):
# ----- enter game (career)
sendSerial('kRET')
# ----- game not loaded
gameOn = False
# ----- every 2.5 seconds, verify FS alive
tt = Timer(2.5, localPutData, args=(['FSAlive','?'], ))
tt.start()
# ======================================================================= manage the orders
while "the FS window is present":
try:
# ----- wait for the orders to control FS
cmd, val = getData(True)
except Empty:
# ----- should not arrive ... but verify FS active
if not getFSWindow(): break
else:
# ----- get FS window handle
h = getFSWindow()
if h: # if active (else break loop)
# ----- put the window foreground
setFSForeground(h)
if 'FSAlive' in cmd:
# ----- test every 2.5 sec if FS alive
tt = Timer(2.5, localPutData, args=([cmd, val], ))
tt.start()
elif 'SKB' in cmd:
# ----- send to keyboard, SKB=
# keyA, keyE, keyH, key{letter},
# kTAB, kRET, kBKS, kESC ...
# relb, rstd
sendSerial(val)
elif 'Cmp' in cmd:
# ----- launch comparison
sendSerial('kBKS')
sendSerial('kTAB')
sendSerial('keyH')
sendSerial('kTAB')
sendSerial('keyH')
elif 'Reload' in cmd:
gameOn = False
sendSerial('sOk0')
localPutData(['Load_R', val])
else:
# ---- game to close if open
if gameOn:
# ----- send the quit game code
sendSerial('sOk0')
sendSerial('qtGm')
sleep(3)
ok = waitForImg(0)
if ok:
sendSerial('kRET')
sleep(.1)
else: ok = True
if ok:
if 'Load' in cmd:
n = int(val)
# print('\t· load game #' + sn)
# ----- send ld+num code of game to load
sendSerial('ld{:02d}'.format(n))
sleep(2 + n/10)
# ----- wait for the image of game loaded
ok = waitForImg(1)
if ok:
# ----- send Enter if ok
sleep(.1)
sendSerial('kRET')
gameOn = True
sleep(0.1)
if '_N' in cmd:
sendSerial('SiGa')
elif '_H' in cmd:
sendSerial('HaGa')
elif '_R' in cmd:
localPutData(['Cmp', '1'])
sendSerial('sOk1')
else: break
elif 'Quit' in cmd:
gameOn = False
elif 'Exit' in cmd:
# ----- send quit FS code
sendSerial('qtFS')
outMsg = 'FSExit'
break
else:
print(' - Farming Simulator exited -')
break
# ---- warn FS Exit or Reboot
if tt is not None:
try: tt.cancel()
except: pass
putData(['SUB', outMsg, procName])
print(' << {}: Off'.format(procName))
#
# -----------------------------------------------------------------------------
# =============================================================================
# MAIN
if __name__ == "__main__":
from configparser import ConfigParser
from multiprocessing import Queue
from defTCPIP import TCPLink
from defSerialLink import serialLink
# ------------------------------------------------------------- read config
config = ConfigParser()
config.filename = 'FS_Hat.ini'
config.savfilename = config.filename.replace('.','_sav.')
Ok = False
for tst in range(2):
# ----- verify config ini ok
try:
config.read(config.filename, encoding='utf-16')
settings = config['SETTINGS']
vrbstr = settings['verbose']
if 'b' in vrbstr: verbose = int(vrbstr.split('b')[1], base=2)
elif 'x' in vrbstr: verbose = int(vrbstr.split('x')[1], base=16)
else: verbose = int(vrbstr)
serial = bool(int(settings['serial']))
DUE = config['DUE']
DUEcom = ['COM{}'.format(int(DUE['cmd_port'])), \
int(DUE['cmd_speed'])]
user = config['USER']['user'].strip()
netCfg = config['NET']
addrCfg = config['ADDRESS']
procName = netCfg['node'].strip().title()
baseAddr = netCfg['base'].strip()
IPtoNnode = {baseAddr+addrCfg[procName]:procName}
if netCfg['links']:
IPtoNlinks = {baseAddr + addrCfg[s.strip()].strip():\
s.strip().title() \
for s in netCfg['links'].split(',')}
else: IPtoNlinks = {}
portBase = int(netCfg['port_base'])
sLang = config['LANG']['lang']
Ok = True
break
except:
# ---- take the save ini
print('\t! Restore ini file')
with open(config.filename, 'w', encoding='utf-16') as ini:
with open(config.savfilename, 'r', encoding='utf-16') as _ini:
ini.write(_ini.read())
if not Ok:
errPrint(r'/!\ Error reading .ini file')
exit(44)
# ----- Proc Name from ini = Fs_Hat
print('> {} [{}] >'.format(procName, strftime('%d/%m/%y %H:%M:%S')))
#
# verbose : 1 = main, 2 = ethernet, 4 = serial
#
vrbm = bool(verbose & 1)
vrbe = bool(verbose & 2)
vrbs = bool(verbose & 4)
qIn = Queue()
putData = qIn.put_nowait
getData = qIn.get
# --------------------------------------------------- list of PC to connect
eth = TCPLink(IPtoNnode, IPtoNlinks, portBase, putData)
print('\t{}: {} '.format(procName, eth.myAddr))
# ----------------------------------------------------- process serial link
qSerial = Queue()
def launchSerial():
pSerial = Process(target=serialLink, \
args=(DUEcom, '\r\n', \
qSerial.get_nowait, putData, vrbs))
pSerial.start()
return pSerial
if serial:
sendSerial = qSerial.put_nowait
pSerial = launchSerial()
else:
sendSerial = print # ----- for debug
# -------------------------------------------------------- game management
qFS = Queue()
sendFS = qFS.put_nowait
def launchFS():
pFS = Process(target=gameManager, args=(user, qFS, sendSerial, putData))
pFS.start()
return pFS
pFS = launchFS()
# ----------------------------------------------------------- begin program
exitVal = 0
exitFlag = False
T0 = time()
Tend = 15
ackn = False
while True:
try:
ret = getData(True)
except Empty:
ret = []
except KeyboardInterrupt:
# ----- simulate an exit order
ret = ['TCP:rcv0', 'Exit=0', 'Ctrl+C']
if ret: typ, msgs, proc = ret
else: typ = ''
if 'TCP' in typ:
# ------------------------------------------------------- TCP entry
typ2 = typ.split(':')[-1]
if 'out' in typ2:
# ---- have to relaunch ethernet link
eth.relaunch(proc)
elif 'snt1' in typ2:
# ---- info of what was sent
if vrbe: print('\t-> {} to {}'.format(msgs, proc))
elif 'snt0' in typ2:
# ---- info of fail sending
print('\t/!\ Fail sending {} to {}'.format(msgs, proc))
elif typ2 in ['rcv1', 'rcv0']:
# ----- received info
if vrbe: print('\t<- {} from {}'.format(msgs, proc))
if exitFlag:
print('\t {} stopped'.format(proc))
if typ2 == 'rcv1':
if all(eth.stop(proc)): break
for cmdval in msgs.split('&'):
# ----- analyse received order ('cmd=val')
cmd, val = cmdval.split('=')
# ----- send aknowledgment if really comming from TCP
ackn = bool(proc != 'Ctrl+C')
if 'Exit' in cmd:
# stop requested by TCP
exitVal = int(val)
if pFS.is_alive(): sendFS([cmd, val])
exitFlag = True
T0 = time()
else:
# ----- send to FS
sendFS([cmd, val])
elif 'rcv?' in typ2:
print('\t ????? {} received from {}'.format(msgs, proc))
ackn = False
# -------------------------------------------------------- Serial entry
elif 'SER' in typ:
char = msgs[0]
if char == '0':
# ----- have to relaunch serial process
pSerial = launchSerial()
elif char == '1':
# ----- to send only one host
eth.sendTo(outLU[msgs[1:4]]+ msgs[4:], 'Rpi_Disp')
elif char == '2':
# ----- send to many hosts
eth.sendTos(outLU[msgs[1:4]]+ msgs[4:], ['Rpi_Out', 'Rpi_Disp'])
# --------------------------------------------------- Sub process entry
elif 'SUB' in typ:
if 'FSExit' in msgs: break
elif 'FSReboot' in msgs:
print('\t· FS Reboot')
pFS.join()
pFS = launchFS()
# ------------------------------------------------------------ unknown
elif typ: print('\t/!\ unknown type : ', typ)
if exitFlag:
# ------ force an exit after timeout Tend
if time() - T0 > Tend or not pFS.is_alive(): break
# ----- free the queue
qIn.cancel_join_thread()
# ----------------------------------------------------------- close TCPLink
eth.close()
# -------------------------------------------------- wait serial link close
if serial:
if pSerial.is_alive(): sendSerial('SerExit')
pSerial.join()
# --------------------------------------------------- wait process FS close
if pFS.is_alive(): pFS.terminate() # ----- should not be
pFS.join()
# -------------------------------------------------------------------- exit
print('< {} [{}] <'.format(procName, strftime('%d/%m/%y %H:%M:%S')))
exit(39 + exitVal)
#
# =============================================================================
____________________________________________________________________________________
To know if it's the good moment to enter a key of a sequence of keys to continue a FS (Farming Simulator) session when loading a game, I simulate the 'PrintScreen' key (sendSerial('kPSC')) and retreive the image in 'C:\Users\{USER}\Documents\My Games\FarmingSimulator2017\screenshots' directory. Then, thanks to PIL, I compare some image areas (green pixels) to verify if the game is loaded (def waitForImg).
As Farming Simulator forks at each new session, I have to control if it's still alive (getFSWindow()) and in this case place it foreground to be sure the simulated keyboard send the keys to it (setFSForeground(h)).
All info coming from TCP/IP are sent to the FS manager (sendFS([cmd, val])). All the info coming from Arduino, depending on the first digit, are sent on the Raspberry Dashboard (Rpi_Disp) and the Raspberry Outputs (Rpi_Out).
To gain time in the serial communication, I use a Lookup Table (outLU) to translate a few chars command (3 based chars) into a "full" command for the TCP/IP link.
The graphic output (HDMI) is split to be displayed on the tractor screen or the main screen and captured through a video grabber (StarTech) to be used by the PC show.
2/ "Show"
This PC is used for the SHOW.
When the animator is not presenting the tractor simulator, it plays some movies in loop (in movies directory) on the main screen. In the meantime, the tractor could be used as a simple simulator playing Farming Simulator.
When the animator switches to the presentation mode, it retreives the images from Farming Simulator (through the video grabber on USB3) and overlay texts, videos, messages on the main screen.
It overlays also the Zen@Terra app (same as the one on the Rasperry "Zen@Terra", Rpi_Pressure).
One can see the demo mode (with a speaker, see video1 ) and the play mode (guests can play with FS17, see video2, video3,).
2.1/ the scrolling banner
This function makes a message scroll at the bottom of the screen, overlaying the videos in loop, announcing the next presentation (ex. "Next presentation in 10 mn").
A final countdown for the last 10 sec with big numbers covering the screen.
defBanner.py_________________________________________________________________________
# -*- coding: utf-8 -*-
""" Banner
======
"""
__author__ = 'Damien HALLEZ'
__version__ = '1.0'
__date__ = 'nov 2017'
import tkinter as tk
from threading import Timer
from multiprocessing import Process
# -----------------------------------------------------------------------------
#
def passmsg(text1, text2, N):
""" pass the 2 texts (original lang and English)
from right to left of the screen
"""
# ----- init fig
bannerFig = tk.Tk()
bannerFig.configure(bg='black')
R = bannerFig.winfo_screenwidth()
H = bannerFig.winfo_screenheight()
# ----- height font = 1/12 screen height
HFnt = int(H/12)
bannerFig.overrideredirect(True)
bannerFig.lift()
bannerFig.wm_attributes("-topmost", True, "-disabled", True,\
"-transparentcolor", "black")
# ----- two lines to display
w1 = tk.Label(bannerFig, text=text1,\
fg='white', bg='black', \
font=('Arial',HFnt,'bold'))
w1.grid(row=0, column=0)
w2 = tk.Label(bannerFig, text=text2 ,\
fg='white', bg='black', \
font=('Arial',HFnt,'italic'))
w2.grid(row=1, column=0)
# ----- poistion in y depending on existing text2
L = max(len(text1),len(text2))
nline = int(H-N*HFnt)
for i in range(-R, R+L*int(HFnt/4.5), 10):
bannerFig.geometry("+{}+{}".format(-i, nline))
bannerFig.update()
bannerFig.after(25)
bannerFig.destroy()
#
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#
def lastsec(nsec, soundCountDown):
""" display sec coutdown 3/4 screen
"""
from winsound import PlaySound, SND_FILENAME
from threading import Thread
# ----- init fig
bannerFig = tk.Tk()
bannerFig.configure(bg='black')
R = bannerFig.winfo_screenwidth()
H = bannerFig.winfo_screenheight()
# ----- height font = 3/4 screen height
HFnt = int(H*3/4)
bannerFig.overrideredirect(True)
bannerFig.lift()
bannerFig.wm_attributes("-topmost", True, "-disabled", True,\
"-transparentcolor", "black")
cntdwn = tk.IntVar()
cntdwn.set(nsec)
# ----- digit(s) to display
w1 = tk.Label(bannerFig, textvariable=cntdwn,\
fg='white', bg='black', \
font=('Arial',HFnt,'bold'))
w1.grid(row=0, column=0)
bannerFig.update()
# ----- if associated sound, play it
if soundCountDown:
Thread(target=PlaySound, args=('BeepTractor.wav', SND_FILENAME)).start()
for i in range(nsec):
cntdwn.set(nsec-i)
bannerFig.update_idletasks()
# ----- get the new size (change from 10 to 9 for example)
Hr = bannerFig.winfo_height()
Rr = bannerFig.winfo_width()
bannerFig.geometry("+{}+{}".format(int((R-Rr)/2), int((H-Hr)/2)))
bannerFig.update()
bannerFig.after(1000)
cntdwn.set(0)
bannerFig.update()
bannerFig.after(250)
bannerFig.destroy()
#
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# Banner
# -----------------------------------------------------------------------------
#
def Banner(qIn, qOut, Text, soundCountDown):
print(' >> Banner proc: On')
# ----- init
banOn = True
# ----- lang = En by default
sLang = 'en'
# ----- step in mn (update every mn)
stepmn = 1
# ----- setp in sec
stepsec = 5
# ----- beginning of countdown
endsec = 10
# ----- begin with countdown in mn
mode = 'mn'
ti = 0
# ----- fake process with is_alive false to initiate proc
class procclass():
def is_alive(self): return False
def terminate(self): pass
def join(self): pass
proc = procclass()
alrdyExit = False
def sndFmt(msg):
# ----- send info back to the father
qOut.put_nowait(['SUB', msg, 'Banner'])
getData = qIn.get
putData = qIn.put_nowait
while banOn:
# ----- get the order
cmdval = getData()
# ----- split command value
cmd, val = cmdval.split('=')
# ----- lang
if cmd == 'Lang':
sLang = val
# ----- launch countdown
elif cmd == 'remTime':
alrdyExit = False
# ---- slpit remaining time and unit (remTime=20:mn)
remstr, mode = [v.strip() for v in val.split(':')]
# ----- remaining time in int
remTime = int(remstr)
lnchflag = True
# ----- mn
if mode == 'mn':
N = 3.25 # factor multply font height (see passmsg)
# ----- text 1 = requested lang
text1 =' {} '.format(Text[sLang].format(remTime))
# ----- text 2 = English lang
text2 =' {} '.format(Text['en'].format(remTime))
# ----- check remaining time doesn't lead to negative min
n, rem = divmod(remTime, stepmn+1)
# ----- compute tsec for the timer to go next value
if n:
# ----- no pb, continue counting in mn
tsec = stepmn
else:
# ----- need to count in sec
tsec = rem - 1
if tsec == 0:
# ----- 1 mn
putdata('remTime=60:sec')
lnchflag = False
elif tsec < 0:
# ----- 0 mn
putData('endTime=1')
lnchflag = False
tsec *= 60
# ----- sec
else:
N = 1.625 # factor multply font height (see passmsg)
text1 = '{} s ...'.format(remTime)
# ----- no need text 2
text2 = ''
# ----- check remaining time doesn't lead to negative sec
n, rem = divmod(remTime, stepsec+endsec)
if n:
# ----- no pb, continue counting in sec
tsec = stepsec
else:
tsec = rem
if tsec <= 0 :
putData('endTime=1')
lnchflag = False
if lnchflag:
# ----- if valable tsec
if ti:
# ----- stop timers
ti.cancel()
ti.join()
if proc.is_alive():
# ----- stop current proc
proc.terminate()
proc.join()
# ----- launch new timer
ti = Timer(tsec, putData, ('next=1',))
ti.start()
# ----- launch new process
proc = Process(target=passmsg, \
args=(text1, text2, N))
proc.start()
# ----- next for countdown
elif cmd == 'next':
if mode == 'mn':
remTime -= stepmn
if remTime <= 1:
putData('remTime=60:sec')
else:
putData('remTime={}:mn'.format(remTime))
else:
remTime -= stepsec
if remTime <= endsec:
p = Process(target=lastsec, args=(endsec, soundCountDown))
p.start()
sndFmt('last10s=1')
p.join()
putData('endTime=1')
else:
putData('remTime={}:sec'.format(remTime))
# ----- set mn step
elif cmd == 'Stepmn':
stepmn = int(val)
# ----- set sec step
elif cmd == 'Stepsec':
stepsec = int(val)
# ----- set end sec
elif cmd == 'Endsec':
endsec = int(val)
elif cmd == 'endTime':
sndFmt('endTime=1')
putData('BanExit=0')
# ----- BanExit : 0 to stay in loop, 1 to exit the process
elif cmd == 'BanExit':
exitVal = int(val)
if not alrdyExit or exitVal:
if exitVal:
banOn = False
print('\tBanner proc: exiting...')
else:
print('\tBanner proc: ready...')
if ti:
ti.cancel()
ti.join()
if proc.is_alive():
proc.terminate()
proc.join()
alrdyExit = True
qIn.cancel_join_thread()
# ----- exit process
print(' << Banner proc: Out')
#
# -----------------------------------------------------------------------------
____________________________________________________________________________________
2.2/ Displaying the benefits (of using Zen@Terra solution)
This function overlays a png file explaining the benefits depending on the used language.
ex. English
defBenef.py___________________________________________________________________________
# -*- coding: utf-8 -*-
""" Benef PROCESS
=============
"""
__author__ = 'Damien HALLEZ'
__version__ = '1.0'
__date__ = 'nov 2017'
import tkinter as tk
# ----------------------------------------------------------------------------
#
class DispBenef:
def __init__(self, lang):
# ===============================================================
# Init Graphic
# ===============================================================
# ----- Graphic init
fig = tk.Tk()
fig.overrideredirect(1)
fig.title('Benef')
fig.configure(bg='black')
fig.wm_attributes("-topmost", True, "-disabled", True,\
"-transparentcolor", "black")
scrSize = [1920, 1080]
cv = tk.Canvas(fig, width=scrSize[0]-4, height=scrSize[1]-4, bg = 'black')
cv.grid()
# ----- background image
self.Img = tk.PhotoImage(file='./Img/{}_Benef.png'.format(lang))
self.wBkgd = cv.create_image(0, 0, image=self.Img, anchor=tk.NW)
fig.geometry('1920x1080+0+0')
self.fig = fig
self.setConfig = cv.itemconfig
self.update()
def update(self):
self.fig.update()
self.fig.after(500, self.update)
def hide(self):
self.fig.withdraw()
self.fig.update_idletasks()
def show(self):
self.fig.deiconify()
self.fig.lift()
self.fig.update()
def lang(self, lang):
self.Img = tk.PhotoImage(file='./Img/{}_Benef.png'.format(lang))
self.setConfig(self.wBkgd, image = self.Img)
def exit(self):
self.fig.destroy()
#
# -----------------------------------------------------------------------------
____________________________________________________________________________________
2.3/ Pictures and videos overlay
This function merges pictures and videos with the Farming Simulator screens (captured with a video grabber) thanks to openCV.
It allows zoom in and zoom out effects.
defObjOverlay.py_______________________________________________________________________
# -*- coding: utf-8 -*-
import cv2
from numpy import linspace
from os import path
# -----------------------------------------------------------------------------
#
class OvlObj():
""" class to manage the video (load, stop, pause, zoom, ..)
"""
def __init__(self, name, offsetloop):
# ----- name of object
objType, filename = name.split(':')
offset, loop = offsetloop
self.name = path.splitext(path.split(filename)[1])[0]
self.fileName = './Img/' + filename
if 'img' in objType:
# ----- image filename
self.vdo = False
# ----- open video (cv capture)
self.firstImage = cv2.imread(self.fileName)
else:
# ----- video filename
self.vdo = True
# ----- open video (cv capture)
self.capt = cv2.VideoCapture(self.fileName)
# ----- read first frame ...
self.firstImage = self.capt.read()[1]
# ----- save the last image
self.lastImage = self.firstImage
# ----- relative position of the video (between 0 and 1)
self.offsetx, self.offsety = offset
# ---- frame per sec
self.fps = 25
# ----- ... to know the size of it
self.rows, self.cols, _ = self.firstImage.shape
# ----- then compute where to place in pixels
self.offsetx = min(max(0, round(1920 * self.offsetx - self.cols/2)), 1920-self.cols)
self.offsety = min(max(0, round(1080 * self.offsety - self.rows/2)), 1080-self.rows)
# ----- compute the slice for the frame (inverted x, y)
self.X = slice(self.offsety, self.offsety + self.rows, 1)
self.Y = slice(self.offsetx, self.offsetx + self.cols, 1)
# ----- mode loop
self.loop = bool(loop)
# ----- flag mode zoom (in or out)
self.zoomFlag = False
# ----- index of zoom range
self.zi = 0
# ----- final index
self.ze = 0
# ----- range (from 0 to 1)
self.zr = []
# ----- pause at frame
self.pauseFrame = 0
self.nbFrames = 0
# ----- pause flag
self.pause = False
# ------------------------------------------------------------- get frames
def getNextFrame(self):
# ----- copy the slices for the frame
X, Y = self.X, self.Y
# ----- flag wether to remove the object or not (for zoomOut)
rmvObj = False
if not self.pause:
# ----- get the next frame
if self.vdo: frame = self.capt.read()[1]
else: frame = self.lastImage
if frame is None:
# ----- if end of video
if self.loop:
# ----- if loop, copy fisrt image in last image
self.lastImage = self.firstImage
# ----- rewind the video
self.rewind()
else:
# ----- if not loop, pause
self.pause = True
else:
self.nbFrames += 1
if self.nbFrames == self.pauseFrame: self.pause = True
if self.zoomFlag:
# ----- if zoom, take the next increment
if self.zi < self.ze:
self.zi += 1
if self.zi == self.ze:
# ----- if end of increment, zoom flag false
self.zoomFlag = False
if self.zoomOutFlag:
# ----- if zoomOut, rewind the file
self.rewind()
# ----- and set the flag to remove it
rmvObj = True
# ----- take the ratio of image
r = self.zr[self.zi-1]
# ----- reduce the frame following the ratio
frame = cv2.resize(frame, (0,0), fx=r, fy=r)
# ----- get the size of the reduced video
rows, cols, _ = frame.shape
# ----- compute the new position (centered) and inverted x, y
x = self.offsety + round((self.rows-rows)/2)
y = self.offsetx + round((self.cols-cols)/2)
# ----- compute the slices
X, Y = slice(x, x + rows, 1), slice(y, y + cols, 1)
# ----- put the frame in lastimage
self.lastImage = frame
return X, Y, self.lastImage, rmvObj
# --------------------------------------------------------- initiate zoom In
def zoomIn(self):
# ---- init zoom from 0 to 1 in 1s
self.zoom([0, 1, 1])
# -------------------------------------------------------- initiate zoom Out
def zoomOut(self):
# ---- init zoom from 1 to 0 in 1s
self.zoom([1, 0, 1])
# ----------------------------------------------------------- initiate zoom
def zoom(self, valz):
# ----- zomm flag true
self.zoomFlag = True
# ----- zoom Out if begin > end
self.zoomOutFlag = bool(valz[0] > valz[1])
# ----- convert sec in nb of frames
valz[2] = int(valz[2] * self.fps)
# ----- compute the range (remove 0 and 1)
zr = linspace(*valz)[1:-1]
# ---- set the range
self.zr = zr
# ----- init the index
self.zi = 0
# ----- set the final index
self.ze = len(zr)
# ----------------------------------------------------------------- rewind
def rewind(self):
if self.vdo:
# ----- set the video at the fisrt frame
self.capt.set(cv2.CAP_PROP_POS_FRAMES, 0)
self.nbFrames = 0
# ------------------------------------------------------- pause at frame nb
def pauseAtFrame(self, noFrame):
if self.vdo:
# ----- set the video at frame #nbFrame
self.pauseFrame = noFrame
# ------------------------------------------------------- pause at frame nb
def goToFrame(self, nbFrame):
if self.vdo:
# ----- set the video at frame #nbFrame
self.capt.set(cv2.CAP_PROP_POS_FRAMES, nbFrame)
self.pause = True
self.nbFrames = nbFrame
#
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#
def objOverlay(getData, vdoChannel, ovlsProp):
from queue import Empty
import numpy as np
from win32api import SetCursorPos
from win32gui import FindWindow, ShowWindow, BringWindowToTop
# ----- transparent image for the background
transpImg = cv2.imread('./Img/TranspImg.png', cv2.IMREAD_UNCHANGED)
# ----- Black image for the background
blkImg = cv2.imread('./Img/BlckBckgrnd.png', cv2.IMREAD_UNCHANGED)
# ----- set video window, fullscreen
cv2.namedWindow('objOverlay', cv2.WND_PROP_FULLSCREEN)
cv2.setWindowProperty('objOverlay', cv2.WND_PROP_FULLSCREEN, \
cv2.WINDOW_FULLSCREEN)
# ----- capture FS mainFrames
vdoCapt0 = cv2.VideoCapture(vdoChannel)
# vdoCapt0 = cv2.VideoCapture('./Movies/00_cubtotal1920.mov') ## debug
# ----- set the video size
vdoCapt0.set(3, 1920)
vdoCapt0.set(4, 1080)
# ----- load the videos to overlay
ovlObjs = [OvlObj(*vdoProp) for vdoProp in ovlsProp.items()]
wait_ms = int(1000/60) # 60 Hz of refresh
wait4data = False
# ----- display flag
dispFlag = True
# ----- seletion of video to display
vdoSel = []
# ----- hide the cursor
SetCursorPos([-1, -1])
winId = FindWindow(0, "objOverlay")
while(True):
try:
# ----- wait for incoming order
data = getData(wait4data)
except Empty:
data =''
if 'Hide' in data:
# ----- hide the display
ShowWindow(winId, 0)
dispFlag = False
wait4data = True
elif 'Show' in data:
# ----- show the display
ShowWindow(winId, 5)
BringWindowToTop(winId)
# SetForegroundWindow(winId)
dispFlag = True
wait4data = False
elif 'hide' in data:
# ----- hide the video object (remove from Selection)
ovlobj = data.split(':')[0]
for obj in ovlObjs:
if ovlobj in obj.name:
if obj in vdoSel : vdoSel.remove(obj)
break
elif 'show' in data:
# ----- show the video object (insert in Selection)
ovlobj = data.split(':')[0]
for obj in ovlObjs:
if ovlobj in obj.name:
if obj not in vdoSel : vdoSel.append(obj)
break
elif 'pauseAtFrame' in data:
# ----- pause at frame #n
ovlobj, val = data.split(':')
n = int(val[val.find('(')+1:val.find(')')])
for obj in ovlObjs:
if ovlobj in obj.name:
if obj not in vdoSel : vdoSel.append(obj)
obj.pauseAtFrame(n)
break
elif 'pauseAtSec' in data:
# ----- pause at frame #n
ovlobj, val = data.split(':')
n = float(val[val.find('(')+1:val.find(')')])
for obj in ovlObjs:
if ovlobj in obj.name:
if obj not in vdoSel : vdoSel.append(obj)
obj.pauseAtFrame(int(n*obj.fps))
break
elif 'pause' in data:
# ----- set pause mode
ovlobj = data.split(':')[0]
for obj in ovlObjs:
if ovlobj in obj.name:
obj.pause = True
break
elif 'goToFrame' in data:
# ----- pause at frame #n
ovlobj, val = data.split(':')
n = int(val[val.find('(')+1:val.find(')')])
for obj in ovlObjs:
if ovlobj in obj.name:
if obj not in vdoSel : vdoSel.append(obj)
obj.goToFrame(n)
break
elif 'goToSec' in data:
# ----- pause at sec #n
ovlobj, val = data.split(':')
n = float(val[val.find('(')+1:val.find(')')])
for obj in ovlObjs:
if ovlobj in obj.name:
if obj not in vdoSel : vdoSel.append(obj)
obj.goToFrame(int(n*obj.fps))
break
elif 'restart' in data:
# ----- unset the pause mode
ovlobj = data.split(':')[0]
for obj in ovlObjs:
if ovlobj in obj.name:
obj.pause = False
break
elif 'rewind' in data:
# ----- rewind the movie
ovlobj = data.split(':')[0]
for obj in ovlObjs:
if ovlobj in obj.name:
obj.rewind()
break
elif 'play' in data:
# ----- play from the beginning
ovlobj = data.split(':')[0]
for obj in ovlObjs:
if ovlobj in obj.name:
if obj not in vdoSel : vdoSel.append(obj)
obj.rewind()
break
elif 'zoomIn' in data:
# ----- insert the video in selection and activate the zoom
ovlobj = data.split(':')[0]
for obj in ovlObjs:
if ovlobj in obj.name:
if obj not in vdoSel : vdoSel.append(obj)
obj.zoomIn()
break
elif 'zoomOut' in data:
# ----- activate the zoom
# obj will be removed from selection with rmvObj flag set with getNextFrame
ovlobj = data.split(':')[0]
for obj in ovlObjs:
if ovlobj in obj.name:
obj.zoomOut()
break
elif 'Exit' in data:
# ----- exit process
break
# ----- Display the current mainFrame
if dispFlag:
# ----- get the main frame
mainFrame = vdoCapt0.read()[1]
if mainFrame is None: mainFrame = blkImg.copy()
# ---- get the objects to remove to false
obj2rmv = []
for obj in vdoSel:
# ----- get the slices, the frame and the remove object flag
X, Y, frame, rmvObj = obj.getNextFrame()
# ----- merge it in the main frame
mainFrame[X, Y] = frame
# ----- add in the list to remove
if rmvObj: obj2rmv.append(obj)
# ----- remove in the selcection the video to remove
for obj in obj2rmv: vdoSel.remove(obj)
# ----- display it
cv2.imshow('objOverlay', mainFrame)
else:
# ---- if display disabled, display a transparent frame
cv2.imshow('objOverlay', transpImg)
# ----- update display
if cv2.waitKey(wait_ms) & 0xFF == ord('q'):
break
# ----- When everything done, release the vdoCapture
for obj in ovlObjs:
if obj.vdo: obj.capt.release()
# ----- destroy the window
cv2.destroyAllWindows()
#
#--------------------------------------------------------------------------
____________________________________________________________________________________
2.4/ Displaying videos with VLC player (loop)
defVLCPlayer.py______________________________________________________________________
# -*- coding: utf-8 -*-
""" VLC Player
==========
"""
__author__ = 'Damien HALLEZ'
__version__ = '1.0'
__date__ = 'nov 2017'
from vlc import MediaPlayer, EventType
from os import listdir
from multiprocessing import Process
from win32api import SetCursorPos
# ----------------------------------------------------------------------------
# VLC Player
# ----------------------------------------------------------------------------
def VLC_player(qIn, qOut):
"""
see example in main
"""
print(' >> VLC player: On')
vlcp = MediaPlayer()
vlcp.set_fullscreen(True)
def fnext(event, put): put('next')
evmngr = vlcp.event_manager()
evmngr.event_attach(EventType.MediaPlayerEndReached, \
fnext, qIn.put_nowait)
VLCOn = True
lst = []
ilst = -1
mtype = ''
loop = False
def sndFmt(msg):
return qOut.put_nowait(['SUB', msg, 'VLC'])
# ----- hide the cursor
SetCursorPos([-1, -1])
while VLCOn:
cmd = qIn.get()
if '>' in cmd:
mtype, nameOpt = cmd.split('>')
if ';' in nameOpt:
name, opts = nameOpt.split(';', maxsplit=1)
for cmdval in opts.split(';'):
cmd_, val = cmdval.split('=')
if 'fullscreen' in cmd_:
vlcp.set_fullscreen(bool(int(val)))
elif 'aspect-ratio' in cmd_:
vlcp.video_set_aspect_ratio(val)
elif 'loop' in cmd_:
loop = bool(int(val))
else:
name = nameOpt
if mtype == 'File':
lst = [name]
elif mtype == 'Dir':
lst = ['{}\\{}'.format(name,fn) for fn in listdir(name)]
elif mtype == 'Opt':
# fullscreen, loop
cmd_, val = nameOpt.split('=')
if 'fullscreen' in cmd_:
vlcp.set_fullscreen(bool(int(val)))
elif 'aspect-ratio' in cmd_:
vlcp.video_set_aspect_ratio(val)
elif 'loop' in cmd_:
loop = bool(int(val))
ilst = 0
qIn.put_nowait('play')
elif cmd == 'play':
vlcp.stop()
vlcp.set_mrl(lst[ilst])
vlcp.play()
elif cmd == 'next':
if mtype =='File':
if loop:
qIn.put_nowait('play')
else:
sndFmt('endPlay=1')
vlcp.stop()
else:
ilst += 1
if ilst >= len(lst): ilst = 0
if loop:
qIn.put_nowait('play')
else:
sndFmt('endPlay=1')
vlcp.stop()
elif cmd == 'Pause':
vlcp.pause()
elif cmd == 'Stop':
print('\tVLCPlayer: ready...')
vlcp.stop()
elif cmd == 'getHWND':
qOut.put_nowait(vlcp.get_hwnd())
elif cmd == 'VLCExit':
print('\tVLCPlayer: exiting...')
vlcp.release()
VLCOn = False
qIn.cancel_join_thread()
print(' << VLC player: Out')
#
# ---------------------------------------------------------------------------
____________________________________________________________________________________
2.5/ Overlay Zen@Terra app
Quite the same as the one on Raspberry Zen@Terra ...
defZenAtTerr.py_______________________________________________________________________
# -*- coding: utf-8 -*-
""" Zen@Terra
=========
"""
__author__ = 'Damien HALLEZ'
__version__ = '1.0'
__date__ = 'Nov 2017'
import tkinter as tk
from configparser import ConfigParser
from multiprocessing import Process, Queue
from queue import Empty
from defErrPrint import errPrint
from time import time, sleep
from os import path
appRed = '#CC0000'
appBlue = '#006699'
appGreen = '#41B649'
appOrange = '#CC6600'
def nullFun(): return
# =============================================================================
#
class ZenAtTerra():
"""
Main animation
"""
# ========================================================== initializing
def __init__(self, qIn, qOut):
# ======================= open graphic window, top screen, full screen
fig = tk.Tk()
fig.overrideredirect(True)
fig.lift()
WxH = [960, 600]
fig.wm_attributes('-topmost', True)
fig.geometry('{}x{}-0-0'.format(*WxH))
fig.title('zen@Terra')
langs = ConfigParser()
langs.read('ZenAtTerra.ini', encoding='utf-16')
fig.bind('q', self.Quit)
fig.bind('Q', self.Quit)
fig.bind('H', self.Hide)
fig.bind('h', self.Hide)
fig.bind('S', self.Show)
fig.bind('s', self.Show)
fig.bind('<Control-KeyPress-F>', self._toFr)
fig.bind('<Control-KeyPress-E>', self._toEn)
fig.bind('<Control-KeyPress-G>', self._toGe)
fig.bind('<Control-KeyPress-R>', self._toRu)
self.getData = qIn.get_nowait
self.enterEmpty = qIn.empty
self.putData = qOut.put_nowait
# ----- attach a canvas to the window
cnv = tk.Canvas(fig, bd=0, bg='white', highlightthickness=0, \
cursor='hand2', \
width=WxH[0], height=WxH[1])
cnv.pack()
# ----- to simplify the notations
setText = cnv.create_text
setImage = cnv.create_image
setCircle = cnv.create_oval
setArc = cnv.create_arc
self.setConfig = cnv.itemconfig
self.getConfig = cnv.itemcget
self.setCoords = cnv.coords
self.getCoords = cnv.coords
self.wbind = cnv.tag_bind
self.getTag = cnv.find_withtag
self.move = cnv.move
curLang = langs['LANG']['curlang']
self.curLang = curLang
self.labels = {}
self.langs = langs
# ----- fonts
fontButton = ('Michelin', 9, 'bold')
fontLabel = ('Michelin', 12, 'bold')
fontPres = ('Michelin', 16, 'bold')
fontTimer = ('Michelin', 16)
fontLabelSmall = ('Michelin', 7, 'bold')
fontSpeed = ('CARACTERES L1', 30)
# ----- Directory containing the pictures
imgDir = 'ZaTimg'
def imgLoadTk(fn): return tk.PhotoImage(file=path.join(imgDir,fn))
# =================================== load and display background image
self.bckImgTk = imgLoadTk('background.png')
self.BckImg = setImage(480, 300, image=self.bckImgTk)
# ===================================================== top view button
self.topviewTk = imgLoadTk('top_view.png')
tk.Button(cnv, bd=8, bg='#6699CC', image= self.topviewTk, \
anchor=tk.CENTER, \
command=self.toggleTopView, height=31, width=31)\
.place(x=960-2, y=50/2, anchor=tk.E)
# ===== load tractor alone: tractor =========================== TRACTOR
self.tractorAloneTk = imgLoadTk('tractor.png')
self.tractorAlone = setImage(371, 321, image=self.tractorAloneTk)
# ----- load the tractor + trailer (3rdWhl)
self.tractorTrailerk = imgLoadTk('tractor_trailer.png')
self.tractorTrailer = setImage(384, 321, image=self.tractorTrailerk,\
state=tk.HIDDEN)
self.wheelTk = {}
self.wheel = {}
self.nbAxles = 2
self.curAxle = 0
self.curWheelClr = ['Green']*3
self.selAxle = {na:[] for na in range(3)}
self.wshift = 114
# -------------------- pressure probe, check sign, sandglass positions
midfx = 280
midfy = 409
midrx = 491
midry = 400
midtx = 576 + self.wshift
midty = midfy
# ----- wheels, check, sandglass positions
xys = [[midfx, midfy],[midrx, midry],[midtx, midty]]
# -------------------------------------------------------------- WHEELS
for clr in ['Blue', 'Orange', 'Green', 'Red']:
tag = ('whl' + clr, 'toShift')
clrl = clr.lower()
self.wheelTk[clr] = []
self.wheel[clr] = []
for i, fn in enumerate(['', 'rear_', '']):
self.wheelTk[clr].append(imgLoadTk(fn+'wheel_{}.png'.format(clrl)))
tagi = ('whl{}{}'.format(i, clr), *tag)
self.wheel[clr].append(\
setImage(*xys[i], state=tk.HIDDEN, tag=tagi, \
image=self.wheelTk[clr][-1]))
self.selAxle[i].append(self.wheel[clr][-1])
self.wShow(self.wheel['Green'][:self.nbAxles])
# ----- pressure probe positions
xyp = [[x, y+z] for [x,y],z in zip(xys,[37, 44, 37])]
# ------------------------------- displays
ptfx = 303
ptfy = 492
ptrx = 515
ptry = ptfy
pttx = 597 + self.wshift
ptty = ptfy
# ----- 'real' pressures
xyd = [[ptfx, ptfy],[ptrx, ptry],[pttx, ptty]]
# ----- target pressures
xyt = [[x+4, y] for x,y in xyd]
# ----- manometer
xym = [[x-15, y+11] for x,y in xyd]
tagi = tag
wTargetPres = []
self.targetPres = [1.8]*3
self.curPres = self.targetPres.copy()
self.prevPres = self.targetPres.copy()
self.oriTargetPres = self.targetPres.copy()
self.xflTimer = [None]*3
self.curPresClr = [appGreen]*3
# ------------------------------- PRESSURE PROBE, CHECK SIGN, SANDGLASS
tag = 'toShift'
self.mesPresTk = []
self.checkTk = imgLoadTk('check.png')
self.check = []
self.sandglassTk = imgLoadTk('sandglass.png')
self.sandglass = []
self.manoTk = imgLoadTk('mano.png')
self.mano = []
wCurPres = []
tagi = tag
state = tk.NORMAL
for i, fn in enumerate(['Fr', 'Re', 'Fr' ]):
self.mesPresTk.append(imgLoadTk('mesPres_{}.png'.format(fn)))
if i == 2:
tagi = ('3rdWhl', tag)
state = tk.HIDDEN
# ----- pressure probes
setImage(*xyp[i], image=self.mesPresTk[-1], anchor=tk.NW, \
state=state, tag=tagi)
# ----- check icones
self.check.append(setImage(*xys[i], anchor=tk.CENTER, state=state, \
image=self.checkTk, tag=tagi))
# ----- target pressures
wTargetPres.append(setText(*xyt[i], text='1.8', \
fill=appGreen, state=state, \
anchor=tk.SW, font=fontPres, tag=tagi))
if i == 2: tagi = ('3rdWhl+', tag)
# ----- sandglass
self.sandglass.append(setImage(*xys[i], image=self.sandglassTk, \
state=tk.HIDDEN, \
anchor=tk.CENTER, tag=tagi))
# ----- 'real' pressure
wCurPres.append(setText(*xyd[i], text='1.8', fill=appOrange, \
state=tk.HIDDEN, tag=tagi, \
anchor=tk.NW, font=fontPres))
# ----- manometers
self.mano.append(setImage(*xym[i], image=self.manoTk, tag=tagi, \
anchor=tk.E, state=tk.HIDDEN))
# -------------------------------------------------- TOOLS (PRESSURES)
self.variableLoadList = ['DIRECTDRILL', 'TRAILER', 'SPREADER' , \
'DRILL']
tool2img = { 'TRACTOR': 'nothing', 'CULTIVATOR': 'cultivator', \
'DIRECTDRILL': 'direct_drill',\
'TRAILER': 'trailer', 'PLOUGH': 'plough',\
'DRILL': 'drill', 'SPREADER':'spreader' }
self.toolPressures = {}
self.toolSpeed = {}
for i, key in enumerate(tool2img.keys()):
if key in self.variableLoadList:
self.toolPressures[key] = {}
self.toolSpeed[key] = {}
for mode in ['road', 'field', 'boost']:
self.toolPressures[key][mode] = []
self.toolSpeed[key][mode] = []
for load in [0.0, 0.5, 1.0]:
pressures, speed = (langs[key]['_{}@{:.1f}'.format(mode, load)]).split('@')
self.toolPressures[key][mode].append(\
eval(pressures.replace('-', '1.2')))
self.toolSpeed[key][mode].append(int(speed))
else:
self.toolPressures[key] = {}
self.toolSpeed[key] = {}
for mode in ['road', 'field', 'boost']:
pressures, speed = (langs[key]['_{}@-'.format(mode)]).split('@')
self.toolPressures[key][mode] = [\
eval(pressures.replace('-', '1.2'))]
self.toolSpeed[key][mode] = [int(speed)]
self.xflatingTool = {na:[self.sandglass[na], wCurPres[na], \
self.mano[na]] for na in range(3)}
# --------------------------------------------------------- TOOLS (draw)
self.toolsTk = {}
for key, img in tool2img.items(): \
self.toolsTk[key] = imgLoadTk(img + '.png')
self.setImage = setImage
self.setText = setText
self.fontButton = fontButton
self.toolInd = 0
self.toolList = {i:key for i, key in enumerate(tool2img.keys())}
self.indTool = {key:i for i, key in self.toolList.items()}
self.curTool = self.toolList[self.toolInd]
self.frontMassTool = ['DIRECTDRILL', 'PLOUGH']
self.load = 0
self.mode = 'road'
# ===== load large button 1 ================================ [BUTTON 1]
tag = 'Tooling'
# ----- label 1
self.labels['TOOLING'] = setText(145+5, 61, font=fontLabel, \
text=langs['TOOLING'][curLang],
anchor=tk.NW)
# ----- shadow
self.but1sTk = imgLoadTk('big_shadow.png')
but1s = setImage(145+3, 85+4, image=self.but1sTk, anchor=tk.NW)
def wbind(ws, tag, wc, but, action):
def handler(event, self=self, ws=ws, tag=tag, wc=wc, action=action):
return self.pressAction(event, ws, tag, wc, but, action)
self.wbind(wc, '<Button-1>', handler)
# ----- button
self.but1Tk = imgLoadTk('big_but.png')
but1 = setImage(145, 85, image=self.but1Tk, tag=tag, anchor=tk.NW)
wbind(but1s, tag, but1, None, self.selTooling)
# ----- image in button
self.tractorButTk = imgLoadTk('tractor_but.png')
tractorBut = setImage(145+166/2, 85+73-20, image=self.tractorButTk, \
tag=tag, anchor=tk.SE)
wbind(but1s, tag, tractorBut, None, self.selTooling)
# ----- front mass
self.frontMassTk = imgLoadTk('front_mass.png')
self.frontMass = self.setImage(145+166/2-59+2, 85+73/2-2, \
image=self.frontMassTk, state=tk.HIDDEN, \
tag=tag, anchor=tk.E)
wbind(but1s, tag, self.frontMass, None, self.selTooling)
# ----- tool attached to tractor
self.toolBut = self.setImage(145+166/2, 85+73-22, \
image=self.toolsTk[self.curTool], \
tag=tag, anchor=tk.SW)
wbind(but1s, tag, self.toolBut, None, self.selTooling)
# ----- text in button
self.txtBut1 = self.setText(145+166/2, 85+70, anchor=tk.S, \
tag=tag, text=self.langs[self.curTool][self.curLang], \
font=self.fontButton )
wbind(but1s, tag, self.txtBut1, None, self.selTooling)
# -------------------------------------------------------- sub button 1
self.subbut1Tks = imgLoadTk('small_shadow.png')
self.subbut1Tkg = imgLoadTk('small_but_grey.png')
self.subbut1Tky = imgLoadTk('small_but_yellow.png')
self.subbut1Tki = []
self.subbut1y = []
tag = 'subequip'
for i, fn in enumerate(['empty', 'half', 'full' ]):
tag2 = 'but1#{}'.format(i)
taga = (tag, tag2)
j = 63*i
# ----- shadow
subbut1s = setImage(145+3+j, 169+4, anchor=tk.NW, \
tag=tag, state=tk.HIDDEN, image=self.subbut1Tks)
# ----- grey background
subbut1b = setImage(145+j, 169, anchor=tk.NW, tag=taga, \
state=tk.HIDDEN, image=self.subbut1Tkg )
wbind(subbut1s, tag2, subbut1b, i, self.selToolingSub)
# ----- yellow background
self.subbut1y.append(\
setImage(145+j, 169, anchor=tk.NW, tag=taga, \
state=tk.HIDDEN, image=self.subbut1Tky ))
wbind(subbut1s, tag2, self.subbut1y[-1], i, self.selToolingSub)
# ----- image
self.subbut1Tki.append(imgLoadTk(fn+'.png'))
subbut1i = setImage(145+j+40/2, 169+39/2, anchor=tk.CENTER, tag=tag, \
state=tk.HIDDEN, image=self.subbut1Tki[-1] )
wbind(subbut1s, tag2, subbut1i, i, self.selToolingSub)
# ===== load large button 2 ================================ [BUTTON 2]
tag = 'Surf'
self.field = False
# ----- label 2
self.labels['USAGE'] = setText(457+4, 61, font=fontLabel, \
text=langs['USAGE'][curLang], \
anchor=tk.NW)
# ----- shadow
but2s = setImage(457+3, 85+4, image=self.but1sTk, anchor=tk.NW)
# ----- button
but2 = setImage(457, 85, image=self.but1Tk, \
tag=tag, anchor=tk.NW)
wbind(but2s, tag, but2, None, self.toggleSurf)
# ----- image in button: Field
self.fieldButTk = imgLoadTk('field.png')
fieldBut = setImage(457+166/2, 85+7, image=self.fieldButTk, \
tag=(tag, 'field'), state=tk.HIDDEN, \
anchor=tk.N)
wbind(but2s, tag, fieldBut, None, self.toggleSurf)
# ----- image in button: Road
self.roadButTk = imgLoadTk('road.png')
roadBut = setImage(457+166/2, 85+7, image=self.roadButTk, \
tag=(tag, 'road'), anchor=tk.N)
wbind(but2s, tag, roadBut, None, self.toggleSurf)
# ----- text in button: field
txtBut2f = setText(457+166/2, 85+69, anchor=tk.S, \
state=tk.HIDDEN, tag=(tag, 'field'), \
text=langs['FIELD'][curLang], \
font=fontButton )
self.labels['FIELD'] = txtBut2f
wbind(but2s, tag, txtBut2f, None, self.toggleSurf)
# ----- text in button: road
txtBut2r = setText(457+166/2, 85+69, anchor=tk.S, \
tag=(tag, 'road'), \
text=langs['ROAD'][curLang], \
font=fontButton )
self.labels['ROAD'] = txtBut2r
wbind(but2s, tag, txtBut2r, None, self.toggleSurf)
# -------------------------------------------------------- sub button 2
self.subbut2Tki = []
self.subbut2y = []
tag = 'field'
for i, fn in enumerate(['bank', 'flat']):
j = 68*i
tag2 = 'but2#{}'.format(i)
taga = (tag, tag2)
# ----- shadow
subbut2s = setImage(487+3+j, 169+4, anchor=tk.NW, state=tk.HIDDEN, \
tag=tag, image=self.subbut1Tks )
# ----- grey background
subbut2b = setImage(487+j, 169, anchor=tk.NW, state=tk.HIDDEN, \
tag=taga, image=self.subbut1Tkg )
wbind(subbut2s, tag2, subbut2b, i, self.surfSub)
# ----- yellow background
self.subbut2y.append(\
setImage(487+j, 169, anchor=tk.NW, state=tk.HIDDEN, \
tag=taga, image=self.subbut1Tky ))
wbind(subbut2s, tag2, self.subbut2y[-1], i, self.surfSub)
# ----- image
self.subbut2Tki.append(imgLoadTk(fn+'.png'))
subbut2i = setImage(487+j+40/2, 169+39/2, anchor=tk.CENTER, state=tk.HIDDEN, \
tag=taga, image=self.subbut2Tki[-1] )
wbind(subbut2s, tag2, subbut2i, i, self.surfSub)
self.bankFlag = False
# ===== load button MANUAL ============================ [BUTTON MANUAL]
tag = 'Manual'
self.manualOn = False
# ----- shadow
self.rightButtTks = imgLoadTk('medium_but_shadow.png')
manualButs = setImage(823+3, 66+4, image=self.rightButtTks, \
anchor=tk.NW)
# ----- normal button
self.manualButtTk = imgLoadTk('manual_but_off.png')
self.manualBut = setImage(823, 66, image=self.manualButtTk, \
tag=tag, anchor=tk.NW)
wbind(manualButs, tag, self.manualBut, None, self.toggleManualMode)
# ----- blue button
self.manualButtTkb = imgLoadTk('manual_but_on.png')
self.manualButBlue = setImage(823, 66, image=self.manualButtTkb, \
tag=tag, state=tk.HIDDEN, anchor=tk.NW)
wbind(manualButs, tag, self.manualButBlue, None, self.toggleManualMode)
# ----- text in button
txtButManual = setText(823+81/2, 66+58-3, anchor=tk.S, \
text=langs['MANUAL'][curLang], \
font=fontLabelSmall, tag=tag )
self.labels['MANUAL'] = txtButManual
wbind(manualButs, tag, txtButManual, None, self.toggleManualMode)
# ===== load button - ====================================== [BUTTON -]
tag = ('submanual', '-')
# ----- shadow
self.plusminusButtTks = imgLoadTk('plus_minus_shadow.png')
minusButs = setImage(176+3, 556+4, image=self.plusminusButtTks, \
state=tk.HIDDEN, tag=tag, anchor=tk.CENTER)
# ----- normal button
self.minusButtTk = imgLoadTk('minus_but.png')
minusBut = setImage(176, 556, image=self.minusButtTk, \
state=tk.HIDDEN, tag=tag, anchor=tk.CENTER)
wbind(minusButs, '-', minusBut, None, self.minusButFun)
# ===== load button Axle ================================ [BUTTON AXLE]
tag = ('submanual', 'axle')
# ----- shadow
self.axleButtTks = imgLoadTk('axle_shadow.png')
axleButs = setImage(513+4, 742+5, image=self.axleButtTks, \
state=tk.HIDDEN, anchor=tk.CENTER, tag=tag)
# ----- normal button
self.axleButtTk = imgLoadTk('axle_but.png')
axleBut = setImage(385, 556, image=self.axleButtTk, \
state=tk.HIDDEN, anchor=tk.CENTER, tag=tag)
wbind(axleButs, 'axle', axleBut, None, self.nextAxle)
# ----- text in button
txtButAxle = setText(385, 556+46/2-5, anchor=tk.S, \
state=tk.HIDDEN, font=fontLabelSmall, \
tag=tag, text=langs['AXLE'][curLang])
self.labels['AXLE'] = txtButAxle
wbind(axleButs, 'axle', txtButAxle, None, self.nextAxle)
self.lastAxle = 1
# ===== load button + ====================================== [BUTTON +]
tag = ('submanual', '+')
# ----- shadow
plusButs = setImage(592+3, 556+4, image=self.plusminusButtTks, \
state=tk.HIDDEN, anchor=tk.CENTER, tag=tag)
# ----- normal button
self.plusButtTk = imgLoadTk('plus_but.png')
plusBut = setImage(592, 556, image=self.plusButtTk, \
state=tk.HIDDEN, anchor=tk.CENTER, tag=tag)
wbind(plusButs, '+', plusBut, None, self.plusButFun)
# ===== load button BOOST ============================== [BUTTON BOOST]
tag = 'Boost'
self.boostOn = False
boostButs = setImage(823+3, 142+5, image=self.rightButtTks, \
state=tk.HIDDEN, tag=tag, anchor=tk.NW)
# ----- normal button
self.boostButtTk = imgLoadTk('boost_but_off.png')
self.boostBut = setImage(823, 142, image=self.boostButtTk, \
state=tk.HIDDEN, tag=tag, anchor=tk.NW)
wbind(boostButs, tag, self.boostBut, None, self.toggleBoostMode)
# ----- red button
self.boostButtTkr = imgLoadTk('boost_but_on.png')
self.boostButRed = setImage(823, 142, image=self.boostButtTkr, \
state=tk.HIDDEN, tag=tag, anchor=tk.NW)
wbind(boostButs, tag, self.boostButRed, None, self.toggleBoostMode)
# ----- text in button
txtButBoost = setText(823+79/2, 142+58-5, anchor=tk.S, \
state=tk.HIDDEN, font=fontLabelSmall, \
tag=tag, text=langs['BOOST'][curLang])
self.labels['BOOST'] = txtButBoost
wbind(boostButs, tag, txtButBoost, None, self.toggleBoostMode)
# --------------------------------------------------------- triple warn
tag = 'subBoost'
self.triplwarnw = []
ux, uy, d = 805, 248, 33
dx, dy, r = ux+d, uy+d, round(d/2)
for i in range(3):
j = i*43
self.triplwarnw.append(\
setCircle(ux+j, uy, dx+j, dy, outline='black', width=3, \
tag=tag, state=tk.HIDDEN))
self.wFill([self.triplwarnw[0]], appRed)
# --------------------------------------------------------- Boost Timer
ux, uy, d = 805, 312, 120
dx, dy, r = ux+d, uy+d, round(d/2)
self.boostTimer = [\
setArc(ux, uy, dx, dy, width=0, fill=appRed, tag=tag, \
start=90, style=tk.PIESLICE, state=tk.HIDDEN, extent=0)]
dr = 11
self.boostTimer.append(\
setCircle(ux+dr, uy+dr, dx-dr, dy-dr, outline='white', width=5, \
fill='black', tag=tag, state=tk.HIDDEN))
self.boostTimer.append(\
setText(ux+r, uy+r, text='03:00', fill='white', font=fontTimer, \
tag=tag, state=tk.HIDDEN))
self.boostTimerTimes = 0
self.boostTime = 180
self.refreshTimerDisplay()
# ----- speed indicator ---------------------------------------- SPEED
ux, uy, d = 805, 261, 106
dx, dy, r = ux+d, uy+d, round(d/2)
tag = 'Speed'
setCircle(ux, uy, dx, dy, tag=tag, outline='black', width=4)
dr = 2
setCircle(ux+dr, uy+dr, dx-dr, dy-dr, tag=tag, outline='white', \
fill=appRed, width=2)
dr = 20
setCircle(ux+dr, uy+dr, dx-dr, dy-dr, tag=tag, outline=None, \
fill='white', width=0)
self.speedvalw = setText(ux+r, uy+r, text='35', font=fontSpeed, \
tag=tag)
# ========================================================== TOP VIEW 1
self.topViewFlag = -1
self.topview1Tk = imgLoadTk('topview_tractor.png')
self.topview = [setImage(0, 220, anchor=tk.NW, state=tk.HIDDEN, \
image=self.topview1Tk )]
# ========================================================== TOP VIEW 2
self.topview2Tk = imgLoadTk('topview_tractor_trailer.png')
self.topview.append(setImage(0, 220, anchor=tk.NW, state=tk.HIDDEN, \
image=self.topview2Tk ))
tag = 'topview'
ptx = [142, 355, 554]
pty = [y+220 for y in [36, 343]]
wTargetPresTV = {}
wCurPresTV = {}
self.manoTV = {}
tagi = tag
for i, x in enumerate(ptx):
wTargetPresTV[i] = []
wCurPresTV[i] = []
self.manoTV[i] = []
for j, y in enumerate(pty):
if i == 2:
tagi = ('3rdWhlTV', tag)
state = tk.HIDDEN
# ----- target pressures
wTargetPresTV[i].append(setText(x+4*j, y+1, text='1.8', \
fill=appGreen, state=state, \
anchor= j and tk.SW or tk.NW, \
font=fontPres, tag=tagi))
if i == 2: tagi = ('3rdWhlTV+', tag)
# ----- 'real' pressure
wCurPresTV[i].append(setText(x+4*(j==0), y+1, text='1.8', fill=appOrange, \
state=tk.HIDDEN, tag=tagi, \
anchor= j and tk.NW or tk.SW, \
font=fontPres))
# ----- manometers
self.manoTV[i].append(setImage(x-15, y-5+16*j, image=self.manoTk, \
tag=tagi, \
anchor=tk.E, state=tk.HIDDEN))
self.topViewOn = False
self.topViewMoved = False
self.topViewPrevAxle = 3
self.xflatingToolTV = {na:[*wCurPresTV[na], \
*self.manoTV[na]] for na in range(3)}
self.wTargetPresAx = []
self.wTargetPresAll = []
self.wCurPresAx = []
for i in range(3):
self.wTargetPresAx.append([wTargetPres[i], \
*wTargetPresTV[i]])
self.wTargetPresAll += self.wTargetPresAx[-1]
self.wCurPresAx.append([wCurPres[i], *wCurPresTV[i]])
# ------------------------------------- some variable to save or to add
self.xflateOn = [False]*3
self.setTimer = fig.after
self.stopTimer = fig.after_cancel
self.fig = fig
self.update = fig.update
self.setTimer(10, self.wait4action)
self.setPresSpeed()
print(' >> Zen@Terra: On')
self.setTimer(5, self.Hide)
self.fig.mainloop()
# ======================================================= widgets management
# -------------------------------------------------------- general functions
def pressAction(self, event, ws, tag, wc, but, action):
""" press button action: remove the shadow (ws)
move the image down (tag)
extra widget to hanlde (but)
set the action after release (action)
"""
self.wHide([ws])
self.move(tag, 3, 4)
def handler(event, self=self, \
ws=ws, tag=tag, wc=wc, but=but, action=action):
return self.releaseAction(event, ws, tag, wc, but, action)
self.wbind(wc, '<ButtonRelease-1>', handler)
def releaseAction(self, event, ws, tag, wc, but, action):
""" release button action: display the shadow (ws)
move the image up (tag)
extra widget to hanlde (but)
set the action after release (action)
"""
def handler(event, self=self, \
ws=ws, tag=tag, wc=wc, but=but, action=action):
return self.pressAction(event, ws, tag, wc, but, action)
self.wbind(wc, '<Button-1>', handler)
self.move(tag, -3, -4)
self.wShow([ws])
if but is None: action()
else: action(ws, but)
def setSpeed(self, v):
""" Display the speed in the roadsign
"""
self.curSpeed = v
self.setConfig(self.speedvalw, text='{:d}'.format(v))
def wHide(self, lst):
""" hide the widgets in the passed list
"""
for tag in lst: self.setConfig(tag, state=tk.HIDDEN)
def wShow(self, lst):
""" show the widgets in the passed list
"""
for tag in lst: self.setConfig(tag, state=tk.NORMAL)
def wFill(self, lst, clr):
""" Fill the shape of the widgets in the passed list
"""
for tag in lst: self.setConfig(tag, fill=clr)
def wGetColor(self, w):
return self.getConfig(w, 'fill')
def refreshTimerDisplay(self):
""" Refresh the timer display
"""
self.setConfig(self.boostTimer[0], extent=-2*self.boostTime)
self.setConfig(self.boostTimer[2], text='{:02d}:{:02d}'\
.format(*divmod(self.boostTime, 60)))
def setTargetPressure(self, na):
""" Display the target pressure of the axle #na
"""
txt = '{:.1f}'.format(self.targetPres[na])
for w in self.wTargetPresAx[na]: self.setConfig(w, text=txt)
def setCurPressure(self, na):
""" Display the current pressure of the axle #na
"""
txt = '{:.1f}'.format(self.curPres[na])
for w in self.wCurPresAx[na]: self.setConfig(w, text=txt)
def _toFr(self, event): self.translate('fr')
def _toEn(self, event): self.translate('en')
def _toGe(self, event): self.translate('ge')
def _toRu(self, event): self.translate('ru')
def translate(self, ln):
""" translate the labels
"""
for key, w in self.labels.items():
self.setConfig(w, text=self.langs[key][ln])
self.setConfig(self.txtBut1, text=self.langs[self.curTool][ln])
self.curLang = ln
# ------------------------------------------------------------------ But 1
def selTooling(self):
self.toolInd += 1
if self.toolInd >= len(self.toolList): self.toolInd = 0
self.curTool = self.toolList[self.toolInd]
self.setTool()
def selToolingSub(self, ws, but):
self.wHide(self.subbut1y)
self.wShow([ws, self.subbut1y[but]])
self.load = but
self.setPresSpeed()
def setTool(self):
""" set the tool attached to but1
"""
# ----- change image of tool
self.setConfig(self.toolBut, image=self.toolsTk[self.curTool])
self.load = 0
# ----- front mass or not
if self.curTool in self.frontMassTool:
self.wShow([self.frontMass])
else:
self.wHide([self.frontMass])
# ----- text in button
self.setConfig(self.txtBut1, \
text=self.langs[self.curTool][self.curLang])
# ----- display or not the load
if self.curTool in self.variableLoadList:
self.wShow(['subequip'])
self.wHide(self.subbut1y)
else:
self.wHide(['subequip'])
# ----- if 3rdWhl : image
if 'TRAILER' in self.curTool:
self.wHide([self.tractorAlone, *self.selAxle[2]])
self.wShow([self.tractorTrailer, '3rdWhl', 'whl2Green'])
if self.nbAxles == 2: self.move('toShift', -self.wshift, 0)
self.nbAxles = 3
self.curPresClr[2] = appGreen
else:
self.wShow([self.tractorAlone])
self.wHide([self.tractorTrailer, '3rdWhl', '3rdWhl+', \
*self.selAxle[2]])
if self.nbAxles == 3: self.move('toShift', self.wshift, 0)
self.nbAxles = 2
self.setPresSpeed(not self.topViewOn)
if self.topViewOn and self.topViewPrevAxle != self.nbAxles:
self.toggleTopView(True)
def setPresSpeed(self, refreshPres=True, saved=None):
self.oriTargetPres = self.toolPressures[self.curTool][self.mode][self.load].copy()
self.setSpeed(self.toolSpeed[self.curTool][self.mode][self.load])
if saved is None:
self.targetPres = self.oriTargetPres.copy()
else:
self.targetPres = saved.copy()
if refreshPres: self.manPress()
# ------------------------------------------------------------------ But 2
def toggleSurf(self):
# ----- no action if boost on
if not self.boostOn:
if self.field:
# ----- field set -> set ROAD
self.wHide(['field', 'Boost'])
self.wShow(['road'])
self.mode = 'road'
else:
# ----- road set -> set FIELD
self.wShow(['field', 'Boost'])
self.wHide(['road', *self.subbut2y, self.boostButRed])
self.mode = 'field'
# ----- warn Master
self.putData(['SUB', 'SurfSet=' + self.mode, 'Master'])
# ----- toggle value
self.field = not self.field
self.setPresSpeed()
def surfSub(self, ws, but):
""" Sub Button 2 [but] released (angle / flat)
"""
self.wHide(self.subbut2y)
self.wShow([ws, self.subbut2y[but]])
if but:
if self.bankFlag:
self.oriTargetPres = [round(v-0.4, 1) \
for v in self.oriTargetPres]
self.bankFlag = False
else:
if not self.bankFlag:
self.oriTargetPres = [round(v+0.4, 1) \
for v in self.oriTargetPres]
self.bankFlag = True
if self.targetPres != self.oriTargetPres:
self.targetPres = self.oriTargetPres.copy()
self.manPress()
# ----------------------------------------------------------------- MANUAL
def toggleManualMode(self):
# ----- boost on not on
if not self.boostOn:
if self.manualOn:
# ----- Manual Off
self.manualOn = False
self.wShow([self.manualBut, \
*self.wheel['Green'][:self.nbAxles]])
self.wHide([self.manualButBlue, 'submanual', 'whlBlue'])
self.wFill(self.wTargetPresAll, appGreen)
self.curWheelClr = ['Green']*3
self.curPresClr = [appGreen]*3
self.targetPres = self.prevPresM.copy()
self.manPress()
else:
# ----- Manual On
self.manualOn = True
self.wHide([self.manualBut])
self.wShow([self.manualButBlue, 'submanual'])
self.prevPresM = self.oriTargetPres.copy()
self.curAxle = self.nbAxles
self.nextAxle()
# --------------------------------------------------------------------- [-]
def minusButFun(self):
if not self.boostOn:
self.targetPres[self.curAxle] = \
max(round(self.targetPres[self.curAxle] - 0.1, 1), 0.4)
self.manPress()
# ------------------------------------------------------------------ [AXLE]
def nextAxle(self):
if not self.boostOn:
self.lastAxle = self.curAxle
self.curAxle += 1
self.xflate(self.lastAxle)
if self.curAxle >= self.nbAxles: self.curAxle = 0
self.wFill(self.wTargetPresAx[self.curAxle], appBlue)
self.wHide([*self.selAxle[self.curAxle]])
self.wShow([self.wheel['Blue'][self.curAxle]])
self.curPresClr[self.curAxle] = appBlue
self.curWheelClr[self.curAxle] = 'Blue'
# --------------------------------------------------------------------- [+]
def plusButFun(self):
if not self.boostOn:
self.targetPres[self.curAxle] = \
min(round(self.targetPres[self.curAxle] + 0.1, 1), 3.0)
self.manPress()
# ------------------------------------------------------------------- BOOST
def toggleBoostMode(self):
if self.boostOn:
# ----- Boost Off
self.boostOn = False
self.wHide([self.boostButRed, 'subBoost', 'whlRed'])
self.wShow([self.boostBut])
if self.manualOn: self.wShow(['submanual'])
self.curWheelClr = self.prevWheelClr.copy()
for na in range(self.nbAxles):
self.wShow([self.wheel[self.curWheelClr[na]][na]])
self.curPresClr = self.prevPresClr.copy()
for na, clr in enumerate(self.curPresClr):
self.wFill(self.wTargetPresAx[na], clr)
self.move('Speed', 0, -200)
self.mode = 'field'
mem = self.prevPresB.copy()
self.boostTime = 180
self.boostTimerTimes = 0
else:
if self.manualOn: self.wHide(['submanual'])
# ----- Boost On
self.boostOn = True
self.wHide([self.boostBut, 'whlGreen', 'whlBlue'])
self.wShow([self.boostButRed, 'subBoost', \
*self.wheel['Red'][:self.nbAxles]])
self.wFill(self.wTargetPresAll, appRed)
self.prevWheelClr = self.curWheelClr.copy()
self.curWheelClr = ['Red']*3
self.prevPresClr = self.curPresClr.copy()
self.curPresClr = [appRed]*3
self.move('Speed', 0, 200)
self.mode = 'boost'
self.prevPresB = self.targetPres.copy()
mem = None
self.setTimer(1000, self.boostDelay)
self.setPresSpeed(saved=mem)
# ============================================================== ANIMATIONS
# --------------------------------------------------------------- Pressures
def manPress(self, redraw=False):
for na in range(self.nbAxles):
self.setCurPressure(na)
self.setTargetPressure(na)
if self.curPres[na] != self.targetPres[na] or redraw:
if appGreen in self.curPresClr[na]:
self.wFill(self.wTargetPresAx[na], 'black')
if not self.xflateOn[na]:
if self.topViewOn: self.wShow(self.xflatingToolTV[na])
self.wShow(self.xflatingTool[na])
if not self.boostOn:
self.wHide([self.wheel[self.curWheelClr[na]][na]])
self.wShow([self.wheel['Orange'][na]])
self.xflateOn[na] = True
if self.xflTimer[na] is not None:
self.stopTimer(self.xflTimer[na])
self.xflTimer[na] = self.setTimer(1000, self.xflate, na)
def xflate(self, na):
if na < self.nbAxles:
if self.curPres[na] == self.targetPres[na]:
if self.topViewOn: self.wHide(self.xflatingToolTV[na])
self.wHide([*self.xflatingTool[na], self.wheel['Orange'][na]])
if 'Blue' in self.curWheelClr[na]:
if na != self.curAxle:
self.curWheelClr[na] = 'Green'
if self.curPres[na] == self.oriTargetPres[na]:
self.curPresClr[na] = appGreen
self.wShow([self.wheel[self.curWheelClr[na]][na]])
self.wFill(self.wTargetPresAx[na], self.curPresClr[na])
self.xflateOn[na] = False
else:
if self.curPres[na] > self.targetPres[na]:
self.curPres[na] = round(self.curPres[na] - 0.1, 1)
else:
self.curPres[na] = round(self.curPres[na] + 0.1, 1)
self.setCurPressure(na)
self.xflTimer[na] = self.setTimer(1000, self.xflate, na)
# -------------------------------------------------------------------- Boost
def boostDelay(self):
self.boostTime -= 1
if not self.boostTime:
self.boostTimerTimes += 1
if self.boostTimerTimes > 2: self.boostTimerTimes = 0
self.wFill([self.triplwarnw[self.boostTimerTimes]], appRed)
self.wFill(self.triplwarnw[1:], '')
self.boostTime = 180
self.toggleBoostMode()
elif self.boostOn: self.setTimer(1000, self.boostDelay)
self.refreshTimerDisplay()
# ------------------------------------------------------------------ top view
def toggleTopView(self, redraw=False):
if self.topViewFlag < 0 or redraw:
self.topViewFlag = self.nbAxles - 2
self.wShow([self.topview[self.topViewFlag], 'topview'])
if self.nbAxles == 2 :
self.wHide([self.topview[1], '3rdWhlTV', '3rdWhlTV+'])
if not self.topViewMoved: self.move('topview', 121, 0)
self.topViewMoved = True
else:
self.wShow([self.topview[0], '3rdWhlTV', '3rdWhlTV+'])
if self.topViewMoved: self.move('topview', -121, 0)
self.topViewMoved = False
self.topViewPrevAxle = self.nbAxles
self.topViewOn = True
else:
self.wHide([*self.topview, 'topview'])
self.topViewFlag = -1
self.topViewOn = False
self.manPress(True)
# ================================================================== ACTIONS
def wait4action(self):
loop = True
while not self.enterEmpty():
ret = self.getData()
cmd, val = ret.split('=')
if ('Tool' in cmd) and (val in self.indTool):
# ----- 'Tool=TRAILER'
self.toolInd = self.indTool[val]
self.curTool = val
self.setTool()
elif 'Lang' in cmd:
# ----- 'Lang=En'
self.translate(val[:2].lower())
elif ('Surf' in cmd) and (('field' in val) != self.field):
# ----- 'Surf=road'
self.toggleSurf()
elif ('Manual' in cmd) and (bool(int(val)) != self.manualOn):
# ----- 'Manual=1'
self.toggleManualMode()
elif ('Boost' in cmd) and (bool(int(val)) != self.boostOn):
# ----- 'Boost=1'
self.toggleBoostMode()
elif 'Zoom' in cmd:
if 'in' in val: self.zoomIn()
elif 'out' in val: self.zoomOut()
elif 'Exit' in cmd:
loop = False
break
self.fig.update()
if loop: self.setTimer(40, self.wait4action)
else: self.Quit()
def Hide(self, event=None):
self.fig.overrideredirect(False)
self.fig.iconify()
def Show(self, event=None):
self.fig.deiconify()
self.fig.overrideredirect(True)
self.fig.lift()
def zoomIn(self):
self.Show()
for h in range(2, 602, 5):
w = round(h * 1.6)
self.fig.geometry('{}x{}-0-0'.format(w, h))
self.update()
self.fig.geometry('960x600-0-0')
def zoomOut(self):
for h in range(600, 2, -5):
w = round(h * 1.6)
self.fig.geometry('{}x{}-0-0'.format(w, h))
self.update()
self.Hide()
def Quit(self, *param):
print(' << Zen@Terra: Out')
self.fig.destroy() # quit the program
#
# =============================================================================
____________________________________________________________________________________
2.6/ Finally, the main program
Show_Proc.py________________________________________________________________________
# -*- coding: utf-8 -*-
""" PROCESS Show_Proc
=================
"""
__author__ = 'Damien HALLEZ'
__version__ = '1.0'
__date__ = 'nov 2017'
from defErrPrint import errPrint
# =============================================================================
# MAIN
# =============================================================================
if __name__ == "__main__":
from configparser import ConfigParser
from defBanner import Banner
from defObjOverlay import objOverlay
from defVLCplayer import VLC_player
from defBenef import DispBenef
from defZenAtTerra import ZenAtTerra
from queue import Empty
from multiprocessing import Process, Queue
from defTCPIP import TCPLink
from os import path
from time import strftime, time, sleep
# ------------------------------------------------------------- read config
config = ConfigParser()
config.filename = 'Show_Proc.ini'
config.savfilename = config.filename.replace('.','_sav.')
Ok = False
for tst in range(2):
# ----- verify config ini ok
try:
config.read(config.filename, encoding='utf-16')
config.langList = [lg.strip().title() for lg in config['LANG']['list'].split(',')]
settings = config['SETTINGS']
vrbstr = settings['verbose']
if 'b' in vrbstr: verbose = int(vrbstr.split('b')[1], base=2)
elif 'x' in vrbstr: verbose = int(vrbstr.split('x')[1], base=16)
else: verbose = int(vrbstr)
countDownSound = bool(int(settings['countdown_sound']))
nextshtime = config['NEXT_SHOW']['nextshtime']
sLang = config['LANG']['lang']
cvtst = [config['CONVERT']['euro2' + lg[:2].lower()] \
for lg in config.langList]
# saves = [config['SAVED'][s.strip() for s in ['wa', 'py', 'sp']]]
netCfg = config['NET']
addrCfg = config['ADDRESS']
procName = netCfg['node'].strip().title()
baseAddr = netCfg['base'].strip()
IPtoNnode = {baseAddr+addrCfg[procName]:procName}
if netCfg['links']:
IPtoNlinks = {baseAddr + addrCfg[s.strip()].strip():\
s.strip().title() \
for s in netCfg['links'].split(',')}
else: IPtoNlinks = {}
portBase = int(netCfg['port_base'])
FSvdoChan = int(config['FS']['vdo_chan'])
Ok = True
break
except:
# ---- take the save ini
print('\t! Restore ini file')
with open(config.filename, 'w', encoding='utf-16') as ini:
with open(config.savfilename, 'r', encoding='utf-16') as _ini:
ini.write(_ini.read())
if not Ok:
errPrint(r'/!\ Error reading .ini file')
exit(44)
# ----- Proc Name from ini = Show_Proc
print('> {} [{}] >'.format(procName, strftime('%d/%m/%y %H:%M:%S')))
#
# verbose : 1 = main, 2 = ethernet
#
vrbm = bool(verbose & 1)
vrbe = bool(verbose & 2)
MovieDir = path.join(path.abspath('.'),'Movies')
ImgDir = path.join(path.abspath('.'),'Img')
# ----- videos to overlay and offsets
ovlsProp = {'vdo:CP_Defl.avi': [[.75, .25], 1], \
'vdo:CP_Infl.avi': [[.75, .25], 1], \
'vdo:tire_Defl.avi': [[.25, .25], 1], \
'vdo:tire_Infl.avi': [[.25, .25], 1], \
'vdo:rut_Defl.avi': [[.50, .75], 1], \
'vdo:rut_Infl.avi': [[.50, .75], 1]}
# ------------------------------------------------ launch background video
qOvlObj = Queue()
def launchOvlObj():
pOvlObj = Process(target=objOverlay, \
args=(qOvlObj.get, FSvdoChan, ovlsProp))
pOvlObj.start()
return pOvlObj
pOvlObj = launchOvlObj()
setOvlObj = qOvlObj.put_nowait
qIn = Queue()
# --------------------------------------------------- list of PC to connect
eth = TCPLink(IPtoNnode, IPtoNlinks, portBase, qIn.put_nowait)
print('\t{}: {} '.format(procName, eth.myAddr))
# -------------------------------------------------------- Launch VLCPlayer
qVLC = Queue()
def launchVLC():
pVLC = Process(target=VLC_player, args=(qVLC, qIn))
pVLC.start()
return pVLC
pVLC = launchVLC()
setVLC = qVLC.put_nowait
# ----------------------------------------------------------- Launch banner
qBanner = Queue()
def launchBanner():
pBanner = Process(target=Banner, \
args=(qBanner, qIn, config['WAITING_TEXT'], \
countDownSound))
pBanner.start()
return pBanner
pBanner = launchBanner()
setBanner = qBanner.put_nowait
# ------------------------------------------------------------ Launch benef
benef = DispBenef(sLang[:2])
benef.hide()
# -------------------------------------------------------- Launch Zen@Terra
qZaT = Queue()
def launchZaT():
pZaT = Process(target=ZenAtTerra, args=(qZaT, qIn))
pZaT.start()
return pZaT
pZaT = launchZaT()
setZaT = qZaT.put_nowait
exitVal = 0
MainOn = True
wait4data = True
exitFlag = False
getdata = qIn.get
putdata = qIn.put_nowait
T0 = time()
Tend = 5
ackn = False
# ----- Status
MOVIES = 1
SIMUON = 2
HADISP = 3
curState = MOVIES
putdata(['TCP:rcv0', 'ShowOn=0', 'local'])
VLCFileOn = False
while MainOn:
try:
ret = getdata(wait4data)
except Empty:
ret = []
except KeyboardInterrupt:
ret = ['TCP:rcv0', 'Exit=0', 'Ctrl+C']
if ret: typ, msgs, proc = ret
else: typ = ''
if 'TCP' in typ:
# ----------------------------------------------------- TCP
typ2 = typ.split(':')[-1]
if 'out' in typ2:
eth.relaunch(proc)
elif 'snt1' in typ2:
if vrbe: print('\t-> {} to {}'.format(msgs, proc))
elif 'snt0' in typ2:
print('\t/!\ Fail sending {} to {}'.format(msgs, proc))
elif typ2 in ['rcv1', 'rcv0']:
# ----- aknowledge
if vrbe: print('\t<- {} from {}'.format(msgs, proc))
if exitFlag:
print('\t {} stopped'.format(proc))
if typ2 == 'rcv1':
MainOn = not all(eth.stop(proc))
for cmdval in msgs.split('&'):
cmd, val = cmdval.split('=', maxsplit=1)
ackn = True
if 'remTime' in cmd:
if curState != MOVIES:
if curState == SIMUON:
# hide FS window
setOvlObj('Hide')
elif curState == HADISP:
benef.hide()
# ----- launch movies
setVLC('Dir>{};loop=1'.format(MovieDir))
curState = MOVIES
# launch chrono
setBanner(cmdval)
elif 'Benef' in cmd:
if 'Show' in val: benef.show()
elif 'Hide' in val: benef.hide()
elif 'ShowOn' in cmd:
# ShowOn = 0, 1
setBanner('BanExit=0')
benef.hide()
if int(val):
setVLC('Stop')
setOvlObj('Show')
curState = SIMUON
else:
setOvlObj('Hide')
setVLC('Dir>{};loop=1'.format(MovieDir))
curState = MOVIES
elif 'OvlOn' in cmd:
setVLC('Stop')
setOvlObj('Show')
elif 'Ovl' in cmd:
"""
Ovl=obj:fun(arg)
obj ='CP_Defl', 'CP_Infl', 'tire_Defl', \
'tire_Infl', 'rut', ...
fun = zoomIn, zoomOut, hide, show, pause, restart,
pauseAtFrame(n), pauseAtSec(n), rewind, play*
arg = n for pauseAtFrame & pauseAtSec
*from the beginning of the movie
"""
setOvlObj(val)
elif 'ZaT' in cmd:
setZaT(val.replace(':', '='))
elif 'VLCPlay' in cmd:
setOvlObj('Hide')
setVLC('File>{};loop=0'.format(path.join(ImgDir, val)))
VLCFileOn = True
elif 'Lang' in cmd:
# Lang = Fr, Ge, ...
# external field (internal in MOVIES)
sLang = val[:2]
config['LANG']['lang'] = sLang
setBanner(cmdval)
setZaT(cmdval)
benef.lang(sLang)
elif 'Exit' in cmd:
# Exit = 0, 1, 2 ..
exitVal = int(val)
exitFlag = True
setOvlObj('Exit')
setVLC('VLCExit')
setBanner('BanExit=1')
benef.exit()
setZaT('Exit=1')
wait4data = False
T0 = time()
elif 'rcv?' in typ2:
if vrbe: print('\t ????? {} received from {}'.format(msgs, proc))
ackn = False
elif 'SUB' in typ:
cmd, val = msgs.split('=')
if cmd in ['endTime', 'last10s', 'SurfSet']:
eth.sendTo(msgs, 'Master')
elif 'waitTime' in cmd:
# internal command (main)
curState = MOVIES
# ----- launch movies
setVLC('Dir>{};loop=1'.format(MovieDir))
# avoid sending Ok to internal cmd
elif 'endPlay' in cmd:
if VLCFileOn:
VLCFileOn = False
setOvlObj('Show')
elif typ:
print('\t/!\ unknown type : ', typ)
# or Texit > 5 s
if exitFlag:
# ===== Exit Loop if all procs exited
MainOn = (pOvlObj.is_alive() or pVLC.is_alive() or \
pBanner.is_alive() or pZaT.is_alive()) and \
not (time() - T0 > Tend)
qIn.cancel_join_thread()
# ------------------------------------------------ wait sub processes ended
pOvlObj.join()
pBanner.join()
pVLC.join()
pZaT.join()
# ----------------------------------------------------------- close TCPLink
eth.close()
# ------------------------------------------------------------- save config
config['LANG']['lang'] = sLang
with open(config.filename, 'wt', encoding='utf-16') as cfgfile:
config.write(cfgfile)
# -------------------------------------------------------------------- exit
print('< {} [{}] <\n\n'.format(procName, strftime('%d/%m/%y %H:%M:%S')))
exit(39 + exitVal)
#
# =============================================================================
____________________________________________________________________________________
3/ "Master"
This PC monitors all the simulator, even the raspberry pis' supplies and the sound between PC FS_Hat and PC_Show (closed when in presentating mode to reproduce the sound of Farming Simulator during the show, opened otherwise).
A first program, TCP2Ser.exe, is launched at the PC boot, to monitor the link with the Arduino Uno. So, the main "Master" program could be used independently by the speaker without any impact on the whole system (as the Arduino Uno manages the Raspberry's supplies, for maintenance purposes).
The goal of this script, is to "translate" commands coming from the Master (local host) to Arduino ones.
Tcp2Ser.py_____________________________________________________________________________
# -*- coding: utf-8 -*-
""" tcp2ser
=======
"""
__author__ = 'Damien HALLEZ'
__version__ = '1.0'
__date__ = 'Sept 2017'
from queue import Empty
from defErrPrint import errPrint
# =============================================================================
#
if __name__ == '__main__':
from multiprocessing import Process, Queue
from queue import Empty
from defTCPIP import TCPLink
from defSerialLink import serialLink
from configparser import ConfigParser
from time import strftime
from threading import Timer
# ----- read params from ini file
config = ConfigParser()
config.filename = 'tcp2ser.ini'
config.savfilename = config.filename.replace('.','_sav.')
Ok = False
for tst in range(2):
# ----- verify config ini ok
try:
config.read(config.filename, encoding='utf-16')
settings = config['SETTINGS']
vrbstr = settings['verbose']
if 'b' in vrbstr: verbose = int(vrbstr.split('b')[1], base=2)
elif 'x' in vrbstr: verbose = int(vrbstr.split('x')[1], base=16)
else: verbose = int(vrbstr)
serial = bool(int(settings['serial']))
UNO = config['UNO']
UNOcom = ['COM{}'.format(int(UNO['com_port'])), \
int(UNO['com_speed'])]
netCfg = config['NET']
addrCfg = config['ADDRESS']
procName = netCfg['node'].strip().title()
baseAddr = netCfg['base'].strip()
IPtoNnode = {'127.0.0.1':procName}
if netCfg['links']:
IPtoNlinks = {baseAddr + addrCfg[s.strip()].strip():\
s.strip().title() \
for s in netCfg['links'].split(',')}
else: IPtoNlinks = {}
portBase = int(netCfg['port_base'])
Ok = True
break
except:
# ---- take the save ini
print(r'\t/!\ Restore ini file')
with open(config.filename, 'w', encoding='utf-16') as ini:
with open(config.savfilename, 'r', encoding='utf-16') as _ini:
ini.write(_ini.read())
if not Ok:
errPrint(r'/!\ Error reading .ini file')
exit(44)
#
# verbose : 1 = main, 2 = ethernet, 4 = serial
#
vrbm = bool(verbose & 1)
vrbe = bool(verbose & 2)
vrbs = bool(verbose & 4)
# ----- Proc Name from ini = Rpi_Outputs
print('> {} [{}] >'.format(procName, strftime('%d/%m/%y %H:%M:%S')))
qIn = Queue()
putData = qIn.put_nowait
getData = qIn.get
# --------------------------------------------------- list of PC to connect
eth = TCPLink(IPtoNnode, IPtoNlinks, portBase, putData)
print('\t{}: {}\n'.format(procName, eth.myAddr))
# ---------------------------------------------------------- process serial
qSerial = Queue()
def launchSerial():
pSerial = Process(target=serialLink, \
args=(UNOcom, '\r\n', \
qSerial.get, putData, vrbs))
pSerial.start()
return pSerial
if serial:
sendSerial = qSerial.put_nowait
pSerial = launchSerial()
else:
sendSerial = print
exitFlag = False
ackn = False
Timer(2, putData, args=(['TCP:rcv0', 'ResetUno=1', 'init'], )).start()
while True:
try:
ret = getData(True)
except Empty:
ret = []
except KeyboardInterrupt:
# ----- simulate an exit order
ret = ['TCP:rcv0', 'Exit=0', 'Ctrl+C']
if ret: typ, msgs, proc = ret
else: typ = ''
if 'TCP' in typ:
# ----------------------------------------------------- TCP
typ2 = typ.split(':')[-1]
if 'out' in typ2:
# ---- have to relaunch ethernet link
eth.relaunch(proc)
elif 'snt1' in typ2:
# ---- info of what was sent
if vrbe: print('\t-> {} to {}'.format(msgs, proc))
elif 'snt0' in typ2:
# ---- info of fail sending
print('\t/!\ Fail sending {} to {}'.format(msgs, proc))
elif typ2 in ['rcv1', 'rcv0']:
# ----- aknowledge
# ----- received info
if vrbe: print('\t<- {} from {}'.format(msgs, proc))
if exitFlag:
print('\t {} stopped'.format(proc))
if typ2 == 'rcv1':
if all(eth.stop(proc)): break
# ----- send aknowledgment if really comming from TCP
fromTCP = bool(proc != 'Ctrl+C')
ackn = fromTCP
for cmdval in msgs.split('&'):
cmd, val = cmdval.split('=')
if 'Sound' in cmd:
# ----- snd{0|1}
sendSerial('snd' + val)
elif 'Rpi#' in cmd:
# rpi{0|1|2|a}{0|1}
sendSerial('rpi' + cmd[4] + val)
elif 'Supply15' in cmd:
# sup{0|1}
sendSerial('sup' + val)
elif 'ResetDue' in cmd:
# rstd
sendSerial('rstd')
elif 'ResetUno' in cmd:
# rstd
sendSerial('rstu')
elif 'TirePos' in cmd:
# tirePos = 0, 1, 2, 3, 4
# ord{n} n the relay to close
sendSerial('ord'+ val)
elif 'Shutdown' in cmd:
# shtd
sendSerial('shtd')
elif 'Test' in cmd:
pass
elif 'Exit' in cmd:
# stop requested by TCP --- JUST FOR MAINTENANCE
sendSerial('shtd')
exitFlag = True
exitVal = int(val)
break
elif 'rcv?' in typ2:
print('\t ????? {} received from {}'.format(msgs, proc))
ackn = False
# -------------------------------------------------------- Serial entry
elif 'SER' in typ:
if msgs[0] == '0': pSerial = launchSerial()
# ------------------------------------------------------------ unknown
elif typ: print('\t/!\ unknown type : ', typ)
# ----- free the queue
qIn.cancel_join_thread()
# ----------------------------------------------------------- close TCPLink
eth.close()
# -------------------------------------------------- wait serial link close
if serial:
if pSerial.is_alive(): sendSerial('SerExit')
pSerial.join()
# -------------------------------------------------------------------- exit
print('< {} [{}] <\n\n'.format(procName, strftime('%d/%m/%y %H:%M:%S')))
exit(0)
#
# =============================================================================
____________________________________________________________________________________
And it's .ini file:
Tcp2Ser.ini_________________________________________________________________________
[SETTINGS]
verbose = b011
serial = 0
[UNO]
com_port = 19
com_speed = 115200
[NET]
base = 192.168.0.
node = tcp2ser
links =
port_base = 17700
[ADDRESS]
fs_hat = 60
rpi_pres = 61
rpi_out = 62
rpi_disp = 63
master= 64
show = 65
tcp2ser = 0
____________________________________________________________________________________
The main "Master" program, now, uses a numpad function to enter the time between presentations,
defNumpad.py_________________________________________________________________________
"""
NUMPAD ===============================================================
"""
class npProcess():
def __init__(self,wVal,wOk):
self.current = str(wVal.get())
self.dispVal = wVal
self.Ok = wOk
self.first = True
def key_press(self, num):
if self.first:
self.first = False
self.current = '0'
currt = self.current
toAd = str(num)
if toAd == '.':
if toAd in currt: return
if currt == '0':
self.current = toAd
else:
self.current = currt + toAd
self.display()
def man_key(self, event):
chr = event.char
if self.first:
self.first = False
if chr != '\x08': self.current = '0'
if chr in '.0123456789':
self.key_press(chr)
elif chr == '\r':
self.OkReturn()
elif chr == '\x08':
self.cancel()
def display(self):
self.dispVal.set(self.current)
def cancel(self):
if self.first: self.first = False
currt = self.current
if len(currt)>1:
self.current = currt[0:-1]
else:
self.current = '0'
self.display()
def OkReturn(self):
self.Ok.set('')
# -------------------------------------------------------------------------------------------------------
def numPad(inival=0, OutType='float'):
import tkinter as tk
# --- prepare the figure
fig = tk.Toplevel()
fig.title('NumPad {}'.format(OutType))
fig.rowconfigure(0, weight=1)
fig.columnconfigure(0, weight=1)
fig.attributes('-topmost', 1)
fig.focus()
npFr = tk.Frame(fig)
npFr.grid()
npFr.columnconfigure(0, weight=1, uniform=1)
npFr.columnconfigure(1, weight=1, uniform=1)
npFr.columnconfigure(2, weight=1, uniform=1)
# --- use font, color and button width
Fmt = dict(font = ("Arial", 23), fg='darkblue')
butWidth = 3
# --- value to display
val = tk.StringVar()
val.set(str(inival))
# --- value Ok
vOk = tk.StringVar()
vOk.set('Ok')
# --- init core process
core = npProcess(val,vOk)
# --- display box
tk.Label(npFr, justify=tk.RIGHT, bg='white', textvariable=val, \
relief=tk.RIDGE, bd = 3, **Fmt)\
.grid(row=0, column=0, columnspan=2, padx=5, pady=5, \
sticky=tk.E+tk.W+tk.S+tk.N)
# --- key '<-' ################
tk.Button(npFr, text='<-', command=core.cancel, width=butWidth,**Fmt)\
.grid(row=0, column=2, pady=5)
# --- make the buttons
keybrd = '789456123'
i = 0
for r in range(1,4):
for c in range(3):
tk.Button(npFr, text= keybrd[i], width=butWidth,\
command=lambda x=keybrd[i]: core.key_press(x), **Fmt)\
.grid(row=r, column=c, padx=5, pady=5)
i += 1
# --- key '.'
p = tk.Button(npFr, text = '.', width=butWidth,\
command = lambda : core.key_press('.'), **Fmt)
p.grid(row = 4, column = 0, padx = 5, pady = 5)
# --- key '.': if int, key not used
if OutType.upper()=='INT': p.configure(command='')
# --- key '0'
tk.Button(npFr, text = '0', width=butWidth,\
command = lambda : core.key_press('0'), **Fmt)\
.grid(row = 4, column = 1, pady = 5)
# --- remaining 'Ok' button
tk.Button(npFr, textvariable = vOk, width=butWidth, \
command = core.OkReturn, **Fmt)\
.grid(row = 4, column = 2, pady = 5)
# --- bind keyboard to numpad
fig.bind('<Key>', core.man_key)
fig.attributes('-topmost', True)
fig.focus_set()
fig.grab_set()
fig.protocol('WM_DELETE_WINDOW',lambda:vOk.set(' '))
# --- Waiting Ok accessed
npFr.wait_variable(vOk)
ress = val.get().strip()
if ress:
if OutType.upper() == 'INT':
res = int(ress)
else:
res = float(ress)
else:
res = inival
fig.destroy()
return res
"""
END NUMPAD ===========================================================
"""
____________________________________________________________________________________
it also needs a menu dialog (to choose the presenting language):
defDlg.py____________________________________________________________________________
import tkinter as tk
# -----------------------------------------------------------------------------
# Class selection in a list
# -----------------------------------------------------------------------------
class menuDlg(tk.Toplevel):
def __init__(self, title, choose_in_list, \
butFmt=dict(font = ("Arial", 12), fg='darkblue')):
tk.Toplevel.__init__(self)
gridFmt = dict(padx=5, pady=2, sticky=tk.E+tk.W)
self.title(title)
self.lift()
self.protocol('WM_DELETE_WINDOW', self.Quit)
self.attributes('-topmost', True)
self.scrSize = [self.winfo_screenwidth(), \
self.winfo_screenheight()]
self.cv = tk.Canvas(self)
self.frame = tk.Frame(self.cv)
self.vsb = tk.Scrollbar(self, orient="vertical", \
command=self.cv.yview)
self.cv.configure(yscrollcommand=self.vsb.set)
self.vsb.grid(row=0, column=1, sticky=tk.N+tk.S)
self.cv.grid(row=0, column=0, sticky='news')
self.cv.create_window((5,5), window=self.frame, anchor='nw')
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
nw = max([len(txt) for txt in choose_in_list]) + 1
self.out = -1
# ----- Buts : files
for i, fname in enumerate(choose_in_list):
tk.Button(self.frame, text = fname, width=nw, \
command = lambda x=i: self.Quit(x), **butFmt)\
.grid(row=i, **gridFmt)
self.cv.update_idletasks()
size = (self.frame.winfo_reqwidth(), \
min(self.frame.winfo_reqheight()+5,int(self.scrSize[1]*.8)))
self.cv.config(scrollregion="0 0 %s %s" % size, height=size[1])
self.cv.itemconfigure(self.frame, height=size[1])
self.frame.bind("<Configure>", self._configure_interior)
self.cv.bind("<Configure>", self._configure_canvas)
self.wait_window(self)
def _configure_interior(self, event):
# update the scrollbars to match the size of the inner frame
size = (self.frame.winfo_reqwidth(), self.frame.winfo_reqheight())
self.cv.config(scrollregion="0 0 %s %s" % size)
if self.frame.winfo_reqwidth() != self.cv.winfo_width():
# update the canvas's width to fit the inner frame
self.cv.config(width=self.frame.winfo_reqwidth())
def _configure_canvas(self, event):
if self.frame.winfo_reqwidth() != self.cv.winfo_width():
# update the inner frame's width to fill the canvas
self.cv.itemconfigure(self.frame, width=self.cv.winfo_width())
def Quit(self, out=-1):
# ----- Quit (index = -1)
self.out = out
self.destroy()
# ----------------------------------------------------------------------------
# inpudDlg
# ----------------------------------------------------------------------------
class inputDlg:
def __init__(self, parent, title, txt, inival, dicts={}):
self.top = tk.Toplevel(parent)
self.top.title(title)
# ----- Label
tk.Label(self.top, text=txt, **dicts).pack()
# ----- Entrée
self.e = tk.Entry(self.top, **dicts)
self.e.pack(padx=5)
self.e.insert(0, str(inival))
self.out = inival
# ----- Bouton
b = tk.Button(self.top, text='OK', command=self.ok, **dicts)
b.pack(pady=5)
def ok(self):
self.out = self.e.get()
self.top.destroy()
____________________________________________________________________________________
Finally, the main program:
Master.py_______________________________________________________________________________________
# -*- coding: utf-8 -*-
""" Master
======
"""
__author__ = 'Damien HALLEZ'
__version__ = '1.1'
__date__ = 'nov 2017'
import tkinter as tk
from configparser import ConfigParser
from multiprocessing import Queue
from subprocess import Popen
from queue import Empty
from defNumpad import numPad
from defDlg import menuDlg
from defErrPrint import errPrint
from textwrap import shorten
import tkinter.messagebox as messagebox
QuestBox = messagebox.askyesno
MsgBox = messagebox.showinfo
from time import time, sleep, strftime
from os import path
from shutil import copyfile
MiBlue = '#01019A'
MiWht1 = '#FFFFFF'
MiWht2 = '#E5E5E5'
# 0:wheat, 1;corn, 2:rape x 3
# 0: Soil prep, 1:sowing, 2:harvest
# wheat / soil prep
# wheat / sowing
# wheat / harvest
# corn / soil prep
# corn / sowing
# corn / harvest
# rape / soil prep
# rape / sowing
# rape / harvest
tools = [['CULTIVATOR', 'DIRECTDRILL', 'TRAILER'], \
['PLOUGH', 'DRILL', 'TRAILER'], \
['CULTIVATOR', 'DRILL', 'TRAILER']]
ShowSteps = ['Show Zen@Terra on road', \
'Start Tractor', \
'Go directly to field' , \
'Show Zen@Terra on field', \
'Launch comparison', \
'-']
# -----------------------------------------------------------------------------
# configuration ligne/colonne
# -----------------------------------------------------------------------------
#
def colrowconfig(frames):
for fri in frames:
c, r = fri.grid_size()
for ic in range(c):
fri.columnconfigure(ic, weight=1)
for ir in range(r):
fri.rowconfigure(ir, weight=1)
colrowconfig(fri.grid_slaves())
#
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# Language Selection
# -----------------------------------------------------------------------------
#
class LangAPP(tk.Toplevel):
def __init__(self, icofile, LangList, func, dx):
tk.Toplevel.__init__(self)
self.resizable(width = False, height = False)
self.title('Lang.')
self.iconbitmap(icofile)
self.protocol('WM_DELETE_WINDOW', self.Quit)
butFmt = dict(font = ('Arial', 16), fg=MiBlue)
butFmtC = dict(font = ('Arial', 14, 'italic'), fg=MiBlue)
gridFmt = dict(padx=10, pady=10, sticky=tk.E+tk.W)
self.transient()
self.geometry('+{}+0'.format(dx))
# ----- Buts : langages
for i, lang in enumerate(LangList):
tk.Button(self, text = lang, width = 10, height = 2,\
command = lambda lg = lang : func(lg,self), **butFmt)\
.grid(row=i, **gridFmt)
tk.Button(self, text = '◄ back', width = 10, height = 2,\
command = self.Quit, **butFmtC)\
.grid(row=i+1, **gridFmt)
self.func = func
def Quit(self):
self.func('', self)
#
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# Time manager
# -----------------------------------------------------------------------------
#
class TimeMan(tk.Toplevel):
def __init__(self, icofile, func1, func2, nt1, nt2, dx, dy, fFmtn, gwdFmt):
tk.Toplevel.__init__(self)
self.resizable(width = False, height = False)
self.title('Time')
self.iconbitmap(icofile)
self.protocol('WM_DELETE_WINDOW', self.Quit)
self.fFmt1 = dict(font = ("Arial", 50, 'bold'), fg=MiBlue)
fFmtC = dict(font = ("Arial", 16, 'italic'), fg=MiBlue)
self.fFmt1i = dict(font = ("Arial", 50, 'bold', 'italic'), fg='darkgrey')
fFmt2 = fFmtn
fFmt3 = dict(font = ("Arial", 12), fg=MiBlue)
gridFmt1 = gwdFmt
gridFmt2 = dict(padx=10, pady=5, sticky='news')
self.transient()
self.geometry('+{}+{}'.format(dx, round(dy/3)))
# ----- digits
self.time = tk.IntVar()
self.time.set(nt1)
self.digit= tk.Button(self, textvariable=self.time, width=4, bg='white', \
command = self.getNum, **self.fFmt1)
self.digit.grid(row=0, column=0, rowspan=3, **gridFmt1)
# ---- add
tk.Button(self, text='▲', command=self.add, **fFmt2)\
.grid(row=0, column=1, **gridFmt2)
self.ttype = tk.StringVar()
self.ttype.set('mn')
tk.Label(self, textvariable=self.ttype, bg=MiWht2, **fFmt3)\
.grid(row=1, column=1, **gridFmt2)
# ---- soustract
tk.Button(self, text='▼', command=self.sous, **fFmt2)\
.grid(row=2, column=1, **gridFmt2)
# ----- set
tk.Button(self, text='Set', width=5, command=self.dsetTime, **fFmt2)\
.grid(row=3, column=0, **gridFmt1)
# ----- display set
self.vsetTime = tk.StringVar()
self.vsetTime.set(str(nt2)+' mn')
tk.Label(self, textvariable=self.vsetTime, width=7, bg=MiWht2, **fFmt2)\
.grid(row=3, column=1, **gridFmt1)
# ----- Refresh
tk.Button(self, text='Refresh', command=self.refresh, **fFmt2)\
.grid(row=4, column=0, columnspan=2, **gridFmt1)
# ----- Cancel
tk.Button(self, text='◄ back', command=self.Quit, **fFmtC)\
.grid(row=5, column=0, columnspan=2, **gridFmt1)
colrowconfig(self.winfo_children())
self.setTime = func1
self.setWait = func2
def getNum(self):
n = numPad(self.time.get(), 'int')
if n == 0:
n = 59
self.ttype.set('sec')
self.digit.config(**self.fFmt1i)
self.time.set(n)
def add(self):
n = self.time.get() + 1
if n > 59:
n = 1
self.ttype.set('mn')
self.digit.config(**self.fFmt1)
self.time.set(n)
def sous(self):
n = self.time.get() - 1
if n < 1 :
n = 59
self.ttype.set('sec')
self.digit.config(**self.fFmt1i)
self.time.set(n)
def dsetTime(self):
n = self.time.get()
self.vsetTime.set(str(n) + ' mn')
self.setTime(n)
def refresh(self):
self.setWait(self.time.get(), self.ttype.get(), self)
def Quit(self):
self.setWait(None, 'mn', self)
#
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# Farming Simulator Show manager
# -----------------------------------------------------------------------------
#
class FSimuShowMan(tk.Toplevel):
def __init__(self, icofile, putData, dx, dy, LangList, \
vLang, fFmtn, gwdFmt, retfun):
tk.Toplevel.__init__(self)
self.resizable(width = False, height = False)
self.title('FS Show')
self.iconbitmap(icofile)
self.protocol('WM_DELETE_WINDOW', self.Quit)
self.transient()
self.geometry('+{}+{}'.format(dx, round(dy/3)))
self.putData = putData
self.icofile = icofile
self.LangList = LangList
self.vLang = vLang
self.fFmtn = fFmtn
self.gwdFmt = gwdFmt
self.retfun = retfun
self.pannelOn = False
self.curCrop = None
self.gameOn = False
self.T0l = time()
self.T0n = self.T0l
self.setTimer = self.after
iRow = 0
# ------- Crop
fr = tk.LabelFrame(self, text='Crop', fg=MiBlue)
fr.grid(row=iRow, column=0, **gwdFmt)
self.crop = tk.IntVar()
self.crop.set(1)
self.crops = ['Wheat', 'Corn', 'Rape']
for i, t in enumerate(self.crops):
tk.Radiobutton(fr, text=t, variable=self.crop, \
value=i, **fFmtn).grid(row=0, column=i,**gwdFmt)
self.selRadio = [fr]
iRow += 1
# ------- Work
fr = tk.LabelFrame(self, text='Work', fg=MiBlue)
fr.grid(row=iRow, column=0, **gwdFmt)
self.prep = tk.IntVar()
self.prep.set(0)
self.works = ['Soil Prep.', 'Sowing']
for i, t in enumerate(self.works):
tk.Radiobutton(fr, text=t, variable=self.prep, \
value=i, **fFmtn).grid(row=0, column=i,**gwdFmt)
self.selRadio.append(fr)
iRow += 1
# ------- Load
self.butLoad = tk.Button(self, text='Load', command=self.load, **fFmtn)
self.butLoad.grid(row=iRow, column=0, **gwdFmt)
# ------- reload
self.fFmtr = fFmtn.copy()
self.fFmtr.update(fg='darkred', \
font=tuple([int(3*v/4) if isinstance(v, int) else v \
for v in self.fFmtr['font']]+['italic']))
iRow += 1
# ------- curGame
self.curGame = tk.StringVar()
self.curGame.set('Current game =')
tk.Label(self, textvariable=self.curGame, anchor=tk.W, fg=MiBlue)\
.grid(row=iRow, column=0, **gwdFmt)
iRow += 1
# ------- next step
self.stepTxt = tk.StringVar()
self.stepTxt.set(ShowSteps[0] + '(1/5)')
self.nextBut = tk.Button(self, textvariable=self.stepTxt, \
state = tk.DISABLED, command=self.nextStep, **fFmtn)
self.nextBut.grid(row=iRow, column=0, **gwdFmt)
self.step=0
iRow += 1
# ------- Anim
self.wAnim = tk.Button(self, text='Animations \t►', state=tk.DISABLED, \
command=self.dlgAnim, **fFmtn)
self.wAnim.grid(row=iRow, column=0, columnspan=2, **gwdFmt)
iRow += 1
# ------- Benef
self.wBenef = tk.Button(self, text='Benefits', state=tk.DISABLED, \
command=self.dlgBenef, **fFmtn)
self.wBenef.grid(row=iRow, column=0, columnspan=2, **gwdFmt)
iRow += 1
# ------- Back
tk.Button(self, text='◄ End', command=self.Quit, **fFmtn)\
.grid(row=iRow, column=0, **gwdFmt)
colrowconfig(self.winfo_children())
self.update()
self.dx, self.dy = [int(x.split('+')[0]) \
for x in self.geometry().split('x')]
self.dx += dx
self.curNGame = 0
self.putData(['SUB', 'TirePos=2', 'Tcp2Ser'])
def load(self):
tCur = time()
if tCur - self.T0l > 1: # avoid button "bouncing"
self.T0l = tCur
iPrep = self.prep.get()
iCrop = self.crop.get()
idx = iPrep + iCrop * 3
ngame = 12 + idx
self.curNGame = ngame
if self.gameOn: self.end()
self.putData(['SUB', 'Load={}&SKB=iOk0'.format(ngame), 'Fs_Hat'])
self.curGame.set('Current game = {} / {} (#{})'\
.format(self.crops[iCrop], self.works[iPrep], \
ngame))
curTool = tools[iCrop][iPrep]
self.putData(['SUB', 'Tool=' + curTool + '&Surf=road', 'Rpi_Pres'])
self.putData(['SUB', 'ZaT=Tool:' + curTool + \
'&ZaT=Surf:road', 'Show'])
self.gameOn = True
self.nextBut.config(state=tk.NORMAL)
self.curCrop = iCrop
self.curPrep = iPrep
self.butLoad.config(state=tk.DISABLED)
for ws in self.selRadio:
for w in ws.winfo_children(): w.config(state=tk.DISABLED)
# vvvv - to delete when synchronized wint Fs_Hat - vvvvv
self.setTimer(5000+ngame*10, self.enterGame)
def enterGame(self):
self.putData(['SUB', 'Sound=0', 'Tcp2Ser'])
self.putData(['SUB', 'VLCPlay=begin_{}_{}.mp4'\
.format(self.curCrop+1, self.curPrep+1), 'Show'])
def end(self):
self.putData(['SUB', 'Tool=TRACTOR&Surf=road', 'Rpi_Pres'])
self.putData(['SUB', 'Quit=1', 'Fs_Hat'])
self.curGame.set('Current game = ')
self.gameOn = False
def nextStep(self, ncrop=0):
"""
ShowSteps = ['Show Zen@Terra on road', \
'Start Tractor', \
'Go directly to field' , \
'Show Zen@Terra on field', \
'Launch comparison', \
'-']
"""
tCur = time()
if tCur - self.T0n > 1: # avoid button "bouncing"
self.T0n = tCur
self.step += 1
lST = len(ShowSteps)
if self.step < lST:
self.stepTxt.set(ShowSteps[self.step]+' ({}/{})'\
.format(self.step+1, lST-1))
step = self.step
iCrop = self.curCrop
iPrep = self.curPrep
if step == 1:
self.putData(['SUB', 'ZaT=Zoom:in', 'Show'])
elif step == 2:
self.putData(['SUB', 'ZaT=Zoom:out' + \
'&VLCPlay=start_{}_{}.mp4'\
.format(iCrop+1, iPrep+1), 'Show'])
elif step == 3:
self.putData(['SUB', 'VLCPlay=stop_{}_{}.mp4'\
.format(iCrop+1, iPrep+1), 'Show'])
elif step == 4:
self.putData(['SUB', 'ZaT=Zoom:in', 'Show'])
elif step == 5:
self.putData(['SUB', 'ZaT=Zoom:out&OvlOn=1', 'Show'])
self.putData(['SUB', 'Sound=1', 'Tcp2Ser'])
self.putData(['SUB', 'Cmp=1', 'Fs_Hat'])
self.stepTxt.set('(Send again comparison if needed)')
self.wAnim.config(state=tk.NORMAL)
self.wBenef.config(state=tk.NORMAL)
self.nextBut.config(**self.fFmtr)
else:
if self.curNGame and QuestBox(title = 'Reload?', \
message = 'Are you sure to reload:\n{}?'\
.format(self.curGame.get())):
self.putData(['SUB', 'Reload={}'.format(self.curNGame), \
'Fs_Hat'])
self.nextBut.config(state=tk.DISABLED)
# ===== ANIM
def dlgAnim(self):
if self.wAnim['relief'] == tk.RAISED:
if not self.pannelOn:
print('\t\t\t» Anims : {}'.format(strftime('%d/%m/%y %H:%M:%S')))
self.wAnim.config(relief=tk.SUNKEN, fg='black')
self.pannelOn = True
AnimMan(self.icofile, self.putData, self.dx, int(self.dy*.3), \
self.fFmtn, self.gwdFmt, self.endAnim)
def endAnim(self, adef):
self.wAnim.config(relief=tk.RAISED, fg=MiBlue)
adef.destroy()
self.pannelOn = False
print('\t\t\t« Anims : {}'.format(strftime('%d/%m/%y %H:%M:%S')))
# ===== BENEF
def dlgBenef(self):
if self.wBenef['relief'] == tk.RAISED:
if not self.pannelOn:
print('\t\t\t» Benef : {}'.format(strftime('%d/%m/%y %H:%M:%S')))
self.wBenef.config(relief=tk.SUNKEN, fg='black')
self.pannelOn = True
self.putData(['SUB', 'Benef=Show', 'Show'])
self.wBenef.config(command=self.endBenef)
def endBenef(self):
self.wBenef.config(relief=tk.RAISED, fg=MiBlue)
self.pannelOn = False
self.putData(['SUB', 'Benef=Hide', 'Show'])
self.wBenef.config(command=self.dlgBenef)
print('\t\t\t« Benef : {}'.format(strftime('%d/%m/%y %H:%M:%S')))
def Quit(self):
if not self.pannelOn:
if self.gameOn: self.end()
self.retfun(self)
self.putData(['SUB', 'TirePos=2', 'Tcp2Ser'])
#
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# Farming Simulator Play manager
# -----------------------------------------------------------------------------
#
class FSimuPlayMan(tk.Toplevel):
def __init__(self, icofile, putData, dx, dy, fFmtn, gwdFmt, retfun):
tk.Toplevel.__init__(self)
self.resizable(width = False, height = False)
self.title('FS Play')
self.iconbitmap(icofile)
self.protocol('WM_DELETE_WINDOW', self.Quit)
self.transient()
self.geometry('+{}+{}'.format(dx, round(dy/3)))
self.putData = putData
self.retfun = retfun
self.gameOn = False
butFmtC = dict(font = ('Arial', 14, 'italic'), fg=MiBlue)
iRow = 0
# ------- Crop
fr = tk.LabelFrame(self, text='Crop', fg=MiBlue)
fr.grid(row=iRow, column=0, **gwdFmt)
self.crop = tk.IntVar()
self.crop.set(0)
self.crops = ['Wheat', 'Corn', 'Rape']
for i, t in enumerate(self.crops):
tk.Radiobutton(fr, text=t, variable=self.crop, \
value=i+1, **fFmtn).grid(row=0, column=i, **gwdFmt)
iRow += 1
# ------- Work
fr = tk.LabelFrame(self, text='Work', fg=MiBlue)
fr.grid(row=iRow, column=0, **gwdFmt)
self.prep = tk.IntVar()
self.prep.set(0)
self.works = ['Soil Preparation', 'Sowing', 'Harvest']
for i, t in enumerate(self.works):
tk.Radiobutton(fr, text=t, variable=self.prep, \
value=i+1, **fFmtn).grid(row=0, column=i, **gwdFmt)
iRow += 1
# ------- Load
tk.Button(self, text='Load', command=self.load, **fFmtn)\
.grid(row=iRow, column=0, **gwdFmt)
iRow += 1
# ------- curGame
self.curGame = tk.StringVar()
self.curGame.set('Current game =')
tk.Label(self, textvariable=self.curGame, anchor=tk.W, fg=MiBlue)\
.grid(row=iRow, column=0, **gwdFmt)
iRow += 1
# ------- extra buttons
fr = tk.LabelFrame(self, text='Extra commands', fg=MiBlue)
fr.grid(row=iRow, column=0, **gwdFmt)
self.buts = [tk.Button(fr, text='in/out/change vehicle [Tab]', command=self.vehChg, \
state=tk.DISABLED, **fFmtn)]
self.buts[-1].grid(row=0, column=0, **gwdFmt)
self.buts.append(tk.Button(fr, text='couple/uncouple tool [A]', \
command=self.couple, \
state=tk.DISABLED, **fFmtn))
self.buts[-1].grid(row=0, column=1, **gwdFmt)
self.buts.append(tk.Button(fr, text='in/out cabin [E]', \
command=self.inOutCabin, \
state=tk.DISABLED, **fFmtn))
self.buts[-1].grid(row=1, column=0, **gwdFmt)
self.buts.append(tk.Button(fr, text='camera in/out [C]', \
command=self.camera, \
state=tk.DISABLED, **fFmtn))
self.buts[-1].grid(row=1, column=1, **gwdFmt)
self.buts.append(tk.Button(fr, text='worker [H]', \
command=self.worker, \
state=tk.DISABLED, **fFmtn))
self.buts[-1].grid(row=2, column=0, **gwdFmt)
self.buts.append(tk.Button(fr, text='[Enter] key', command=self.enterKey,
state=tk.DISABLED, **fFmtn))
self.buts[-1].grid(row=2, column=1, **gwdFmt)
iRow += 1
# ------- Quit game
self.buts.append(tk.Button(self, text='Quit the current game', \
state=tk.DISABLED, command=self.end, **fFmtn))
self.buts[-1].grid(row=iRow, column=0, **gwdFmt)
iRow += 1
# ------- Back
tk.Button(self, text='◄ back', command=self.Quit, **butFmtC)\
.grid(row=iRow, column=0, **gwdFmt)
colrowconfig(self.winfo_children())
def _resetSel(self):
self.crop.set(0)
self.prep.set(0)
def load(self):
iPrep = self.prep.get() - 1
iCrop = self.crop.get() - 1
if iPrep >= 0 and iCrop >= 0:
idx = iPrep + iCrop * 3
ngame = 3 + idx
if self.gameOn: self.end()
self.putData(['SUB', 'Load_{}={}&SKB=iOk1'\
.format(['N', 'H'][iPrep==2], ngame), 'Fs_Hat'])
self.curGame.set('Current game = {} / {} (#{})'\
.format(self.crops[iCrop], self.works[iPrep], \
ngame))
self.putData(['SUB', 'Tool=' + tools[iCrop][iPrep] + \
'&Surf='+['field', 'road'][iPrep==2], \
'Rpi_Pres'])
for b in self.buts: b.config(state=tk.NORMAL)
self._resetSel()
self.gameOn = True
def vehChg(self):
self.putData(['SUB', 'SKB=kTAB', 'Fs_Hat'])
def couple(self):
self.putData(['SUB', 'SKB=keyA', 'Fs_Hat'])
def inOutCabin(self):
self.putData(['SUB', 'SKB=keyE', 'Fs_Hat'])
def camera(self):
self.putData(['SUB', 'SKB=keyC', 'Fs_Hat'])
def worker(self):
self.putData(['SUB', 'SKB=keyH', 'Fs_Hat'])
def enterKey(self):
self.putData(['SUB', 'SKB=kRET', 'Fs_Hat'])
def end(self):
self.putData(['SUB', 'Boost=0&Manual=0&Tool=TRACTOR&Surf=road', \
'Rpi_Pres'])
self.putData(['SUB', 'Quit=1', 'Fs_Hat'])
self.curGame.set('Current game = ')
self.gameOn = False
for b in self.buts: b.config(state=tk.DISABLED)
def Quit(self):
if self.gameOn: self.end()
self.retfun(self)
#
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# Animation manager
# -----------------------------------------------------------------------------
class AnimMan(tk.Toplevel):
def __init__(self, icofile, putData, dx, dy, fFmtn, gwdFmt, retfun):
tk.Toplevel.__init__(self)
self.resizable(width = False, height = False)
self.title('Animation')
self.iconbitmap(icofile)
self.protocol('WM_DELETE_WINDOW', self.Quit)
self.transient()
self.geometry('+{}+{}'.format(dx, round(dy/3)))
self.putData = putData
self.retfun = retfun
# ------- INFL /DEFL
xflNames = ['Inflating', 'Deflating']
fr = tk.LabelFrame(self, text=' / '.join(xflNames), fg=MiBlue)
fr.grid(row=0, column=0, **gwdFmt)
self.wxfl = []
for i, label in enumerate(xflNames):
self.wxfl.append(tk.Button(fr, text=xflNames[i], \
command=lambda n=i+1:self.xflSel(n), **fFmtn))
self.wxfl[-1].grid(row=0, column=i, **gwdFmt)
self.nXfl = ['', 'Infl', 'Defl' ]
self.iXfl = 0
# ------- VIEW
fr = tk.LabelFrame(self, text='Animations', fg=MiBlue)
fr.grid(row=1, column=0, **gwdFmt)
self.views = tk.IntVar()
self.views.set(0)
viewNames = ['Front View', 'Contact Patch', 'Rut Difference']
self.rb = []
for i, label in enumerate(viewNames):
self.rb.append(tk.Button(fr, text=viewNames[i], \
command=lambda n=i+1: self.viewSel(n), \
**fFmtn))
self.rb[-1].grid(row=0, column=i, **gwdFmt)
self.nView = [ '', 'tire', 'CP', 'rut']
self.viewOn = [False]*4
# ----- END
tk.Button(self, text='◄ END', command=self.Quit, **fFmtn)\
.grid(row=3, column=0, **gwdFmt)
colrowconfig(self.winfo_children())
self.T0n = time() - 1.1
self.wxfl[-1].invoke()
def unselectView(self):
tosend = ''
for i in range(3):
if self.viewOn[i]:
tosend += 'Ovl={}_{}:zoomOut&'\
.format(self.nView[i+1], self.nXfl[self.iXfl])
self.rb[i].config(relief=tk.RAISED)
self.viewOn[i] = False
if tosend: self.putData(['SUB', tosend[:-1], 'Show'])
def xflSel(self, n):
tCur = time()
if tCur - self.T0n > 1: # avoid button "bouncing"
self.T0n = tCur
self.iXfl = n
n -= 1
for i in range(2): self.wxfl[i].config(relief=tk.RAISED)
self.wxfl[n].config(relief=tk.SUNKEN)
self.unselectView()
def viewSel(self, n):
tCur = time()
if tCur - self.T0n > 1: # avoid button "bouncing"
self.T0n = tCur
n -= 1
if self.viewOn[n]:
self.rb[n].config(relief=tk.RAISED)
self.putData(['SUB', 'Ovl={}_{}:zoomOut'\
.format(self.nView[n+1], self.nXfl[self.iXfl]), \
'Show'])
self.viewOn[n] = False
else:
self.rb[n].config(relief=tk.SUNKEN)
self.putData(['SUB', 'Ovl={}_{}:zoomIn'\
.format(self.nView[n+1], self.nXfl[self.iXfl]) , \
'Show'])
self.viewOn[n] = True
def Quit(self):
self.unselectView()
self.retfun(self)
#
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# Maintenance manager
# -----------------------------------------------------------------------------
class MntMan(tk.Toplevel):
def __init__(self, icofile, putData, RpiOn, dx, dy, fFmtn, gwdFmt, retfun):
tk.Toplevel.__init__(self)
self.resizable(width = False, height = False)
self.title('Maintenance')
self.iconbitmap(icofile)
self.protocol('WM_DELETE_WINDOW', self.Quit)
self.transient()
self.geometry('+{}+{}'.format(dx, round(dy/3)))
butFmtC = dict(font = ('Arial', 14, 'italic'), fg=MiBlue)
# ----- log
fr = tk.LabelFrame(self, text='Log', fg=MiBlue)
fr.grid(row=0, column=0, **gwdFmt)
self.wti = fr
tk.Button(fr, text='View', command=self.viewlog, **fFmtn)\
.grid(row=0, column=0, **gwdFmt)
tk.Button(fr, text='Delete', command=self.dellog, **fFmtn)\
.grid(row=0, column=1, **gwdFmt)
# ----- procs
self.Arduinos = ['DUE', 'UNO']
self.vnArduinos = []
self.RpiOn = RpiOn
self.Rpis = ['Rpi_Disp', 'Rpi_Pres', 'Rpi_Out']
self.nProcs = ['Fs_Hat', 'Show', 'Tcp2Ser'] + self.Rpis
self.vnProcs = []
# ----- Arduinos
lbf = tk.LabelFrame(self, text='Arduino\'s', fg=MiBlue)
lbf.grid(row=1, column=0, **gwdFmt)
for i in range(2):
self.vnArduinos.append(tk.IntVar())
self.vnArduinos[i].set(0)
tk.Checkbutton(lbf, text=self.Arduinos[i], \
variable=self.vnArduinos[i], \
anchor=tk.W, **fFmtn)\
.grid(row=0, column=i, **gwdFmt)
tk.Button(lbf, text='Reset', \
command=self.resetArduino, **fFmtn)\
.grid(row=1, column=0, columnspan=2, **gwdFmt)
fr = tk.LabelFrame(self, text='Procs', fg=MiBlue)
fr.grid(row=2, column=0, **gwdFmt)
self.allFlag = tk.IntVar()
self.allFlag.set(0)
tk.Checkbutton(fr, text='all', variable=self.allFlag, \
command=self.selectAll, \
anchor=tk.W, **fFmtn)\
.grid(row=1, column=0, **gwdFmt)
# ----- PC
lbf = tk.LabelFrame(fr, text='PC\'s', fg=MiBlue)
lbf.grid(row=3, column=0, **gwdFmt)
for i in range(3):
self.vnProcs.append(tk.IntVar())
self.vnProcs[i].set(0)
tk.Checkbutton(lbf, text=self.nProcs[i], variable=self.vnProcs[i], \
anchor=tk.W, **fFmtn)\
.grid(row=0, column=i, **gwdFmt)
# ----- Raspberry
lbf = tk.LabelFrame(fr, text='Raspberry Pi\'s', fg=MiBlue)
lbf.grid(row=4, column=0, **gwdFmt)
for k in range(3):
j = k + 3
self.vnProcs.append(tk.IntVar())
self.vnProcs[j].set(0)
tk.Checkbutton(lbf, text=self.nProcs[j], \
variable=self.vnProcs[j], \
anchor=tk.W, **fFmtn)\
.grid(row=0, column=k, **gwdFmt)
tk.Button(lbf, text='Replug', command=self.replugRpis, **fFmtn)\
.grid(row=1, column=0, columnspan=3, **gwdFmt)
fr2 = tk.LabelFrame(fr, text='Command', fg=MiBlue)
fr2.grid(row=5, column=0, **gwdFmt)
self.editTxt = tk.StringVar()
self.editTxt.set('')
tk.Entry(fr2, exportselection=0, textvariable=self.editTxt, **fFmtn)\
.grid(row=0, column=0, columnspan=3, **gwdFmt)
tk.Button(fr2, text='send', command=self.sendTxt, **fFmtn)\
.grid(row=0, column=4, columnspan=3, **gwdFmt)
"""
Exit : 0 : RESTART loop
1 : RESTART prog
2 : REBOOT device
3 : SHUTDOWN device
"""
fr2 = tk.LabelFrame(fr, text='Prog', fg=MiBlue)
fr2.grid(row=6, column=0, **gwdFmt)
tk.Button(fr2, text='Quit', \
command=lambda n=0: self.exitn(n), **fFmtn)\
.grid(row=0, column=0, **gwdFmt)
tk.Button(fr2, text='Restart', \
command=lambda n=1: self.exitn(n), **fFmtn)\
.grid(row=0, column=1, **gwdFmt)
fr2 = tk.LabelFrame(fr, text='Device', fg=MiBlue)
fr2.grid(row=7, column=0, **gwdFmt)
tk.Button(fr2, text='Reboot', \
command=lambda n=2: self.exitn(n), **fFmtn)\
.grid(row=0, column=0, **gwdFmt)
tk.Button(fr2, text='Shutdown', \
command=lambda n=3: self.exitn(n), **fFmtn)\
.grid(row=0, column=1, **gwdFmt)
# ----- END
tk.Button(self, text='◄ back', command=self.Quit, **butFmtC)\
.grid(row=8, column=0, **gwdFmt)
colrowconfig(self.winfo_children())
self.retfun = retfun
self.putData = putData
self.setTimer = self.after
self.tiOn = False
def sendTxt(self):
txt = self.editTxt.get()
if '=' in txt:
anySent = False
for i, p in enumerate(self.nProcs):
if self.vnProcs[i].get():
self.putData(['SUB', txt, p])
anySent = True
for v in self.vnProcs: v.set(0)
if anySent: self.editTxt.set('')
self.allFlag.set(0)
def selectAll(self):
val = self.allFlag.get()
for v in self.vnProcs: v.set(val)
def viewlog(self):
with open('__Master.log', 'w', encoding='utf-16') as log:
with open('Master.log', 'r', encoding='utf-16') as _log:
log.write(_log.read())
with open('_Master.log', 'r', encoding='utf-16') as _log:
log.write(_log.read())
Popen('notepad __Master.log')
def dellog(self):
copyfile('Master.log', './log/M{}.log'.format(strftime('%y%m%d%H%M%S')))
with open('Master.log', 'w', encoding='utf-16'): pass
def resetArduino(self):
if self.vnArduinos[0].get():
self.putData(['SUB', 'SKB=rstd', 'Fs_Hat'])
if self.vnArduinos[1].get():
self.putData(['SUB', 'ResetUno=1', 'Tcp2Ser'])
for v in self.vnArduinos: v.set(0)
def replugRpis(self):
for i in range(3):
if self.vnProcs[i+3].get():
self.unplugRpiN(i)
sleep(.1)
def unplugRpiN(self, n):
self.putData(['SUB', 'Rpi#{}=0'.format(n), 'Tcp2Ser'])
self.tiOn = True
self.RpiOn[i] = False
self.setTimer(5000, self.replugRpiN, n)
def replugRpiN(self, n):
self.putData(['SUB', 'Rpi#{}=1'.format(n), 'Tcp2Ser'])
self.RpiOn[n] = True
self.vnProcs[n+3].set(0)
self.tiOn = not all(self.RpiOn)
def exitn(self, n):
for i, p in enumerate(self.nProcs):
if self.vnProcs[i].get() and i != 2:
self.putData(['SUB', 'Exit={}'.format(n), p])
sleep(.1)
for v in self.vnProcs: v.set(0)
self.allFlag.set(0)
def Quit(self):
if self.tiOn:
MsgBox('Action in progress',\
'Cannot close now: action in progress....\n' + \
'Try again in a few sec ...')
else:
self.retfun(self.RpiOn, self)
#
# -----------------------------------------------------------------------------
# =============================================================================
#
class mainBoard():
"""
Main animation
"""
# ========================================================== initializing
def __init__(self, qIn, qOut, config):
# ======================= open graphic window, top screen, full screen
fig = tk.Tk()
#fig.overrideredirect(True)
#fig.lift()
fig.wm_attributes('-topmost', True)
fig.geometry('+0+0')
fig.iconbitmap(config.iconfile)
fig.title('Master')
fig.protocol('WM_DELETE_WINDOW', self.dlgEnd)
fig.bind('q', self.Quit)
fig.bind('Q', self.Quit)
self.config = config
self.getData = qIn.get_nowait
# self.putData = print # <<<<< DEBUG
self.putData = qOut.put_nowait
# ------------------------------------------------------------ formats
fs = 16
self.fFmtn = dict(font=("Arial", fs), fg=MiBlue)
self.fFmtb = dict(font=("Arial", fs, 'bold'), fg=MiBlue)
self.fFmti = dict(font=("Arial", fs-2, 'italic'), fg=MiBlue)
fs2 = 50
self.fFmtc1 = dict(font=("Arial", fs2, 'bold'), fg=MiBlue)
self.fFmtc2 = dict(font=("Arial", fs2, 'italic'), fg='blue')
self.fFmtc3 = dict(font=("Arial", fs2), fg='darkgrey')
self.fFmtc4 = dict(font=("Arial", fs2, 'italic'), fg='darkgrey')
self.gwdFmt = dict(padx=5, pady=5, sticky='news')
# ----------------------------------------------------------- variable
self.nextShowTime = int(config['NEXT_SHOW']['nextshtime'])
sLang = config['LANG']['lang']
lglst = [lg[:2].lower() for lg in config.langList]
iRow = 0
# ---- line 1 : lang
self.vLang = tk.StringVar()
self.vLang.set(sLang)
tk.Label(textvariable=self.vLang, bg=MiWht2, **self.fFmtn)\
.grid(row=iRow, column=0, columnspan=2, **self.gwdFmt)
self.wLang = tk.Button(text='Lang\t►', \
command=self.askLang, **self.fFmtn)
self.wLang.grid(row=iRow, column=2, **self.gwdFmt)
iRow += 1
# ----- Counter (STEP#0)
self.wStep = [tk.Label(text='►', disabledforeground=MiWht2 , \
**self.fFmtn)]
self.wStep[-1].grid(row=iRow+1, column=0, **self.gwdFmt)
self.vCnt = tk.StringVar()
self.vCnt.set('_ _')
self.wCnt = [tk.Label(textvariable=self.vCnt, width = 3, \
bg=MiWht1, **self.fFmtc1)]
self.wCnt[-1].grid(row=iRow, column=1, rowspan=3, **self.gwdFmt)
self.wCnt.append(tk.Button(text='Wait\t►', \
command=self.askTime, **self.fFmtn))
iRow += 1
self.wCnt[-1].grid(row=iRow, column=2, **self.gwdFmt)
iRow += 2
# ----- Farming Simulator Show (STEP#1)
self.wStep.append(tk.Label(text='►', disabledforeground=MiWht2 , \
**self.fFmtn))
self.wStep[-1].grid(row=iRow, column=0, **self.gwdFmt)
self.wFSimuS = tk.Button(text='Farming Simulator Show\t►', \
command=self.dlgFSimuShow, **self.fFmtn)
self.wFSimuS.grid(row=iRow, column=1, columnspan=2, **self.gwdFmt)
iRow += 1
# ----- Farming Simulator Play (STEP#2)
self.wStep.append(tk.Label(text='►', disabledforeground=MiWht2 , \
**self.fFmtn))
self.wStep[-1].grid(row=iRow, column=0, **self.gwdFmt)
self.wFSimuP = tk.Button(text='Farming Simulator Play\t►', \
command=self.dlgFSimuPlay, **self.fFmtn)
self.wFSimuP.grid(row=iRow, column=1, columnspan=2, **self.gwdFmt)
iRow += 1
# ----- end (STEP#3)
self.wStep.append(tk.Label(text='►', disabledforeground=MiWht2 , \
**self.fFmtn))
self.wStep[-1].grid(row=iRow, column=0, **self.gwdFmt)
self.wEnd = tk.Button(text='End', command=self.dlgEnd, **self.fFmtn)
self.wEnd.grid(row=iRow, column=1, columnspan=2, **self.gwdFmt)
iRow += 1
# ----- status
self.vStatus = tk.StringVar()
self.vStatus.set('Ok')
self.wStatus = tk.Label(textvariable=self.vStatus, \
bg='green', fg='white')
self.wStatus.grid(row=iRow, column=0, columnspan=3, **self.gwdFmt)
iRow += 1
# ----- maintenance (STEP#4)
self.wStep.append(tk.Label(text='►', disabledforeground=MiWht2 , \
**self.fFmtn))
self.wStep[-1].grid(row=iRow, column=0, **self.gwdFmt)
self.wMnt = tk.Button(text='Maintenance\t\t►', \
command=self.dlgMnt, **self.fFmti)
self.wMnt.grid(row=iRow, column=1, columnspan=2, **self.gwdFmt)
fig.update()
self.dx, self.dy = [int(x.split('+')[0]) \
for x in fig.geometry().split('x')]
# ----- pannels
self.panLang = None
self.panTime = None
self.tiCnt = None
self.CountFlag = 0
self.mnsec = 'mn'
self.mnsec2 = 'sec'
self.cntTime = 0
self.step = 0
self.setState(0)
self.panFSimuShow = None
self.panFSimuPlay = None
self.panMnt = None
self.pannelOn = False
self.RpiOn = [True]*4
self.enterGameFlag = tk.IntVar()
self.icofile = config.iconfile
self.mainloop = fig.mainloop
self.destroy = fig.destroy
self.setTimer = fig.after
self.fig = fig
colrowconfig(self.fig.winfo_children())
self.fig.after(10, self.wait4action)
self.mainloop()
# === ask for language
def askLang(self):
if self.wLang['relief'] == tk.RAISED:
self.wLang.config(relief=tk.SUNKEN, fg='black')
self.panLang = LangAPP(self.icofile, self.config.langList, \
self.setLang, self.dx)
self.panLang.mainloop()
# ----- Set language
def setLang(self, lang, adef):
if lang:
self.vLang.set(lang)
self.putData(['SUB', 'Lang=' + lang[:2], 'Show'])
self.putData(['SUB', 'Lang=' + lang[:2], 'Rpi_Pres'])
self.wLang.config(relief=tk.RAISED, fg=MiBlue)
self.panLang = None
adef.destroy()
# ===== ask for time
def askTime(self):
if self.wCnt[1]['relief'] == tk.RAISED:
if self.step == 0 and not self.pannelOn:
self.pannelOn = True
self.mnsec = 'mn'
self.wCnt[1].config(relief=tk.SUNKEN, fg='black')
if self.mnsec2 == 'mn':
n = self.nextShowTime - self.cntTime
else:
n = self.nextShowTime
if n < 0: n = self.nextShowTime
self.panTime = TimeMan(self.icofile, \
self.setTime, self.setWait, \
n, self.nextShowTime, self.dx, int(self.dy*.05),\
self.fFmtn, self.gwdFmt)
self.panTime.mainloop()
# ----- Set next show time
def setTime(self, waittime):
if waittime:
self.nextShowTime = waittime
# ----- Set remTime
def setWait(self, nexttime, mnsec, adef):
if nexttime is not None:
if mnsec == 'mn':
self.wCnt[0].config(**self.fFmtc1)
else:
if nexttime >= 60:
self.wCnt[0].config(**self.fFmtc1)
mnsec = 'mn'
nexttime = round(nexttime / 60)
else:
self.wCnt[0].config(**self.fFmtc2)
self.mnsec = mnsec
if self.tiCnt is not None:
self.wCnt[0].after_cancel(self.tiCnt)
self.tiCnt = None
self.putData(['SUB', 'remTime={}:{}'.format(nexttime, mnsec), 'Show'])
self.wCnt[1].config(relief=tk.RAISED, fg=MiBlue)
adef.destroy()
self.CountFlag = -1
self.countDown(nexttime)
else:
adef.destroy()
self.wCnt[1].config(relief=tk.RAISED, fg=MiBlue)
self.panTime = None
self.pannelOn = False
# ----- countDown
def countDown(self, n, endTime=0):
if n > 1:
self.vCnt.set(str(n))
nw = 1
if self.mnsec == 'mn' : nw *= 60
self.tiCnt = self.wCnt[0].after(nw*1000, self.countDown, n-1)
else:
if self.mnsec == 'mn':
self.mnsec = 'sec'
n = 60
self.wCnt[0].config(**self.fFmtc2)
self.countDown(n)
else:
self.CountFlag = 0
self.setState(endTime)
# ----- prepare to count Up
def startCntUp(self):
self.wCnt[0].config(**self.fFmtc4)
self.mnsec2 = 'sec'
if self.tiCnt is not None:
self.wCnt[0].after_cancel(self.tiCnt)
self.tiCnt = None
self.CountFlag = 1
self.countUp(0)
# ----- countUp
def countUp(self, n):
self.cntTime = n
self.vCnt.set(str(n))
if self.mnsec2 == 'sec':
if n < 60:
self.tiCnt = self.wCnt[0].after(1000, self.countUp, n+1)
else:
self.wCnt[0].config(**self.fFmtc3)
self.mnsec2 = 'mn'
self.countUp(1)
else:
self.tiCnt = self.wCnt[0].after(60000, self.countUp, n+1)
# ----- iddle Time
def iddleTime(self):
self.wCnt[0].config(**self.fFmtc1)
self.vCnt.set('_ _')
self.CountFlag = 0
if self.tiCnt is not None:
self.wCnt[0].after_cancel(self.tiCnt)
self.tiCnt = None
# ===== set state
def setState(self, n, iddle=True):
for w in self.wStep:
w.config(state=tk.DISABLED)
self.wStep[n].config(state=tk.NORMAL)
prev = self.step
self.step = n
if n == 0:
if iddle: self.iddleTime()
elif n == 1:
if self.wFSimuS['relief'] == tk.RAISED:
self.wFSimuS.invoke()
elif n == 2:
if self.wFSimuP['relief'] == tk.RAISED:
self.wFSimuP.invoke()
elif n == 3:
self.step = prev
if self.wEnd['relief'] == tk.RAISED:
self.wEnd.invoke()
elif n == 4:
self.step = prev
if self.wMnt['relief'] == tk.RAISED:
self.wMnt.invoke()
# ===== FARMNG SIMULATOR - SHOW - 1
def dlgFSimuShow(self):
if self.wFSimuS['relief'] == tk.RAISED and not self.pannelOn:
Ok = True
if self.CountFlag == -1:
Ok = bool(QuestBox(title = 'Launch Farming Simulator Show?', \
message = 'Do you want to launch the FS show right now?'))
if Ok:
if self.CountFlag != 1: self.startCntUp()
self.pannelOn = True
self.wFSimuS.config(relief=tk.SUNKEN, fg='black')
self.setState(1)
print('\t\t» FSimuShow : {}'.format(strftime('%d/%m/%y %H:%M:%S')))
self.putData(['SUB','ShowOn=1', 'Show'])
self.putData(['SUB','Sound=1', 'Tcp2Ser'])
self.panFSimuShow = FSimuShowMan(self.icofile, self.putData, self.dx, \
int(self.dy*.1), \
self.config.langList, \
self.vLang, \
self.fFmtn, self.gwdFmt,\
self.endFSimuShow)
self.panFSimuShow.mainloop()
def endFSimuShow(self, adef):
self.wFSimuS.config(relief=tk.RAISED, fg=MiBlue)
adef.destroy()
self.pannelOn = False
self.panFSimuShow = None
self.putData(['SUB','ShowOn=0', 'Show'])
self.putData(['SUB','Sound=0', 'Tcp2Ser'])
print('\t\t« FSimuShow : {}'.format(strftime('%d/%m/%y %H:%M:%S')))
self.setState(0)
# ===== FARMNG SIMULATOR - PLAY - 2
def dlgFSimuPlay(self):
if self.wFSimuP['relief'] == tk.RAISED and not self.pannelOn:
self.wFSimuP.config(relief=tk.SUNKEN, fg='black')
self.setState(2)
self.pannelOn = True