Module Euro-Truck-Simulator-2-Lane-Assist.main

The main file that runs the programs loop.

Expand source code
'''
The main file that runs the programs loop.
'''

# This section is for modules that I've added later as they might 
# not have been installed yet

import src.settings as settings

try:
    if "DXCamScreenCapture" in settings.GetSettings("Plugins", "Enabled"):
        settings.RemoveFromList("Plugins", "Enabled", "DXCamScreenCapture")
        settings.AddToList("Plugins", "Enabled", "BetterCamScreenCapture")
except: pass

import src.variables as variables # Stores all main variables for the program
if settings.GetSettings("User Interface", "hide_console", False) == True:
    import win32gui, win32con
    window_found = False
    target_text = "/venv/Scripts/python"
    top_windows = []
    win32gui.EnumWindows(lambda hwnd, top_windows: top_windows.append((hwnd, win32gui.GetWindowText(hwnd))), top_windows)
    for hwnd, window_text in top_windows:
        if target_text in window_text:
            window_found = True
            variables.CONSOLENAME = hwnd
            break
    if window_found == False:
        print("Console window not found, unable to hide!")
    else:
        print(f"Console Name: {window_text}, Console ID: {hwnd}")
        win32gui.ShowWindow(variables.CONSOLENAME, win32con.SW_HIDE)

    
import os
import pkg_resources
listOfRequirementsAddedLater = ["colorama", "bettercam", "matplotlib", "pywebview", "vdf", "deep_translator", "babel"]
# Check all of them
installed = [pkg.key for pkg in pkg_resources.working_set]
requirementsset = set(listOfRequirementsAddedLater)
installedset = set(installed)
missing = requirementsset - installedset

if missing:
    for modules in missing:
        if modules == "deep_translator": # For some reason this is always missing
            continue
        print("installing" + " " + modules)
        os.system("pip install" + " " + modules)

# Check that all requirements from requirements.txt are installed
with open(variables.PATH + r"\requirements.txt") as f:
    requirements = f.read().splitlines()

installed = [pkg.key for pkg in pkg_resources.working_set]
requirementsset = set(requirements)
installedset = set(installed)
missing = requirementsset - installedset

if missing:
    for modules in missing:
        if "deep_translator" in modules:
            pass
        elif "--upgrade --no-cache-dir gdown" in modules:
            pass
        elif "sv_ttk" in modules:
            pass
        elif "playsound2" in modules:
            os.system("pip uninstall -y playsound")
            os.system("pip install playsound2")
        else:
            print("installing" + " " + modules)
            os.system("pip install" + " " + modules)
else:
    pass

import requests
def UpdateChecker():
    print("Checking for updates...")
    currentVer = variables.VERSION.split(".")
    githubUrl = "https://raw.githubusercontent.com/Tumppi066/Euro-Truck-Simulator-2-Lane-Assist/main/"
    sourceForgeUrl = "https://sourceforge.net/p/eurotrucksimulator2-laneassist/code/ci/main/tree/"
    try:
        remoteVer = requests.get(githubUrl + "version.txt").text.strip().split(".")
        remote = "github"
    except:
        try:
            remoteVer = requests.get(sourceForgeUrl + "version.txt?format=raw").text.strip().split(".")
            remote = "sourceforge"
        except:
            print("Failed to check for updates")
            print("Please check your internet connection and try again later")
            return
    if int(currentVer[0]) < int(remoteVer[0]):
        update = True
    elif int(currentVer[1]) < int(remoteVer[1]):
        update = True
    elif int(currentVer[2]) < int(remoteVer[2]):
        update = True
    else:
        update = False
    
    if remote == "github":
        url = githubUrl
    else:
        url = sourceForgeUrl
    
    print(f"Current version: {'.'.join(currentVer)}")
    print(f"Remote version: {'.'.join(remoteVer)}")
    print(f"Update available: {update}")
    if update:
        if remote == "github":
            changelog = requests.get(url + "changelog.txt").text
        elif remote == "sourceforge":
            changelog = requests.get(url + "changelog.txt?format=raw").text
            
        print(f"Changelog:\n{changelog}")
        from tkinter import messagebox
        if messagebox.askokcancel("Updater", (f"We have detected an update, do you want to install it?\nCurrent - {'.'.join(currentVer)}\nUpdated - {'.'.join(remoteVer)}\n\nChangelog:\n{changelog}")):
            os.system("git stash")
            os.system("git pull")
            if messagebox.askyesno("Updater", ("The update has been installed and the application needs to be restarted. Do you want to quit the app?")):
                quit()
        else:
            variables.UPDATEAVAILABLE = remoteVer
            pass

try:
    devmode = settings.GetSettings("Dev", "disable_update_checker", False)
    if devmode == False:
        UpdateChecker()
except Exception as ex:
    # If the exception is for the quit command, then do that
    if ex.args == ("quit",):
        quit()

# Check tkinter tcl version
import tkinter as tk
from tkinter import messagebox
tcl = tk.Tcl()
acceptedVersions = ["8.6.11", "8.6.12", "8.6.13"]
version = str(tcl.call('info', 'patchlevel'))
if version not in acceptedVersions:
    messagebox.showwarning("Warning", f"Your tkinter version ({version} is not >= 8.6.11) is too old. Windows scaling will be broken with this version.")
    print(f"Your tkinter version ({version} is not >= 8.6.11) is too old. Windows scaling will be broken with this version.")

# Load the UI framework
import src.mainUI as mainUI
mainUI.CreateRoot()

import src.loading as loading # And then create a loading window

# Load the rest of the modules
import sys
import time
import json
from src.logger import print
import src.logger as logger
import traceback
import src.translator as translator
import src.controls as controls
import psutil
import cv2
import src.scsLogReader as LogReader
from src.server import SendCrashReport

logger.printDebug = settings.GetSettings("logger", "debug")
if logger.printDebug == None:
    logger.printDebug = False
    settings.CreateSettings("logger", "debug", False)
    
def GetEnabledPlugins():
    global enabledPlugins
    enabledPlugins = settings.GetSettings("Plugins", "Enabled")
    if enabledPlugins == None:
        enabledPlugins = [""]

panels = []
def FindPlugins(reloadFully=False):
    global plugins
    global panels
    global pluginObjects
    global pluginNames
    
    # Update the list of plugins and panels for the hash check
    pluginNames = GetListOfAllPluginAndPanelNames()
    
    # Find plugins
    path = os.path.join(variables.PATH, "plugins")
    plugins = []
    panels = []
    for file in os.listdir(path):
        if os.path.isdir(os.path.join(path, file)):
            # Check for main.py
            if "main.py" in os.listdir(os.path.join(path, file)):
                # Check for PluginInformation class
                try:
                    pluginPath = "plugins." + file + ".main"
                    plugin = __import__(pluginPath, fromlist=["PluginInformation"])
                    if plugin.PluginInfo.type == "dynamic":
                        if plugin.PluginInfo.name in enabledPlugins:
                            plugins.append(plugin.PluginInfo)
                    else:
                        panels.append(__import__("plugins." + plugin.PluginInfo.name + ".main", fromlist=["UI", "PluginInfo"]))
                except Exception as ex:
                    print(str(ex.args) + f" [{file}]")
                    pass

    pluginObjects = []
    for plugin in plugins:
        pluginObjects.append(__import__("plugins." + plugin.name + ".main", fromlist=["plugin", "UI", "PluginInfo", "onEnable"]))
        pluginObjects[-1].onEnable()
        
def ReloadPluginCode():
    FindPlugins()
    # Use the inbuilt python modules to reload the code of the plugins
    import importlib
    import progress.bar as Bar
    with Bar.PixelBar("Reloading plugins...", max=len(pluginObjects)) as progressBar:
        for plugin in pluginObjects:
            try:
                importlib.reload(plugin)
            except Exception as ex:
                print(ex.args)
                pass
            progressBar.next()
            
    with Bar.PixelBar("Reloading panels...", max=len(panels)) as progressBar:
        for panel in panels:
            try:
                importlib.reload(panel)
            except Exception as ex:
                print(ex.args)
                pass
            progressBar.next()
            
    print("Reloading UI root code...")
    try:
        mainUI.DeleteRoot()
        importlib.reload(mainUI)
        mainUI.CreateRoot()
        mainUI.drawButtons()
    except Exception as ex:
        print(ex.args)
        pass
    print("Reloaded UI root code.")

def RunOnEnable():
    for plugin in pluginObjects:
        try:
            plugin.onEnable()
        except Exception as ex:
            print(ex.args)
            pass
        
import concurrent.futures
import threading
def UpdatePlugins(dynamicOrder, data):
    for plugin in pluginObjects:
        try:
            if plugin.PluginInfo.dynamicOrder == dynamicOrder:
                startTime = time.time()

                pluginData = plugin.plugin(data)
                
                if pluginData != None:
                    data = pluginData
                else:
                    print(f"Plugin '{plugin.PluginInfo.name}' returned NoneType instead of a the data variable. Please make sure that you return the data variable.")
                
                endTime = time.time()    
                data["executionTimes"][plugin.PluginInfo.name] = endTime - startTime
                        
        except Exception as ex:
            print(ex.args + f"[{plugin.PluginInfo.name}]")
            pass
    return data

def GetListOfAllPluginAndPanelNames():
    # Find plugins
    path = os.path.join(variables.PATH, "plugins")
    plugins = []
    for file in os.listdir(path):
        if os.path.isdir(os.path.join(path, file)):
            # Check for main.py
            if "main.py" in os.listdir(os.path.join(path, file)):
                # Check for PluginInformation class
                try:
                    pluginName = file
                    plugins.append(pluginName)
                except Exception as ex:
                    print(ex.args)
                    pass
    
    return plugins

pluginNames = GetListOfAllPluginAndPanelNames()

def InstallPlugins():
    global startInstall
    global loadingWindow
    
    list = settings.GetSettings("Plugins", "Installed")
    if list == None:
        settings.CreateSettings("Plugins", "Installed", [])
    
    # Find plugins
    path = os.path.join(variables.PATH, "plugins")
    installers = []
    pluginNames = []
    for file in os.listdir(path):
        if os.path.isdir(os.path.join(path, file)):
            # Check for main.py
            if "main.py" in os.listdir(os.path.join(path, file)):
                # Check for PluginInformation class
                try:
                    # Get installers for plugins that are not installed
                    if file not in settings.GetSettings("Plugins", "Installed"):
                        pluginPath = "plugins." + file + ".install"
                        try:
                            pluginNames.append(f"{file}")
                            installers.append(__import__(pluginPath, fromlist=["install"]))
                        except: # No installer
                            pass
                        
                    
                except Exception as ex:
                    print(ex.args)
                    pass
    
    if installers == []:
        return
    
    import tkinter as tk
    from tkinter import ttk
    
    try:
        loadingWindow.destroy()
    except:
        pass
    
    # Create a new tab for the installer
    installFrame = ttk.Frame(mainUI.pluginNotebook, width=600, height=520)
    installFrame.pack(anchor=tk.CENTER, expand=True, fill=tk.BOTH)
    mainUI.pluginNotebook.add(installFrame, text="Plugin Installer")
    mainUI.pluginNotebook.select(mainUI.pluginNotebook.tabs()[-1])
    
    ttk.Label(installFrame, text="The app has detected plugins that have not yet been installed.").pack()
    ttk.Label(installFrame, text="Please install them before continuing.").pack()
    ttk.Label(installFrame, text="").pack()
    ttk.Label(installFrame, text="WARNING: Make sure you trust the authors of the plugins.").pack()
    ttk.Label(installFrame, text="If you are at all skeptical then you can see the install script at").pack()
    ttk.Label(installFrame, text="app/plugins/<plugin name>/installer.py").pack()
    ttk.Label(installFrame, text="").pack()
    
    startInstall = False
    def SetInstallToTrue():
        global startInstall
        startInstall = True
    ttk.Button(installFrame, text="Install plugins", command=lambda: SetInstallToTrue()).pack()
    
    ttk.Label(installFrame, text="").pack()
    ttk.Label(installFrame, text="The following plugins require installation: ").pack()
    # Make tk list object
    listbox = tk.Listbox(installFrame, width=75, height=30)
    listbox.pack()
    # Add the plugins there
    for plugin in pluginNames:
        listbox.insert(tk.END, plugin)
    # Center the listbox text
    listbox.config(justify=tk.CENTER)
    
    while not startInstall:
        mainUI.root.update()
    
    # Destroy all the widgets
    for child in installFrame.winfo_children():
        child.destroy()
        
    # Create the progress indicators
    ttk.Label(installFrame, text="\n\n\n\n\n\n\n").pack()
    currentPlugin = tk.StringVar(installFrame)
    currentPlugin.set("Installing plugins...")
    ttk.Label(installFrame, textvariable=currentPlugin).pack()
    bar = ttk.Progressbar(installFrame, orient=tk.HORIZONTAL, length=200, mode='determinate')
    bar.pack(pady=15)
    percentage = tk.StringVar(installFrame)
    ttk.Label(installFrame, textvariable=percentage).pack()
    ttk.Label(installFrame, text="").pack()
    ttk.Label(installFrame, text="This may take a while...").pack()
    ttk.Label(installFrame, text="For more information check the console.").pack()
    
    mainUI.root.update()
    
    loadingWindow = loading.LoadingWindow("Installing plugins...")
    
    index = 0
    import progress.bar as Bar
    with Bar.PixelBar("Installing plugins...", max=len(installers)) as progressBar:
        for installer, name in zip(installers, pluginNames):
            sys.stdout.write(f"\nInstalling '{name}'...\n")
            currentPlugin.set(f"Installing '{name}'...")
            bar.config(value=(index / len(installers)) * 100)
            percentage.set(f"{round((index / len(installers)) * 100)}%")
            mainUI.root.update()
            try:
                installer.install()
                settings.AddToList("Plugins", "Installed", name.split(" - ")[0])
            except:
                print(f"Warning. Failed to install '{name}' fully! The plugin might still work though.")
                pass
            index += 1
            loadingWindow.update(text=f"Installing '{name}'...")
            os.system("cls")
            progressBar.next()
    
    # Destroy all the widgets
    for child in installFrame.winfo_children():
        child.destroy()
        
    # Remove the tab
    settings.RemoveFromList("Plugins", "OpenTabs", "Plugin Installer")
    variables.RELOADPLUGINS = True
        

def CheckForONNXRuntimeChange():
    change = settings.GetSettings("SwitchLaneDetectionDevice", "switchTo")
    if change != None:
        if change == "GPU":
            loadingWindow.update(text="Uninstalling ONNX...")
            os.system("pip uninstall onnxruntime -y")
            loadingWindow.update(text="Installing ONNX GPU...")
            os.system("pip install onnxruntime-gpu")
        else:
            loadingWindow.update(text="Uninstalling ONNX GPU...")
            os.system("pip uninstall onnxruntime-gpu -y")
            loadingWindow.update(text="Installing ONNX...")
            os.system("pip install onnxruntime")
            
    settings.CreateSettings("SwitchLaneDetectionDevice", "switchTo", None)

def CheckLastKnownVersion():
    lastVersion = settings.GetSettings("User Interface", "version")
    if lastVersion == None:
        settings.UpdateSettings("User Interface", "version", variables.VERSION)
        mainUI.switchSelectedPlugin("plugins.Changelog.main")
        return
    
    if lastVersion != variables.VERSION:
        settings.UpdateSettings("User Interface", "version", variables.VERSION)
        mainUI.switchSelectedPlugin("plugins.Changelog.main")
        return
    
def CloseAllPlugins():
    for plugin in pluginObjects:
        plugin.onDisable()
        del plugin
        

timesLoaded = 0
def LoadApplication():
    global mainUI
    global uiUpdateRate
    global loadingWindow
    global timesLoaded

    loadingWindow = loading.LoadingWindow("Please wait initializing...")
    
    if timesLoaded > 0:
        try:
            mainUI.DeleteRoot()
            del mainUI
            import src.mainUI as mainUI
            mainUI.CreateRoot()
        except:
            pass
        
    timesLoaded += 1
        

    CheckForONNXRuntimeChange()

    # Check for new plugin installs
    InstallPlugins()
    
    # Load all plugins 
    loadingWindow.update(text="Loading plugins...")
    GetEnabledPlugins()
    FindPlugins()
    loadingWindow.update(text="Initializing plugins...")
    RunOnEnable()

    logger.printDebug = settings.GetSettings("logger", "debug")
    if logger.printDebug == None:
        logger.printDebug = False
        settings.CreateSettings("logger", "debug", False)

    # We've loaded all necessary modules
    showCopyrightInTitlebar = settings.GetSettings("User Interface", "TitleCopyright")
    if showCopyrightInTitlebar == None:
        settings.CreateSettings("User Interface", "TitleCopyright", True)
        showCopyrightInTitlebar = True
    
    mainUI.titlePath = "- " + open(settings.currentProfile, "r").readline().replace("\n", "") + " "
    mainUI.UpdateTitle()
    mainUI.root.update()
    mainUI.drawButtons()

    loadingWindow.destroy()
    del loadingWindow

    uiUpdateRate = settings.GetSettings("User Interface", "updateRate")
    if uiUpdateRate == None: 
        uiUpdateRate = 0
        settings.CreateSettings("User Interface", "updateRate", 0)

    CheckLastKnownVersion()
    # Show the root window
    mainUI.root.deiconify()

LoadApplication()

import hashlib
lastChecksums = {}
def CheckForFileChanges():
    """Will check the plugin main files for changes and reload them if they've changed."""
    global lastChecksums
    # Check if it's the first time running this function
    if lastChecksums == {}:
        for plugin in pluginNames:
            try:
                checksum = hashlib.md5(open(os.path.join(variables.PATH, "plugins", plugin, "main.py"), "rb").read()).hexdigest()
                lastChecksums[plugin] = checksum
            except:
                pass
        return
    
    # Check for changes in the plugins
    for plugin in pluginNames:
        try:
            checksum = hashlib.md5(open(os.path.join(variables.PATH, "plugins", plugin, "main.py"), "rb").read()).hexdigest()
            if checksum != lastChecksums[plugin]:
                print(f"Detected changes in {plugin}...")
                ReloadPluginCode()
                RunOnEnable()
                variables.RELOADPLUGINS = False
                variables.RELOAD = False # Already reloaded
                lastChecksums[plugin] = checksum
                break
        except:
            pass

data = {}
uiFrameTimer = 0
pluginChangeTimer = time.time()
if __name__ == "__main__":
    while True:
        # Main Application Loop
        try:
            
            allStart = time.time()
            
            # Remove "last" from the data and set it as this frame's "last"
            try: 
                data.pop("last")
                data = {
                    "last": data, 
                    "executionTimes": {}
                }
            except Exception as ex:
                data = {
                    "last": {},
                    "executionTimes": {}
                }  
            
            if variables.RELOADPLUGINS:
                print("Reloading plugins...")
                ReloadPluginCode()
                RunOnEnable()
                variables.RELOADPLUGINS = False
                variables.RELOAD = False # Already reloaded
            
            if variables.RELOAD:
                print("Reloading application...")
                # Reset the open tabs
                settings.UpdateSettings("User Interface", "OpenTabs", [])
                LoadApplication()
                variables.RELOAD = False
            
            # Update the input manager.
            controlsStartTime = time.time()
            data = controls.plugin(data)
            controlsEndTime = time.time()
            data["executionTimes"]["Control callbacks"] = controlsEndTime - controlsStartTime
            
            # Check for plugin changes (every second)
            pluginChangeTime = time.time()
            if time.time() - pluginChangeTimer > 1:
                pluginChangeTimer = time.time()
                CheckForFileChanges()
            pluginChangeEndTime = time.time()
            data["executionTimes"]["Filesystem Check"] = pluginChangeEndTime - pluginChangeTime
            
            # Check for log file changes
            logCheckTime = time.time()
            data = LogReader.plugin(data)
            logCheckEndTime = time.time()
            data["executionTimes"]["Log Check"] = logCheckEndTime - logCheckTime
            
            
            try:
                if variables.APPENDDATANEXTFRAME != None or variables.APPENDDATANEXTFRAME != [] or variables.APPENDDATANEXTFRAME != {} or variables.APPENDDATANEXTFRAME != "":
                    # Merge the two dictionaries
                    data.update(variables.APPENDDATANEXTFRAME)
                    variables.APPENDDATANEXTFRAME = None
            except: pass
            
            # Enable / Disable the main loop
            if variables.ENABLELOOP == False:
                start = time.time()
                mainUI.update(data)
                end = time.time()
                data["executionTimes"]["UI"] = end - start
                allEnd = time.time()
                data["executionTimes"]["all"] = allEnd - allStart
                try:
                    cv2.destroyWindow("Lane Assist")
                except:
                    pass
                try:
                    cv2.destroyWindow('Traffic Light Detection - B/W')
                except:
                    pass
                try:
                    cv2.destroyWindow('Traffic Light Detection - Red/Yellow/Green')
                except:
                    pass
                try:
                    cv2.destroyWindow('Traffic Light Detection - Final')
                except:
                    pass
                try:
                    cv2.destroyWindow('TruckStats')
                except:
                    pass
                continue
            
            if variables.UPDATEPLUGINS:
                GetEnabledPlugins()
                FindPlugins()
                variables.UPDATEPLUGINS = False
            
            data = UpdatePlugins("before image capture", data)
            data = UpdatePlugins("image capture", data)
            
            data = UpdatePlugins("before lane detection", data)
            data = UpdatePlugins("lane detection", data)
            
            data = UpdatePlugins("before controller", data)
            data = UpdatePlugins("controller", data)
            
            data = UpdatePlugins("before game", data)
            data = UpdatePlugins("game", data)
            
            data = UpdatePlugins("before UI", data)
            
            
            # Calculate the execution time of the UI
            start = time.time()
            uiFrameTimer += 1
            if uiFrameTimer > uiUpdateRate:
                mainUI.update(data)
                uiFrameTimer = 0
            end = time.time()
            data["executionTimes"]["UI"] = end - start
            
            data = UpdatePlugins("last", data)
            
            # And then the entire app
            allEnd = time.time()
            data["executionTimes"]["all"] = allEnd - allStart

            # Check if the frame took more than 200ms (5fps)
            if (allEnd - allStart) - data["executionTimes"]["UI"] > 0.2:
                print(f"Frame took {round((allEnd - allStart) * 1000)}ms to execute!")
                # Check if the anomalousFrames folder exists
                if not os.path.exists(os.path.join(variables.PATH, "anomalousFrames")):
                    os.mkdir(os.path.join(variables.PATH, "anomalousFrames"))
                # Save a new text file with the data
                with open(os.path.join(variables.PATH, "anomalousFrames", f"{time.time()}.txt"), "w") as f:
                    # Go throught each key and try and write it
                    for key in data:
                        try:
                            f.write(f"{key}: {data[key]}\n")
                        except:
                            pass
        
        except Exception as ex:
            try:
                if settings.GetSettings("User Interface", "hide_console") == True:
                    win32gui.ShowWindow(variables.CONSOLENAME, win32con.SW_RESTORE)
            except:
                pass
            if ex.args != ('The main window has been closed.', 'If you closed the app this is normal.'):
                import keyboard
                # Press the F1 key to pause the game
                keyboard.press_and_release("F1")
                from tkinter import messagebox
                import traceback
                exc = traceback.format_exc()
                traceback.print_exc()
                # Get the user name
                username = os.getlogin()
                # Send a crash report
                SendCrashReport("Main loop crash.", exc.replace(username, "censored"))
                if not messagebox.askretrycancel("Error", translator.Translate("The application has encountered an error in the main thread!\nPlease either retry execution or close the application (cancel)!\n\n") + exc):
                    break
                else:
                    pass
            else:
                CloseAllPlugins()
                try:
                    if settings.GetSettings("User Interface", "hide_console") == True:
                        import ctypes
                        ctypes.windll.user32.PostMessageW(variables.CONSOLENAME, 0x10, 0, 0)
                except:
                    print("Failed to close console!")
                break

Functions

def CheckForFileChanges()

Will check the plugin main files for changes and reload them if they've changed.

Expand source code
def CheckForFileChanges():
    """Will check the plugin main files for changes and reload them if they've changed."""
    global lastChecksums
    # Check if it's the first time running this function
    if lastChecksums == {}:
        for plugin in pluginNames:
            try:
                checksum = hashlib.md5(open(os.path.join(variables.PATH, "plugins", plugin, "main.py"), "rb").read()).hexdigest()
                lastChecksums[plugin] = checksum
            except:
                pass
        return
    
    # Check for changes in the plugins
    for plugin in pluginNames:
        try:
            checksum = hashlib.md5(open(os.path.join(variables.PATH, "plugins", plugin, "main.py"), "rb").read()).hexdigest()
            if checksum != lastChecksums[plugin]:
                print(f"Detected changes in {plugin}...")
                ReloadPluginCode()
                RunOnEnable()
                variables.RELOADPLUGINS = False
                variables.RELOAD = False # Already reloaded
                lastChecksums[plugin] = checksum
                break
        except:
            pass
def CheckForONNXRuntimeChange()
Expand source code
def CheckForONNXRuntimeChange():
    change = settings.GetSettings("SwitchLaneDetectionDevice", "switchTo")
    if change != None:
        if change == "GPU":
            loadingWindow.update(text="Uninstalling ONNX...")
            os.system("pip uninstall onnxruntime -y")
            loadingWindow.update(text="Installing ONNX GPU...")
            os.system("pip install onnxruntime-gpu")
        else:
            loadingWindow.update(text="Uninstalling ONNX GPU...")
            os.system("pip uninstall onnxruntime-gpu -y")
            loadingWindow.update(text="Installing ONNX...")
            os.system("pip install onnxruntime")
            
    settings.CreateSettings("SwitchLaneDetectionDevice", "switchTo", None)
def CheckLastKnownVersion()
Expand source code
def CheckLastKnownVersion():
    lastVersion = settings.GetSettings("User Interface", "version")
    if lastVersion == None:
        settings.UpdateSettings("User Interface", "version", variables.VERSION)
        mainUI.switchSelectedPlugin("plugins.Changelog.main")
        return
    
    if lastVersion != variables.VERSION:
        settings.UpdateSettings("User Interface", "version", variables.VERSION)
        mainUI.switchSelectedPlugin("plugins.Changelog.main")
        return
def CloseAllPlugins()
Expand source code
def CloseAllPlugins():
    for plugin in pluginObjects:
        plugin.onDisable()
        del plugin
def FindPlugins(reloadFully=False)
Expand source code
def FindPlugins(reloadFully=False):
    global plugins
    global panels
    global pluginObjects
    global pluginNames
    
    # Update the list of plugins and panels for the hash check
    pluginNames = GetListOfAllPluginAndPanelNames()
    
    # Find plugins
    path = os.path.join(variables.PATH, "plugins")
    plugins = []
    panels = []
    for file in os.listdir(path):
        if os.path.isdir(os.path.join(path, file)):
            # Check for main.py
            if "main.py" in os.listdir(os.path.join(path, file)):
                # Check for PluginInformation class
                try:
                    pluginPath = "plugins." + file + ".main"
                    plugin = __import__(pluginPath, fromlist=["PluginInformation"])
                    if plugin.PluginInfo.type == "dynamic":
                        if plugin.PluginInfo.name in enabledPlugins:
                            plugins.append(plugin.PluginInfo)
                    else:
                        panels.append(__import__("plugins." + plugin.PluginInfo.name + ".main", fromlist=["UI", "PluginInfo"]))
                except Exception as ex:
                    print(str(ex.args) + f" [{file}]")
                    pass

    pluginObjects = []
    for plugin in plugins:
        pluginObjects.append(__import__("plugins." + plugin.name + ".main", fromlist=["plugin", "UI", "PluginInfo", "onEnable"]))
        pluginObjects[-1].onEnable()
def GetEnabledPlugins()
Expand source code
def GetEnabledPlugins():
    global enabledPlugins
    enabledPlugins = settings.GetSettings("Plugins", "Enabled")
    if enabledPlugins == None:
        enabledPlugins = [""]
def GetListOfAllPluginAndPanelNames()
Expand source code
def GetListOfAllPluginAndPanelNames():
    # Find plugins
    path = os.path.join(variables.PATH, "plugins")
    plugins = []
    for file in os.listdir(path):
        if os.path.isdir(os.path.join(path, file)):
            # Check for main.py
            if "main.py" in os.listdir(os.path.join(path, file)):
                # Check for PluginInformation class
                try:
                    pluginName = file
                    plugins.append(pluginName)
                except Exception as ex:
                    print(ex.args)
                    pass
    
    return plugins
def InstallPlugins()
Expand source code
def InstallPlugins():
    global startInstall
    global loadingWindow
    
    list = settings.GetSettings("Plugins", "Installed")
    if list == None:
        settings.CreateSettings("Plugins", "Installed", [])
    
    # Find plugins
    path = os.path.join(variables.PATH, "plugins")
    installers = []
    pluginNames = []
    for file in os.listdir(path):
        if os.path.isdir(os.path.join(path, file)):
            # Check for main.py
            if "main.py" in os.listdir(os.path.join(path, file)):
                # Check for PluginInformation class
                try:
                    # Get installers for plugins that are not installed
                    if file not in settings.GetSettings("Plugins", "Installed"):
                        pluginPath = "plugins." + file + ".install"
                        try:
                            pluginNames.append(f"{file}")
                            installers.append(__import__(pluginPath, fromlist=["install"]))
                        except: # No installer
                            pass
                        
                    
                except Exception as ex:
                    print(ex.args)
                    pass
    
    if installers == []:
        return
    
    import tkinter as tk
    from tkinter import ttk
    
    try:
        loadingWindow.destroy()
    except:
        pass
    
    # Create a new tab for the installer
    installFrame = ttk.Frame(mainUI.pluginNotebook, width=600, height=520)
    installFrame.pack(anchor=tk.CENTER, expand=True, fill=tk.BOTH)
    mainUI.pluginNotebook.add(installFrame, text="Plugin Installer")
    mainUI.pluginNotebook.select(mainUI.pluginNotebook.tabs()[-1])
    
    ttk.Label(installFrame, text="The app has detected plugins that have not yet been installed.").pack()
    ttk.Label(installFrame, text="Please install them before continuing.").pack()
    ttk.Label(installFrame, text="").pack()
    ttk.Label(installFrame, text="WARNING: Make sure you trust the authors of the plugins.").pack()
    ttk.Label(installFrame, text="If you are at all skeptical then you can see the install script at").pack()
    ttk.Label(installFrame, text="app/plugins/<plugin name>/installer.py").pack()
    ttk.Label(installFrame, text="").pack()
    
    startInstall = False
    def SetInstallToTrue():
        global startInstall
        startInstall = True
    ttk.Button(installFrame, text="Install plugins", command=lambda: SetInstallToTrue()).pack()
    
    ttk.Label(installFrame, text="").pack()
    ttk.Label(installFrame, text="The following plugins require installation: ").pack()
    # Make tk list object
    listbox = tk.Listbox(installFrame, width=75, height=30)
    listbox.pack()
    # Add the plugins there
    for plugin in pluginNames:
        listbox.insert(tk.END, plugin)
    # Center the listbox text
    listbox.config(justify=tk.CENTER)
    
    while not startInstall:
        mainUI.root.update()
    
    # Destroy all the widgets
    for child in installFrame.winfo_children():
        child.destroy()
        
    # Create the progress indicators
    ttk.Label(installFrame, text="\n\n\n\n\n\n\n").pack()
    currentPlugin = tk.StringVar(installFrame)
    currentPlugin.set("Installing plugins...")
    ttk.Label(installFrame, textvariable=currentPlugin).pack()
    bar = ttk.Progressbar(installFrame, orient=tk.HORIZONTAL, length=200, mode='determinate')
    bar.pack(pady=15)
    percentage = tk.StringVar(installFrame)
    ttk.Label(installFrame, textvariable=percentage).pack()
    ttk.Label(installFrame, text="").pack()
    ttk.Label(installFrame, text="This may take a while...").pack()
    ttk.Label(installFrame, text="For more information check the console.").pack()
    
    mainUI.root.update()
    
    loadingWindow = loading.LoadingWindow("Installing plugins...")
    
    index = 0
    import progress.bar as Bar
    with Bar.PixelBar("Installing plugins...", max=len(installers)) as progressBar:
        for installer, name in zip(installers, pluginNames):
            sys.stdout.write(f"\nInstalling '{name}'...\n")
            currentPlugin.set(f"Installing '{name}'...")
            bar.config(value=(index / len(installers)) * 100)
            percentage.set(f"{round((index / len(installers)) * 100)}%")
            mainUI.root.update()
            try:
                installer.install()
                settings.AddToList("Plugins", "Installed", name.split(" - ")[0])
            except:
                print(f"Warning. Failed to install '{name}' fully! The plugin might still work though.")
                pass
            index += 1
            loadingWindow.update(text=f"Installing '{name}'...")
            os.system("cls")
            progressBar.next()
    
    # Destroy all the widgets
    for child in installFrame.winfo_children():
        child.destroy()
        
    # Remove the tab
    settings.RemoveFromList("Plugins", "OpenTabs", "Plugin Installer")
    variables.RELOADPLUGINS = True
def LoadApplication()
Expand source code
def LoadApplication():
    global mainUI
    global uiUpdateRate
    global loadingWindow
    global timesLoaded

    loadingWindow = loading.LoadingWindow("Please wait initializing...")
    
    if timesLoaded > 0:
        try:
            mainUI.DeleteRoot()
            del mainUI
            import src.mainUI as mainUI
            mainUI.CreateRoot()
        except:
            pass
        
    timesLoaded += 1
        

    CheckForONNXRuntimeChange()

    # Check for new plugin installs
    InstallPlugins()
    
    # Load all plugins 
    loadingWindow.update(text="Loading plugins...")
    GetEnabledPlugins()
    FindPlugins()
    loadingWindow.update(text="Initializing plugins...")
    RunOnEnable()

    logger.printDebug = settings.GetSettings("logger", "debug")
    if logger.printDebug == None:
        logger.printDebug = False
        settings.CreateSettings("logger", "debug", False)

    # We've loaded all necessary modules
    showCopyrightInTitlebar = settings.GetSettings("User Interface", "TitleCopyright")
    if showCopyrightInTitlebar == None:
        settings.CreateSettings("User Interface", "TitleCopyright", True)
        showCopyrightInTitlebar = True
    
    mainUI.titlePath = "- " + open(settings.currentProfile, "r").readline().replace("\n", "") + " "
    mainUI.UpdateTitle()
    mainUI.root.update()
    mainUI.drawButtons()

    loadingWindow.destroy()
    del loadingWindow

    uiUpdateRate = settings.GetSettings("User Interface", "updateRate")
    if uiUpdateRate == None: 
        uiUpdateRate = 0
        settings.CreateSettings("User Interface", "updateRate", 0)

    CheckLastKnownVersion()
    # Show the root window
    mainUI.root.deiconify()
def ReloadPluginCode()
Expand source code
def ReloadPluginCode():
    FindPlugins()
    # Use the inbuilt python modules to reload the code of the plugins
    import importlib
    import progress.bar as Bar
    with Bar.PixelBar("Reloading plugins...", max=len(pluginObjects)) as progressBar:
        for plugin in pluginObjects:
            try:
                importlib.reload(plugin)
            except Exception as ex:
                print(ex.args)
                pass
            progressBar.next()
            
    with Bar.PixelBar("Reloading panels...", max=len(panels)) as progressBar:
        for panel in panels:
            try:
                importlib.reload(panel)
            except Exception as ex:
                print(ex.args)
                pass
            progressBar.next()
            
    print("Reloading UI root code...")
    try:
        mainUI.DeleteRoot()
        importlib.reload(mainUI)
        mainUI.CreateRoot()
        mainUI.drawButtons()
    except Exception as ex:
        print(ex.args)
        pass
    print("Reloaded UI root code.")
def RunOnEnable()
Expand source code
def RunOnEnable():
    for plugin in pluginObjects:
        try:
            plugin.onEnable()
        except Exception as ex:
            print(ex.args)
            pass
def UpdateChecker()
Expand source code
def UpdateChecker():
    print("Checking for updates...")
    currentVer = variables.VERSION.split(".")
    githubUrl = "https://raw.githubusercontent.com/Tumppi066/Euro-Truck-Simulator-2-Lane-Assist/main/"
    sourceForgeUrl = "https://sourceforge.net/p/eurotrucksimulator2-laneassist/code/ci/main/tree/"
    try:
        remoteVer = requests.get(githubUrl + "version.txt").text.strip().split(".")
        remote = "github"
    except:
        try:
            remoteVer = requests.get(sourceForgeUrl + "version.txt?format=raw").text.strip().split(".")
            remote = "sourceforge"
        except:
            print("Failed to check for updates")
            print("Please check your internet connection and try again later")
            return
    if int(currentVer[0]) < int(remoteVer[0]):
        update = True
    elif int(currentVer[1]) < int(remoteVer[1]):
        update = True
    elif int(currentVer[2]) < int(remoteVer[2]):
        update = True
    else:
        update = False
    
    if remote == "github":
        url = githubUrl
    else:
        url = sourceForgeUrl
    
    print(f"Current version: {'.'.join(currentVer)}")
    print(f"Remote version: {'.'.join(remoteVer)}")
    print(f"Update available: {update}")
    if update:
        if remote == "github":
            changelog = requests.get(url + "changelog.txt").text
        elif remote == "sourceforge":
            changelog = requests.get(url + "changelog.txt?format=raw").text
            
        print(f"Changelog:\n{changelog}")
        from tkinter import messagebox
        if messagebox.askokcancel("Updater", (f"We have detected an update, do you want to install it?\nCurrent - {'.'.join(currentVer)}\nUpdated - {'.'.join(remoteVer)}\n\nChangelog:\n{changelog}")):
            os.system("git stash")
            os.system("git pull")
            if messagebox.askyesno("Updater", ("The update has been installed and the application needs to be restarted. Do you want to quit the app?")):
                quit()
        else:
            variables.UPDATEAVAILABLE = remoteVer
            pass
def UpdatePlugins(dynamicOrder, data)
Expand source code
def UpdatePlugins(dynamicOrder, data):
    for plugin in pluginObjects:
        try:
            if plugin.PluginInfo.dynamicOrder == dynamicOrder:
                startTime = time.time()

                pluginData = plugin.plugin(data)
                
                if pluginData != None:
                    data = pluginData
                else:
                    print(f"Plugin '{plugin.PluginInfo.name}' returned NoneType instead of a the data variable. Please make sure that you return the data variable.")
                
                endTime = time.time()    
                data["executionTimes"][plugin.PluginInfo.name] = endTime - startTime
                        
        except Exception as ex:
            print(ex.args + f"[{plugin.PluginInfo.name}]")
            pass
    return data