Phil Dibowitz wrote:
> Stephen Warren wrote:
>> Version 6 of congruity is released.
>>
>> congruity is a Python/wxPython-based GUI for libconcord. It is intended
>> to handle downloads from the Harmony website, program the remote, and
>> communicate results back to the website - i.e. provide an open-source
>> replacement for the website interaction portion of the existing
>> proprietary GUI application.
> 
> I'm getting a syntax error - though I have made no attempt to debug it:
> 
>   [EMAIL PROTECTED] congruity-6]$ congruity
>     File "/usr/bin/congruity", line 58
>       finally:
>             ^
>   SyntaxError: invalid syntax

Try the attached congruity script. I may have been relying on Python 2.5
syntax (I guess I should test with an older Python version sometime...)
#!/usr/bin/python

# This code NOT copyright Stephen Warren <[EMAIL PROTECTED]>
# This code is released into the public domain.

from ctypes import *
import os
import os.path
import sys
import thread
import time
import traceback
import wx
import wx.lib.dialogs

import libconcord

version = "6+"

def program_callback_imp(count, current, total, cb_func):
    if not cb_func:
        return

    try:
        cb_func(False, (current * 100) / total)
    except:
        print
        traceback.print_exc()

def exception_message():
    msg = ''
    if type(sys.exc_value) == libconcord.LibConcordException:
        msg += sys.exc_value.result_str + '\n\n'
    msg += traceback.format_exc()
    return msg

def program(type, xml, xml_size, handler):
    action_func = {
        libconcord.LC_FILE_TYPE_CONNECTIVITY:  program_body_connectivity,
        libconcord.LC_FILE_TYPE_CONFIGURATION: program_body_configuration,
        libconcord.LC_FILE_TYPE_FIRMWARE:      program_body_firmware
    }[type]

    need_deinit = [False]

    try:
        try:
            program_callback = libconcord.callback_type(program_callback_imp)

            program_body_connect(program_callback, handler, need_deinit)
            set_time = action_func(xml, xml_size, program_callback, handler)
            need_deinit[0] = False
            program_body_disconnect(program_callback, handler)
            if set_time:
                program_body_reconnect_set_time(program_callback, handler, 
need_deinit)
            handler.on_final_status(True, '')
        except:
            handler.on_final_status(False, exception_message())
    finally:
        try:
            if need_de_init[0]:
                program_disconnect(handler)
        except:
            pass

def program_body_connect(program_callback, handler, need_deinit):
    if handler:
        handler.on_requesting_id(False, 0)

    libconcord.init_concord()
    need_deinit[0] = True

    if handler:
        handler_f = py_object(handler.on_requesting_id)
    else:
        handler_f = None
    libconcord.get_identity(
        program_callback,
        handler_f
    )
    sys.stdout.flush()

    if handler:
        handler.on_requesting_id(True, 100)

def program_body_connectivity(xml, xml_size, program_callback, handler):
    handler.on_web_notify_final(False, 0)
    libconcord.post_connect_test_success(xml, xml_size)
    handler.on_web_notify_final(True, 100)
    return False

def program_body_configuration(xml, xml_size, program_callback, handler):
    handler.on_web_notify_init(False, 0)
    libconcord.post_preconfig(xml, xml_size)
    handler.on_web_notify_init(True, 100)

    handler.on_prepare(False, 0)

    bin_data = POINTER(c_ubyte)()
    bin_size = c_uint()
    libconcord.find_config_binary(
        xml,
        xml_size,
        byref(bin_data),
        byref(bin_size)
    )

    handler.on_prepare(False, 50)
    libconcord.invalidate_flash()
    handler.on_prepare(True, 100)

    handler.on_erasing_flash(False, 0)
    libconcord.erase_config(
        bin_size,
        program_callback,
        py_object(handler.on_erasing_flash)
    )
    handler.on_erasing_flash(True, 100)

    handler.on_writing_config(False, 0)
    libconcord.write_config_to_remote(
        bin_data,
        bin_size,
        program_callback,
        py_object(handler.on_writing_config)
    )
    handler.on_writing_config(True, 100)

    handler.on_verifying_config(False, 0)
    libconcord.verify_remote_config(
        bin_data,
        bin_size,
        program_callback,
        py_object(handler.on_verifying_config)
    )
    handler.on_verifying_config(True, 100)

    handler.on_web_notify_final(False, 0)
    libconcord.post_postconfig(xml, xml_size)
    handler.on_web_notify_final(True, 100)

    libconcord.reset_remote()

    return True

def program_body_firmware(xml, xml_size, program_callback, handler):
    handler.on_prepare(False, 0)

    # is_fw_update_supported returns error code; 0 OK, otherwise failure
    if not libconcord.is_fw_update_supported(0):
        is_direct = False
    elif not libconcord.is_fw_update_supported(1):
        is_direct = True
    else:
        raise Exception('Firmware update not supported on this remote.')

    handler.on_prepare(True, 25)

    bin_data = POINTER(c_ubyte)()
    bin_size = c_uint()
    libconcord.extract_firmware_binary(
        xml,
        xml_size,
        byref(bin_data),
        byref(bin_size)
    )

    handler.on_prepare(True, 50)
    if not is_direct:
        libconcord.prep_firmware()
    handler.on_prepare(True, 75)
    libconcord.invalidate_flash()
    handler.on_prepare(True, 100)

    handler.on_erasing_flash(False, 0)
    libconcord.erase_firmware(
        is_direct,
        program_callback,
        py_object(handler.on_erasing_flash)
    )
    handler.on_erasing_flash(True, 100)

    handler.on_writing_config(False, 0)
    libconcord.write_firmware_to_remote(
        bin_data,
        bin_size,
        c_int(is_direct and 1 or 0),
        program_callback,
        py_object(handler.on_writing_config)
    )
    handler.on_writing_config(True, 100)

    handler.on_finalize(False, 0)
    if not is_direct:
        libconcord.finish_firmware()
    handler.on_finalize(True, 100)

    handler.on_web_notify_final(False, 0)
    libconcord.post_postfirmware(xml, xml_size)
    handler.on_web_notify_final(True, 100)

    libconcord.reset_remote()

    return True

def program_body_disconnect(program_callback, handler):
    libconcord.deinit_concord()

def program_body_reconnect_set_time(program_callback, handler, need_deinit):
    max_attempts = 60
    connected = False
    for attempt in range(max_attempts):
        handler.on_reconnect(False, (attempt * 100) / max_attempts)
        try:
            program_body_connect(program_callback, None, need_deinit)
            connected = True
            break
        except:
            pass
        time.sleep(1)
    if not connected:
        # Force exception if we still can't connect
        program_body_connect(program_callback, None, need_deinit)
    handler.on_reconnect(True, 100)

    handler.on_set_time(False, 0)
    libconcord.set_time()
    handler.on_set_time(True, 100)

    need_deinit[0] = False
    program_body_disconnect(program_callback, handler)

class HandlerProxy(object):
    def __init__(self, real_handler):
        self.real_handler = real_handler

    def on_requesting_id(self, is_done, percent):
        wx.CallAfter(self.real_handler.on_requesting_id, is_done, percent)

    def on_web_notify_init(self, is_done, percent):
        wx.CallAfter(self.real_handler.on_web_notify_init, is_done, percent)

    def on_prepare(self, is_done, percent):
        wx.CallAfter(self.real_handler.on_prepare, is_done, percent)

    def on_erasing_flash(self, is_done, percent):
        wx.CallAfter(self.real_handler.on_erasing_flash, is_done, percent)

    def on_writing_config(self, is_done, percent):
        wx.CallAfter(self.real_handler.on_writing_config, is_done, percent)

    def on_verifying_config(self, is_done, percent):
        wx.CallAfter(self.real_handler.on_verifying_config, is_done, percent)

    def on_finalize(self, is_done, percent):
        wx.CallAfter(self.real_handler.on_finalize, is_done, percent)

    def on_web_notify_final(self, is_done, percent):
        wx.CallAfter(self.real_handler.on_web_notify_final, is_done, percent)

    def on_reconnect(self, is_done, percent):
        wx.CallAfter(self.real_handler.on_reconnect, is_done, percent)

    def on_set_time(self, is_done, percent):
        wx.CallAfter(self.real_handler.on_set_time, is_done, percent)

    def on_final_status(self, was_success, log):
        wx.CallAfter(self.real_handler.on_final_status, was_success, log)

class MessagePanelBase(wx.Panel):
    def __init__(self, parent, resources, message):
        self.resources = resources

        wx.Panel.__init__(self, parent)

        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.text_message = wx.StaticText(self, -1, message)
        self.text_message.Wrap(450)
        self.sizer.Add(self.text_message, 0, wx.ALIGN_LEFT | wx.ALL, 5)
        self.SetSizer(self.sizer)

    def OnActivated(self):
        pass

class WelcomePanel(MessagePanelBase):
    def __init__(self, parent, resources):
        MessagePanelBase.__init__(
            self,
            parent,
            resources,
            "Welcome to congruity; a programming application " + 
            "for Logitech Harmony remote controls.\n\n" +
            "Click Next to start programming."
        )

    def GetTitle(self):
        return "Welcome"

    def IsTerminal(self):
        return False

    def GetExitCode(self):
        return None

    def IsNextInitiallyDisabled(self):
        return False

    def IsCancelable(self):
        return True

    def GetNext(self):
        try:
            xml = POINTER(c_ubyte)()
            xml_size = c_uint()
            libconcord.read_file(
                self.resources.ezhex_filename,
                byref(xml),
                byref(xml_size)
            )
            self.resources.SetXmlData(xml, xml_size)
        except:
            self.resources.page_failure.SetMessages(
                "The programming file cannot be read. " +
                "Programming cannot continue.",
                exception_message()
            )
            return self.resources.page_failure

        try:
            type = c_int()
            libconcord.identify_file(xml, xml_size, byref(type))

            next_page = {
                libconcord.LC_FILE_TYPE_CONNECTIVITY:
                    self.resources.page_check_connectivity,
                libconcord.LC_FILE_TYPE_CONFIGURATION:
                    self.resources.page_write_configuration,
                libconcord.LC_FILE_TYPE_FIRMWARE:
                    self.resources.page_update_firmware
            }.get(type.value, None)

            if next_page:
                return next_page

            self.resources.page_failure.SetMessages(
                "This kind of programming file is not currently " +
                "supported. Programming cannot continue.",
                None
            )
            return self.resources.page_failure
        except:
            self.resources.page_failure.SetMessages(
                "The programming file could not be parsed " +
                "to determine its type. Programming cannot continue.",
                exception_message()
            )
            return self.resources.page_failure

class ProgramRemotePanelBase(wx.Panel):
    def __init__(self, parent, resources, file_type):
        self.parent = parent
        self.resources = resources
        self.file_type = file_type

        wx.Panel.__init__(self, parent)

        sizer = wx.GridBagSizer(5, 5)

        iw = max(
            resources.icon_in_progress.GetWidth(),
            resources.icon_complete.GetWidth(),
            resources.icon_failed.GetWidth()
        )
        ih = max(
            resources.icon_in_progress.GetHeight(),
            resources.icon_complete.GetHeight(),
            resources.icon_failed.GetHeight()
        )
        iwh = (iw, ih)

        vpos = 0

        self.bmp_identify = wx.StaticBitmap(self, -1, resources.icon_unstarted, 
None, iwh)
        self.text_identify = wx.StaticText(self, -1, "Identify Remote")
        self.gauge_identify = wx.Gauge(self, -1, 100, None, (250, ih))
        sizer.Add(self.bmp_identify,   (vpos, 0), (1, 1), wx.ALIGN_LEFT | 
wx.ALIGN_BOTTOM          | wx.ALL, 5)
        sizer.Add(self.text_identify,  (vpos, 1), (1, 1), wx.ALIGN_LEFT | 
wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
        sizer.Add(self.gauge_identify, (vpos, 2), (1, 1), wx.ALIGN_LEFT | 
wx.ALIGN_BOTTOM          | wx.ALL, 5)
        vpos += 1

        if self.file_type == libconcord.LC_FILE_TYPE_CONFIGURATION:
            self.bmp_web_notify_init = wx.StaticBitmap(self, -1, 
resources.icon_unstarted, None, iwh)
            self.text_web_notify_init = wx.StaticText(self, -1, "Check Website")
            self.gauge_web_notify_init = wx.Gauge(self, -1, 100, None, (250, 
ih))
            sizer.Add(self.bmp_web_notify_init,   (vpos, 0), (1, 1), 
wx.ALIGN_LEFT | wx.ALIGN_BOTTOM          | wx.ALL, 5)
            sizer.Add(self.text_web_notify_init,  (vpos, 1), (1, 1), 
wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
            sizer.Add(self.gauge_web_notify_init, (vpos, 2), (1, 1), 
wx.ALIGN_LEFT | wx.ALIGN_BOTTOM          | wx.ALL, 5)
            vpos += 1

        if self.file_type != libconcord.LC_FILE_TYPE_CONNECTIVITY:
            self.bmp_prepare = wx.StaticBitmap(self, -1, 
resources.icon_unstarted, None, iwh)
            self.text_prepare = wx.StaticText(self, -1, "Prepare Remote")
            self.gauge_prepare = wx.Gauge(self, -1, 100, None, (250, ih))
            sizer.Add(self.bmp_prepare,   (vpos, 0), (1, 1), wx.ALIGN_LEFT | 
wx.ALIGN_BOTTOM          | wx.ALL, 5)
            sizer.Add(self.text_prepare,  (vpos, 1), (1, 1), wx.ALIGN_LEFT | 
wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
            sizer.Add(self.gauge_prepare, (vpos, 2), (1, 1), wx.ALIGN_LEFT | 
wx.ALIGN_BOTTOM          | wx.ALL, 5)
            vpos += 1

            self.bmp_erase_flash = wx.StaticBitmap(self, -1, 
resources.icon_unstarted, None, iwh)
            self.text_erase_flash = wx.StaticText(self, -1, "Erase Flash")
            self.gauge_erase_flash = wx.Gauge(self, -1, 100, None, (250, ih))
            sizer.Add(self.bmp_erase_flash,   (vpos, 0), (1, 1), wx.ALIGN_LEFT 
| wx.ALIGN_BOTTOM          | wx.ALL, 5)
            sizer.Add(self.text_erase_flash,  (vpos, 1), (1, 1), wx.ALIGN_LEFT 
| wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
            sizer.Add(self.gauge_erase_flash, (vpos, 2), (1, 1), wx.ALIGN_LEFT 
| wx.ALIGN_BOTTOM          | wx.ALL, 5)
            vpos += 1

            if self.file_type == libconcord.LC_FILE_TYPE_CONFIGURATION:
                write_type = "Configuration"
            else:
                write_type = "Firmware"

            self.bmp_write_config = wx.StaticBitmap(self, -1, 
resources.icon_unstarted, None, iwh)
            self.text_write_config = wx.StaticText(self, -1, "Write " + 
write_type)
            self.gauge_write_config = wx.Gauge(self, -1, 100, None, (250, ih))
            sizer.Add(self.bmp_write_config,   (vpos, 0), (1, 1), wx.ALIGN_LEFT 
| wx.ALIGN_BOTTOM          | wx.ALL, 5)
            sizer.Add(self.text_write_config,  (vpos, 1), (1, 1), wx.ALIGN_LEFT 
| wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
            sizer.Add(self.gauge_write_config, (vpos, 2), (1, 1), wx.ALIGN_LEFT 
| wx.ALIGN_BOTTOM          | wx.ALL, 5)
            vpos += 1

        if self.file_type == libconcord.LC_FILE_TYPE_CONFIGURATION:
            self.bmp_verify_config = wx.StaticBitmap(self, -1, 
resources.icon_unstarted, None, iwh)
            self.text_verify_config = wx.StaticText(self, -1, "Verify Upgrade")
            self.gauge_verify_config = wx.Gauge(self, -1, 100, None, (250, ih))
            sizer.Add(self.bmp_verify_config,   (vpos, 0), (1, 1), 
wx.ALIGN_LEFT | wx.ALIGN_BOTTOM          | wx.ALL, 5)
            sizer.Add(self.text_verify_config,  (vpos, 1), (1, 1), 
wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
            sizer.Add(self.gauge_verify_config, (vpos, 2), (1, 1), 
wx.ALIGN_LEFT | wx.ALIGN_BOTTOM          | wx.ALL, 5)
            vpos += 1

        if self.file_type == libconcord.LC_FILE_TYPE_FIRMWARE:
            self.bmp_finalize = wx.StaticBitmap(self, -1, 
resources.icon_unstarted, None, iwh)
            self.text_finalize = wx.StaticText(self, -1, "Finalize Programming")
            self.gauge_finalize = wx.Gauge(self, -1, 100, None, (250, ih))
            sizer.Add(self.bmp_finalize,   (vpos, 0), (1, 1), wx.ALIGN_LEFT | 
wx.ALIGN_BOTTOM          | wx.ALL, 5)
            sizer.Add(self.text_finalize,  (vpos, 1), (1, 1), wx.ALIGN_LEFT | 
wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
            sizer.Add(self.gauge_finalize, (vpos, 2), (1, 1), wx.ALIGN_LEFT | 
wx.ALIGN_BOTTOM          | wx.ALL, 5)
            vpos += 1

        self.bmp_web_notify_final = wx.StaticBitmap(self, -1, 
resources.icon_unstarted, None, iwh)
        self.text_web_notify_final = wx.StaticText(self, -1, "Notify Website")
        self.gauge_web_notify_final = wx.Gauge(self, -1, 100, None, (250, ih))
        sizer.Add(self.bmp_web_notify_final,   (vpos, 0), (1, 1), wx.ALIGN_LEFT 
| wx.ALIGN_BOTTOM          | wx.ALL, 5)
        sizer.Add(self.text_web_notify_final,  (vpos, 1), (1, 1), wx.ALIGN_LEFT 
| wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
        sizer.Add(self.gauge_web_notify_final, (vpos, 2), (1, 1), wx.ALIGN_LEFT 
| wx.ALIGN_BOTTOM          | wx.ALL, 5)
        vpos += 1

        if self.file_type != libconcord.LC_FILE_TYPE_CONNECTIVITY:
            self.bmp_reconnect = wx.StaticBitmap(self, -1, 
resources.icon_unstarted, None, iwh)
            self.text_reconnect = wx.StaticText(self, -1, "Reconnect to Remote")
            self.gauge_reconnect = wx.Gauge(self, -1, 100, None, (250, ih))
            sizer.Add(self.bmp_reconnect,   (vpos, 0), (1, 1), wx.ALIGN_LEFT | 
wx.ALIGN_BOTTOM          | wx.ALL, 5)
            sizer.Add(self.text_reconnect,  (vpos, 1), (1, 1), wx.ALIGN_LEFT | 
wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
            sizer.Add(self.gauge_reconnect, (vpos, 2), (1, 1), wx.ALIGN_LEFT | 
wx.ALIGN_BOTTOM          | wx.ALL, 5)
            vpos += 1

            self.bmp_set_time = wx.StaticBitmap(self, -1, 
resources.icon_unstarted, None, iwh)
            self.text_set_time = wx.StaticText(self, -1, "Set Time")
            self.gauge_set_time = wx.Gauge(self, -1, 100, None, (250, ih))
            sizer.Add(self.bmp_set_time,   (vpos, 0), (1, 1), wx.ALIGN_LEFT | 
wx.ALIGN_BOTTOM          | wx.ALL, 5)
            sizer.Add(self.text_set_time,  (vpos, 1), (1, 1), wx.ALIGN_LEFT | 
wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
            sizer.Add(self.gauge_set_time, (vpos, 2), (1, 1), wx.ALIGN_LEFT | 
wx.ALIGN_BOTTOM          | wx.ALL, 5)
            vpos += 1

        sizer.AddGrowableCol(2)

        self.SetSizerAndFit(sizer)

        self.failure_bitmap = None
        self.was_success = False

    def _worker_function(self):
        handler = HandlerProxy(self)
        program(self.file_type, self.resources.xml, self.resources.xml_size, 
handler)

    def _on_progress(self, is_done, percent, bitmap, gauge):
        if is_done:
            bitmap.SetBitmap(self.resources.icon_complete)
        else:
            bitmap.SetBitmap(self.resources.icon_in_progress)
        self.failure_bitmap = bitmap
        gauge.SetValue(percent)

    def on_requesting_id(self, is_done, percent):
        self._on_progress(is_done, percent, self.bmp_identify, 
self.gauge_identify)

    def on_web_notify_init(self, is_done, percent):
        self._on_progress(is_done, percent, self.bmp_web_notify_init, 
self.gauge_web_notify_init)

    def on_prepare(self, is_done, percent):
        if self.file_type == libconcord.LC_FILE_TYPE_CONNECTIVITY:
            return
        self._on_progress(is_done, percent, self.bmp_prepare, 
self.gauge_prepare)

    def on_erasing_flash(self, is_done, percent):
        if self.file_type == libconcord.LC_FILE_TYPE_CONNECTIVITY:
            return
        self._on_progress(is_done, percent, self.bmp_erase_flash, 
self.gauge_erase_flash)

    def on_writing_config(self, is_done, percent):
        if self.file_type == libconcord.LC_FILE_TYPE_CONNECTIVITY:
            return
        self._on_progress(is_done, percent, self.bmp_write_config, 
self.gauge_write_config)

    def on_verifying_config(self, is_done, percent):
        if self.file_type == libconcord.LC_FILE_TYPE_CONNECTIVITY:
            return
        self._on_progress(is_done, percent, self.bmp_verify_config, 
self.gauge_verify_config)

    def on_finalize(self, is_done, percent):
        if self.file_type == libconcord.LC_FILE_TYPE_CONNECTIVITY:
            return
        self._on_progress(is_done, percent, self.bmp_finalize, 
self.gauge_finalize)

    def on_web_notify_final(self, is_done, percent):
        self._on_progress(is_done, percent, self.bmp_web_notify_final, 
self.gauge_web_notify_final)

    def on_reconnect(self, is_done, percent):
        self._on_progress(is_done, percent, self.bmp_reconnect, 
self.gauge_reconnect)

    def on_set_time(self, is_done, percent):
        self._on_progress(is_done, percent, self.bmp_set_time, 
self.gauge_set_time)

    def on_final_status(self, was_success, log):
        if not was_success:
            bitmap = self.failure_bitmap
            if not bitmap:
                bitmap = self.bmp_identify
            bitmap.SetBitmap(self.resources.icon_failed)
            self.resources.page_failure.SetMessages('Programming failed.', log)
        self.was_success = was_success
        self.parent.ReenableNext()

    def OnActivated(self):
        thread.start_new_thread(self._worker_function, ())

    def GetTitle(self):
        return {
            libconcord.LC_FILE_TYPE_CONNECTIVITY:  "Checking Connectivity",
            libconcord.LC_FILE_TYPE_CONFIGURATION: "Updating Configuration",
            libconcord.LC_FILE_TYPE_FIRMWARE:      "Updating Firmware"
        }[self.file_type]

    def IsTerminal(self):
        return False

    def GetExitCode(self):
        return None

    def IsNextInitiallyDisabled(self):
        return True

    def IsCancelable(self):
        return False

    def GetNext(self):
        if self.was_success:
            return self.resources.page_success
        else:
            return self.resources.page_failure

class CheckConnectivityPanel(ProgramRemotePanelBase):
    def __init__(self, parent, resources):
        ProgramRemotePanelBase.__init__(self, parent, resources, 
libconcord.LC_FILE_TYPE_CONNECTIVITY)

class WriteConfigurationPanel(ProgramRemotePanelBase):
    def __init__(self, parent, resources):
        ProgramRemotePanelBase.__init__(self, parent, resources, 
libconcord.LC_FILE_TYPE_CONFIGURATION)

class UpdateFirmwarePanel(ProgramRemotePanelBase):
    def __init__(self, parent, resources):
        ProgramRemotePanelBase.__init__(self, parent, resources, 
libconcord.LC_FILE_TYPE_FIRMWARE)

class SuccessPanel(MessagePanelBase):
    def __init__(self, parent, resources):
        MessagePanelBase.__init__(self, parent, resources, "Programming has 
completed successfully.")

    def OnActivated(self):
        pass

    def GetTitle(self):
        return "Success"

    def IsTerminal(self):
        return True

    def GetExitCode(self):
        return 0

    def IsNextInitiallyDisabled(self):
        return False

    def IsCancelable(self):
        return False

    def GetNext(self):
        return None

class FailurePanel(wx.Panel):
    def __init__(self, parent, resources):
        self.parent = parent
        self.resources = resources

        wx.Panel.__init__(self, parent)

        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.text_message = wx.StaticText(self, -1, "Programming failed; see 
below for details")
        self.text_message.Wrap(450)
        self.sizer.Add(self.text_message, 0, wx.ALIGN_LEFT | wx.ALL, 5)
        self.btn_details = wx.Button(self, -1, "&Details...")
        self.Bind(wx.EVT_BUTTON, self.OnShowLogText, self.btn_details)
        self.sizer.Add(self.btn_details, 0, wx.ALIGN_LEFT | wx.ALL, 5)
        self.SetSizerAndFit(self.sizer)
        self.log_text = ''

    def SetMessages(self, message, traceback):
        msg = message
        if traceback:
            msg += "\n\nSee below for details."
        else:
            self.btn_details.Hide()
        self.text_message.SetLabel(msg)
        self.log_text = traceback

    def OnShowLogText(self, event):
        size = self.parent.GetClientSizeTuple()
        size = (size[0] * 90 / 100, size[1] * 90 / 100)
        wx.lib.dialogs.ScrolledMessageDialog(self, self.log_text, "Error Log", 
(-1, -1), size).ShowModal()
        self.parent.ReenableNext()

    def OnActivated(self):
        self.btn_details.SetFocus()

    def GetTitle(self):
        return "Failure"

    def IsTerminal(self):
        return True

    def GetExitCode(self):
        return 1

    def IsNextInitiallyDisabled(self):
        return False

    def IsCancelable(self):
        return False

    def GetNext(self):
        return None

class Wizard(wx.Dialog):
    def __init__(self, resources):
        wx.Dialog.__init__(self, None, -1, 'Congruity version ' + version)

        sizer_main = wx.BoxSizer(wx.VERTICAL)

        sizer_top = wx.BoxSizer(wx.HORIZONTAL)
        bitmap = wx.StaticBitmap(self, -1, resources.img_remote)
        sizer_top.Add(bitmap, 0, wx.EXPAND | wx.ALL, 5)

        self.sizer_top_right = wx.BoxSizer(wx.VERTICAL)
        self.title = wx.StaticText(self, -1, "Title")
        font = wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD)
        self.title.SetFont(font)
        self.sizer_top_right.Add(self.title, 0, wx.EXPAND)
        divider_top_right = wx.StaticLine(self, -1, None, None, 
wx.LI_HORIZONTAL)
        self.sizer_top_right.Add(divider_top_right, 0, wx.EXPAND)

        sizer_top.Add(self.sizer_top_right, 1, wx.EXPAND | wx.ALL, 5)
        sizer_main.Add(sizer_top, 1, wx.EXPAND | wx.ALL, 5)

        divider_main = wx.StaticLine(self, -1, None, None, wx.LI_HORIZONTAL)
        sizer_main.Add(divider_main, 0, wx.EXPAND | wx.ALL, 5)

        sizer_buttons = wx.BoxSizer(wx.HORIZONTAL)
        panel_btn_dummy = wx.Panel(self)
        sizer_buttons.Add(panel_btn_dummy, 1, wx.EXPAND | wx.ALL, 5)
        self.btn_next = wx.Button(self, -1, "&Next >")
        self.Bind(wx.EVT_BUTTON, self._OnNext, self.btn_next)
        sizer_buttons.Add(self.btn_next, 0, wx.EXPAND | wx.ALL, 5)
        self.btn_cancel = wx.Button(self, -1, "&Cancel")
        self.Bind(wx.EVT_BUTTON, self._OnCancel, self.btn_cancel)
        sizer_buttons.Add(self.btn_cancel, 0, wx.EXPAND | wx.ALL, 5)
        sizer_main.Add(sizer_buttons, 0, wx.EXPAND | wx.ALL, 5)

        self.SetSizerAndFit(sizer_main)

        self.cur_page = None

    def SetPages(self, pages):
        def tuple_max(a, b):
            return (max(a[0], b[0]), max(a[1], b[1]))

        self.pages = pages

        for page in self.pages:
            page.Hide()

        self.size_wiz = self.GetSizeTuple()
        for page in self.pages:
            page.Show()
            self.sizer_top_right.Add(page, 1, wx.EXPAND)
            self.Fit()
            size_page = self.GetSizeTuple()
            self.size_wiz = tuple_max(self.size_wiz, size_page)
            page.Hide()
            self.sizer_top_right.Remove(page)

        self.SetSize(self.size_wiz)

    def SetInitialPage(self, page):
        if self.cur_page:
            raise Exception("Current page already set")
        self._SetPage(page)

    def ReenableNext(self):
        self.btn_next.Enable(True)
        self.btn_next.SetFocus()

    def _OnNext(self, event):
        if self.cur_page.IsTerminal():
            os._exit(self.cur_page.GetExitCode())
        next_page = self.cur_page.GetNext()
        self._SetPage(next_page)

    def _OnCancel(self, event):
        os._exit(0)

    def _SetPage(self, page):
        if not page in self.pages:
            raise Exception("Invalid page")

        if self.cur_page:
            self.cur_page.Hide()
            self.sizer_top_right.Remove(self.cur_page)

        self.cur_page = page

        self.cur_page.Show()
        self.sizer_top_right.Add(self.cur_page, 1, wx.EXPAND)

        self.title.SetLabel(self.cur_page.GetTitle())

        self.Layout()

        is_terminal = self.cur_page.IsTerminal()
        if is_terminal:
            self.btn_next.SetLabel("&Finish")
        else:
            self.btn_next.SetLabel("&Next >")

        self.btn_next.Enable(not self.cur_page.IsNextInitiallyDisabled())
        self.btn_cancel.Enable((not is_terminal) and 
self.cur_page.IsCancelable())
        self.btn_next.SetFocus()

        self.cur_page.OnActivated()

class Resources(object):
    def __init__(self, appdir):
        self.appdir = appdir

    def SetEzHexFilename(self, ezhex_filename):
        self.ezhex_filename = ezhex_filename

    def LoadImages(self):
        def load(filename, appdir = self.appdir):
            dirs = ['/usr/share/congruity', appdir, '.']
            for dir in dirs:
                fpath = os.path.join(dir, filename)
                if not os.path.isfile(fpath):
                    continue
                print fpath
                return wx.Image(fpath, wx.BITMAP_TYPE_PNG).ConvertToBitmap()
            raise Exception("Can't load " + filename)

        self.img_remote       = load("remote.png")
        self.icon_unstarted   = load("icon-unstarted.png")
        self.icon_in_progress = load("icon-in-progress.png")
        self.icon_complete    = load("icon-complete.png")
        self.icon_failed      = load("icon-failed.png")

    def CreatePages(self, wizard):
        self.page_welcome = WelcomePanel(wizard, self)
        self.page_check_connectivity = CheckConnectivityPanel(wizard, self)
        self.page_write_configuration = WriteConfigurationPanel(wizard, self)
        self.page_update_firmware = UpdateFirmwarePanel(wizard, self)
        self.page_success = SuccessPanel(wizard, self)
        self.page_failure = FailurePanel(wizard, self)

    def SetXmlData(self, xml, xml_size):
        self.xml = xml
        self.xml_size = xml_size

def main(argv):
    app = argv.pop(0)
    appdir = os.path.dirname(app)

    while len(argv) and argv[0].startswith('-'):
        arg = argv.pop(0)
        if arg == '--version':
            print version
            sys.exit(0)
        else:
            print >>sys.stderr, "ERROR: Option '%s' not recognized" % arg
            sys.exit(0)
    if len(argv) != 1:
        print >>sys.stderr, "ERROR: Precisely one filename argument is required"
        sys.exit(0)
    ezhex_filename = argv.pop(0)

    app = wx.PySimpleApp()
    wx.InitAllImageHandlers()

    resources = Resources(appdir)
    resources.LoadImages()
    resources.SetEzHexFilename(ezhex_filename)

    wizard = Wizard(resources)

    resources.CreatePages(wizard)
    wizard.SetPages([
        resources.page_welcome,
        resources.page_check_connectivity,
        resources.page_write_configuration,
        resources.page_update_firmware,
        resources.page_success,
        resources.page_failure
    ])
    wizard.SetInitialPage(resources.page_welcome)

    wizard.Show()

    app.MainLoop()

if __name__ == "__main__":
    main(sys.argv)

-------------------------------------------------------------------------
This SF.net email is sponsored by the 2008 JavaOne(SM) Conference 
Don't miss this year's exciting event. There's still time to save $100. 
Use priority code J8TL2D2. 
http://ad.doubleclick.net/clk;198757673;13503038;p?http://java.sun.com/javaone
_______________________________________________
concordance-devel mailing list
concordance-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/concordance-devel

Reply via email to