Python Forum

Full Version: Serial Port As Global
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
How do I define the selected port as global so I can call it anywhere in my code.

In below code I have selecting the serial port inside a function.SO I cannot use it anywhere in my code.

How can I make it as global?

import tkinter as tk
import tkinter.ttk as ttk
import serial.tools.list_ports
import serial
import time


# --- main ---
root = tk.Tk()
#This is our label
label = ttk.Label(root, text = "Please Select a COM PORT")
label.grid(column = 1, row = 4)
label.pack()

#Functions
def on_select(event=None):

    # get selection from event    
    print("event.widget:", event.widget.get())

    # or get selection directly from combobox
    print("comboboxes: ", cb.get())
    val = cb.get()
    label.configure(text= val[:5])
    selected_COM = val[:5]
#====================

    # create a serial object
    selected_COM = val[:5]
    ser = serial.Serial(selected_COM) # replace 'COM1' with the name of your serial port
    ser.baudrate = 9600
    ser.bytesize=8
    ser.parity  ='N'   # No parity
    ser.stopbits = 1   # Number of Stop bits = 1
    time.sleep(3)
    #ser.write(b"A")    #transmit 'A' (8bit) to micro/Arduino
    #ser.close()      # Close the port

#=====================

cb = ttk.Combobox(root, values=serial_ports())
cb.pack()
label2 = ttk.Label(root, text = "Please Insert a Number")
label2.pack()

# assign function to combobox
cb.bind('<<ComboboxSelected>>', on_select) 	
You might could do something like this.

# Do the imports
import serial
from serial.tools import list_ports
import tkinter as tk
from tkinter import ttk

# Get ports
ports = list_ports.comports()

# Function for getting and displaying port data
def getdata(com, frame):

    # Get port name
    data = serial.Serial(com.get())

    # Set index to 0 for looping
    index = 0

    # Get item data in ther serial port return data
    # Place in tkinter labels
    for key, value in data.__dict__.items():
        label = tk.Label(frame, text=f'{key.strip("_").upper()}:', anchor='w', relief='groove')
        label2 = tk.Label(frame, text=value, anchor='w', relief='groove')
        label.grid(column=0, row=index, sticky='new')
        label2.grid(column=1, row=index, sticky='new')
        index += 1


# Start tkinter 
root = tk.Tk()
root.columnconfigure(0, weight=1)
root['padx'] = 5
root['pady'] = 8

# Create a string variable and set default value
var = tk.StringVar()
var.set('COM1')

# Create a frame to hold display data
frame = tk.Frame(root)
frame.grid(column=0, row=1, sticky='news', pady=5)

# Create and populate a combobox
combobox = ttk.Combobox(root, values=ports, width=80)
combobox.grid(columnspan=2, column=0, row=0, sticky='new')
combobox['state'] = 'readonly'

# Set current value for combobox
combobox.current(0)

# Bind combobox to function
combobox.bind('<<ComboboxSelected>>', lambda _: getdata(var, frame))

# Uncomment for data to pull up on opening window
# getdata(var, frame)

root.mainloop()


        
I think the best solution is to use classes. This example makes Window class that is a Tk that knows about serial ports. It is a subclass of tkinter.Tk, so making a Window is similar to "root=Tk()".
import tkinter as tk
import tkinter.ttk as ttk
from serial import Serial
from serial.tools import list_ports


class Window(tk.Tk):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.label = tk.Label(self)
        ports = [port.name for port in list_ports.comports()]
        if not ports:
            quit("There are no serial ports on this device.")
        self.serial_ports = ttk.Combobox(self, values=ports, width=7)
        self.serial_ports.bind("<<ComboboxSelected>>", self.select_serial_port)
        self.serial_port = None
        self.serial_ports.set(ports[0])
        self.send_ab = tk.Button(self, text="write('A')", command=self.writea())

        self.serial_ports.pack(padx=50, pady=(50, 0))
        self.send_ab.pack(pady=(10, 50))

    def select_serial_port(self, _):
        """Select serial port from available ports."""
        if self.serial_port:
            self.serial_port.close()

        self.serial_port = Serial(
            self.serial_ports.get(), baudrate=9600, bytesize=8, parity="N", stopbits=1
        )
        self.serial_port.close()

    def writea(self):
        """write 'A' out the serial port."""
        if self.serial_port:
            self.serial_port.open()
            self.serial_port.write(b"A")
            self.serial_port.close()


Window().mainloop()
One of the neat things about classes, is they give you a new namespace. You are already familiar with the global and local namespaces. An assignment made in the local namespace will not be visible in the global namespace (the source of your problem) unless you use the "global" keyword. Classes let you make additional namespaces. When I create an instance of "Window", this not only creates an object, it also creates a new namespace that only exists inside that object. Things added to the "instance namespace" will exist as long as the instance does. This lets me assign an instance variable in one method and use it in another. That is used in Window to make a "serial_port" in the "select_serial_port()" method, and use that same serial port in the "writea()" method. Very useful.