Wednesday, January 13, 2010
And repackaged TimeLapse on the XO with all of the changes that we have made. Then I uploaded the repackaged TimeLapse and finally published the activity, with all of the information about it filled in. This also included a screenshot of TimeLapse in action.
http://activities.sugarlabs.org/en-US/sugar/addon/4270
Is the link to where TimeLapse is available for experimental download.
Going to Frank's House
Today, after quickly fixing a bug in TimeLapse at the career center, I went with Peter to Frank's house. The entire thing ran very smoothly and Frank was impressed with the changes we had made, especially the improved sound!! He also had some suggestions about changes that we could make to improve TimeLapse.
1) Have items play when they are double-clicked
2) Enable items that do not have a picture to play
3) Create a confirmation window for the delete all button
Hopefully, we will be able to make this changes without a hitch!
Old TimeLapse
Tuesday, December 15, 2009
@glade_callback
def doubleclicked(self, data, third):
if os.path.isfile(os.path.abspath(self.soundfile)):
self.player.set_property("uri", "file://" +
os.path.abspath(self.soundfile))
self.player.set_state(gst.STATE_PLAYING)
else:
pass
Fix bug where data can not be selected if it does not have a picture
def itemSelected(self,data):
self.timestamp = data.get_cells()[0].get_property("text")
def datum_by_tag(tag):
return xmlhelper.get_datum_by_timestamp(self.timestamp, tag)
self.filepath = datum_by_tag("filepath")
try:
image = datum_by_tag("image")
except:
image = "noImage.jpg"
myPixbuf = gtk.gdk.pixbuf_new_from_file_at_size(image, 400, 400)
self.MainImage.set_from_pixbuf(myPixbuf)
self.soundfile = datum_by_tag("sound")
if self.soundfile is not None:
self.interface.get_widget("play_button").set_sensitive(True)
else:
self.interface.get_widget("play_button").set_sensitive(False)
Confirmation Window for Delete All button
@glade_callback
def deleteAllClicked(self, data):
self.interface.get_widget("delete_confirmation").show()
@glade_callback
def deleteAllConfirmed(self, data):
print "all deleted"
self.update_pixbuf()
shutil.rmtree("data/")
os.mkdir("data")
xmlhelper.remove_all_data()
self.update_pixbuf()
self.interface.get_widget("delete_confirmation").hide()
@glade_callback
def deleteAllCanceled(self, data):
self.interface.get_widget("delete_confirmation").hide()
The first thing that I fixed was the spacing and placement of each collection.
The next thing that I did was add a horizontal and vertical scroll bar to the window where the collections were being displayed.
Creating An .xo file
Step 1
# Copyright (C) 2006, Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
from sugar.activity import bundlebuilder
bundlebuilder.start()
Next create a directory within the one that the program is in and name it Activity.
Step 3
Create a file named activity.info within the Activity directory and copy the lines below into it (replace example and ex with the name of your activity)
name = (name of your activity)
bundle_id = example
icon = exampleicon
exec = sugar-activity exActivity.exampleActivity
show_launcher = yes
activity_version = 1
license = GPLv2+
- name
The name of your Activity as it will appear to the user.- bundle_id or service_name
A unique name that Sugar will use to refer to your Activity. Any Journal entry created by your Activity will have this name stored in its metadata, so that when someone resumes the Journal entry Sugar knows to use the program that created it to read it.- icon
The name of the icon file you have created for the Activity. Since icons are always .svg files the icon file in the example is named example.svg.- exec
- This tells Sugar how to launch your Activity. What it says is to create an instance of the class exampleActivity which it will find in file exampleActivity.py.
- show_launcher
- There are two ways to launch an Activity. The first is to click on the icon in the Activity view. The second is to resume an entry in the Journal. Activities that don't create Journal entries can only be resumed from the Journal, so there is no point in putting an icon in the Activity ring for them.
- activity_version
- An integer that represents the version number of your program. The first version is 1, the next is 2, and so on.
- license
- With a computer program there is always a license that tells the person receiving the program what he or she is allowed to do with it. GPLv2+ is a popular standard license that can be used for Activities.
To make the icon, I would recommend using Inkscape.
In Inkscape, go to file, new and select icon_48x48.
This icon size drawing is ideal for sugar activities.
Step 5
Modify icon so it can
work with Sugar.
Specifically, make the icon show Sugar that Sugar can use
its own choice of stroke color and fill color.
(The SVG file format is
based on XML, which means it is a text file with some special tags in
it.)
Once Inkscapecan file is complete, load the file into any text editor and edit it as a text file.
Make the following changes:
Before:
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
After:
<!ENTITY stroke_color "#000000">
<!ENTITY fill_color "#FFFFFF">
]><svg
Now in the body of the document there are references to fill and stroke as part of an attribute called style.
Every line or shape that is drawn will have these something like this:
style="fill:#ffffff;stroke:#000000;stroke-opacity:1"
id="rect904"
width="36.142857"
height="32.142857"
x="4.1428571"
y="7.1428571" />
Change each one to look like this:
style="fill:&fill_color;;stroke:&stroke_color;;stroke-opacity:1"
id="rect904"
width="36.142857"
height="32.142857"
x="4.1428571"
y="7.1428571" />
(Note that &stroke_color; and &fill_color;
both end with semicolons (;), and semicolons are also used to separate
the properties for style. Because of this it is an extremely common
beginner's mistake to leave off the trailing semicolon because two
semicolons in a row don't look right. Be assured that the two
semicolons in a row are intentional and absolutely necessary!)
Step 6
Create a file called MANIFEST in the folder that contains the activity.
( Do not put the MANIFEST file in the activity folder in the activity)
The MANIFEST file should contain everything that is a part of the activity.
EX:
setup.py
example.py
activity/example.svg
activity/activity.info
./setup.py dev
This is because, the activity needs to be installed, which in this case means making a symbolic link between the directory that is being used for the code in the home/Activities/ directory.
A symbolic link is a way to make a file or directory appear to be located in more than one place without copying it.
By typing ./setup.py dev , this makes symbolic link by running setup.py again
Step 8
type
./setup.py dist_xo
This will create the xo file and place it in a new folder called dist in the Activity directory.
Tuesday, January 12, 2010
you will notice that width, height, and rate are all = %d, and right after each of them I have % qualitytab.tab.width or qualitytab.tab.height, qualitytab.tab.rate
This means that the value for what width, height, and rate are is based upon the values dictated in my qualitytab.py which is in-turn based upon where the slider is placed by the user.
You will also notice that at the end of each def, wrote print qualitytab.tab.width or qualitytab.tab.height, qualitytab.tab.rate, that way the developers can look at the terminal to see what values are actually being used in TimeLapse, helping anyone who wants to make changes to the existing qualitytab.py
[the post below contains my code for the double-click function]
Today I worked on making the sliders actually do something in Timelapse. This led to me writing the code in the post below.
By the end of the day, I had created a quality tab that actually worked for TimeLapse.
To make the values for video, only input width x height ratios of 4:3 and for audio values, I only input rates based on a 6,000 scale.
Because the coding would be to complicated to have the slider stop at specific values, I just made Timelapse give a value based upon where the slider is in proximity with the closest value.
For example, if the slider goes to 2.76 then the code will use the value width = 256 & height = 192.
Computer
if os.path.isfile(os.path.abspath(self.soundfile)):
self.player.set_property("uri", "file://" +
os.path.abspath(self.soundfile))
self.player.set_state(gst.STATE_PLAYING)
else:
pass
Quality Tab
class Tab(interface.NewTab):
def make(self):
# change these to glade callbacks later.
self.video, self.audio, self.image, self.lapse, self.width, self.height, self.rate = 4, 4, 4, 4, 100, 75, 12000
def on_video_changed(self, widget):
self.video = widget.get_value()
print "video quality", self.video
if self.video < 1.50:
self.width = 100
self.height = 75
if self.video >= 1.50 and self.video < 2.50:
self.width = 200
self.height = 150
print "optimal"
if self.video >= 2.50 and self.video < 3.50:
self.width = 256
self.height = 192
if self.video >= 3.50 and self.video < 4.50:
self.width = 320
self.height = 240
if self.video >= 4.50 and self.video < 5.50:
self.width = 400
self.width = 300
if self.video >= 5.50 and self.video <= 6.00:
self.width = 512
self.height = 384
def on_audio_changed(self, widget):
self.audio = widget.get_value()
print "audio quality", self.audio
if self.audio < 1.50:
self.rate = 12000
if self.audio >= 1.50 and self.video < 2.50:
self.rate = 18000
if self.audio >= 2.50 and self.video < 3.50:
self.rate = 24000
if self.audio >= 3.50 and self.video < 4.50:
self.rate = 30000
if self.audio >= 4.50 and self.video < 5.50:
self.rate = 36000
if self.audio >= 5.50 and self.video <= 6.00:
self.rate = 48000
Cameramic
def video(self):
VIDEO_GST_PIPE = ['v4l2src', 'queue', 'videorate',
'video/x-raw-yuv,framerate=15/1', 'videoscale',
'video/x-raw-yuv,width=%d,height=%d' % (qualitytab.tab.width, qualitytab.tab.height),
'ffmpegcolorspace', 'theoraenc', 'oggmux']
pipe = VIDEO_GST_PIPE + ["filesink location=%s" % (self.video_temp)]
self.video_pipe = gst.parse_launch('!'.join(pipe))
self.video_pipe.set_state(gst.STATE_PLAYING)
print qualitytab.tab.width
print qualitytab.tab.height
def audio(self):
AUDIO_GST_PIPE = ["alsasrc",
"audio/x-raw-int,rate=%d,channels=1,depth=16" %
qualitytab.tab.rate, "audioconvert","flacenc"]
pipe = AUDIO_GST_PIPE + ["filesink location=%s" % (self.audio_temp)]
self.audio_pipe = gst.parse_launch('!'.join(pipe))
self.audio_pipe.set_state(gst.STATE_PLAYING)
print qualitytab.tab.rate
Thursday, January 7, 2010
http://tadeboro.blogspot.com/2009/09/glade3-tutorial-4-gtktreeview-data.html
I finally found this site, which told me to set the page_size = 0.00, after I did that and opened TimeLapse again, the sliders worked.
Tuesday, January 5, 2010
def image(self):
self.image_path = "/tmp/tmp" + str(time.time())
IMAGE_GST_PIPE = ['v4l2src', 'ffmpegcolorspace', 'pngenc']
pipe = IMAGE_GST_PIPE + ['filesink location=%s' % (self.image_path)]
self.bus = self.pipe.get_bus()
self.pipe.set_state(gst.STATE_PLAYING)
self.bus.poll(gst.MESSAGE_EOS, -1)
self.file_dsobject = datastore.create()
self.file_dsobject.metadata['title'] = "Image Sample"
self.file_dsobject.metadata['mime_type'] = 'image/png'
self.file_dsobject.set_file_path(self.image_path)
datastore.write(self.file_dsobject)
self.file_dsobject.destroy()
Today
To fix this problem i went into cameramic.py and looked at
def video(self):
VIDEO_GST_PIPE = ['v4l2src', 'queue', 'videorate',
'video/x-raw-yuv,framerate=15/1', 'videoscale',
'video/x-raw-yuv,width=160,height=120',
'ffmpegcolorspace', 'theoraenc', 'oggmux']
pipe = VIDEO_GST_PIPE + ["filesink location=%s" % (self.video_path)]
self.video_pipe = gst.parse_launch('!'.join(pipe))
self.video_pipe.set_state(gst.STATE_PLAYING)
I had originally tried to up the video scale to
def video(self):
VIDEO_GST_PIPE = ['v4l2src', 'queue', 'videorate',
'video/x-raw-yuv,framerate=15/1', 'videoscale',
'video/x-raw-yuv,width=640,height=480',
'ffmpegcolorspace', 'theoraenc', 'oggmux']
pipe = VIDEO_GST_PIPE + ["filesink location=%s" % (self.video_path)]
self.video_pipe = gst.parse_launch('!'.join(pipe))
self.video_pipe.set_state(gst.STATE_PLAYING)
but it ended up creating a very crisp video but it was incredibaly choppy. I spent a long time fiddling with the video scale til I came up
def video(self):
VIDEO_GST_PIPE = ['v4l2src', 'queue', 'videorate',
'video/x-raw-yuv,framerate=15/1', 'videoscale',
'video/x-raw-yuv,width=200,height=150',
'ffmpegcolorspace', 'theoraenc', 'oggmux']
pipe = VIDEO_GST_PIPE + ["filesink location=%s" % (self.video_path)]
self.video_pipe = gst.parse_launch('!'.join(pipe))
self.video_pipe.set_state(gst.STATE_PLAYING)
Today
Yes I am a few weeks behind, but I am hoping that with some extra time and elbow grease, I will be able to fully update this sight.