pythontkintertwisted

tkinter buttons send twisted message only once


I'm writing a simple application that will play a wav file on one computer based on the message received from the other. I'm using twisted for connection and tkinter for simple gui with buttons.

I'm experienced with neither. As far as I am aware server is working fine when i was using just twisted and sending messages from command line input it was working as expected.

Now that I added gui when I run it both buttons commands execute automatically and they execute once after that i can click them all I want and nothing happens.

Code for client:

from twisted.internet.protocol import Protocol, Factory
from twisted.internet.endpoints import TCP4ClientEndpoint
from twisted.internet import tksupport, reactor, protocol
from tkinter import *


class UI:
    def __init__(self, master):
        # set up start of screen
        self.root = master
        # set up frame
        self.frame = Frame(self.root, width=80, height=50)
        self.frame.pack_propagate(0)
        self.frame.pack()
        #set up buttons
        self._20 = Button(self.frame, text="20", command=self.send_message("20"))
        self._20.pack(side=LEFT)
        self._30 = Button(self.frame, text="30", command=self.send_message("30"))
        self._30.pack(side=RIGHT)

    def send_message(self, message):
        point = TCP4ClientEndpoint(reactor, "localhost", 2000)
        d = point.connect(GreeterFactory())
        d.addCallback(lambda p: p.sendMessage(message))
        d.addCallback(lambda p: p.transport.loseConnection())

class Greeter(Protocol):
    def sendMessage(self, msg):
        self.transport.write(msg.encode('utf-8'))

class GreeterFactory(Factory):
    def buildProtocol(self, addr):
        return Greeter()

# start UI        
root = Tk()
root.tk_bisque()
root.title('Q')
root.resizable(width=FALSE, height=FALSE)
ui = UI(root)
tksupport.install(root)
reactor.run()

I expect that after each button press my server will receive message assigned to each button


Solution

  • The thing you pass to command= needs to be a function. You are not passing a function, you are CALLING a function and passing whatever it returns as the command.

    So, your code will call send_message immediately, during UI.__init__. send_message returns None, so you end up passing command=None.

    You can use lambda to defer the function call:

            #set up buttons
            self._20 = Button(self.frame, text="20", command=lambda: self.send_message("20"))
            self._20.pack(side=LEFT)
            self._30 = Button(self.frame, text="30", command=lambda: self.send_message("30"))
            self._30.pack(side=RIGHT)