views:

25

answers:

1

I have a problem that I have not faced before:

It seems that the order of interpretation in my program is somehow different from what I expect. I have written a small Twitter client. It takes a few seconds for my program to actually post a tweet after I click the "GO" button (which can also be activated by hitting ENTER on the keyboard). I don't want to click multiple times within this time period thinking that I hadn't clicked it the first time. Therefore, when the button is clicked, I would like the label text to display something that tells me that the button has been clicked.

I have implemented this message by altering the label text before I send the tweet across. However, for some reason, the message does not display until the tweet has been attempted. But since I have a confirmation message after the tweet, I never get to see this message and my original problem goes unsolved.

I would really appreciate any help. Here is the relevant code:

class SimpleTextBoxForm(Form):

  def __init__(self):

    # set window properties

    self.Text = "Tweeter"
    self.Width = 235
    self.Height = 250

    #tweet away

    self.label = Label()
    self.label.Text = "Tweet Away..."
    self.label.Location = Point(10, 10)
    self.label.Height = 25
    self.label.Width = 200

    #get the tweet

    self.tweetBox = TextBox()
    self.tweetBox.Location = Point(10, 45)
    self.tweetBox.Width = 200
    self.tweetBox.Height = 60
    self.tweetBox.Multiline = True
    self.tweetBox.WordWrap = True
    self.tweetBox.MaxLength = 140;

    #ask for the login ID

    self.askLogin = Label()
    self.askLogin.Text = "Login:"
    self.askLogin.Location = Point(10, 120)
    self.askLogin.Height = 20
    self.askLogin.Width = 60

    self.login = TextBox()
    self.login.Text= ""
    self.login.Location = Point(80, 120)
    self.login.Height = 40
    self.login.Width = 100

    #ask for the password

    self.askPass = Label()
    self.askPass.Text = "Password:"
    self.askPass.Location = Point(10, 150)
    self.askPass.Height = 20
    self.askPass.Width = 60

    # display password box with character hiding

    self.password = TextBox()
    self.password.Location = Point(80, 150)
    self.password.PasswordChar = "x"
    self.password.Height = 40
    self.password.Width = 100

    #submit button

    self.button1 = Button()
    self.button1.Text = 'Tweet'
    self.button1.Location = Point(10, 180)
    self.button1.Click += self.update

    self.AcceptButton = self.button1

    #pack all the elements of the form

    self.Controls.Add(self.label)
    self.Controls.Add(self.tweetBox)
    self.Controls.Add(self.askLogin)
    self.Controls.Add(self.login)
    self.Controls.Add(self.askPass)
    self.Controls.Add(self.password)
    self.Controls.Add(self.button1)

  def update(self, sender, event):

    if not self.password.Text:
        self.label.Text = "You forgot to enter your password..."
    else:
        self.tweet(self.tweetBox.Text, self.login.Text, self.password.Text)

  def tweet(self, msg, login, password):

    self.label.Text = "Attempting Tweet..."  # this should be executed before sending the tweet is attempted. But this seems to be executed only after the try block

    try:
        success = 'Tweet successfully completed... yay!\n' + 'At: ' + time.asctime().split()[3]

        ServicePointManager.Expect100Continue = False
        Twitter().UpdateAsXML(login, password, msg)

    except:
        error = 'Unhandled Exception. Tweet unsuccessful'
        self.label.Text = error

    else:
        self.label.Text = success
        self.tweetBox.Text = ""
+2  A: 

The interpretation/execution order is correct, but what's happening is that you're not giving Windows Forms time to action the label text change before going into your blocking Web request. Basically setting the label.Text posts a message to the form requesting that the text be changed, but it won't be actioned until you return from the event handler (and the form starts pumping messages again). At the moment, you're not returning from the event handler until the Web request is finished, at which point of course (a) the message is irrelevant and (b) the label.Text instantly gets overwritten by the next queued-up update.

The solution is to do your Web service request in a background thread, for example by using the BackgroundWorker component. This allows the form to start pumping messages again immediately, while the background thread runs, so the label will be updated immediately. Note however that this also means the rest of the form will be responsive, which means the user could push the button again, so you may want to disable things as well as just updating the status.

itowlson
By the way, a simpler, but kludgy solution is to call `Application.DoEvents` after setting `label.Text`. This pumps the window's remaining messages -- in this case the "set label text" message. Application.DoEvents is widely regarded as a sign of hacking around proper threading, but for a proof of concept or a hobby project it may be an acceptable way to avoid the extra work of handling multiple threads.
itowlson
This IS a hobby project so I chose Application.DoEvents and it works like a charm. Thank you
inspectorG4dget