Friday, April 25, 2014

Lloyd's Video Analysis Tool, Part 3: Building a Log of Video Clips

OK, now that we have a command of some of the basics of using video within LiveCode, let's begin to explore how to build some custom features for the video analysis tool. A key element of the prototype of my video analysis tool is giving the user a way to log a series of video clips. Then, each clip can be played back, tagged, and commented on.

Here's a screen shot of the LiveCode file we will be building:


[ Get the free LiveCode Community version. ]
 


As you can see, I widened the stack (to 1024 pixels, the landscape width of an iPad) and I moved the elements from the previous version toward the left. On the right is a scrolling list field that contains an ever-growing list of video clips. The idea is to let the user define a starting and ending point for as many clips as they wish. Each can then be played back at will.

Let's start by adding a scrolling list field on the right hand side of the card and title it "log." Be sure it is the "scrolling list field" and not a "scrolling field." Hover and pause over the various list objects in the tool palette to see the object names appear. I'll explain later the difference

Next, add a button titled "New Entry" and add this code to it:

on mouseUp
   put the number of lines in field "log" into l
   //start of video snippet
   put the currentTime of player "player" into item 1 of line l+1 of field "log"
   //end of video snippet
   put the timescale of player "Player" into ts
   put the currentTime of player "player" +(ts*5)  into item 2 of line l+1 of field "log"
end mouseUp

The first line looks at how many lines in the field "log" have content and puts that value into a local variable "l" (short for log). When the field empty, the result will be 0. Our goal will be to add content for the next clip after that line.

Next we need to define a video clip. We will take what we learned in the previous blog post to build the starting and ending points of the clip. The third line notes the current frame of the video player and then puts this as the first item of the first empty line of field "log" (i.e. l+1). I want the default length of the video clip to be 5 seconds. To do this, I must first check to see what the timescale of the movie is, and line 5 accomplishes this and puts the value in a local variable "ts." Line 6 multiples ts by 5, adds this amount to the current position of the video playhead (as denoted by the property currentTime), then puts the result also into line l+1, but as item 2 (LiveCode will automatically add a comma to partition the two items).

Great! We now have a starting and ending point for the video clip. Try it out by moving the playhead to different locations and pressing the "New Entry" button. It should add a starting point at the position of the playhead and an ending point five seconds later. A new video snippet will be added to each successive line in the field.

I think it would be useful to add a button that clears out all the log entries. This will be particularly helpful as we test out the prototype. Add a button called "Clear Log" with this one line of code:

on mouseUp
   put empty into field "log"  
end mouseUp

Notice that I put this button in the upper right hand portion of the screen. I didn''t want it next to the other log buttons so as to avoid inadvertently pressing it and deleting all the entries by mistake.

Let's now create a button that plays that video snippet. Title it "Play Entry" and place it just below the "New Entry" button. Here is the code for it:

on mouseUp
   set the playSelection of player "player" to true
   set the startTime of player "player" to item 1 of the selectedtext of field "log"
   set the endTime of player "player" to item 2 of the selectedtext of field "log"
   start player "player"
end mouseUp

The first line is important because the property "playSelection" is set to false by default. So, what is this property? If it is true, then the player will start playing the video and not stop until the end of the entire movie has been reached. Of course, we want it to stop when the playhead reaches the final frame of the clip. Note that you can change this property also by going to the basic properties page of the player object's inspector (i.e. "Play selection only"), but I think it's preferable to code this directly into the button.

The next two lines set two important player properties. As their names suggest, each refers to the starting and ending position of a video (or audio) clip. Therefore, we set the property "startTime" to item 1 of the respective line of the log field, and "endTime" to item 2.

Take a few minutes now to add three or four log entries. Each will be five seconds in length. Click on the respective line in the log field and press the "Play Entry" button. That entry should play and end in five seconds.

Please also take a moment now to recognize the difference between a scrolling list field and a regular scrolling field. When the user clicks a line in a scrolling list field, the entire line is selected, as compared to placing the cursor at a certain location within that line. This difference allows us to use the function "selectedtext"to select the entire line. A subtle, but important and very useful, difference.

Giving Users the Option to Update an Entry


All is working great, but obviously a five second clip will unlikely be satisfactory to the user. So, the next step is to give the user the option to update the beginning or ending points of the clip. The design is straightforward. Have the user click on an entry and give them the option to move the playhead anywhere they want, then give them the buttons "Update Start" or "Update End" to update the clip. Let's add both of these buttons now.

Create a button named "Update Start" and add this script to it:

on mouseUp
   put the hilitedLine of field "log" into l
   put the currentTime of player "player" into ct
   //error trap - starting point must be before ending point
   if ct > item 2 of line l of field "log" then
      answer error "Not Allowed! Starting point must come before the ending point!" with "OK"
      exit mouseUp
   end if
   put ct into item 1 of line l of field "log"
   set the hilitedLine of field "log" to l
end mouseUp

Line 1 uses a cool property called "hilitedLine." As the name suggests, this property takes note of what line has just been highlighted in a text field. Line l puts this line number into a local variable "l" (this is a lower case "L" remember, not the number one).

Line 2 notes the current position of the video playhead and puts it into a local variable "ct." (Recall that the user would have moved the playhead before clicking this button.)

The next several lines do some error trapping, as we humans are very prone to errors. We'll come back to these in a moment.

Let's jump to line 8. This line puts the value of "ct" into the selected line of field "log," replacing whatever value was already there. Well, of course, the idea of this button is to update the entry after all.

The last line highlights the line in the log we just edited. This is important from a user design standpoint. Without this line, the user would have to again click on that line if they wanted to make another update. I think it's easy to imagine the user making lots of updates in a row until they get things "just right." (Tha'ts what I found myself doing as I tried out the prototype.) So, this line highlights the line for them.

OK, what about that error trapping? Think about it for a moment. We have to ensure that the starting point of the video clip is a number less than the ending point of the clip. So, line 4 starts an if/then statement to check this. If "ct" (the new position of the videohead) is greater than the ending point of the video clip, then that's a problem. So, LiveCode pops up with a message saying something to the effect that you can't do that, using the handy "answer" command. The next line - "exit mouseUp" terminates button's script, so nothing else happens. (Alas, error trapping is a very time-consuming, but necessary part of all software design. One has to be very cunning to determine where human error is likely to occur. This is very hard to predict and is one of the valuable outcomes of extensive field testing.)

Next, add another button and title it "Update End." As you might suspect, the script for this button is very similar to the button "Update Start":

on mouseUp
   put the hilitedLine of field "log" into l
   put the currentTime of player "player" into ct
   //error trap - ending point must be after ending point
   if ct < item 1 of line l of field "log" then
      answer error "Not Allowed! Ending point must come after the starting point!" with "OK"
      exit mouseUp
   end if
   put ct into item 2 of line l of field "log"
   set the hilitedLine of field "log" to l
end mouseUp

In fact, you could just copy and paste the "Update Start" button and make just a few, small (but critical) revisions. For example, in line 8, the only revision is changing item 1 to item 2.

The only other changes are in the error trapping. In this case, we have to be sure that the ending point comes after the starting point. Here is a quick summary of the changes to these lines of code:
  • Change the "greater than" sign to a "lesser than" sign;
  • Change "item 2" to "item 1"; and
  • Change the text of the message as needed.
Finally, I added a blue rectangle to more appropriately show this "editing zone." To move the rectangle "to the back" go to the "size and position" page of the property inspector and change its layer.

Revising the +10 and -10 Second Editor


Let's do one more thing. Recall from my previous blog posting that we created two buttons that advanced or rewound the playhead by 10 seconds. An increment of 10 seconds was a good choice there to demonstrate this feature, but it really is not a useful increment for "fine tuning." So, let's revise those two buttons so that the increment is only one second. (One could argue that even a smaller increment, such a half or tenth of a second, would be even better.) Making this revision is a snap.

Let's take a look at the existing script for the "-10 seconds" button:

on mouseUp
   put the timescale of player "Player" into ts
   put the currentTime of player "Player" into t
   put t-(ts*10) into t
   set the currentTime of player "Player" to t
end mouseUp

All we need to do is change the 10 to a 1 in line 3. (You might also argue that we could just delete the "*10" altogether, but I don't recommend that because we might find ourselves coming back and changing this later to a different value, such as .5.

Do the same thing for the "+10 seconds" button and, of course, don't forget to change the button titles.

Final Thought


My final thought is simply "Hey, pretty cool!" We have a good start to an interesting video analysis tool.

In my next post, I'll focus solely on how to convert or transform LiveCode's time code intervals to something we humans can better understand, such as hours:minutes:seconds.













Wednesday, April 23, 2014

Lloyd's Video Analysis Tool, Part 2: Some Video Basics

In this post I'll explore a few of the basics of bringing video into a LiveCode project. Here is screen shot of the stack we'll build:

[ Get the free LiveCode Community version. ]

 

You'll obviously need your own movie to use in this project.

The first step couldn't be easier. Just drag a player object onto a card:



The player object's name defaults to "Player," but feel free to rename this as you wish. (But, note that we will be referring to the player object's name in the scripting below, so decide on the name now.) I'll leave it named as "Player." A good next step is to explore the properties of the player object by browsing the various pages of the object's inspector window.

Source video files can either be stored on your computer, or you can enter the URL of the source video. There are some limitations on what video file types can be used. According to the LiveCode manual, video is limited to QuickTime, AVI, or MPEG file types. So, unfortunately and much to my disappointment, flash video is not a supported format (and hence, one cannot use YouTube videos.)

For many projects, this is all you need to know! The player object will now play your video and it includes all of the expected video options, such as play, pause, step forward and backward, audio control, and user control of the playhead.

Note: If you choose to resize your video, be sure to check the box beside "lock size and position" inside the size and position properties page. Otherwise, it will appear full size the next time you open the file.

Video-Related Scripting


To create a more sophisticated video application, such as the video analysis tool I have in mind, we need to explore how to script various video functions. Let's explore just a few of the most basic here. A good place to start is scripting our own play and stop buttons. The scripts for each button are very simple:

on mouseUp
   start player "Player"
end mouseUp

on mouseUp
   stop player "Player"
end mouseUp

In my earliest Video Analysis Tool prototype, I've already included the option to move the video playhead forward or back by one second. Let's explore how to do that as our next script. Instead of one second, let's use an increment of 10 seconds in order to really notice the playhead skipping ahead or back.

To do this, we first have to note where the playhead is currently in the video. Then, we'll just add or subtract 10 seconds worth of video and jump to that new spot. Let's say we stopped the video one minute and 15 seconds into the video. LiveCode does not "see" the video in seconds or minutes. Instead, it will play a particular video in a certain number of intervals per second. This interval will vary from video to video. Fortunately, we can use the timeScale property to learn what this interval is. Let's open the Message box (an option under "Tools") and type in this script:

   put the timescale of player "Player"

After you press Return, a number will appear in the message box. For the video I'm using, the number is 90,000. Yours will most likely be different. So, this means that for my video, the time scale of my video is 90,000 intervals per second. (An amazingly large number, in my opinion, and I freely admit I'm not sure why it is not something more standard like 30 or 24. I'm sure I'll learn about this eventually.) Let's use this information to script out a button titled "+10 seconds" that will move the playhead forward 10 seconds:

on mouseUp
   put the timeScale of player "Player" into ts
   put the currentTime of player "Player" into t
   put t+(ts*10) into t
   set the currentTime of player "Player" to t
end mouseUp

I'm using two local variables here: ts (short for timeScale) and t (short for time).

The first line puts the timeScale property of the movie into ts. In my case, it will be 90,000. So, each second will last for 90,000 intervals. The second line notes what is the current position of the playhead in the video using the "currentTime" property and puts that value into t. Line 3 does a little bit of arithmetic. First, it multiplies ts by 10, so that we will have an interval of 10 seconds. Next, it adds that interval to t. Finally, we set the currentTime of the player to t, which of course is 10 seconds ahead.

To create a button that will skip backwards 10 seconds, let's start by copying and pasting the "+10 seconds" button and title it "-10 seconds," and then change the plus sign in line 3 to a minus sign:

   put t-(ts*10) into t

This is a good start to learning how to control video with LiveCode scripts. Don't worry, much more is to come.

If you are ready to learn more about working with video in LiveCode, here is a good LiveCode tutorial to check out. I found it very helpful to begin to learn how to script video options and techniques for me, even though I didn't actually built the project shown in the tutorial. Basically, it helped push me in the right direction.

Saturday, April 19, 2014

Lloyd's Video Analysis Tool, Part 1

One of my favorite sayings these days when I talk about teaching and technology is "video is the new PowerPoint." That is, the ability to create and use video for teaching is about as easy as it was to first start using PowerPoint when it became available back in 1990. In fact, given how long PowerPoint has been around, it's hard to remember that it too had a learning curve like any other software tool. Similarly, the ability to create, edit, and -- most important -- distribute video has become very routine for many teachers. I do think that the ease of distribution, thanks mostly to YouTube, has been the true game changer. After all, we've had the ability to easily create and edit homemade videos for many years, but the tough nut to crack was how to share the video with others.

Video has also become an increasingly popular tool and approach over the past 20 years or so for educational researchers. It's very difficult to observe and notice the rich and complex information within a classroom or other learning environment in real time. Recording the event to review later when there is enough time -- and the opportunity to rewind -- makes a lot of sense. Video has also become an excellent way for teachers to reflect and get feedback on ways to improve their teaching. Video recordings of one's teaching are also becoming a requirement or condition for certification in many states, especially with the advent of edTPA. These teacher certification requirements have been the source of many conversations between myself and teacher education faculty at the University of Georgia. Many of these faculty not only want to learn how to meet the requirements of edTPA, but also how to use video as the means of providing formative feedback to pre-service teachers. These conversations inevitably end with the question "Is there a video analysis tool out there we can use to give our candidates quick and useful feedback on their teaching?"

Fortunately, there is a commercial tool available that is also very affordable. The product is called VideoWurks and it is produced by a company named Instructive Insight. The tool was originally created at the University of Georgia under the leadership of Dr. Art Recesso. I highly recommend taking a look at this tool. I think many teacher educators will find it meets their needs quite well. I also just learned from the Chronicle of Higher Education's Profhacker blog of a new online tool called VidBolt that allows you to annotate videos on the fly with some very cool community features.

But, I have found few other video analysis tools for educational purposes available (my google search did find some for analyzing sports-related activities, such as baseball and golf swing analyzers). So, I have begun to create my own video analysis tool with LiveCode. Before starting this project, I had only explored a minimal amount of LiveCode's ability to work with video. So, I think this is a perfect project for learning more.

I have invested about seven hours in the prototype so far, with -- believe it or not -- three of these devoted to graphic design and interface design. (Despite what the design literature says and my own experience, it always surprises me how much time is devoted to user design issues.) Here is a YouTube demonstration of the current prototype:


I'm sure you are thinking, "Man, this prototype has a long way to go!" Well yes, yes it does! You might also doubt that I've spent three hours on its graphic/interface design. So, for comparison purposes,  here is a screen shot of the very first version of this prototype:


See what I mean?

I'm particularly interested in designing this tool to be very simple to use, to the point of using technologies or strategies that might be considered out-of-date. For example, I have this idea that it would be great to have the output of the video analysis be a simple text file that could be easily shared between candidate and professor, something each could even send to the other within the body of an email. (I don't know about you, but I have found myself organizing and archiving my professional "life" with email, particularly in my ability to search for past emails of important events and activities.) If you've read any of my other posts, you know I'm also very fond of off-loading difficult tasks to other applications whenever possible, such as Excel. So, my design here is again based on creating a comma-delimited text field with all of the time code, tags, and comments for each of the video log entries for each sharing and importing within tools like Excel.

I hope this post gave you a good overview of the project at its earliest point. I'm deliberately not giving any attention to LiveCode here, but instead I will be writing many more posts, each with a focus on some interesting LiveCode programming detail, all in the spirit of helping others begin their own video projects.

Postscript: People have asked me to explain a bit about the video I used in my prototype. This video is a short "mash-up" of video snippets from my MOOC "Statistics in Education for Mere Mortals" offered by Canvas.net. The current course section is about to end, but I plan to offer it again with a starting date tentatively set for July 7, 2014. And, you can access all of the course videos on YouTube - here is a link to the playlist (the mash-up is the first in the list).







Tuesday, March 4, 2014

EventTracker: Creating a Chess Clock for Faculty Meetings and Other Events

Being a university professor, I sit through a lot of meetings. It is not unusual for one or two people to dominate a meeting, but to be fair I really don't think most of the time they know they're doing it. But, sometimes I wish each person at the meeting had one of those timers that chess players use. You know, the one with a clock for each player that tracks how much time each is taking during the match. Wouldn't it be great, I think to myself as I sit there, if everyone at the meeting had to start a clock each time they started to speak? Then, at the end of the meeting we could tell how much time each of us spent talking. Hey, maybe it would encourage a little more listening!

This little daydream has inspired my next LiveCode project - an app that allows someone to document who is at a meeting, when and how often they talk, and how long they talk.

Here's a description of the basic design. You show up at a meeting, take a look around the room, and quickly enter everyone's name into a text field. Most meetings I attend have about 10-15 people, so this wouldn't take very long. Then, in the blink of an eye, a set of unique buttons appears on the screen, one with each person's name on the list. Someone else quickly shows up at the last minute? Just add their name to the list and tap the create button again to update. You also have the option to sort the list.

Then, during the meeting, as different people speak, you just tap the button with their name and their name and a date/time stamp are added to a text field. You can also edit the text field if you like, such as by adding some comments to the end of any of the text entries.

When the meeting is over, you can generate a quick report of who spoke how often and for how long -- just summative stuff. But, you could also copy all of the raw data and paste it into Excel or SPSS for a more sophisticated analysis.

Of course, a tool like this could be used for all sorts of things, such as the following:
  • Sports: Which side controls the ball during a basketball game and for how long?
  • Nature: Which birds you see at a bird feeder during the day and how long they feed?
  • Politics: Which panelists or guests on a Sunday morning news show talk when and for how long?
  • Young children's behavior patterns: What a young child does during a specific hour of the day and for how long?
  • Television: When particular commercials come on during a favorite program and for how long? 
  • Education: Who participates in a small group discussion?
So, I'm calling this app "EventTracker" - here is a screen shot:

[ Get the free LiveCode Community version. ]


 https://drive.google.com/file/d/0B3W_1u8Y2RLRVGVDYk5EZ3hKVnM/edit?usp=sharing


This is really just an offshoot of the Classroom Observation Tool I wrote about previously. But, there are some key differences. For example, the design of this app records the time for each single event, whereas the Classroom Observation Tool allowed the user to string together a series of markers before applying a time stamp. The most significant difference, I think, is that this app takes advantage of the fact that you can create a script to copy and paste any object -- such as a button -- within the program.

Copying and Pasting Buttons with Code


OK, let's consider how copying and pasting buttons with code is done. To demonstrate, I've built another LiveCode stack with two cards -- a main card and a card for a library. This stack will help explain the key ideas which I use in the EventTracker app. Here's a screen shot:


[ Get the free LiveCode Community version. ]



On the library card (not shown), I've placed a single button called "template." On the main card, I have several buttons with progressively more complex code. The first is titled "Version 1: copy/paste 1 button" -- here's the script:

on mouseUp
   copy button "template" on card "library" to this card
   set the location of button "template" to 100,50
   set name of button "template" to "New Button"
   set label of button "New Button" to "New Button"
end mouseUp

Line 1 copies and pastes the "template" button from the library card to the main card. It then moves this freshly pasted button to the top, left position of the screen, as denoted by 100,50 (100 pixels from the left side and 50 pixels from the top).

Line 3 renames the button as "New Button," and line 4 gives it the label "New Button."

What if we want more than just one new button? Well, let's repeat this process a few times in a second version of the script found in the button titled "Version 2: copy/paste 3 buttons":

on mouseUp
     put 50 into y
     repeat 3 times
      copy button "template" on card "library" to this card
      set the location of button "template" to 100,y
      put item 2 of the location of button "template" into y
      set name of button "template" to "New Button"
      set label of button "New Button" to "New Button"
      add 30 to y
   end repeat
end mouseUp

I first create a local variable "y" and put the value 50 into it. This will be used to indicate how far down the first copied and pasted button should be placed.

I then have a block of code that I repeat three times just for illustration purposes. It's almost identical to the version 1 script, except that it takes into account the fact that buttons 2 and 3 have to be placed slightly further down on the card after they are pasted. So, I just arbitrarily added 30 to y at the end of each repeat. Button 2 is placed at location 100,80 and Button 3 is placed at location 100,110.

OK, this is all well and good, but how do I get unique names into each of the new buttons. Version 3 of the script (in the button "Copy/Paste Button") takes care of this by relying on a text field called "names." It looks at all of the names entered into the field and creates a unique button for each:

on mouseUp
   put 50 into y
   repeat with i = 1 to the number of lines in field "names"
      put line i of field "names" into varName
      copy button "template" on card "library" to this card
      set the location of button "template" to 100,y
      put item 2 of the location of button "template" into y
      set name of button "template" to varName
      set label of button varName to varName
   add 10+height of button "template" on card "library" to y
   end repeat
end mouseUp

Instead of just repeating three times, this script repeats for as many names that appear in the field. During each repeat loop, it puts the name of the next person on the list into a local variable called "varName," with help from another local variable "i" that is keeping track of which line of the text field (and hence which person's name) is being considered.

The button "Delete All Buttons" does just that - it deletes the buttons. This button relies on another field "previous names" to do this accurately. I refer below to the "previous names" field as a "shadow field" because it is hidden in the final version and works "in the shadows." The list of names is copied and pasted to the "previous names" field right before the group of buttons is created from the list of names. When the button group needs to be updated, the first thing I do is delete all of the existing buttons by referring to the "previous names" field. 

Features of the Final Version


OK, let's take a look at the final version of the app. The final version has a lot of other interesting features besides the copying and pasting of buttons with code.

Distributing the Buttons Evenly


I was tempted to take the easy way out and just have the buttons -- no matter how many -- begin displaying in the top left of the card and down until a first column filled, then start a second column. However, I decided it would be cool to distribute the buttons evenly in the designated space on the card. The designated space is the left half of the card. This was fun to do and created a little brain teaser for me involving some simple mathematics. A total of 10 buttons fit well in one column. So, if there were 10 or less buttons, I wanted those to be shown in a column in the exact middle of the designated space.

When there are more than 10 buttons, but no more than 20, then I figured that the spacing would need to be the width of the stack divided by 2, and then take and divide that number by 3, or (w/2)/3 where w is the width of the stack.

When there are more than 20 buttons, but no more than 30, then the spacing needs to be the width of the stack divided by 8.

When there are more than 30 buttons, but no more than 40, then the spacing needs to be the width of the stack divided by 16. However, the first column starts w/8 from the left edge of the screen.

I added some code to catch if more than 40 names or events are entered. If this happens, a short message pops up for 2 seconds to let the user know about the 40 event limit.

Use of "Shadow Fields"


I am finding myself using what I personally call "Shadow Fields" to facilitate projects such as this. Shadow fields are simply fields that help out various scripts in various ways, but they aren't visible to the user -- they are working "in the shadows."

I have two particular noteworthy examples at work in this app. The first is the field called "previous names" that I've already referred to in this post. As soon as the list of events is created and settled upon, I use the following script in the button "Generate Buttons" to copy the events from the field "names" and paste them into the field "previous names":

   put field "names" into field "previous names"

This acts as a way to preserve the current list of button labels, which is important for many of the app's scripts, such as when all the event buttons need to be deleted, as I've already mentioned.

The second shadow field I use in this application is called "datastreamSeconds," which works closely with the field "datastream," which is the field containing the data log (on the right side of the screen). The field "datastreamSeconds" is used to assist in figuring out the time in seconds for each event on the fly. For example, as you run the app, notice how the event in the last line shows "pending" for the time and then is updated with the number of seconds as soon as the next event is logged. The field ""datastreamSeconds" maintains a log of the time for each event in seconds using the function "seconds."At first, I tried keeping track of this information using a set of variables, but this proved too clunky, especially when you consider that I always give the user the option to erase the last entry. To compute the time for a particular event, I just subtract the time of one event with the next event. To see how it works, just turn on the visibility of the field "datastreamSeconds" using the Application Browser tool.

Generating a Summary Report


Like a lot of my projects, I like to rely on other tools, such as Excel, do the heavy lifting on other tasks or needs, such as generating reports. So, the list of events generated in this app is "Excel Ready" in the sense that one can copy and paste the lines in the field directly into Excel. Then, using the "Text to Columns" option in Excel, you can distribute the comma-separated data into individual columns. But, I thought it would be good to quickly give the user the most basic summary statistics: frequency of each event (i.e. person), and total time of each event. 

Here is the complete script for the button "Generate Report," with my explanation coming right after:

on mouseUp
   show field "report"
   hide me
   hide field "datastream"
   hide button "non-event"
   hide button "event marker"
   hide button "Edit Buttons"
   hide button "Erase Last Entry"
   hide button "Delete All Entries"
   show button "close report"
   put empty into field "report"
   put "Excel Ready Format" into line 1 of field report
   put "Button Label, Frequency, Total Time (Seconds)" into line 2 of field report
   repeat with i = 1 to the number of lines in field "previous names"
      put line i of field "previous names" into varTempName
     
      put 0 into varCount
      repeat with j = 2 to the number of lines in field "datastream"
         if item 4 of line j of field "datastream" = varTempName then add 1 to varCount
      end repeat

     
      put 0 into varTimeTotal
      repeat with k = 2 to the number of lines in field "datastream"-1
         if item 4 of line k of field "datastream" = varTempName then
            add item 3 of line k of field "datastream" to varTimeTotal
         end if
      end repeat

     
      put varTempName&","&varCount&","&varTimeTotal into line i+2 of field "report"
     
   end repeat
  
end mouseUp
There are three important loops here, each with its own repeat command. I color coded them to make them easier to see and understand. First, there is an overall loop (the start and end points color coded blue) that repeats for as many buttons there are. For example, let's imagine we have five people at a meeting: Tom, Dick, Harry, Susan, and Jane. Each person's name, in succession (i.e. once per loop), is put into a local variable "varTempName." Within this loop are two independent loops that look for that person's name. 

The first (color coded green) just looks to see how many times that person's name is listed in the field "datastream." The local variable "varCount" is incremented by one each time the name is found.

The second loop (color coded orange) also looks to see if the person's name is mentioned in each line of the field "datastream," but it then takes item 4 of the line containing the number of seconds for that event and adds it to another local variable called "varTimeTotal".

The second to last line of the blue loop puts the summary information for each person on a unique line in the field "report."

I also put this summary information in "Excel ready" format so that one could easily create graphs or charts using Excel.

Option to Sort the List


Sorting a list of information is amazingly easy to do, thanks to the fact that LiveCode has a "sort" command:

     sort field "names" ascending text

However, I wanted to give the user the option of sorting. So, I added a checkmark button called "sort." The "hilite" property is used detect whether the button is checked or unchecked (i.e. true or false):

   if the hilite of button "sort" is true then
      sort field "names" ascending text
   end if

Search and Destroy Empty Lines


I found that errors occurred if the user leaves any blank lines after editing the button list. In short, a blank line creates a button without a name, which is problematic for many reasons. So, I had to create some script to "search and destroy" any blank lines. Early on, I wrote this script in the "Generate Buttons":

   put 0 into c
   repeat with i = 1 to the number of lines in field "names"
      if line i of field "names" = "" then add 1 to c
   end repeat
   repeat c times
      delete line 1 of field "names"
   end repeat

This worked great, but only on the condition that the list is always sorted alphabetically first -- by doing so the blank lines "rise" to the top of the list. This script does not work if the list remains unsorted, an option I decided late in my design. So, I had to come up with a script that work would either way.

My first solution did not work, though I remain a little baffled as to why:

   repeat with i = 1 to the number of lines in field "names"
      if line i of field "names" = "" then delete line i of field "names"
   end repeat

This "kind of" works, but not reliably. It will catch a stray empty line, but if there are two or more empty lines in a row, it does not reliably delete them all. So, I came up with the following script that, although somewhat inelegant, seems to work great:

   put false into flag
   repeat until flag is true
      repeat with i = 1 to the number of lines in field "names"
         if line i of field "names" = "" then delete line i of field "names"
      end repeat
      put true into flag
      repeat with i = 1 to the number of lines in field "names"
         if line i of field "names" = "" then put false into flag
      end repeat
   end repeat

This again has a total of three loops, with again two loops inside of one loop. Basically, this combines my initial script with code to repeat so long as there continues to be a blank line in the list. First, I set a local variable "flag" to false and runs that first "search and destroy" script that almost works. Then I "put true into flag" with the hope that all blank lines are gone. But, I then run another loops that searches for blank lines. If it finds one, it sets flag back to false at which point the first loop (starting with "repeat until flag is true") runs again. It continues this until all blank lines are gone.

Interestingly, in testing this out, it never seems to take more than a total of 4 loops to "eradicate" all of the blank lines, no matter how many blank lines I include or where I include them. I obviously don't understand completely the nuances of the "delete line" command, but this script does the job nonetheless.

Final Thoughts


This app was yet another weekend project. I don't think I spent more than about 4 hours on it. This again speaks to the power of LiveCode as a prototyping tool. Obviously, much more work could be devoted to this app, its graphic design being just one example. As always, if I were to create a standalone application, I'd have to add the script that saves the data to a text file. But, when just left as a LiveCode file, the names and data log remain in their respective fields between sessions.

As I close this blog posting, you might be wondering how much I tend to dominate meetings. My humble response is that I am a model of restraint, no matter how spirited the conversation. (Yeah, right!) Well, at least I've proved in this post that I am thinking about my own talking and listening behaviors. And I hypothesize that if you reflect on whether or not you are talking too much during a meeting, you are likely to talk less and listen more. Perhaps I should collect some data to test this hypothesis.

Wednesday, February 19, 2014

Another LiveCode First Project: Follow the Bouncing Ball

I continue to introduce faculty and students at UGA to LiveCode. For a recent workshop, I came up with another first project example, which I call "Follow the Bouncing Ball." This is the one that the attendees voted to have me build for them during the workshop.

It's a very graphical project where an animated soccer ball bounces around the screen based on some information provided by the user. This information is simply the horizontal and vertical speed of the ball.

I made a YouTube video of me demonstrating how to build this project from scratch, so I'm not bothering to go into any detail here on how it works.

Here is a snapshot of the project:

[ Get the free LiveCode Community version. ]


What is rather cool about this project is that it shows some very fundamental and important physics concepts and principles. It's a fun way to show the hierarchical relationship between an object's location, distance traveled, velocity of the object, and acceleration of the object. The study of a moving object is a great example of rate of change problems. The distance an object travels shows its change in position. The velocity (speed and direction) of an object shows its change in distance over time, and acceleration denotes the change in velocity. This project didn't go as far as demonstrating acceleration, but it did a good job of showing velocity as consisting of both the speed the soccer ball is traveling and direction in which it is moving. Of course, I know I run the risk of alienating people if I go too far with the physics explanations! 

(But, the history of the discovery of calculus is closely tied to dynamic problems such as this. Velocity is the first derivative of distance and acceleration is the second derivative of distance. Ergo, acceleration is the first derivative of velocity. How cool is that!)

I built this project only using local variables in order to try to show the difference between global and local variables. 

Here is the script of the button "Bounce":

on mouseUp
   hide me
  
   put item 1 of the location of button "ball" into x
   put item 2 of the location of button "ball" into y
  
   put line 1 of field "speed" into varSpeedx
   put line 2 of field "speed" into varSpeedy
  
   repeat until the mouseclick
     
      if x > 320 then put -1*varSpeedx into varSpeedx
      if x < 0 then put -1*varSpeedx into varSpeedx
      if y > 480 then put -1*varSpeedy into varSpeedy
      if y < 0 then put -1*varSpeedy into varSpeedy
     
      wait 1 millisecond
      add varSpeedx to x
      add varSpeedy to y
     
      set the location of button "ball" to x, y

   end repeat
  
   show me
  
end mouseUp

Sir Isaac Newton would be proud.


Saturday, January 18, 2014

LiveCode First Projects: Designing a LiveCode Demonstration that Motivates People to Want to Learn How to Code

One of my goals for this semester here at the University of Georgia in my role as Director of Innovation in Teaching and Technology is to introduce LiveCode to the College of Education community. My plan is to conduct a short (about an hour) seminar that demonstrates LiveCode in the context of learning to code. That is, I want to clearly and immediately make the connection between learning LiveCode and learning to code (i.e. program). A typical first project that one usually finds within most tutorials of computer languages is the "Hello world" example where one simply learns how to program the computer to display "Hello World" on the screen. However, I hope to do something much more interesting, so I've come up with a few example projects. I'm calling these "first projects."

Part of my motivation for doing this was inspired by the recent "Hour of Code" promotion which sought to have every K-12 student in American spend one hour learning to program. I also think learning to code is very relevant to supporting STEM (science, technology, engineering, and mathematics) education. In fact, I would argue that computer programming has not been emphasized nearly enough by STEM educators. Plus, I feel that learning to code also brings the arts into the discussion almost immediately given the ease and importance of integrating graphics, sound, music, and video within most programming projects. And what programming project would be complete without attention to technical, narrative, and creative writing? So I consider the language arts a critical part of a STEM education. Adding the arts turns STEM into STEAM, and I like the sound of that.  (Here are two good articles about the need to learn to code from Wired and the Pittsburgh Post-Gazette.)

My objective for this first seminar is less about learning and more about motivation, particularly triggering a sense of confidence and competence. That is, I want people at the end of this seminar to go beyond just saying to themselves "Wow, that's cool!" to also quickly say "I think I can do that!" So, what examples I show are critical. So far, I've come up with three that I think are pretty good.

The first project is a simple mad lib program where four fields are created, one for adjectives, nouns, verbs, and adverbs. Clicking a button chooses one word from each field at random and puts them together in a sentence.

[ Get the free LiveCode Community version. ]


The second is a countdown program that counts from 3 to 0, then triggers the display of "Go!" after 0 is reached. I couch this one in the context of imagining that you have created some sort of race game and you need a counter to let the player know when the race is about to begin.


[ Get the free LiveCode Community version. ]


I also created a second version of this to show a stop light that goes from red to yellow to green, but without changing the program's algorithm (which I like to refer to as the program's "engine").

[ Get the free LiveCode Community version. ]


The third is a simple guessing game. The computer picks a random number from 1 to 3 and it is the player's job to guess what number it is. Not a very fun game, I know, but it does contain at least the seeds of a good guessing game with initial code that is easy to understand.

[ Get the free LiveCode Community version. ]



I'm also considering to use my "Colorful Addendum" that I wrote about back in May as a fourth example. It's a lot of fun to watch and I think the script is easy to understand. But, the script is a little long, so that might be a little "put-offish" to some people.

Here is my "Lesson Plan" for this one hour seminar:

  1. (2 min.) Welcome everyone to the session. The goal is to demonstrate the computer programming environment of LiveCode. Give enough information to describe LiveCode and its history, but keep this very brief as people really won't care too much at this stuff at this point.
  2. (1 min.) Emphasize that LiveCode involves computer programming, also known simply as coding. Mention the "Hour of Code" promotion (and perhaps show the Web site). Make the point that there is much emphasis on STEM (science, technology, engineering, and math; and some add "arts" to turn STEM into STEAM). It is not enough just to use existing software, one must learn how to build software. To do so, one must learn programming. (Perhaps make the point that learning to programming is far too underemphasized in STEM circles, perhaps because there are not enough teachers who are able to teach it.)
  3. (1 min.) Emphasize that one of my reasons for learning LiveCode was because of its strengths in creating native mobile applications (apps) on both iOS (iPhone, iPad) and Android.
  4. (3 min.) Show "Lunar Hotel Shuttle" as an example that I've created.
  5. (3 min.) Consider also showing one or more other examples quickly. My Classroom Observation Research Data Collection (see blog posting from October, 2013) example might be a good one given the academic context of these session. The air traffic controller example found on the LiveCode web site is another good one.
  6. (10 min.) Show briefly all three "first projects" mentioned above, taking care to show each very, very fast. Then, pass out a ballot where people vote for the the project they wish to learn how to make during this session. (Let people vote for more than one if they have more than one favorite.) Collect the ballots and ask someone to tally them up. 
  7. (30 min.) Build the "winning" project from scratch.
  8. (10 min.) End with some discussion and Q&A.
I've already field-tested this seminar by trying it out a few days ago with a group of our very smart, motivated doctoral students who are taking this semester's LDT Doctoral Studio course with Dr. Greg Clinton. I think the session went very well. The LiveCode project the group voted to build was the Mad Libs project. It was their favorite by a large margin. The next favorite project was the Countdown project. Surprisingly, the Guess the Number! project was a distant third.

I decided I will create a YouTube video for each project that a group selects each time I offer the seminar. My YouTube video for creating the Mad Libs project is about 30 minutes in length, which is longer than I would have anticipated. But, I don't think there is any "fat" in the demo, especially because I felt it was important to spend a little extra attention on some of the fundamentals of LiveCode and programming in a few places.
I look forward to presenting this seminar to the College of Education community at the University of Georgia. I'm also going to try to come up with more good "first project" examples. I'd like to come up with a couple that use more graphics.

Saturday, December 7, 2013

LiveCode 6.5 Now Offers Automatic Scaling

LiveCode 6.5 was just released with a new feature that certainly has my attention -- automatic scaling of applications on mobile platforms. The more precise term for this, or at least the one the company is using, is resolution independence.

To learn more, check out the following web site:

http://livecode.com/livecode-6-5/

Better yet, check out this short video:

https://www.youtube.com/watch?v=idctzDFoGj8#t=13

This is a rather big deal. This means that one's application, designed for one screen size such as the iPhone, will now scale automatically if used on the iPad, iPad-mini, or on the myriad of screen sizes found on the Android platform.

Disclaimer: I have not yet tried working with this new feature, so I'm just trusting the company's information at this point.

To take advantage of this feature, only one line of code is needed within a preOpenStack handler:

on preOpenStack
     set the fullscreenmode of this stack to "exactFit"
end preOpenStack

Below is a copy and paste of excerpts about the fullscreenmode property from the LiveCode dictionary:

And I quote...

fullscreenmode

Syntax:
set the fullscreenmode of stack to {empty|"exactFit"|"letterbox"|"noBorder"|"noScale"}
 

Summary:
Sets the full screen scaling mode of a stack.

Examples:
set the fullscreenmode of this stack to empty
set the fullscreenmode of this stack to "noScale"

Use the fullscreenmode property to choose the most appropriate full screen scaling mode for the application.
 

Parameters:
empty - The stack is resized (not scaled) to fit the screen. (default) This is the existing behavior.
"exactFit" - Scale the stack to fill the screen. This stretches the stack if the aspect ratio of the screen does not match that of the stack.
"letterbox" - Scale the stack, preserving the aspect ratio, so all content is visible. Some blank space may remain if the screen and stack aspect ratios do not match.
"noBorder" - Scale the stack to fill the screen, preserving the aspect ratio. If the stack and screen aspect ratios do not match, the left / right or top / bottom extremes of the stack are not visible.
"noScale" - The stack is not scaled, but is centered on the screen instead.

Value:
The fullscreenmode returns the mode to which this property is set.

Comments:
There are multiple ways in which a stack can be resized or scaled to take full advantage of the available screen space. fullscreenmode allows the developer to choose the most appropriate for their application.

Note: The fullscreenmode only takes affect when a stack is full screen. This is the case on mobile platforms where stacks are always full screen, or on the desktop when the fullscreen property of the stack is set to true.

The full screen scaling mode is available on all desktop and mobile platforms and operates independently from Hi-DPI support.