Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blank GUI Map #151

Open
lagilfillan69 opened this issue Dec 1, 2024 · 0 comments
Open

Blank GUI Map #151

lagilfillan69 opened this issue Dec 1, 2024 · 0 comments

Comments

@lagilfillan69
Copy link

Hi All,
Having issues with getting a map to not be blank with my code. I tried running the demo code and it functions fine. I tried to model my code after it but I am still getting no map. Right now I am doing an offline implementation to rule out internet issues. Here is my code, please let me know if anyone is aware how to fix this.

One important thing to know is that I am using asyncio to work with a bluetooth module that connects to the GUI

import tkinter

import tkintermapview
import asyncio
import tkinter as tk
from tkinter import messagebox
import tkinter.ttk as ttk
import asyncio
import time
from bleak import BleakClient, BleakError, BleakScanner
import bleak

import sys,os
#   attaches /Main/Py_Modules
#   if you raise this a folder, delete a '..'
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'Py_Modules')))
from Path_Planning import generate_corners
from helper_functions import *

    
write_uuid = '0000ffe0-0000-1000-8000-00805f9b34fb'
read_uuid = '0000ffe0-0000-1000-8000-00805f9b34fb'



class App(tkinter.Tk):
    def __init__(self, *args, **kwargs):
        tkinter.Tk.__init__(self, *args, **kwargs)
        global main_window, device_list, device_data, message_variable, input_buffer, is_connected
        default_gps = [(40.35193576273826, -79.92251632083196), (40.35216725384582, -79.92235178305927),
                       (40.357392805988894, -79.93423733144638)]
        main_window = tk.Tk()
        main_window.title('Tkinter/bleak asyncio Demo')

        main_window.title("GPS & Bluetooth Controller")
        main_window.geometry("1000x1000")
        main_window.configure(bg="#2c3e50")  # Background color

        # Title Label
        title_label = tk.Label(main_window, text="Wall-e Control", font=("Arial", 28, "bold"), fg="white", bg="#2c3e50")
        title_label.pack(pady=10)


        control_frame = ttk.Frame(main_window)
        control_frame.pack(pady=10)
        main_window.protocol("WM_DELETE_WINDOW", BLE.stop_loop)

        message_variable = tk.StringVar()
        input_buffer = tk.StringVar()

        main_window.start_button = ttk.Button(control_frame, text="Start", style="TButton", command= lambda : asyncio.create_task(BLE.start_task()))
        main_window.start_button.grid(row=0, column=0, padx=20, pady=10, sticky="nsew", ipadx=20)

        main_window.stop_button = ttk.Button(control_frame, text="Stop", style="TButton", command= lambda : asyncio.create_task(BLE.write("STOP\t")))
        main_window.stop_button.grid(row=0, column=2, padx=20, pady=10, sticky="nsew", ipadx=20)

        main_window.pause_button = ttk.Button(control_frame, text="Pause", style="TButton", command= lambda : asyncio.create_task(BLE.write("PAUS\t")))
        main_window.pause_button.grid(row=0, column=1, padx=20, pady=10, sticky="nsew", ipadx=20)

        # Bluetooth Frame
        bluetooth_frame = ttk.Frame(main_window
    )
        bluetooth_frame.pack(pady=10)

        main_window.bt_button = ttk.Button(bluetooth_frame, text="Connect Bluetooth", command=lambda : asyncio.create_task(BLE.connect()))
        main_window.bt_button.grid(row=0, column=0, padx=10)

        main_window.status_label = tk.Label(bluetooth_frame, textvariable=message_variable, font=("Arial", 12),
                                     fg="red", bg="white")
        main_window.status_label.grid(row=0, column=1, padx=10)

        # Map Frame
        # map_frame = ttk.Frame(main_window)
        # map_frame.pack(pady=10)
        script_directory = os.path.dirname(os.path.abspath(__file__))
        database_path = os.path.join(script_directory, "Offline_Maps/offline_tiles.db")

        main_window.map_widget = tkintermapview.TkinterMapView(main_window, width=700, height=500, corner_radius=0, use_database_only=True,database_path=database_path)
        main_window.map_widget.place(relx=0.5, rely=0.5, anchor=tk.CENTER)
        # main_window.map_widget.grid(row=1, column=0, columnspan=3, sticky="nsew")
        main_window.map_widget.pack()
        # main_window.map_widget.set_position(48.860381, 2.338594)  # Paris, France
        main_window.map_widget.set_zoom(15)
        main_window.map_widget.set_tile_server("https://mt0.google.com/vt/lyrs=s&hl=en&x={x}&y={y}&z={z}&s=Ga")

        # Add initial markers
        # main_window.markers = [
        #     main_window.map_widget.set_marker(48.860381, 2.338594, text="Start"),
        #     main_window.map_widget.set_marker(48.860381, 2.338594, text="Corner 1"),
        #     main_window.map_widget.set_marker(48.860381, 2.3385942, text="Corner 2")
        # ]

        # GPS Input Frame
        gps_input_frame = ttk.Frame(main_window)
        gps_input_frame.pack(pady=20)

        main_window.gps_fields = []
        for i in range(3):
            gps_frame = ttk.Frame(gps_input_frame)
            gps_frame.grid(row=i, column=0, padx=10, pady=5, sticky="w")

            lat_label = ttk.Label(gps_frame, text=f"Latitude {i + 1}:", font=("Arial", 12))
            lat_label.grid(row=0, column=0, padx=5)
            lat_entry = ttk.Entry(gps_frame, width=10)
            lat_entry.insert(0,default_gps[i][0])
            lat_entry.grid(row=0, column=1, padx=5)

            lon_label = ttk.Label(gps_frame, text=f"Longitude {i + 1}:", font=("Arial", 12))
            lon_label.grid(row=0, column=2, padx=5)
            lon_entry = ttk.Entry(gps_frame, width=10)
            lon_entry.insert(0, default_gps[i][1])
            lon_entry.grid(row=0, column=3, padx=5)

            main_window.gps_fields.append((lat_entry, lon_entry))

        main_window.set_button = ttk.Button(gps_frame, text=f"Set Coords",
                                            command=gps_update)
        main_window.set_button.grid(row=2, column=4, padx=5)

        buffer_input_frame = ttk.Frame(main_window)
        buffer_input_frame.pack(pady=20)
        main_window.input_label = tk.Label(buffer_input_frame, textvariable=input_buffer, font=("Arial", 12),
                                            fg="red", bg="white")

        main_window.input_label.grid(row=0, column=1, padx=20)

        ESTOP_frame = ttk.Frame(main_window)
        ESTOP_frame.pack(pady=20)
        main_window.estop_button = ttk.Button(ESTOP_frame, text="ESTOP", command= lambda : asyncio.create_task(BLE.write("ESTO\t")))
        main_window.pic = ttk.Button(ESTOP_frame, text="PIC",
                                              command=lambda: asyncio.create_task(BLE.write("CAMR\t")))
        main_window.estop_button.grid(row=0, column=1, padx=20)
        main_window.pic.grid(row=0, column=2, padx=20)

         ###################


    # Don't do: main_window.mainloop()!
    # We are using the asyncio event loop in 'show' to call
    # main_window.update() regularly.



def gps_update():
    for i, (lat_entry, lon_entry) in enumerate(main_window.gps_fields):
        try:
            lat = lat_entry.get()
            lon = lon_entry.get()

            if (lat == "" or lon == ""):
                messagebox.showwarning("Error", "Some fields are empty")
                return

            lat = float(lat)
            lon = float(lon)

            if not (-90 <= lat <= 90) or not (-180 <= lon <= 180):
                messagebox.showerror("Invalid latitude or longitude",message="Entries for Marker" +  str(i + 1) +  "are not valid points, please try again.")
                return

            if len(lat_entry.get().split('.')[-1]) < 4 or len(lon_entry.get().split('.')[-1]) < 4:
                messagebox.showerror("Not Enough Precision","Coordinates for Marker" + str(i + 1) + " must have at least 4 decimal places.")
                return
            lat_entry.config(foreground="green")
            lon_entry.config(foreground="green")

            # Update the corresponding marker position on the map
            marker = main_window.markers[i]
            marker.set_position(lat, lon)


        except ValueError as e:
            # Show error message if the input is invalid
            messagebox.showerror("Error", str(e))


class BLE():
    def __init__(self):
        self.client = None
        is_connected=False
    async def write(self,message):
        if self.client == None:
            messagebox.showerror("Bluetooth Not Connected", "Cannot write to bluetooth")
            return False
        buffer = bytes(message, "utf-8")
        try :
            await self.client.write_gatt_char('0000ffe1-0000-1000-8000-00805f9b34fb', buffer, response=False)
        except (BleakError, asyncio.TimeoutError):
            message_variable.set(f'Writing not successful')
            return False
        message_variable.set(f'Message Sent')
        return True

    async def notify_read(self):
        await self.client.start_notify('0000ffe1-0000-1000-8000-00805f9b34fb', self.read_callback)


    async def start_task(self):
        # add logic for if gps coorindates are not set then it will not go
        message = ""
        gps_points = []
        if(self.client == None):
            messagebox.showerror("Error", "Bluetooth is not connected")
            return
        for i, (lat_entry, lon_entry) in enumerate(main_window.gps_fields):
            try:
                lat = float(lat_entry.get())
                lon = float(lon_entry.get())
                gps_points.append((lat,lon))
            except(ValueError):
                messagebox.showerror("Error", "GPS POINTS NOT SET")
                return
        corners = generate_corners(gps_points[0],gps_points[1],gps_points[2])
        await self.write("C1:" + str(corners[0][0]) + "," +  str(corners[0][1]) + "\n")
        await self.write("C2:" + str(corners[1][0]) + "," + str(corners[1][1]) + "\n")
        await self.write("C3:" + str(corners[2][0]) + "," + str(corners[2][1]) + "\n")
        message_variable.set("Running Bot")

    async def connect(self):
        """Connect to or disconnect from selected/connected device."""
        if not(self.client is None) and self.client.is_connected:
            message_variable.set('Trying to disconnect...')
            disconnect.set()
            return
        # Pick the BLE device from the scan result:
        device = await BleakScanner.find_device_by_name("lgilfillan")
        if device == None:
            message_variable.set('Could Not Connect...')
            return
        name = device.name if device.name is not None else device.address

        # try:
        message_variable.set(f'Trying to connect to {name}')
        self.client = BleakClient(device)
        try :
            await self.client.connect()
            message_variable.set(f'Device {name} is connected!')
            is_connected = True
        except (BleakError, asyncio.TimeoutError):
            message_variable.set(f'Connecting to {name}\nnot successful')
            is_connected = False

        try :
            await self.notify_read()
        except (BleakError, asyncio.TimeoutError):
            message_variable.set(f'Failed to establish 2 way connection')
            is_connected = False

        while not disconnect.is_set():
            await asyncio.sleep(0.1)
        is_connected = False
        return


    async def detailed_scanner(self):
        for service in self.client.services:
            print()
            print(f'\t\tDescription: {service.description}')
            print(f'\t\tService: {service}')

            print('\t\tCharacteristics:')
            for c in service.characteristics:
                print()
                print(f'\t\t\tUUID: {c.uuid}'),
                print(f'\t\t\tDescription: {c.description}')
                print(f'\t\t\tHandle: {c.handle}'),
                print(f'\t\t\tProperties: {c.properties}')
                print('\t\tDescriptors:')
                for descrip in c.descriptors:
                    print(descrip)

    async def read_callback(self, sender : bleak.BleakGATTCharacteristic, data : bytearray):
        data = data.decode('utf-8')
        pts = data[:8]
        input_buffer.set(time.ctime(time.time()) + data)
        pickup = messagebox.askyesno("Trash Found!",message="Trash Found at " + data)
        if pickup == True:
            await self.write("OKAY\t")
        else :
            await self.write("NKAY\t")

    def disconnect_callback(self,client):
        """Handle disconnection.

        This callback is called when the device is disconnected.
        """
        message_variable.set(f'Device {client.address} has/was\ndisconnected')


    async def show(self):
       while not stop.is_set():
            main_window.update()
            self.bt_alive()
            await asyncio.sleep(0.1)


    def bt_alive(self):
        if(not(self.client is None) and self.client.is_connected):
            try:
                if(self.client.is_connected):
                    message_variable.set(f'Connection Alive')
                else:
                    message_variable.set(f'Disconnected....')
            except(BleakError, asyncio.TimeoutError):
                pass



    def stop_loop(self):
        """Set stop event."""
        stop.set()


async def main():
    """Start the GUI."""
    global stop, disconnect, BLE
    BLE = BLE()
    stop = asyncio.Event()
    disconnect = asyncio.Event()
    GUI = App()
    await BLE.show()
    main_window.destroy()


asyncio.run(main())

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant