# Copyright (C) 2003 by Intevation GmbH
# Authors:
# Bernhard Herzog <bh@intevation.de>
#
# This program is free software under the GPL (>=v2)
# Read the file COPYING coming with the software for details.

"""Performance Measurement

This module implements two Thuban commands in a new Profiling menu:

   Profile Screen Render -- Run the screen rendering code in a profile

   Time Screen Render -- Measure the time taken for a complete redraw

See the individual functions for more details.
"""

__version__ = "$Revision: 1.3 $"
# $Source: /thubanrepository/thuban/Extensions/profiling/profiling.py,v $
# $Id: profiling.py,v 1.3 2003/11/03 14:19:40 bh Exp $

import os
import StringIO
import sys
import tempfile
import profile
import time
import pstats

from wxPython.lib.dialogs import wxScrolledMessageDialog

from Thuban import _
from Thuban.UI.command import registry, Command
import Thuban.UI.mainwindow

#
# Customization
#
# Assign to these in your ~/.thuban/thubanstart

# The machine specific profiler bias. See the standard python profile
# module for details on how to find out which value to use.
profiler_bias = 0

# The directory the profile output is to be written to
# (Call mktemp once to initialize tempfile.tempdir)
tempfile.mktemp()
profile_dir = tempfile.tempdir

# Wether to pop up a dialog box with the result.
popup_dialog_box = True


#
#       Timing and profiling a complete redraw
#

def do_redraw(context):
    """Perform a complete redraw in the canvas in context"""
    canvas = context.mainwindow.canvas

    # Make sure there are no no finished bitmaps and active renderer
    canvas.full_redraw()

    # Iterate until all is drawn
    for c in canvas._render_iterator():
        pass


#
# Profiling the redraw
#


def profile_screen_renderer(context):
    """Script to run the redraw in the profiler

    The data gathered by the profiler will be written to
    <TMPDIR>/thuban-render.profile (<TMPDIR> is your system specific
    temporary directory.

    See the python documentation of the profile and pstats modules for
    how to access the data in the generated .profile file.
    """
    print "profiling screen renderer...",
    sys.stdout.flush()
    prof = profile.Profile(bias = profiler_bias)
    prof.runctx("do_redraw(context)", globals(), locals())
    filename = os.path.join(profile_dir, "thuban-render.profile")
    prof.dump_stats(filename)
    print "done and saved to", filename

    if popup_dialog_box:
        # catch the printout to stdout so that we can present the
        # text in a dialog
        f = StringIO.StringIO()
        orig_stdout = sys.stdout
        sys.stdout = f
        try:
            p = pstats.Stats(filename)
            msg = _('These are the statistics sorted by cumulative time:')
            p.strip_dirs().sort_stats('cumulative').print_stats()
            m = f.getvalue()
            msg = '%s\n\n%s' % (msg, m)
        finally:
            sys.stdout = orig_stdout

        dlg = wxScrolledMessageDialog(context.mainwindow, msg,
                                      _('Profile Screen Render'))
        dlg.ShowModal()


registry.Add(Command("profile_screen_renderer", _('Profile Screen Render'),
                     profile_screen_renderer,
                     helptext = _('Profile the screen render')))


#
# Timing the redraw
#

def time_screen_renderer(context):
    """Script to measure the time of a complete redraw.

    The time taken will be printed to stdout.
    """
    start = time.clock()
    do_redraw(context)
    duration = time.clock() - start
    msg = _('Redraw finished in %g seconds.') % duration
    if popup_dialog_box:
        context.mainwindow.RunMessageBox(_('Time Screen Render'), msg)
    else:
        print msg


registry.Add(Command("time_screen_renderer", _('Time Screen Render'),
                     time_screen_renderer,
                     helptext = _('Time the screen render')))


# find the extensions menu (create it anew if not found)
main_menu = Thuban.UI.mainwindow.main_menu
extensions_menu = main_menu.find_menu('extensions')
if extensions_menu is None:
    extensions_menu = main_menu.InsertMenu('extensions', _('E&xtensions'))

profiler_menu = extensions_menu.InsertMenu("profiler", _('&Profiler'))
profiler_menu.InsertItem("time_screen_renderer")
profiler_menu.InsertItem("profile_screen_renderer")
