1f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang# Use of this source code is governed by a BSD-style license that can be
3f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang# found in the LICENSE file.
4f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
58c5e275df682ef5b0ad52574ae1e2e31fc724509Joseph Hwang"""This module provides GUI for touch device firmware test using GTK."""
6f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
78abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwangimport os
8f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwangimport re
98abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwangimport shutil
10f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
11f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwangimport gobject
12f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwangimport gtk
13f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwangimport gtk.gdk
141ab9e6de0a658633e76b27fe00acce2039741694Joseph Hwangimport pango
158abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwangimport tempfile
16f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
178abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwangimport common_util
18f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwangimport firmware_utils
191ab9e6de0a658633e76b27fe00acce2039741694Joseph Hwangimport test_conf as conf
20f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
215a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwangfrom firmware_constants import TFK
225a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang
23f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
248c5e275df682ef5b0ad52574ae1e2e31fc724509Joseph HwangTITLE = "Touch Firmware Test"
25f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
26f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
27f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwangclass BaseFrame(object):
28f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang    """A simple base frame class."""
29f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang    def __init__(self, label=None, size=None, aspect=False):
30f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        # Create a regular/aspect frame
31f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.frame = gtk.AspectFrame() if aspect else gtk.Frame()
32f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.frame.set_shadow_type(gtk.SHADOW_ETCHED_OUT)
3387df9932d281d5db1cbe9779eb049925424f46eaJoseph Hwang        self.size = size
34f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        if label:
35f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang            self.frame.set_label(label)
36f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang            self.frame.set_label_align(0.0, 0.0)
37f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang            frame_label = self.frame.get_label_widget()
38f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang            markup_str = '<span foreground="%s" size="x-large">%s</span>'
39f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang            frame_label.set_markup(markup_str % ('black', label))
40f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        if size:
41f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang            width, height = size
42f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang            self.frame.set_size_request(width, height)
43f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang            if aspect:
44f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang                self.frame.set(ratio=(float(width) / height))
45f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
46f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
47f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwangclass PromptFrame(BaseFrame):
48f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang    """A simple frame widget to display the prompt.
49f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
50f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang    It consists of:
51f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang      - A frame
52f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang      - a label showing the gesture name
53f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang      - a label showing the prompt
54f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang      - a label showing the keyboard interactions
55f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang    """
56f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
57f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang    def __init__(self, label=None, size=None):
58f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        super(PromptFrame, self).__init__(label, size)
59f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
60f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        # Create a vertical packing box.
61f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.vbox = gtk.VBox(False, 0)
62f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.frame.add(self.vbox)
63f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
64f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        # Create a label to show the gesture name
65f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.label_gesture = gtk.Label('Gesture Name')
66f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.label_gesture.set_justify(gtk.JUSTIFY_LEFT)
67f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.vbox.pack_start(self.label_gesture, True, True, 0)
6887df9932d281d5db1cbe9779eb049925424f46eaJoseph Hwang        # Expand the lable to be wider and wrap the line if necessary.
6987df9932d281d5db1cbe9779eb049925424f46eaJoseph Hwang        if self.size:
7087df9932d281d5db1cbe9779eb049925424f46eaJoseph Hwang            _, label_height = self.label_gesture.get_size_request()
7187df9932d281d5db1cbe9779eb049925424f46eaJoseph Hwang            width, _ = self.size
7287df9932d281d5db1cbe9779eb049925424f46eaJoseph Hwang            label_width = int(width * 0.9)
7387df9932d281d5db1cbe9779eb049925424f46eaJoseph Hwang            self.label_gesture.set_size_request(label_width, label_height)
7487df9932d281d5db1cbe9779eb049925424f46eaJoseph Hwang        self.label_gesture.set_line_wrap(True)
7587df9932d281d5db1cbe9779eb049925424f46eaJoseph Hwang
7687df9932d281d5db1cbe9779eb049925424f46eaJoseph Hwang        # Pack a horizontal separator
7787df9932d281d5db1cbe9779eb049925424f46eaJoseph Hwang        self.vbox.pack_start(gtk.HSeparator(), True, True, 0)
78f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
79f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        # Create a label to show the prompt
80f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.label_prompt = gtk.Label('Prompt')
81f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.label_prompt.set_justify(gtk.JUSTIFY_CENTER)
82f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.vbox.pack_start(self.label_prompt, True, True, 0)
83f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
84f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        # Create a label to show the choice
85f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.label_choice = gtk.Label('')
86f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.label_choice.set_justify(gtk.JUSTIFY_LEFT)
87f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.vbox.pack_start(self.label_choice, True, True, 0)
88f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
89f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        # Show all widgets added to this frame
90f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.frame.show_all()
91f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
92f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang    def set_gesture_name(self, string, color='blue'):
93f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        """Set the gesture name in label_gesture."""
94f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        markup_str = '<b><span foreground="%s" size="xx-large"> %s </span></b>'
95f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.label_gesture.set_markup(markup_str % (color, string))
96f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
97f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang    def set_prompt(self, string, color='black'):
98f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        """Set the prompt in label_prompt."""
99f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        markup_str = '<span foreground="%s" size="x-large"> %s </span>'
100f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.label_prompt.set_markup(markup_str % (color, string))
101f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
102f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang    def set_choice(self, string):
103f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        """Set the choice in label_choice."""
104f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.label_choice.set_text(string)
105f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
106f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
107f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwangclass ResultFrame(BaseFrame):
108f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang    """A simple frame widget to display the test result.
109f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
110f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang    It consists of:
111f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang      - A frame
1125a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang      - a scrolled window
113f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang      - a label showing the test result
114f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang    """
1155a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang    SCROLL_STEP = 100.0
116f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
117f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang    def __init__(self, label=None, size=None):
118f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        super(ResultFrame, self).__init__(label, size)
119f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
1205a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        # Create a scrolled window widget
1215a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        self.scrolled_window = gtk.ScrolledWindow()
1225a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        self.scrolled_window.set_policy(gtk.POLICY_AUTOMATIC,
1235a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang                                        gtk.POLICY_AUTOMATIC)
1245a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        self.frame.add(self.scrolled_window)
1255a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang
126f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        # Create a vertical packing box.
127f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.vbox = gtk.VBox(False, 0)
1285a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        self.scrolled_window.add_with_viewport(self.vbox)
129f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
130f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        # Create a label to show the gesture name
131f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.result = gtk.Label()
132f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.vbox.pack_start(self.result , False, False, 0)
133f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
134f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        # Show all widgets added to this frame
135f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.frame.show_all()
136f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
1375a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        # Get the vertical and horizontal adjustments
1385a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        self.vadj = self.scrolled_window.get_vadjustment()
1395a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        self.hadj = self.scrolled_window.get_hadjustment()
1405a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang
1415a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        self._scroll_func_dict = {TFK.UP: self._scroll_up,
1425a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang                                  TFK.DOWN: self._scroll_down,
1435a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang                                  TFK.LEFT: self._scroll_left,
1445a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang                                  TFK.RIGHT: self._scroll_right}
1455a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang
1461ab9e6de0a658633e76b27fe00acce2039741694Joseph Hwang    def _calc_result_font_size(self):
1471ab9e6de0a658633e76b27fe00acce2039741694Joseph Hwang        """Calculate the font size so that it does not overflow."""
1481ab9e6de0a658633e76b27fe00acce2039741694Joseph Hwang        label_width_in_px, _ = self.size
1491ab9e6de0a658633e76b27fe00acce2039741694Joseph Hwang        font_size = int(float(label_width_in_px) / conf.num_chars_per_row *
1501ab9e6de0a658633e76b27fe00acce2039741694Joseph Hwang                        pango.SCALE)
1511ab9e6de0a658633e76b27fe00acce2039741694Joseph Hwang        return font_size
1521ab9e6de0a658633e76b27fe00acce2039741694Joseph Hwang
153f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang    def set_result(self, text, color='black'):
154f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        """Set the text in the result label."""
155f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        mod_text = re.sub('<', '&lt;', text)
156f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        mod_text = re.sub('>', '&gt;', mod_text)
1571ab9e6de0a658633e76b27fe00acce2039741694Joseph Hwang        markup_str = '<b><span foreground="%s" size="%d"> %s </span></b>'
1581ab9e6de0a658633e76b27fe00acce2039741694Joseph Hwang        font_size = self._calc_result_font_size()
1591ab9e6de0a658633e76b27fe00acce2039741694Joseph Hwang        self.result.set_markup(markup_str % (color, font_size, mod_text))
160f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
1615a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang    def _calc_inc_value(self, adj):
1625a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        """Calculate new increased value of the specified adjustement object."""
1635a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        value = adj.get_value()
1645a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        new_value = min(value + self.SCROLL_STEP, adj.upper - adj.page_size)
1655a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        return new_value
1665a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang
1675a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang    def _calc_dec_value(self, adj):
1685a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        """Calculate new decreased value of the specified adjustement object."""
1695a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        value = adj.get_value()
1705a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        new_value = max(value - self.SCROLL_STEP, adj.lower)
1715a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        return new_value
1725a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang
1735a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang    def _scroll_down(self):
1745a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        """Scroll the scrolled_window down."""
1755a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        self.vadj.set_value(self._calc_inc_value(self.vadj))
1765a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang
1775a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang    def _scroll_up(self):
1785a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        """Scroll the scrolled_window up."""
1795a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        self.vadj.set_value(self._calc_dec_value(self.vadj))
1805a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang
1815a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang    def _scroll_right(self):
1825a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        """Scroll the scrolled_window to the right."""
1835a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        self.hadj.set_value(self._calc_inc_value(self.hadj))
1845a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang
1855a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang    def _scroll_left(self):
1865a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        """Scroll the scrolled_window to the left."""
1875a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        self.hadj.set_value(self._calc_dec_value(self.hadj))
1885a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang
1895a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang    def scroll(self, choice):
1905a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        """Scroll the result frame using the choice key."""
1915a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        scroll_method = self._scroll_func_dict.get(choice)
1925a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        if scroll_method:
1935a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang            scroll_method()
1945a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        else:
1955a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang            print 'Warning: the key choice "%s" is not legal!' % choice
1965a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang
197f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
198f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwangclass ImageFrame(BaseFrame):
199f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang    """A simple frame widget to display the mtplot window.
200f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
201f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang    It consists of:
202f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang      - An aspect frame
203f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang      - an image widget showing mtplot
204f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang    """
205f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
206f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang    def __init__(self, label=None, size=None):
207f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        super(ImageFrame, self).__init__(label, size, aspect=True)
208f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
209f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        # Use a fixed widget to display the image.
210f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.fixed = gtk.Fixed()
211f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.frame.add(self.fixed)
212f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
213f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        # Create an image widget.
214f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.image = gtk.Image()
215f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.fixed.put(self.image, 0, 0)
216f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
217f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        # Show all widgets added to this frame
218f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.frame.show_all()
219f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
220f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang    def set_from_file(self, filename):
221f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        """Set the image file."""
222f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.image.set_from_file(filename)
223f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.frame.show_all()
224f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
225f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
226f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwangclass FirmwareWindow(object):
2278c5e275df682ef5b0ad52574ae1e2e31fc724509Joseph Hwang    """A simple window class to display the touch firmware test window."""
228f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
229f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang    def __init__(self, size=None, prompt_size=None, result_size=None,
230f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang                 image_size=None):
2318abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        # Setup gtk environment correctly.
2328abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        self._setup_gtk_environment()
23321df1fa2829a71c8c43ea807c75595d39352b229Joseph Hwang
234f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        # Create a new window
235f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.win = gtk.Window(gtk.WINDOW_TOPLEVEL)
236f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        if size:
237f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang            self.win_size = size
238f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang            self.win.resize(*size)
239f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.win.set_title(TITLE)
240f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.win.set_border_width(0)
241f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
242f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        # Create the prompt frame
243f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.prompt_frame = PromptFrame(TITLE, prompt_size)
244f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
245f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        # Create the result frame
246f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.result_frame = ResultFrame("Test results:", size=result_size)
247f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
248f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        # Create the image frame for mtplot
249f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.image_frame = ImageFrame(size=image_size)
250f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
251f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        # Handle layout below
252f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.box0 = gtk.VBox(False, 0)
253f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.box1 = gtk.HBox(False, 0)
254f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        # Arrange the layout about box0
255f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.win.add(self.box0)
256f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.box0.pack_start(self.prompt_frame.frame, True, True, 0)
257f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.box0.pack_start(self.box1, True, True, 0)
258f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        # Arrange the layout about box1
259f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.box1.pack_start(self.image_frame.frame, True, True, 0)
260f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.box1.pack_start(self.result_frame.frame, True, True, 0)
261f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
262f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        # Capture keyboard events.
263f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.win.add_events(gtk.gdk.KEY_PRESS_MASK | gtk.gdk.KEY_RELEASE_MASK)
264f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
265f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        # Set a handler for delete_event that immediately exits GTK.
266f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.win.connect("delete_event", self.delete_event)
267f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
268f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        # Show all widgets.
269f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.win.show_all()
270f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
2718abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang    def _setup_gtk_environment(self):
2728abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        """Set up the gtk environment correctly."""
2738abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang
2748abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        def _warning(msg=None):
2758abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            print 'Warning: fail to setup gtk environment.'
2768abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            if msg:
2778abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang                print '\t' + msg
2788abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            print '\tImage files would not be shown properly.'
2798abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            print '\tIt does not affect the test results though.'
2808abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang
2818abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        def _make_symlink(path, symlink):
2828abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            """Remove the symlink if exists. Create a new symlink to point to
2838abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            the given path.
2848abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            """
2858abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            if os.path.islink(symlink):
2868abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang                os.remove(symlink)
2878abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            os.symlink(real_gtk_dir, self.gtk_symlink)
2888abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            self.new_symlink = True
2898abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang
2908abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        self.gtk_symlink = None
2918abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        self.tmp = tempfile.mkdtemp()
2928abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        self.moved_flag = False
2938abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        self.original_gtk_realpath = None
2948abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        self.new_symlink = False
2958abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang
2968abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        # Get LoaderDir:
2978abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        # The output of gdk-pixbuf-query-loaders looks like:
2988abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        #
2998abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        #   GdkPixbuf Image Loader Modules file
3008abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        #   Automatically generated file, do not edit
3018abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        #   Created by gdk-pixbuf-query-loaders from gtk+-2.20.1
3028abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        #
3038abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        #   LoaderDir = /usr/lib64/gtk-2.0/2.10.0/loaders
3048abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        loader_dir_str = common_util.simple_system_output(
3058abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang                'gdk-pixbuf-query-loaders | grep LoaderDir')
3068abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        result = re.search('(/.*?)/(gtk-.*?)/', loader_dir_str)
3078abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        if result:
3088abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            prefix = result.group(1)
3098abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            self.gtk_version = result.group(2)
3108abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        else:
3118abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            _warning('Cannot derive gtk version from LoaderDir.')
3128abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            return
3138abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang
3148abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        # Verify the existence of the loaders file.
3158abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        gdk_pixbuf_loaders = ('/usr/local/etc/%s/gdk-pixbuf.loaders' %
3168abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang                              self.gtk_version)
3178abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        if not os.path.isfile(gdk_pixbuf_loaders):
3188abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            msg = 'The loaders file "%s" does not exist.' % gdk_pixbuf_loaders
3198abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            _warning(msg)
3208abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            return
3218abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang
3228abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        # Setup the environment variable for GdkPixbuf Image Loader Modules file
3238abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        # so that gtk library could find it.
3248abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        os.environ['GDK_PIXBUF_MODULE_FILE'] = gdk_pixbuf_loaders
3258abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang
3268abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        # In the loaders file, it specifies the paths of various
3278abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        # sharable objects (.so) which are used to load images of corresponding
3288abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        # image formats. For example, for png loader, the path looks like
3298abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        #
3308abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        # "/usr/lib64/gtk-2.0/2.10.0/loaders/libpixbufloader-png.so"
3318abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        # "png" 5 "gtk20" "The PNG image format" "LGPL"
3328abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        # "image/png" ""
3338abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        # "png" ""
3348abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        # "\211PNG\r\n\032\n" "" 100
3358abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        #
3368abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        # However, the real path for the .so file is under
3378abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        # "/usr/local/lib64/..."
3388abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        # Hence, we would like to make a temporary symlink so that
3398abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        # gtk library could find the .so file correctly.
3408abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        self.gtk_symlink = os.path.join(prefix, self.gtk_version)
3418abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        prefix_list = prefix.split('/')
3428abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        prefix_list.insert(prefix_list.index('usr') + 1, 'local')
3438abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        real_gtk_dir = os.path.join('/', *(prefix_list + [self.gtk_version]))
3448abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang
3458abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        # Make sure that the directory of .so files does exist.
3468abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        if not os.path.isdir(real_gtk_dir):
3478abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            msg = 'The directory of gtk image loaders "%s" does not exist.'
3488abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            _warning(msg % real_gtk_dir)
3498abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            return
3508abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang
3518abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        # Take care of an existing symlink.
3528abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        if os.path.islink(self.gtk_symlink):
3538abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            # If the symlink does not point to the correct path,
3548abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            # save the real path of the symlink and re-create the symlink.
3558abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            if not os.path.samefile(self.gtk_symlink, real_gtk_dir):
3568abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang                self.original_gtk_realpath = os.path.realpath(self.gtk_symlink)
3578abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang                _make_symlink(real_gtk_dir, self.gtk_symlink)
3588abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang
3598abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        # Take care of an existing directory.
3608abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        elif os.path.isdir(self.gtk_symlink):
3618abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            # Move the directory only if it is not what we expect.
3628abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            if not os.path.samefile(self.gtk_symlink, real_gtk_dir):
3638abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang                shutil.move(self.gtk_symlink, self.tmp)
3648abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang                self.moved_flag = True
3658abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang                _make_symlink(real_gtk_dir, self.gtk_symlink)
3668abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang
3678abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        # Take care of an existing file.
3688abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        # Such a file is not supposed to exist here. Move it anyway.
3698abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        elif os.path.isfile(self.gtk_symlink):
3708abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            shutil.move(self.gtk_symlink, self.tmp)
3718abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            self.moved_flag = True
3728abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            _make_symlink(real_gtk_dir, self.gtk_symlink)
3738abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang
3748abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        # Just create the temporary symlink since there is nothing here.
3758abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        else:
3768abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            _make_symlink(real_gtk_dir, self.gtk_symlink)
3778abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang
3788abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang    def close(self):
3798abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        """Cleanup by restoring any symlink, file, or directory if necessary."""
3808abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        # Remove the symlink that the test created.
3818abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        if self.new_symlink:
3828abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            os.remove(self.gtk_symlink)
3838abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang
3848abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        # Restore the original symlink.
3858abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        if self.original_gtk_realpath:
3868abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            os.symlink(self.original_gtk_realpath, self.gtk_symlink)
3878abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        # Restore the original file or directory.
3888abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        elif self.moved_flag:
3898abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            tmp_gtk_path = os.path.join(self.tmp, self.gtk_version)
3908abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang            if (os.path.isdir(tmp_gtk_path) or os.path.isfile(tmp_gtk_path)):
3918abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang                shutil.move(tmp_gtk_path, os.path.dirname(self.gtk_symlink))
3928abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang                self.moved_flag = False
3938abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang                shutil.rmtree(self.tmp)
3948abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang
395f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang    def register_callback(self, event, callback):
396f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        """Register a callback function for an event."""
397f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.win.connect(event, callback)
398f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
399f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang    def register_timeout_add(self, callback, timeout):
400f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        """Register a callback function for gobject.timeout_add."""
401f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        return gobject.timeout_add(timeout, callback)
402f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
403ed82f0322e54196f116566309594b7e5f958fae4Joseph Hwang    def register_io_add_watch(self, callback, fd, data=None,
404f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang                              condition=gobject.IO_IN):
405f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        """Register a callback function for gobject.io_add_watch."""
406ed82f0322e54196f116566309594b7e5f958fae4Joseph Hwang        if data:
407ed82f0322e54196f116566309594b7e5f958fae4Joseph Hwang            return gobject.io_add_watch(fd, condition, callback, data)
408ed82f0322e54196f116566309594b7e5f958fae4Joseph Hwang        else:
409ed82f0322e54196f116566309594b7e5f958fae4Joseph Hwang            return gobject.io_add_watch(fd, condition, callback)
410f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
4115a61f5a19dfde799a3ca407e761f400184ae5fddJoseph Hwang    def create_key_press_event(self, keyval):
4125a61f5a19dfde799a3ca407e761f400184ae5fddJoseph Hwang        """Create a key_press_event."""
4135a61f5a19dfde799a3ca407e761f400184ae5fddJoseph Hwang        event = gtk.gdk.Event(gtk.gdk.KEY_PRESS)
4145a61f5a19dfde799a3ca407e761f400184ae5fddJoseph Hwang        # Assign current time to the event
4155a61f5a19dfde799a3ca407e761f400184ae5fddJoseph Hwang        event.time = 0
4165a61f5a19dfde799a3ca407e761f400184ae5fddJoseph Hwang        event.keyval = keyval
4175a61f5a19dfde799a3ca407e761f400184ae5fddJoseph Hwang        self.win.emit('key_press_event', event)
4185a61f5a19dfde799a3ca407e761f400184ae5fddJoseph Hwang
419f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang    def remove_event_source(self, tag):
420f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        """Remove the registered callback."""
421f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        gobject.source_remove(tag)
422f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
423f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang    def delete_event(self, widget, event, data=None):
424f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        """A handler to exit the window."""
425f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.stop()
426f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        return False
427f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
428f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang    def set_gesture_name(self, string, color='blue'):
429f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        """A helper method to set gesture name."""
430f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.prompt_frame.set_gesture_name(string, color)
431f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
432f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang    def set_prompt(self, string, color='black'):
433f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        """A helper method to set the prompt."""
434f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.prompt_frame.set_prompt(string, color)
435f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
436f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang    def set_choice(self, string):
437f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        """A helper method to set the choice."""
438f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.prompt_frame.set_choice(string)
439f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
440f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang    def set_result(self, text):
441f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        """A helper method to set the text in the result."""
442f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.result_frame.set_result(text)
443f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
444f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang    def set_image(self, filename):
445f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        """Set an image in the image frame."""
446f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        self.image_frame.set_from_file(filename)
447f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
4485a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang    def scroll(self, choice):
4495a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        """Scroll the result frame using the choice key."""
4505a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        self.result_frame.scroll(choice)
4515a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang
4521fd993155b6d1c6996daafd5789101d3342c7bc9Joseph Hwang    def stop(self):
453f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        """Quit the window."""
4548abd550453a67f7abb6eb2042ed4298dd10ec5b0Joseph Hwang        self.close()
455f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        gtk.main_quit()
456f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang
457f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang    def main(self):
458f20171fad5962655690812bfed2b76ec9001ae55Joseph Hwang        """Main function of the window."""
459f669f3062ea1192553f2305432c568ad1eb64217Joseph Hwang        try:
460f669f3062ea1192553f2305432c568ad1eb64217Joseph Hwang            gtk.main()
461f669f3062ea1192553f2305432c568ad1eb64217Joseph Hwang        except KeyboardInterrupt:
462f669f3062ea1192553f2305432c568ad1eb64217Joseph Hwang            self.close()
463