Friday, November 18, 2016

Using Task Instead of BackgroundWorker to Update UI

The BackgroundWorker component allows for convenient access to a thread. It supports
  • Reporting Progress
  • Error Handling
  • Returning a Result
  • Cancellation
Updating the UI is a common action in GUI applications that perform a time consuming task. A background worker allows for a simple implementation of updating the UI thread and for the UI thread to control the background worker.

A Task is another way to implement a separate worker thread. I will demonstrate how to transform all the actions performed in a background worker into the context of a Task.

I have used the following resources for Tasks
The example I am using is from the Windows Forms 2.0 book by Chris Sells.

Calculate Pi Example

 The calculations for Pi are a mystery, but they take time.

    void CalcPi(int digits) {
      StringBuilder pi = new StringBuilder("3", digits + 2);

      if( digits > 0 ) {
        pi.Append(".");
     
        for( int i = 0; i < digits; i += 9 ) {
          int nineDigits = NineDigitsOfPi.StartingAt(i + 1);
          int digitCount = Math.Min(digits - i, 9);
          string ds = string.Format("{0:D9}", nineDigits);
          pi.Append(ds.Substring(0, digitCount));
      }
    }

A method encapsulates access to the UI.

    void ShowProgress(string pi, int totalDigits, int digitsSoFar) {
      // Display progress in UI
      this.resultsTextBox.Text = pi;
      this.calcToolStripProgressBar.Maximum = totalDigits;
      this.calcToolStripProgressBar.Value = digitsSoFar;
    }
 
A custom class is used to encapsulate data that is passed to the UI.

    class CalcPiUserState {
      public readonly string Pi;
      public readonly int TotalDigits;
      public readonly int DigitsSoFar;

      public CalcPiUserState(string pi, int totalDigits, int digitsSoFar) {
        this.Pi = pi;
        this.TotalDigits = totalDigits;
        this.DigitsSoFar = digitsSoFar;
      }
    }

Helper methods are used to control modify the appearance of the UI.

        void SetUIReady()
        {
            // Reset progress UI
            this.calcToolStripStatusLabel.Text = "Ready";
            this.calcToolStripProgressBar.Visible = false;
        }

        void SetUIBusy()
        {
            // Set calculating UI
            this.calcToolStripProgressBar.Visible = true;
            this.calcToolStripStatusLabel.Text = "Calculating...";
        }

Starting a Worker Thread that Reports Progress

BackgroundWorker

Handle the DoWork event

    void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) {
        CalcPi((int)e.Argument);
    }

Set  WorkerSupportsProgress property
Handle ReportProgress event

    void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) {
      // Show progress
      CalcPiUserState progress = (CalcPiUserState)e.UserState;
      ShowProgress(
        progress.Pi, progress.TotalDigits, progress.DigitsSoFar);
    }

Call RunWorkerAsync

    void calcButton_Click(object sender, EventArgs e) {
      SetUIBusy();

      // Begin calculating pi asynchronously
      this.backgroundWorker.RunWorkerAsync(
        (int)this.decimalPlacesNumericUpDown.Value);
    }

 Handle RunWorkerCompleted event

    void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
   {
       SetUIReady();  
   }

Modify the CalcPi method so that it reports progress regularly.

    void CalcPi(int digits) {
      StringBuilder pi = new StringBuilder("3", digits + 2);

      CalcPiUserState state = new CalcPiUserState(pi.ToString(), digits, 0);
      // Report initial progress
      this.backgroundWorker.ReportProgress(0, state);

      if( digits > 0 ) {
        pi.Append(".");

        for( int i = 0; i < digits; i += 9 ) {
          int nineDigits = NineDigitsOfPi.StartingAt(i + 1);
          int digitCount = Math.Min(digits - i, 9);
          string ds = string.Format("{0:D9}", nineDigits);
          pi.Append(ds.Substring(0, digitCount));

          // Report continuing progress
          state = new CalcPiUserState(pi.ToString(), digits, i + digitCount);
          this.backgroundWorker.ReportProgress(0, state);

          // Check for cancelation
          if (this.backgroundWorker.CancellationPending) { return; }         
        }
      }
    }

Task

Create a helper method to update the UI from the custom state class.

        void ShowProgressState(CalcPiUserState state)
        {
            try
            {
                // Display progress in UI
                this.resultsTextBox.Text = state.Pi;
                this.calcToolStripProgressBar.Maximum = state.TotalDigits;
                this.calcToolStripProgressBar.Value = state.DigitsSoFar;
            }
            catch (Exception e)
            {
                Console.WriteLine("{0} in ShowProgressState", e.Message);
                throw e;
            }
        }

Create a method with return type Task that encapsulates the work. In order to report progress, create an Progress delegate with an Action that reports progress. CalcPiWithProgress is CPU-bound, so call it with Task.Run. The async keyword in the signature means that await is a keyword in the body of the method.

        async Task DoWork()
        {
            //Any method that is an Action, has one parameter of the gerenic type and returns void.
            Action actionProgress = ShowProgressState;
            //The Action and the Progress must have the same generic type.
            IProgress progress = new Progress(actionProgress);
            await Task.Run(() => CalcPiWithProgress(
                  (int)this.decimalPlacesNumericUpDown.Value, progress)
                );

        }


Start task. The await keyword causes the method to start asynchronously. When the thread completes, the code after the await will be run in the same thread context. The handler does not block: SetUIReady is called, but only after the thread completes, and well after the handler completes.

        async void calcButton_Click(object sender, EventArgs e)
        {
            SetUIBusy();

            // Begin calculating pi asynchronously
            await DoWork();

            SetUIReady();
        }

A special method is not needed to catch the completion of the thread, as with a background worker.

Modify the CalcPi method so it reports progress.

    void CalcPiWithProgress(int digits, IProgress progress,) {
      StringBuilder pi = new StringBuilder("3", digits + 2);

      CalcPiUserState state = new CalcPiUserState(pi.ToString(), digits, 0);
      // Report initial progress
      if (progress != null)
                progress.Report(state);

      if( digits > 0 ) {
        pi.Append(".");

        for( int i = 0; i < digits; i += 9 ) {
          int nineDigits = NineDigitsOfPi.StartingAt(i + 1);
          int digitCount = Math.Min(digits - i, 9);
          string ds = string.Format("{0:D9}", nineDigits);
          pi.Append(ds.Substring(0, digitCount));

          // Report continuing progress
          state = new CalcPiUserState(pi.ToString(), digits, i + digitCount);
          if (progress != null)
                progress.Report(state);  
        }
      }
    }

Catch Exceptions

Background Worker

Test the Error property in the RunWorkerCompletedEventArgs in the RunWorkerCompleted handler.

void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
  
      SetUIReady();

      // Was there an error?
      if( e.Error != null ) {
        this.resultsTextBox.Text = e.Error.Message;
        return;
      }

    }

Task 

With Task, the code after the await is run on the UI thread. The code after the await is executed after the thread completes. The handler would have completed long ago.

Catch the exception and reset the UI.

        async void calcButton_Click(object sender, EventArgs e)
        {
            try
            {
                SetUIBusy();

                // Begin calculating pi asynchronously
                await DoWork();

            }
            catch (Exception ex)
            {
                this.resultsTextBox.Text = ex.Message;
            }
            finally
            {
                SetUIReady();
            }
        }

Return a Result from the Thread 

Background Worker

Set the result of the thread in the DoWorkEventArgs in the DoWork handler.

  void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) { 
      // Track start time
      DateTime start = DateTime.Now;

      CalcPi((int)e.Argument);     

      // Return elapsed time
      DateTime end = DateTime.Now;
      TimeSpan elapsed = end - start;
      e.Result = elapsed;
  }

Retrieve the result from the RunWorkerCompletedEventArgs in the RunWorkerCompleted handler.

void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
  
      SetUIReady();

      // e.Result will cause an error if the thread threw an exception
      if( e.Error != null ) {
        this.resultsTextBox.Text = e.Error.Message;
        return;
      }

      // Show elapsed time
      TimeSpan elapsed = (TimeSpan) e.Result;
      MessageBox.Show("Elapsed: " + elapsed.ToString());
}

Task 

Instead of using Task, use Task. The async command  changes the meaning of the method signature. It does not mean that the method returns Task, it means that the method has a call to await and returns type T.

        async Task DoWork()
        {
            // Track start time
            DateTime start = DateTime.Now;

            //Any method that is an Action, has one parameter of the gerenic type and returns void.
            Action actionProgress = ShowProgressState;
            //The Action and the Progress must have the same generic type.
            IProgress progress = new Progress(actionProgress);
            await Task.Run(() => CalcPiWithProgress(
                  (int)this.decimalPlacesNumericUpDown.Value, progress)
                );

            // Return elapsed time
            DateTime end = DateTime.Now;
            TimeSpan elapsed = end - start;
            return elapsed;
        }


The result will be assigned from the await command.

        async void calcButton_Click(object sender, EventArgs e)
        {
            try
            {
                SetUIBusy();

                // Begin calculating pi asynchronously
                TimeSpan elapsed = await DoWork();

                // Show elapsed time
                MessageBox.Show("Elapsed: " + elapsed.ToString());

            }
            catch (Exception ex)
            {
                this.resultsTextBox.Text = ex.Message;
            }
            finally
            {
                SetUIReady();
            }
        }

Adding the Ability to Cancel

Background Worker


Set the WorkerSupportsCancellation property

Signal that the thread should stop.

void calcButton_Click(object sender, EventArgs e) {
      // Don't process if cancel request pending
      // (Should not be called, since we disabled the button...)
      if( this.backgroundWorker.CancellationPending ) { return; }

      // If worker thread currently executing, cancel it
      if( this.backgroundWorker.IsBusy ) {
        this.calcButton.Enabled = false;
        this.backgroundWorker.CancelAsync();
        return;
      }

      SetUIBusy();

      // Begin calculating pi asynchronously
      this.backgroundWorker.RunWorkerAsync(
        (int)this.decimalPlacesNumericUpDown.Value);
    }

The thread monitors the flag for cancellation. When requested, the thread stops working.

  void CalcPi(int digits)
  {
      StringBuilder pi = new StringBuilder("3", digits + 2);

      CalcPiUserState state = new CalcPiUserState(pi.ToString(), digits, 0);
      // Report initial progress
      this.backgroundWorker.ReportProgress(0, state);

      // Check for cancelation
      if (this.backgroundWorker.CancellationPending) { return; }

      if( digits > 0 ) {
        pi.Append(".");

        for( int i = 0; i < digits; i += 9 ) {
          int nineDigits = NineDigitsOfPi.StartingAt(i + 1);
          int digitCount = Math.Min(digits - i, 9);
          string ds = string.Format("{0:D9}", nineDigits);
          pi.Append(ds.Substring(0, digitCount));

          // Report continuing progress
          state = new CalcPiUserState(pi.ToString(), digits, i + digitCount);
          this.backgroundWorker.ReportProgress(0, state);

          // Check for cancelation
          if (this.backgroundWorker.CancellationPending) { return; }         
        }
      }
    }

In the RunWorkerCompleted handler check the Cancelled property in the event args.

void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
      // Reset progress UI
      this.calcButton.Text = "Calculate";
      this.calcButton.Enabled = true;
      this.calcToolStripStatusLabel.Text = "Ready";
      this.calcToolStripProgressBar.Visible = false;

      // Was the worker thread cancelled?
      if (e.Cancelled)
      {
          this.resultsTextBox.Text = "Cancelled";
      }
      else
      {
          // Show elapsed time
          TimeSpan elapsed = (TimeSpan)e.Result;
          MessageBox.Show("Elapsed: " + elapsed.ToString());
      }

Task


As the thread is asked to respond in more ways, it is useful separate the actions performed when the thread ends. As part of the Task class, use the Continue with to specify a method that has the format for Action. Action, Func and Func can also be configured. If the UI will be accessed, then continue from the current context.

        async void calcButton_Click(object sender, EventArgs e)
        {

            SetUIBusy();

            // Begin calculating pi asynchronously
            await DoWork().ContinueWith(WorkCompleted,
                TaskScheduler.FromCurrentSynchronizationContext());

        }




Many states of the task can be inspected. This version does the same as the earlier version of the application. Next, cancellation will be added.

        private void WorkCompleted(Task task)
        {
            switch (task.Status)
            {
                case TaskStatus.Canceled:
                    break;
                case TaskStatus.Faulted:
                    this.resultsTextBox.Text = task.Exception.ToString();
                    break;
                case TaskStatus.RanToCompletion:
                    MessageBox.Show("Elapsed: " + task.Result.ToString());
                    break;
                default:
                    break;
            }
            SetUIReady();
        }


For cancellation, CancellationTaskSource is used. It contains a token that can be set. The token can be monitored by the thread.

        CancellationTokenSource tokenSource;
        CancellationToken token;

        public MainForm() {
            InitializeComponent();
            tokenSource = new CancellationTokenSource();
            token = tokenSource.Token;
            SetUIReady();
        }

A new helper will be added to report progress and check for cancellation, with the ThrowIfCancellationRequested() method of the token.

        void ProgressTestCancel(
            IProgress progress, CancellationToken token, CalcPiUserState state)
        {
            // Report progress
            if (progress != null)
                progress.Report(state);
            token.ThrowIfCancellationRequested();    
        }


The worker thread reports progress and tests for cancellation at regular intervals.

    void CalcPiWithProgress(
          int digits, IProgress progress, CancellationToken token) 
   {
      StringBuilder pi = new StringBuilder("3", digits + 2);

      CalcPiUserState state = new CalcPiUserState(pi.ToString(), digits, 0);
      // Report initial progress
      ProgessTestCancel(progess, token, state);

      if( digits > 0 ) {
        pi.Append(".");

        for( int i = 0; i < digits; i += 9 ) {
          int nineDigits = NineDigitsOfPi.StartingAt(i + 1);
          int digitCount = Math.Min(digits - i, 9);
          string ds = string.Format("{0:D9}", nineDigits);
          pi.Append(ds.Substring(0, digitCount));

          // Report continuing progress
          state = new CalcPiUserState(pi.ToString(), digits, i + digitCount);
          ProgessTestCancel(progess, token, state); 
        }
      }
    }

Pass the token into the method that does the asynchronous work. The task will be needed in other parts of the application to improve the interaction of the UI, so the task is added as an instance variable. Does this remind you of background worker???

         //Needed in order to test if Task is busy in other methods
        Task piTask = null;
        async Task DoWork()
        {
            // Track start time
            DateTime start = DateTime.Now;

            //Any method that is an Action, has one parameter of the gerenic type and returns void.
            Action actionProgress = ShowProgressState;
            //The Action and the Progress must have the same generic type.
            IProgress progress = new Progress(actionProgress);
            piTask = new Task(() =>
                 TimePiThread(
                    (int)this.decimalPlacesNumericUpDown.Value, progress, tokenSource.Token)
                 , tokenSource.Token);
            piTask.Start();
            return await piTask;
         
        }


The button handler will have two possible states: ready to run, ready to cancel. Update the UI based on the state of the task. If the task is running, cancel it on click.

        async void calcButton_Click(object sender, EventArgs e)
        {
            // Don't process if cancel request pending
            // Should not happen, since button will be disabled
            if (this.tokenSource.IsCancellationRequested) { return; }

            if (piTask != null && !piTask.IsCompleted)
            {
                this.calcButton.Enabled = false;
                tokenSource.Cancel();
            }
            else
            {

                SetUIBusy();

                // Begin calculating pi asynchronously
                await DoWork().ContinueWith(WorkCompleted,
                    TaskScheduler.FromCurrentSynchronizationContext());
            }

        }

The helpers for updating the UI need modification, too:

        void SetUIReady()
        {
            // Reset progress UI
            this.calcToolStripStatusLabel.Text = "Ready";
            this.calcToolStripProgressBar.Visible = false;
            this.calcButton.Text = "Calculate";
            this.calcButton.Enabled = true;
        }

        void SetUIBusy()
        {
            // Set calculating UI
            this.calcToolStripProgressBar.Visible = true;
            this.calcButton.Text = "Cancel";
            this.calcToolStripStatusLabel.Text = "Calculating...";
        }

Conclusion

For updating the UI, I still like background worker. In large part, this is due to my understanding of background worker. Task is new to me. It has more power than background worker for other tasks, but background worker is tailored for interacting with the UI. While writing this article, I was frustrated multiple times with the syntax of lambda functions and the .NET delegates. Background worker hides the details, but allows all the necessary features for interacting with the UI.

Wednesday, September 14, 2016

Using GitHub for Homework Submissions

I teach a course in Windows programming. I use Visual Studio. All programming projects are team projects. I have eight teams, with a repository for each team.

Each group in my class has four or five students. Four seems to be the best number, since it is harder to arrange a meeting for five people. For each group, I created a team in GitHub.

Each student must create a GitHub account and send it to me, then I invite them to the team.

I created eight private repositories in GitHub, so only an authorized team has access. The repository is for all the homework assignments. Each repository has one team assigned to it.

The master branch in the repository only has a README file. The file indicates that each homework assignment will be in a separate branch. I populate the branch with a Visual Studio solution.

Once the student, X, has accepted the invitation to the team, X will be able to view the repository in GitHub. The repository has a button for "Clone or Download". Clone is the preferred method. The URL to use to clone the repository is displayed. X should copy the URL and open Visual Studio.

In Visual Studio, X should open Team Explorer. Look for the icon that looks like a plug. It is for Connections. Open it and look under Local Git Repositories. Click the Clone drop-down indicator. Paste the URL from GitHub and click Clone.

After the repository has been downloaded, click the Home icon to go the home screen. On the home screen, click the Branches link. Near the bottom of the screen are the Active Git Repositories. Open the remotes/origin link. Select the branch for the current homework.

Once the correct branch is selected, return to the Home screen. At the bottom, under solutions, should be the homework solution. Double click it and the solution files will be opened in Visual Studio. Open Solution Explorer and start modifying the file.

After making edits, return to Team Explorer and select Changes. Commit All the changes and Push at the same time.

Before starting a new editing session, open Team Explorer, select the solution, and Sync to the remote server.

To populate with an existing solution,
In GitHub,
  1. Create a branch for the assignment in the repo on GitHub
In VS,
  1. Create a new local repo. 
  2. In the local repo settings, set the remote address to the clone address for the remote repo.
  3. Return to Home and select Sync
  4. Perform a fetch to get the hw branch.

The Open solution option in the local repo will only work if the repo is empty. To circumvent,
  1. Open File Explorer and navigate to the repo folder. 
  2. Copy the initial folder that contains the solution into the folder.

Back in VS, the solution will now appear in the local repo.
  1. From Home, select Changes and Commit and Push to commit the .git files
  2. Double-click the solution to open it.
  3. From Home, select Changes and Commit and Push to commit the .git files

Later, I wanted to update the repository with the next version of the submitted homework, since not all students chose to use the repository. Many changes had been made since the initial contents.

I created a branch in GitHub for me from the initial state and sync'd it in VS. Then I opened the new solution in VS. From Home I viewed Changes and committed and pushed to GitHub. In GitHub, I performed a pull request into the original branch from the new branch. No conflicts were present, so the merge was possible. 

Monday, August 29, 2016

Yii User Setting isAdmin

The Yii framework has CUserIdentity as the basis for logging into an application.

It is possible to add state to the class, so an identity can be tested easily; for example, testing if a menu option should displayed for the current user.

array(
    'label'=>'List OfficeVisit', 
    'url'=>array('index'), 
    'visible'=>yii::app()->user->isAdmin)

The CUserIdentity class has support for adding state with setState. The state can be retrieved with getState, or by accessing the state directly as a property.

In the UserIdentity class in the application, which extends CUserIdentity, add the following in the authenticate method, after authenticating:

if ($this->username==='dbadmin') {
     $this->setState('isAdmin', true);

}

The state can be accessed later with either:

yii::app()->user->getState('isAdmin')

or

yii::app()->user->isAdmin

Refer to http://www.yiiframework.com/wiki/6/how-to-add-more-information-to-yii-app-user/ for more information.

 

Thursday, February 4, 2016

New Roulette

Here is a new roulette game.
  1. Everyone has an initial stake that can be any amount of money.
  2. For each spin, you can play or you can watch.
  3. If you play, you must wager 1% of your stake. Watching is free.
  4. A number is chosen for each player and watcher; no two players or watchers have the same number.
  5. The roulette wheel has exactly enough numbers for all the players and watchers.
  6. After each spin, all players and watchers win %100 of their stake.
Here is how you increase your stake:
  1. The roulette wheel is spun.
  2. If the ball lands on the number of a watcher, no ones stake increases.
  3. If the ball lands on the number of a player, then the stake for each player and watcher is raised by an amount between 1% and 2.5%.
  4. Any increase is to the stake for all players and watchers for this spin and all future spins. The longer you play or watch, the bigger your stake becomes.
Would you play or watch?

The name of the game is The Power of a Union in a Right-to-work State.

Friday, January 22, 2016

Installing Python3 on Mac OSX

I have python 2.6 installed on my Mac.

I already had homebrew installed for perl.

I used the command

brew install python

and python 2.7 was installed. The bash profile command was altered to include the new python path at the start of the path command.

PATH="/Library/Frameworks/Python.framework/Versions/2.7/bin:${PATH}"

Install Python 3

Next, I tried to install python 3.

brew search

The name of the package is python3.

brew install python3

This was the first error:


C compiler cannot create executables

I had read a comment earlier about installing Xcode. I already had Xcode. Another comment was to enable command line tools. I did not know if they were already enabled. They weren't.

xcode-select --install

After this command and install, I was able to get further, but the install failed. After error

Need 'brew link xz'

Typed the command

brew link xz

and received an error. So, I ran

brew doctor

I changed the permission on a file. I was able to continue. python 3 is now installed in /usr/local/bin/python3

Install Virtual Environment 

I accessed Python Virtual Environment and learned about virtual environments.
 

Next, I created a virtual environment with the new python.

virtualenv -p python3 python3env

After installing, switch to the folder and activate the environment.

source bin/activate

Now, the path has the local python as the default python.

The virtual environment is alright, but I am limited to a text editor and console window.

Install Eclipse IDE

An IDE with code completion and syntax highlighting is preferable. 

Next, I installed Eclipse and added the PyDev plugin. I installed Eclipse for Java, since no option existed for python.

I followed the instructions at http://www.pydev.org/manual_101_install.html. I was required to accept a certificate that was not mentioned in the instructions.

For Mac, the preferences are not under the window menu, but in the usual preferences under Eclipse in the menu strip.

Next, I followed the instructions for creating a project. The interpreter was the default 2.7, not the new 3.5. I added the new interpreter to the preferences.

The tutorial is for 2. I am using 3, so tutorial code must be modified.

Install NetBeans IDE


Next, I checked to see if NetBeans had a plugin for python. NetBeans 8.1 has a python package that can be installed easily, 8.02 requires some worked. I upgraded to 8.1.

The default python was for jython in NetBeans. I added a reference to my python 3.

The default template for creating a module used python 2 syntax. The template for creating a package used python 3 syntax. 

Update

https://opensource.com/article/19/5/python-3-default-mac

use pip to install modules







 



Followers