#1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2013
    Posts
    4
    Rep Power
    0

    Loop Issue in Tkinter?


    Thanks in advance for the assistance. I have a Tkinter program that allows a user to select a video source and output and it will send a command to an RS-232 device. I have it almost there but can't seem to wrap my head around the logic or loop to have the program wait for source/output pair then print. Right now it just grabs the last button press but what this does is if I select source 1 and output A then go to select source 2 output B as soon as I hit source 2 it remembers the output A and switches to that first so my first selection is always changed. Is there a way to either loop the selection so it waits for source/output pair or just reloads after a pair is selected?
    Here is what I have so far:

    Code:
    # File:displayapp3.py
    
    from Tkinter import *
    
    class App:
        
        def __init__(self, master):
    
    	self.myLastButtonInvokedS = None
    	self.myLastButtonInvokedM = None
    
            frame = Frame(master)
            frame.grid()
    
           	self.src1 = Button(master, text="Camera", command=self.src1)
    	self.src1.grid(column=0, row=0)
    	#self.photod=PhotoImage(file="surgcamera.gif")
    	#self.src1.config(image=self.photod, width="160", height="160")
    	
    	self.src2 = Button(master, text="Pacs", command=self.src2)
    	self.src2.grid(column=0, row=1)
    	#self.photoc=PhotoImage(file="pacs.gif")
    	#self.src2.config(image=self.photoc, width="160", height="160")
            
    	self.monA = Button(master, text="Monitor A", command=self.monA)
    	self.monA.grid(column=1, row=0)
    	#self.photoa=PhotoImage(file="monitora.gif")
    	#self.monA.config(image=self.photoa, width="160", height="160")
    
    	self.monB = Button(master, text="Monitor B", command=self.monB)
    	self.monB.grid(column=1, row=1)
    	#self.photob=PhotoImage(file="monitorb.gif")
    	#self.monB.config(image=self.photob, width="160", height="160")
    	
        def src1(self):
            print ("r 1"), self.myLastButtonInvokedM
    	self.myLastButtonInvokedS = "1"
    
        def src2(self):
            print ("r 2"), self.myLastButtonInvokedM
    	self.myLastButtonInvokedS = "2"
    
        def monA(self):
            print ("r"), self.myLastButtonInvokedS, "A"
    	self.myLastButtonInvokedM = "A"
    
        def monB(self):
            print ("r"), self.myLastButtonInvokedS, "B"
    	self.myLastButtonInvokedM = "B"
        
    print "\n"*100
    root = Tk()
    root.title('Matrix')
    
    app = App(root)
    
    root.mainloop()

    Thanks again!
    Jason
  2. #2
  3. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Feb 2013
    Posts
    138
    Rep Power
    2
    Please use code tags when posting code.
  4. #3
  5. Contributing User
    Devshed Demi-God (4500 - 4999 posts)

    Join Date
    Aug 2011
    Posts
    4,841
    Rep Power
    480
    I'd use another button that says and does "use this pairing". Then the operator could pick and reselect inputs and outputs until my face turns blue. When finally satisfied the operator clicks "Use this pair. GO!"

    Or maybe I didn't understand the problem. Your code as posted has too many missing parts to run successfully on my configuration.

    Traceback (most recent call last):
    File "q.py", line 56, in <module>
    app = App(root)
    File "q.py", line 19, in __init__
    self.src1.config(image=self.photod, width="160", height="160")
    AttributeError: App instance has no attribute 'photod'
    [code]Code tags[/code] are essential for python code and Makefiles!
  6. #4
  7. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2013
    Posts
    4
    Rep Power
    0
    Sorry I fixed the commenting so the photo would not be referenced. Not sure if this will work as there will be no logical place for the Select button as when the app is complete it may have up to 8 inputs and 8 outputs. Just trying to make it as simple as possible for the user. This behavior is what they are accustomed to. Thanks for the assistance.

    Jason
  8. #5
  9. Contributing User
    Devshed Demi-God (4500 - 4999 posts)

    Join Date
    Aug 2011
    Posts
    4,841
    Rep Power
    480
    Does this meet your description? You repeatedly defined self.buttonName then overwrote the attribute with methods named buttonName . I fixed that. You have a function for each device. Silly, at least for the code you supplied. I fixed that. There was duplicate code. I fixed that.
    Code:
    from Tkinter import *
    
    class App:
    
        def __init__(self,
                     master,
                     sources = ('Camera;surgcamera.gif', 'Pacs;pacs.gif',),
                     sinks = ('Monitor A;monitora.gif', 'Monitor B;monitorb.gif',)):
    
            self.source_name = None
            self.output_name = None
    
            self.master = master
            frame = Frame(master)
            frame.grid()
    
            for (row, data,) in enumerate(sources):
                self.device(function = self.source, row = row, column = 0, data = data)
    
            for (row, data,) in enumerate(sinks):
                self.device(function = self.output, row = row, column = 1, data = data)
    
        def device(self, function, row, column, data):
            (name, gif,) = data.split(';')
            b = Button(self.master, text=name, command = (lambda a = name: function(a)))
            b.grid(column=column, row=row,)
            #self.photod=PhotoImage(file=gif)
            #b.config(image=self.photod, width="160", height="160")
    
        def source(self,name):
            self.source_name = name
            print('current source: {}'.format(name))
    
        def output(self,name):
            self.output_name = name
            if None == self.source_name:
                print('please select source')
            else:
                print('Using source {} with output {}.'.format(self.source_name, self.output_name))
                self.source_name = None
                self.output_name = None
    
    print "\n"*100
    root = Tk()
    root.title('Matrix')
    
    sources = 'front camera;fc.gif', 'security;sc.gif', 'http://www.youtube.com/watch?v=jG7vhMMXagQ;Tau.gif', 'Camera;surgcamera.gif', 'Pacs;pacs.gif',
    
    app = App(root, sources,)
    
    root.mainloop()
    This version is about 8 lines shorter than yours. The key feature, however, is that to add more devices you need only to modify the data passed into the constructor. Since the code is data driven you can read the data from a file and you don't need to change the program. You could have a sources file and a file of sinks.
    [code]Code tags[/code] are essential for python code and Makefiles!
  10. #6
  11. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2013
    Posts
    4
    Rep Power
    0
    Hadn't thought of enumerating through it certainly makes for cleaner setup and for tailoring to each different configuration. Instead of the name can I assign each source a number and each output a letter to print in the following format:
    r 1 A
    I will be piping output to a serial console RS-232 and this will be the format required for the device. So I understand what is happening better can I add a string to each device in this format:

    Code:
    sources = ('Camera;surgcamera.gif;1', 'Pacs;pacs.gif;2')
    Then change the actual data passed to the variable instead of the name. So instead of :
    Code:
    def source(self,name): self.source_name = name
    Something like:
    Code:
    def source(self,name,var): self.source_var = var
    Where var would correspond to the 1 or 2 for the source number so the print statement would then be:

    Code:
    print('Using source {} with output {}.'.format(self.source_var, self.output_var))


    Again thank you for the assistance.

    Jason
  12. #7
  13. Contributing User
    Devshed Demi-God (4500 - 4999 posts)

    Join Date
    Aug 2011
    Posts
    4,841
    Rep Power
    480
    Sure, you may need to understand this bit of code:
    Code:
            b = Button(self.master, text=name, command = (lambda a = name: function(a)))
    and why I chose semi-colon as a separator. Other ideas could also work.
    [code]Code tags[/code] are essential for python code and Makefiles!
  14. #8
  15. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2013
    Posts
    1
    Rep Power
    0

    Welcome


    Welcome to have more fun game waiting for you
  16. #9
  17. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2013
    Posts
    4
    Rep Power
    0
    So I got all the logic worked out so that it prints the way I need but I cannot get it to use the image on the button. Only the last output shows an image and it is the only button that works when I add the image lines:

    Code:
    def device(self, function, row, column, data):
            (name, img, var,) = data.split(';')
            b = Button(self.master, text=name, command = (lambda a = var: function(a)))
    	self.pic=PhotoImage(file=img)
            b.config(image=self.pic, width="160", height="160")
    	b.grid(column=column, row=row,)
    If I add a print statement at the end:

    Code:
    print self.pic
    It just prints "pyimage" 4 times but the last button has the image and works none of the other buttons respond to a press.

    If I comment out the two image lines and add the print statement:

    Code:
    print img
    It prints the name of the image correctly as it is in the sources.

    It has to be the syntax in the line:

    Code:
    self.pic=PhotoImage(file=img)
    That doesn't identify file descriptor properly. I have tried to research but don't find too much on the way to apply the file attribute in the way it is pulled in from the for statement. Thanks again for the assistance.

IMN logo majestic logo threadwatch logo seochat tools logo