Jimari
Posts: 9
Joined: Thu Nov 21, 2019 2:52 pm

Python, Tkinter

Tue Dec 03, 2019 3:20 pm

Hi,

I have some code that can help me find i2c addresses (from MCP23017 devices). For every device it finds it creates a label with two radio buttons next to it, where the user has a choice of selecting the corresponding address as an input or output. (So that the GPIO on that mcp becomes all inputs or all outputs.)
Can somebody help me get functionality to these radio buttons as when selecting it as input or output, because now there just labels and the radio buttons are not connected to these labels.
Or is it better not to use a for loop for creating variable buttons/labels (, because of ...)?

Goal: For every live address select it as input or output, then save these choices by clicking a button and the code configures the mcp on the live addresses as inputs or outputs.

thanks in advance!

this is my code so far:

Code: Select all

from smbus2 import SMBus, i2c_msg
from tkinter import * 
from tkinter.ttk import *
from tkinter import messagebox


##channel = 1
##i2c = SMBus(channel)

with SMBus(1) as bus:
    dev = []
    for addr in range(32,40):	#mcp addresses range from 0x20 to 0x27
        try:
            msg = i2c_msg.read(addr,4)
            bus.i2c_rdwr(msg)
            msg= i2c_msg.write(addr,4)
            bus.i2c_rdwr(msg)
        except IOError:
            print("No addr on" , addr)
        else:
            dev+= [addr]

        print(addr)

print(dev)
   
print("I found I2C devices on address:", *dev,sep="\n")
window = Tk()
window.title("IO Module")
window.geometry('350x200')

for i in range(len(dev)):
    lbl = Label(window,text = dev[0+i],relief="groove",borderwidth = 5)
    lbl.grid(column=0,row=0+i,padx=10)
    rad = Radiobutton(window, text='Input', value = 1+i)
    rad1 = Radiobutton(window, text='Output',value = 9+i)
    rad.grid(column=1, row = 0+i)
    rad1.grid(column=2,row=0+i)
    
window.mainloop()

Jimari
Posts: 9
Joined: Thu Nov 21, 2019 2:52 pm

Re: Python, Tkinter

Tue Dec 03, 2019 6:13 pm

Now i have this code (see bottom of post).
I want to increment the variable=v in the Radiobutton line. Because with one variable the user can only choose 1 radiobutton. With increasing the variable, i want to achieve that the user can choose input or output for every label/i2c address. But python does not allow incrementing of IntVar(). Other solutions of not using IntVar() has led to errors of index out or range .

Are there other solutions for incrementing the variable=v, or does somebody have an idea on how to do this?

Thanks in advance!

Code: Select all

def showChoice():
    
    print(v.get())
    
   

##for i in range(len(dev)):
##    lbl = Label(window,text = dev[0+i],relief="groove",borderwidth = 5)
##    lbl.grid(column=0,row=0+i,padx=10)
##    rad = Radiobutton(window, text='Input',variable = v,command = showChoice, value = 1+i)
##    rad1 = Radiobutton(window, text='Output',variable=v1,command=showChoice,value = 9+i)
##    rad.grid(column=1, row = 0+i)
##    rad1.grid(column=2,row=0+i)

for i in range(len(dev)):
    lbl = Label(window,text = dev[0+i],relief="groove",borderwidth = 5)
    lbl.grid(column=0,row=0+i,padx=10)
    
    for x in range(len(dev)):
        v = IntVar()
       
        rad = Radiobutton(window,text='input',variable=v,command=showChoice,value=1+x)
        		 rad1=Radiobutton(window,text='output',variable=v,command=showChoice,value=9+x)
        rad.grid(column=1,row=0+x)
        rad1.grid(column=2,row=0+x)
       
        print(v)
        
      
    
window.mainloop()

scotty101
Posts: 3750
Joined: Fri Jun 08, 2012 6:03 pm

Re: Python, Tkinter

Tue Dec 03, 2019 9:50 pm

I've made a few changes to your code (including commenting out the SMBus stuff since I don't have any I2C devices handy)

The important change is around creating a new IntVar for each I2C device which is then stored in a list "device_vars". Performing a .get() on each item in the "device_vars" list will tell you whether the device is set as an input or output.

Code: Select all

#from smbus2 import SMBus, i2c_msg
from tkinter import * 
from tkinter.ttk import *
from tkinter import messagebox


##channel = 1
##i2c = SMBus(channel)

##with SMBus(1) as bus:
##    dev = []
##    for addr in range(32,40):	#mcp addresses range from 0x20 to 0x27
##        try:
##            msg = i2c_msg.read(addr,4)
##            bus.i2c_rdwr(msg)
##            msg= i2c_msg.write(addr,4)
##            bus.i2c_rdwr(msg)
##        except IOError:
##            print("No addr on" , addr)
##        else:
##            dev+= [addr]
##
##        print(addr)
devices = [0x30, 0x35, 0x40]

print(devices)
   
print("I found I2C devices on address:", *devices,sep="\n")
window = Tk()
window.title("IO Module")
window.geometry('350x200')

device_vars = []

for idx,dev in enumerate(devices):
    tmpVar = IntVar(window)
    lbl = Label(window,text = dev,relief="groove",borderwidth = 5)
    lbl.grid(column=0,row=0+idx,padx=10)
    rad = Radiobutton(window, text='Input', value = 1+idx, variable=tmpVar)
    rad1 = Radiobutton(window, text='Output',value = 9+idx, variable=tmpVar)
    rad.grid(column=1, row = 0+idx)
    rad1.grid(column=2,row=0+idx)
    device_vars.append(tmpVar)
    
    
window.mainloop()
Electronic and Computer Engineer
Pi Interests: Home Automation, IOT, Python and Tkinter

Jimari
Posts: 9
Joined: Thu Nov 21, 2019 2:52 pm

Re: Python, Tkinter

Tue Dec 03, 2019 10:33 pm

Thank you for that code: it works great. I don't get the errors anymore :D .
However there is still a problem.
If i have 1 device, the values returning from print(tmpVar.get()) are 1 for input and 9 for output. Just the way i wanted. But when there are more than one i2c devices, the values for the first address are 0 for input and 0 for output. But when i select the input for the second address it becomes 2 for input and 10 for output, just as what the code is supposed to do. But when i select input or output for the first address, the value of that becomes the previous value from the second address. I try to summarize this in a table:

1 i2c device:
(addr1)input : 1 / output: 9

2 i2c devices:
(addr1)input: 0/ output: 0
(addr2)input: 2/ output: 10
=> after selecting input or output for addr2 => value of addr1 becomes input: 2 or 10/ output: 2 or 10 (values for addr2 stay the same, so 2 for input and 10 for output)

How can i resolve this matter, because i can not wrap my mind around it?
(PS. when there are 3 i2c devices, values for addr1 and addr2 are both 0, so only the last device gets to have values)

thank you in advance!

scotty101
Posts: 3750
Joined: Fri Jun 08, 2012 6:03 pm

Re: Python, Tkinter

Wed Dec 04, 2019 9:31 am

I don't see this behaviour...

I've modified the code to add a button which will output the state of the radio buttons.

Code: Select all

#from smbus2 import SMBus, i2c_msg
from tkinter import * 
from tkinter.ttk import *
from tkinter import messagebox


##channel = 1
##i2c = SMBus(channel)

##with SMBus(1) as bus:
##    dev = []
##    for addr in range(32,40):	#mcp addresses range from 0x20 to 0x27
##        try:
##            msg = i2c_msg.read(addr,4)
##            bus.i2c_rdwr(msg)
##            msg= i2c_msg.write(addr,4)
##            bus.i2c_rdwr(msg)
##        except IOError:
##            print("No addr on" , addr)
##        else:
##            dev+= [addr]
##
##        print(addr)
def getAddresses():
    for dev,var in zip(devices,device_vars):
        print(f"Device {dev} is set to {var.get()}")

devices = [0x30, 0x35, 0x40]

print(devices)
   
print("I found I2C devices on address:", *devices,sep="\n")
window = Tk()
window.title("IO Module")
window.geometry('350x200')

device_vars = []

for idx,dev in enumerate(devices):
    tmpVar = IntVar(window)
    lbl = Label(window,text = dev,relief="groove",borderwidth = 5)
    lbl.grid(column=0,row=0+idx,padx=10)
    rad = Radiobutton(window, text='Input', value = 1+idx, variable=tmpVar)
    rad1 = Radiobutton(window, text='Output',value = 9+idx, variable=tmpVar)
    rad.grid(column=1, row = 0+idx)
    rad1.grid(column=2,row=0+idx)
    device_vars.append(tmpVar)

button = Button(window,text="Get Addresses", command=getAddresses)
button.grid(column=1,row=len(device_vars)+1)
    
window.mainloop()

When all the devices are inputs I get

Code: Select all

Device 48 is set to 1
Device 53 is set to 2
Device 64 is set to 3
When they are set to outputs I get

Code: Select all

Device 48 is set to 9
Device 53 is set to 10
Device 64 is set to 11
These values are set from the "1+idx" and "9+idx" sections in the code.
Electronic and Computer Engineer
Pi Interests: Home Automation, IOT, Python and Tkinter

Jimari
Posts: 9
Joined: Thu Nov 21, 2019 2:52 pm

Re: Python, Tkinter

Wed Dec 04, 2019 1:24 pm

Thank you!!!
I modified the code a little for my purpose

Code: Select all

v=[]
def getAddresses():
    del v[:] #delete previous array, otherwise array builds up with old data
    for dev,var in zip(devices,device_vars):
        print("Device", dev," is set to", var.get())
        
        
        v.append(var.get())
        
        for dev in range(1):
            if var.get() == 1 or var.get()==2 or var.get()==3:
                print('input')
            elif var.get() == 9 or var.get() ==10 or var.get()==11:
                print('output')
    print(*v,sep="\n")
    
with this code i check if the values are an input or output, so that where is says print('input') and print('output'), the code will become to initialise the GPIO on the corresponding MCP23017. Is this modified code any good, or are there other better solutions for this?
(You might have guessed that my knowlegde in python is not that big, sorry for this. But mostly thank you for your help)

scotty101
Posts: 3750
Joined: Fri Jun 08, 2012 6:03 pm

Re: Python, Tkinter

Wed Dec 04, 2019 1:56 pm

Well its up to you to decide whether it works or not. Without understanding the longer term goals of your project it is difficult to advise whether your code will be suitable for this or whether there is a better way.

There is something unusual in your code

Code: Select all

for dev in range(1):
This creates a loop that is only run once. Why?
Electronic and Computer Engineer
Pi Interests: Home Automation, IOT, Python and Tkinter

Jimari
Posts: 9
Joined: Thu Nov 21, 2019 2:52 pm

Re: Python, Tkinter

Wed Dec 04, 2019 2:36 pm

Because it first looked like this:

Code: Select all

v=[]
def getAddresses():
    del v[:] #delete previous array, otherwise array builds up with old data
    for dev,var in zip(devices,device_vars):
        print("Device", dev," is set to", var.get())
        
        
        v.append(var.get())
        print('dev= ',dev)
        for dev in range(len(devices)):
            
            if var.get() == 1 or var.get()==2 or var.get()==3:
                print('input')
            
            if var.get() == 9 or var.get() ==10 or var.get()==11:
                print('output')
    print(*v,sep="\n")
with the following as output:

Code: Select all

Device 48  is set to 1
dev=  48
input
input
input
Device 53  is set to 10
dev=  53
output
output
output
Device 64  is set to 3
dev=  64
input
input
input
1
10
3
And i dont want to initialise every mcp address x times the number of mcp addresses on the i2c bus. I need to just count how many input and output with what address there are and then initialise the mcp.

Return to “Python”