pyscreen: komp ekranik
Oznacza, że ekranem będzie obiekt (submodel w kabinie) "komp" a jego tekstura będzie generowana skryptem/funkcją "ekranik". Jeśli w danej kabinie nie zostanie znaleziony taki obiekt, zostanie on zignorowany.pyscreenrendererpriority normal // (normal, lower, lowest, idle) priorytet wątku pythonowego renderera. Odciąża procesor zmniejszając odświeżanie ekranów w lokomotywach.
Ciężki skrypt na wolnym komputerze może znacznie spowolnić symulację. Niższy priorytet za to powodować, że klatka ekranu będzie liczona nawet raz na kilka tysięcy klatek symulacji, przez co ciężko będzie nawet dostrzec animację tekstury.Data aktualna, predkosc cyfrowa pod predkoscia pasek rozszerzajacy sie domyslam sie ze to procent max szybkosci oraz oststni prad trakcyjny cyfrowy z paskiem sily trakcyjnej.
from PIL import Image
Mamy teraz dostęp do wszystkich funkcji klasy Imageclass ekranik(abstractscreenrenderer):
def __init__(self,lookup_path,name,cab):
#tu będą inicjowane stałe
def _render(self, state):
#tu będą zmienne odświeżane w każdej klatce
obrazek=Image.new(mode, size, color=0)
mode=RGB (3x8-bit pixels, true color) obrazek=Image.new(RGB, (256,256), color=(57,57,57))
return obrazek
from PIL import Image, ImageDraw
draw=ImageDraw.Draw(obrazek)
#czas
if state['seconds'] != self.last_time_update:
dt = state['seconds'] - self.last_time_update
if dt < 0:
dt+=60
self.kilometry += dt*speed * 0.0002778
self.last_time_update = state['seconds']
if state['hours']<10: #dodaje zero przed liczbą gdy ma tylko jedną cyfrę
godz = "0" + str(state['hours'])
else:
godz = str(state['hours']) #zapisuje do zmiennej "godz" liczbę godzin jako string
if state['minutes']<10:
min = "0" + str(state['minutes'])
else:
min = str(state['minutes'])
if state['seconds']<10:
sec = "0" +str(state['seconds'])
else:
sec = str(state['seconds'])
Licznika kilometrów nam nie potrzeba, więc go usuwamy.draw.text((x,y), godz +":"+ min +":"+ sec, fill, font)
Musimy podać pozycję, kolor i czcionkę napisu. Użyjemy obecnej już w symulatorze czcionki Alterebro Pixel Font Regular. Otwieram w gimpie podkładkę i piszę nią napis by pokrył się ze zdjęciem. Odczytuję:self.pixel = ImageFont.truetype(lookup_path + "Alterebro Pixel Font Regular.ttf", 68)
Używam modułu ImageFont, więc dodaję go do nagłówka. Mamy tam już:from PIL import ImageDraw, ImageFont, Image
Dodajemy również nasz kolor. Można go za każdym razem podawać jako RGB, ale w ten sposób w jednym miejscu możemy go zmieniać jakby efekt był niezadowalający.self.zolty = (193,164,50)
draw.text((7, 20), godz +":"+ min +":"+ sec, fill=selfzolty, font=self.pixel)
Używamy domyślnej funkcji draw.text justującej do lewej. Składamy godziny, minuty i sekundy, oddzielając je dwukropkami. Zazwyczaj dwukropki migają z pulsacją 1s, ale nie wiem jak osiągnąć taktowanie 0,5s na wł/wył. def print_fixed_with(self, draw, text, start_point, character_count, font, color, correction=0):
rozmiar_osemki = font.getsize("8")[0]
start_point_tmp = start_point[0]
for znak in range(len(text), character_count):
start_point_tmp += rozmiar_osemki-correction
for znak in range(0,len(text)):
rozmiar_act = font.getsize(text[znak])[0]
draw.text((start_point_tmp + rozmiar_osemki - rozmiar_act, start_point[1]), text[znak], font=font, fill=color)
start_point_tmp += rozmiar_osemki-correction
def print_center(self, draw, text, X, Y, font, color):
w = draw.textsize(text, font)
draw.text(((X-w[0]/2),(Y-w[1]/2)), text, font=font, fill=color)
speed = float(state['velocity'])
Pod zmienną lokalną "speed" podstawiamy wartość wysłaną przez moduł fizyki jako zmiennoprzecinkową float.draw.text((21, 96), '%d' % speed, fill=self.zolty, font=self.pixel)
"%d"-formatowanie; tylko część całkowita. Zamiast tego można by skonwertować ją do int. if speed > 100:
speed = 100
Gdy zmienna przekracza 100, ucinamy do setki. x2=17+1.08*speed
draw.rectangle(((17,150),(x2,162)), fill=self.zolty)
from PIL import ImageDraw, ImageFont, Image #Ładujemy biblioteki
class ekranik(abstractscreenrenderer): #klasa renderera
def __init__(self,lookup_path,name,cab): #inicjalizacja stałych
self.pixel = ImageFont.truetype(lookup_path + "Alterebro Pixel Font Regular.ttf", 68) #definicja czcionki na napisy
self.zolty = (193,164,50) #kolor na napisy
def _render(self, state): #pętla rendera
#czas
if state['hours']<10: #dodaje zero przed liczbą gdy ma tylko jedną cyfrę
godz = "0" + str(state['hours'])
else:
godz = str(state['hours']) #zapisuje do zmiennej "godz" liczbę godzin jako string
if state['minutes']<10:
min = "0" + str(state['minutes'])
else:
min = str(state['minutes'])
if state['seconds']<10:
sec = "0" +str(state['seconds'])
else:
sec = str(state['seconds'])
obrazek=Image.new(RGB, (256,256), color=(57,57,57)) #tworzymy nowy obrazek tła
draw=ImageDraw.Draw(obrazek) #ładujemy go do edycji
draw.text((7, 20), godz +":"+ min +":"+ sec, fill=self.zolty, font=self.pixel) #napis-zegarek
#prędkość
speed = float(state['velocity']) #odczyt prędkości
draw.text((21, 96), '%d' % speed, fill=self.zolty, font=self.pixel) #napis-prędkość
if speed > 100: #ucięcie prędkości jakby wyszła poza zakres
speed = 100
x2=17+1.08*speed #długość paska prędkości jako procent Vmax x2=17+(speed/100*108)
draw.rectangle(((17,150),(x2,162)), fill=self.zolty) #prostokąt procentu prędkości
return obrazek #wysyłamy finalny obrazek do bufora
battery (bool) - stan baterii w pojeździe kontrolowanym
Jako warunek z podłączonymi ikonkami.eimp_u + (nr zespołu) + _ + (parametr)
gdzie:
(nr zespołu) to liczba z zakresu od 1 do 8,
(parametr) przyjmuje wartości:
pf - podniesiony przedni pantograf
pr - podniesiony tylny pantograf
Jako warunki z ikonkami patyków góra-dół.
prad = state['im']
self.print_fixed_with(draw_txt, prad, (250,250), 30, self.segment_font, (255,127,39), 7)
Ekranik musi się świecić w nocy. Świecenie jest atrybutem materiału obiektu a nie tekstury, więc pythonem na nie nie wpłyniemy. Ekranik mu się więc świecić cały czas/być podłączony pod universala. Częścią świecącą będzie sam czarny prostokącik (świecenie na czarno=brak świecenia).
czas += 1
if czas == 300:
czas = 0
takt += 1
if takt == 2:
takt = 0
if takt == 1:
(tutaj funkcja rysująca tekst)
Niestety nie działa. Wiesz może jak zrobić by dane polecenie wywoływało się tylko raz na początku?
# przeniesienie ścieżki do funkcji zapisującej
self.lookup_path = lookup_path
# zapisywanie ostatniej sekundy od liczenia przebiegu
self.last_time_update = 0
# losowanie przebiegustartowego
self.kilometry = PS+(random()*PL)+(1+self.rok-RokProd)*PR+self.dzis*PD
# ostatni zapisany przebieg
self.kilometry_z = 0
# otwieranie pliku
plik = open(lookup_path + "tacho.txt")
try:
tempkm = plik.read()
if tempkm == "":
tempkm = "x"
else:
self.kilometry = float(tempkm)
self.kilometry_z = self.kilometry
finally:
plik.close()
Gdzie zastosowano zmienne czasu: self.dzis = datetime.now().timetuple().tm_yday
self.rok = datetime.now().year
przy losowaniu przebiegu zastosowane są również parametry: #liczenie przebiegu co sekundę
if state['seconds'] != self.last_time_update:
dt = state['seconds'] - self.last_time_update
if dt < 0:
dt+=60
self.kilometry += dt*speed * 0.0002778
self.last_time_update = state['seconds']
gdzie zastosowano zmienną: speed = float(state['velocity'])
#zapis stanu licznika do pliku
if self.kilometry>(self.kilometry_z + (0.1+0.005*speed)):
plik = open(self.lookup_path + "tacho.txt", 'wb')
try:
plik.write(str(self.kilometry))
self.kilometry_z = self.kilometry
finally:
plik.close()
jeżeli nawrotnik jest w pozycji 0
wyświetl pusty ekran
w przeciwnym razie
wyświetl ekran normalnie (tak jak obecnie)
Niestety wywala błąd. Teoretycznie wszystko zrobiłem dobrze, a błąd polega na tym że nie ma odwołania do lokomotywy (!) mimo że nie znalazłem tej definicji. Jak go naprawić? Podaję plik pythona i log.Mierniki kołowe:Mozliwym rozwiazaniem jest tutaj zwykle oszustwo: krzywa modelujesz jako osobny pasek geometrii, nalozony "na" ekran (czyli umieszczony pare mm przed nim zeby ograniczyc z-fighting) zawiniety w wymagany ksztalt i z przypisana ta sama tekstura, ktorej uzywa ekran. Rezerwujesz dla niego waski pas np na dole tekstury, i w kodzie pythona rysujesz tam odpowiedniej wielkosci wypelniony prostokat z dowolnymi bajerami, odpowiadajacy stopniowi wypelnienia krzywej... a z punktu widzenia uzytkownika wyglada to na rosnaca krzywa na ekranie.
Czyli coś w stylu siłomierza traxxa. Figury ograniczonej krzywymi nie możemy narysować o ile mi wiadomo.
# przeniesienie ścieżki do funkcji zapisującej
self.lookup_path = lookup_path
# zapisywanie ostatniej sekundy od liczenia przebiegu
self.last_time_update = 0
# losowanie przebiegustartowego
self.kilometry = PS+(random()*PL)+(1+self.rok-RokProd)*PR+self.dzis*PD
# ostatni zapisany przebieg
self.kilometry_z = 0
try:
with open(lookup_path + "tacho.txt", "r") as tacho_file:
tempkm = tacho_file.read()
if tempkm == "":
raise IOError
except IOError:
tempkm = randint(1, 499000)
finally:
self.kilometry = float(tempkm)
self.kilometry_z = self.kilometry
with open(lookup_path + "tacho.txt", "w") as tacho_file:
tacho_file.write(str(tempkm))
2. Na końcu funkcji render dopisujemy:#zapis stanu licznika do pliku
if self.kilometry>(self.kilometry_z + (0.1+0.005*speed)):
with open(self.lookup_path + "tacho.txt", 'w') as tacho_file:
tacho_file.write(str(self.kilometry))
self.kilometry_z = self.kilometry
Za dołączonymi bibliotekami a przed ciałem klasy definiujemy stałą globalną, nasz kolor. Można go za każdym razem podawać jako RGB, ale w ten sposób w jednym miejscu możemy go zmieniać jakby efekt był niezadowalający.Kod: [Zaznacz]zolty = (193,164,50)
self.zolty = (193,164,50)
oraz analogicznie dla dowolnego koloru, wówczas do koloru odwołujemy się poprzez self.zolty a nie zolty.