-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On Fri, 29 Aug 2008 21:52:17 +0200
Luca Bruno <[EMAIL PROTECTED]> wrote:

> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
> 
> Hello,
> in reply to the discussion: 
> http://lists.alioth.debian.org/pipermail/reportbug-maint/2008-August/000338.html
> I've been working this day on a GTK+ UI for reportbug.
> A first work is attached. I've completely rewritten the code of the previous 
> gnome-reportbug to use GtkAssistant and using a more OO approach.
> To let gnome2 be imported correctly by reportbug you should apply this patch: 
> http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=497024
> Any hints? Can I continue with the work?
> 
> Best regards,
> 

Continued the work, as usual I've attached the .py file.
It's almost complete for basic bug reporting.
Please let me know what you think about.

- -- 
http://syx.googlecode.com - Smalltalk YX
http://lethalman.blogspot.com - Thoughts about computer technologies
http://www.ammazzatecitutti.org - Ammazzateci tutti
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (GNU/Linux)

iEYEARECAAYFAki4iwYACgkQw9Qj+8Kak3H6/ACglHW9upxm4XcCha5yJwaNlqyV
r2AAnj3P/qm64geCq886STjrfrBdo2NN
=vCiy
-----END PGP SIGNATURE-----
import gtk
import sys
from gtk import gdk
import gobject
import re

gdk.threads_init ()

from Queue import Queue
import threading

from reportbug_ui_text import ewrite
from reportbug_exceptions import NoPackage, NoBugs, NoNetwork, NoReport

ISATTY = True

# Utilities

def highlight (s):
    return '<b>%s</b>' % s

re_markup_free = re.compile ("<.*?>")

def markup_free (s):
    return re_markup_free.sub ("", s)

def create_scrollable (widget):
    scrolled = gtk.ScrolledWindow ()
    scrolled.set_shadow_type (gtk.SHADOW_ETCHED_IN)
    scrolled.set_policy (gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
    scrolled.add (widget)
    return scrolled

# Application

class ReportbugApplication (threading.Thread):
    def __init__ (self):
        threading.Thread.__init__ (self)
        self.queue = Queue ()
        
    def run (self):
        gtk.main ()

    def get_last_value (self):
        return self.queue.get ()

    def put_next_value (self):
        self.queue.put (self.next_value)
        self.next_value = None
    
    def set_next_value (self, value):
        self.next_value = value

    @staticmethod
    def create_idle_callback (func, *args, **kwargs):
        def callback ():
            func (*args, **kwargs)
            return False
        return callback

    def run_once_in_main_thread (self, func, *args, **kwargs):
        gobject.idle_add (self.create_idle_callback (func, *args, **kwargs))

application = ReportbugApplication ()

# Syncronize "pipe" with reportbug

class SyncError (RuntimeError):
    def __init__ (self, result):
        RuntimeError.__init__ ()
        self.result = result

# Pipe

class ReportbugConnector (object):
    def execute_operation (self, *args, **kwargs):
        pass

    def sync_pre_operation (cls, *args, **kwargs):
        return args, kwargs
        
# Assistant

class Page (ReportbugConnector):
    next_page_num = 0
    page_type = gtk.ASSISTANT_PAGE_CONTENT
    default_complete = False
    side_image = "/usr/share/pixmaps/debian-logo.png"

    def __init__ (self, assistant):
        self.assistant = assistant
        self.application = assistant.application
        self.widget = self.create_widget ()
        self.widget.page = self
        self.widget.set_border_width (6)
        self.widget.show_all ()
        self.page_num = Page.next_page_num
        Page.next_page_num += 1

    def execute_operation (self, *args, **kwargs):
        self.switch ()
        self.connect_signals ()
        self.execute (*args, **kwargs)

    def connect_signals (self):
        pass

    def set_page_complete (self, complete):
        self.assistant.set_page_complete (self.widget, complete)

    def set_page_type (self, type):
        self.assistant.set_page_type (self.widget, type)

    def set_page_title (self, title):
        if title:
            self.assistant.set_page_title (self.widget, title)

    def switch (self):
        self.assistant.insert_page (self.widget, self.page_num)
        self.set_page_complete (self.default_complete)
        self.set_page_type (self.page_type)
        self.assistant.set_page_side_image (self.widget, gdk.pixbuf_new_from_file (self.side_image))
        self.assistant.set_next_page (self)

    def is_valid (self, value):
        return bool (value)

    def validate (self, *args, **kwargs):
        value = self.get_value ()
        if self.is_valid (value):
            self.assistant.application.set_next_value (value)
            self.set_page_complete (True)
        else:
            self.set_page_complete (False)

class IntroPage (Page):
    page_type = gtk.ASSISTANT_PAGE_INTRO
    default_complete = True

    def create_widget (self):
        vbox = gtk.VBox ()
        vbox.pack_start (gtk.Label ("ReportBUG"))
        return vbox

class GetStringPage (Page):
    def create_widget (self):
        vbox = gtk.VBox (spacing=12)
        self.label = gtk.Label ()
        self.label.set_line_wrap (True)
        self.entry = gtk.Entry ()
        vbox.pack_start (self.label, expand=False)
        vbox.pack_start (self.entry)
        return vbox

    def connect_signals (self):
        self.entry.connect ('changed', self.validate)

    def get_value (self):
        return self.entry.get_text ()

    def execute (self, prompt, options=None, title=None, force_prompt=False, default='', ui=None):
        self.label.set_text (prompt)
        self.set_page_title (title)

class TreePage (Page):
    value_column = None

    def __init__ (self, *args, **kwargs):
        Page.__init__ (self, *args, **kwargs)
        self.selection = self.view.get_selection()

    def connect_signals (self):
        self.selection.connect ('changed', self.validate)

    def get_value (self):
        model, paths = self.selection.get_selected_rows ()
        multiple = self.selection.get_mode () == gtk.SELECTION_MULTIPLE
        result = []
        for path in paths:
            result.append (markup_free (model.get_value (model.get_iter (path), self.value_column)))
        if not result:
            return
        if not multiple:
            return result[0]
        return result

class MenuPage (TreePage):
    value_column = 0

    def create_widget (self):
        vbox = gtk.VBox (spacing=12)
        self.label = gtk.Label ()
        vbox.pack_start (self.label)

        self.view = gtk.TreeView ()
        scrolled = create_scrollable (self.view)
        vbox.pack_start (scrolled)
        vbox.show_all ()
        return vbox

    def execute (self, par, options, prompt, default=None, title=None, any_ok=False,
                 order=None, extras=None, multiple=False, empty_ok=False):
        self.label.set_text (par)

        self.model = gtk.ListStore (str, str)
        self.view.set_model (self.model)

        if multiple:
            self.selection.set_mode (gtk.SELECTION_MULTIPLE)

        self.view.append_column (gtk.TreeViewColumn ('Option', gtk.CellRendererText (), markup=0))
        self.view.append_column (gtk.TreeViewColumn ('Description', gtk.CellRendererText (), text=1))

        default_iter = None
        if isinstance (options, dict):
            for option, desc in options.iteritems ():
                iter = self.model.append ((highlight (option), desc))
                if option == default:
                    default_iter = iter
        else:
            for row in options:
                iter = self.model.append ((highlight (row[0]), row[1]))
                if row[0] == default:
                    default_iter = iter

        if default_iter:
            self.selection.select_iter (default_iter)
        self.set_page_title (title)

class HandleBTSQueryPage (TreePage):
    default_complete = True
    value_column = 0

    def sync_pre_operation (self, package, bts, mirrors=None, http_proxy="", queryonly=False, screen=None,
                            title="", archived='no', source=False, version=None):
        import debianbts

        sysinfo = debianbts.SYSTEMS[bts]
        root = sysinfo.get('btsroot')
        if not root:
            ewrite("%s bug tracking system has no web URL; bypassing query.\n",
                   sysinfo['name'])
            return

        if isinstance(package, basestring):
            pkgname = package
            if source:
                pkgname += ' (source)'

            progress_label = 'Querying %s bug tracking system for reports on %s' % (debianbts.SYSTEMS[bts]['name'], pkgname)
        else:
            progress_label = 'Querying %s bug tracking system for reports %s' % (debianbts.SYSTEMS[bts]['name'], ' '.join([str(x) for x in package]))


        self.application.run_once_in_main_thread (self.assistant.set_progress_label, progress_label)
        self.application.run_once_in_main_thread (self.set_page_title, title)

        result = None
        try:
            (count, sectitle, hierarchy) = debianbts.get_reports(
                package, bts, mirrors=mirrors, version=version,
                http_proxy=http_proxy, archived=archived, source=source)

            if not count:
                ui.run_wrapper(nullfunc)
                if hierarchy == None:
                    raise NoPackage
                else:
                    raise NoBugs
            else:
                if count > 1:
                    sectitle = '%d bug reports found' % (count,)
                else:
                    sectitle = 'One bug report found'

                report = []
                for category, bugs in hierarchy:
                    buglist = []
                    for bug in bugs:
                        # Skip the '#'
                        bug = bug[1:]
                        bits = re.split(r'[: ]', bug, 2)
                        if len(bits) > 2:
                            id, tag, info = bits
                            info = info.strip()
                            if not info:
                                info = '(no subject)'
                        else:
                            id = '(no id)'
                            tag = '(no tag)'
                            info = bug
                        buglist.append((id, tag, info, None))
                    report.append ((category, buglist))

                return (report,), {'title': sectitle}

        except (IOError, NoNetwork):
            ui.run_wrapper(nullfunc)
            long_message('Unable to connect to %s BTS.', sysinfo['name'],
                         title=title)
        except NoPackage:
            ui.run_wrapper(nullfunc)
            long_message('No record of this package found.', title=title)
            raise NoPackage

        if result and result < 0:
            raise NoReport

        raise SyncReturn (result)

    def create_widget (self):
        vbox = gtk.VBox (spacing=12)
        self.label = gtk.Label ("List of bugs. Select a bug to retrieve and submit more informations.")
        vbox.pack_start (self.label, expand=False)

        self.view = gtk.TreeView ()
        scrolled = create_scrollable (self.view)
        vbox.pack_start (scrolled)
        return vbox

    def is_valid (self, value):
        return True

    def execute (self, buglist, title=None):
        self.model = gtk.TreeStore (str, str, str, str)
        self.view.set_model (self.model)

        self.view.append_column (gtk.TreeViewColumn ('#ID', gtk.CellRendererText (), text=0))
        self.view.append_column (gtk.TreeViewColumn ('Tag', gtk.CellRendererText (), text=1))
        self.view.append_column (gtk.TreeViewColumn ('Subject', gtk.CellRendererText (), text=2))
        self.view.append_column (gtk.TreeViewColumn ('Date', gtk.CellRendererText (), text=3))

        for category in buglist:
            iter = self.model.append (None, (None, None, category[0], None))
            for bug in category[1]:
                self.model.append (iter, bug)

        self.set_page_title (title)

class DisplayReportPage (Page):
    default_complete = True

    def create_widget (self):
        self.view = gtk.TextView ()
        self.view.set_editable (False)
        scrolled = create_scrollable (self.view)
        return scrolled

    def execute (self, message, title=None):
        self.view.get_buffer().set_text (message)
        self.set_page_title (title)

class ProgressPage (Page):
    page_type = gtk.ASSISTANT_PAGE_PROGRESS

    def pulse (self):
        self.progress.pulse ()
        return True

    def create_widget (self):
        vbox = gtk.VBox (spacing=6)
        self.label = gtk.Label ()
        self.progress = gtk.ProgressBar ()
        self.progress.set_pulse_step (0.01)
        vbox.pack_start (self.label, expand=False)
        vbox.pack_start (self.progress, expand=False)
        gobject.timeout_add (10, self.pulse)
        return vbox

    def set_label (self, text):
        self.label.set_text (text)

    def reset_label (self):
        self.set_label ("This operation may take a while")

class ReportbugAssistant (gtk.Assistant):
    def __init__ (self, application):
        gtk.Assistant.__init__ (self)
        self.set_title ('Reportbug')
        self.application = application
        self.showing_page = None
        self.requested_page = None
        self.progress_page = None
        self.set_forward_page_func (self.forward)
        self.connect_signals ()
        self.setup_pages ()

    def connect_signals (self):
        self.connect ('cancel', self.close)
        self.connect ('prepare', self.on_prepare)
        self.connect ('delete-event', self.close)
        
    def on_prepare (self, assistant, widget):
        # If the user goes back then forward, we must ensure the feedback value to reportbug must be sent
        # when the user clicks on "Forward" to the requested page by reportbug
        if self.showing_page and self.showing_page == self.requested_page and self.get_current_page () > self.showing_page.page_num:
            self.application.put_next_value ()
            # Reportbug doesn't support going back, so make widgets insensitive
            self.showing_page.widget.set_sensitive (False)

        self.showing_page = widget.page
        # Some pages might have changed the label in the while
        if self.showing_page == self.progress_page:
            self.progress_page.reset_label ()

    def close (self, *args):
        sys.exit (0)
        
    def forward (self, page_num):
        return page_num + 1

    def set_next_page (self, page):
        self.requested_page = page
        if self.get_current_page () == Page.next_page_num:
            self.set_current_page (page.page_num)

    def set_progress_label (self, text, *args, **kwargs):
        self.progress_page.set_label (text % args)

    def setup_pages (self):
        # We insert pages between the intro and the progress, so that we give the user the feedback
        # that the applications is still running when he presses the "Forward" button
        self.showing_page = IntroPage (self)
        self.progress_page = ProgressPage (self)
        Page.next_page_num = 1
        self.showing_page.switch ()
        self.progress_page.switch ()
        self.set_current_page (0)

assistant = ReportbugAssistant (application)
assistant.show_all ()


# Dialogs


class YesNoDialog (ReportbugConnector, gtk.MessageDialog):
    def __init__ (self, application):
        self.application = application
        gtk.MessageDialog.__init__ (self, assistant, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                                    gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO)
        self.connect ('response', self.on_response)

    def on_response (self, dialog, res):
        self.application.set_next_value (res == gtk.RESPONSE_YES)
        self.application.put_next_value ()
        self.destroy ()

    def execute_operation (self, msg, yeshelp, nohelp, default=True, nowrap=False, ui=None):
        self.set_markup (msg+"?")
        self.show_all ()
                                    
log_message = assistant.set_progress_label
display_failure = ewrite
pages = { 'get_string': GetStringPage,
          'menu': MenuPage,
          'handle_bts_query': HandleBTSQueryPage,
          'display_report': DisplayReportPage }
dialogs = {'yes_no': YesNoDialog}

# Begin the circle

application.start ()

def create_forwarder (parent, klass):
    def func (*args, **kwargs):
        op = klass (parent)
        try:
            args, kwargs = op.sync_pre_operation (*args, **kwargs)
        except SyncError, e:
            return e.result
        application.run_once_in_main_thread (op.execute_operation, *args, **kwargs)
        return application.get_last_value ()
    return func
      
def forward_operations (parent, operations):
    for operation, klass in operations.iteritems ():
        globals()[operation] = create_forwarder (parent, klass)

forward_operations (assistant, pages)
forward_operations (application, dialogs)
_______________________________________________
Reportbug-maint mailing list
Reportbug-maint@lists.alioth.debian.org
http://lists.alioth.debian.org/mailman/listinfo/reportbug-maint

Reply via email to