~ubuntu-branches/ubuntu/karmic/eric/karmic

« back to all changes in this revision

Viewing changes to eric/DataViews/PyProfileDialog.py

  • Committer: Bazaar Package Importer
  • Author(s): Scott Kitterman
  • Date: 2008-01-28 18:02:25 UTC
  • mfrom: (1.1.4 upstream)
  • Revision ID: james.westby@ubuntu.com-20080128180225-6nrox6yrworh2c4v
Tags: 4.0.4-1ubuntu1
* Add python-qt3 to build-depends becuase that's where Ubuntu puts 
  pyqtconfig
* Change maintainer to MOTU

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
 
 
3
# Copyright (c) 2003 - 2007 Detlev Offenbach <detlev@die-offenbachs.de>
 
4
#
 
5
 
 
6
"""
 
7
Module implementing a dialog to display profile data.
 
8
"""
 
9
 
 
10
import sys
 
11
import os
 
12
import marshal
 
13
 
 
14
from PyQt4.QtCore import *
 
15
from PyQt4.QtGui import *
 
16
 
 
17
from KdeQt import KQMessageBox
 
18
 
 
19
from Ui_PyProfileDialog import Ui_PyProfileDialog
 
20
import Utilities
 
21
 
 
22
from eric4config import getConfig
 
23
 
 
24
class ProfileTreeWidgetItem(QTreeWidgetItem):
 
25
    """
 
26
    Class implementing a custom QTreeWidgetItem to allow sorting on numeric values.
 
27
    """
 
28
    def __getNC(self, itm):
 
29
        """
 
30
        Private method to get the value to compare on for the first column.
 
31
        
 
32
        @param itm item to operate on (ProfileTreeWidgetItem)
 
33
        """
 
34
        s = str(itm.text(0))
 
35
        return int(s.split('/')[0])
 
36
        
 
37
    def __lt__(self, other):
 
38
        """
 
39
        Public method to check, if the item is less than the other one.
 
40
        
 
41
        @param other reference to item to compare against (ProfileTreeWidgetItem)
 
42
        @return true, if this item is less than other (boolean)
 
43
        """
 
44
        column = self.treeWidget().sortColumn()
 
45
        if column == 0:
 
46
            return self.__getNC(self) < self.__getNC(other)
 
47
        if column == 6:
 
48
            return int(str(self.text(column))) < int(str(other.text(column)))
 
49
        return self.text(column) < other.text(column)
 
50
        
 
51
class PyProfileDialog(QDialog, Ui_PyProfileDialog):
 
52
    """
 
53
    Class implementing a dialog to display the results of a syntax check run.
 
54
    """
 
55
    def __init__(self, parent = None):
 
56
        """
 
57
        Constructor
 
58
        
 
59
        @param parent parent widget (QWidget)
 
60
        """
 
61
        QDialog.__init__(self, parent)
 
62
        self.setupUi(self)
 
63
        
 
64
        self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False)
 
65
        self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True)
 
66
        
 
67
        self.cancelled = False
 
68
        self.exclude = True
 
69
        self.ericpath = getConfig('ericDir')
 
70
        self.pyLibPath = Utilities.getPythonLibPath()
 
71
        
 
72
        self.resultList.header().setSortIndicator(0, Qt.DescendingOrder)
 
73
        
 
74
        self.__menu = QMenu(self)
 
75
        self.filterItm = self.__menu.addAction(self.trUtf8('Exclude Python Library'), 
 
76
            self.__filter)
 
77
        self.__menu.addSeparator()
 
78
        self.__menu.addAction(self.trUtf8('Erase Profiling Info'), 
 
79
            self.__eraseProfile)
 
80
        self.__menu.addAction(self.trUtf8('Erase Timing Info'), self.__eraseTiming)
 
81
        self.__menu.addSeparator()
 
82
        self.__menu.addAction(self.trUtf8('Erase All Infos'), self.__eraseAll)
 
83
        self.resultList.setContextMenuPolicy(Qt.CustomContextMenu)
 
84
        self.connect(self.resultList, 
 
85
                     SIGNAL('customContextMenuRequested(const QPoint &)'),
 
86
                     self.__showContextMenu)
 
87
        self.summaryList.setContextMenuPolicy(Qt.CustomContextMenu)
 
88
        self.connect(self.summaryList, 
 
89
                     SIGNAL('customContextMenuRequested(const QPoint &)'),
 
90
                     self.__showContextMenu)
 
91
        
 
92
    def __createResultItem(self, calls, totalTime, totalTimePerCall, cumulativeTime, 
 
93
                           cumulativeTimePerCall, file, line, functionName):
 
94
        """
 
95
        Private method to create an entry in the result list.
 
96
        
 
97
        @param calls number of calls (integer)
 
98
        @param totalTime total time (double)
 
99
        @param totalTimePerCall total time per call (double)
 
100
        @param cumulativeTime cumulative time (double)
 
101
        @param cumulativeTimePerCall cumulative time per call (double)
 
102
        @param file filename of file (string or QString)
 
103
        @param line linenumber (integer)
 
104
        @param functionName function name (string or QString)
 
105
        """
 
106
        itm = ProfileTreeWidgetItem(self.resultList, QStringList() \
 
107
            << calls << "% 8.3f" % totalTime << totalTimePerCall \
 
108
            << "% 8.3f" % cumulativeTime << cumulativeTimePerCall \
 
109
            << file << str(line) << functionName)
 
110
        for col in [0, 1, 2, 3, 4, 6]:
 
111
            itm.setTextAlignment(col, Qt.AlignRight)
 
112
        
 
113
    def __createSummaryItem(self, label, contents):
 
114
        """
 
115
        Private method to create an entry in the summary list.
 
116
        
 
117
        @param label text of the first column (string or QString)
 
118
        @param contents text of the second column (string or QString)
 
119
        """
 
120
        itm = QTreeWidgetItem(self.summaryList, QStringList() << label << contents)
 
121
        itm.setTextAlignment(1, Qt.AlignRight)
 
122
        
 
123
    def __resortResultList(self):
 
124
        """
 
125
        Private method to resort the tree.
 
126
        """
 
127
        self.resultList.sortItems(self.resultList.sortColumn(), 
 
128
                                  self.resultList.header().sortIndicatorOrder())
 
129
        
 
130
    def __populateLists(self, exclude = False):
 
131
        """
 
132
        Private method used to populate the listviews.
 
133
        
 
134
        @param exclude flag indicating whether files residing in the
 
135
                Python library should be excluded
 
136
        """
 
137
        self.resultList.clear()
 
138
        self.summaryList.clear()
 
139
        
 
140
        self.checkProgress.setMaximum(len(self.stats))
 
141
        QApplication.processEvents()
 
142
        
 
143
        progress = 0
 
144
        total_calls = 0
 
145
        prim_calls  = 0
 
146
        total_tt    = 0
 
147
        
 
148
        try:
 
149
            # disable updates of the list for speed
 
150
            self.resultList.setUpdatesEnabled(False)
 
151
            self.resultList.setSortingEnabled(False)
 
152
            
 
153
            # now go through all the files
 
154
            for func, (cc, nc, tt, ct, callers) in self.stats.items():
 
155
                if self.cancelled:
 
156
                    return
 
157
                
 
158
                if not (self.ericpath and func[0].startswith(self.ericpath)) and \
 
159
                   not (exclude and func[0].startswith(self.pyLibPath)):
 
160
                    if self.file is None or func[0].startswith(self.file) or \
 
161
                       func[0].startswith(self.pyLibPath):
 
162
                        # calculate the totals
 
163
                        total_calls += nc
 
164
                        prim_calls  += cc
 
165
                        total_tt    += tt
 
166
                        
 
167
                        if nc != cc:
 
168
                            c = "%d/%d" % (nc, cc)
 
169
                        else:
 
170
                            c = str(nc)
 
171
                        if nc == 0:
 
172
                            tpc = "% 8.3f" % 0.0
 
173
                        else:
 
174
                            tpc = "% 8.3f" % (tt/nc,)
 
175
                        if cc == 0:
 
176
                            cpc = "% 8.3f" % 0.0
 
177
                        else:
 
178
                            cpc = "% 8.3f" % (ct/cc,)
 
179
                        self.__createResultItem(c, tt, tpc, ct, cpc, func[0], 
 
180
                                                str(func[1]), func[2])
 
181
                    
 
182
                progress += 1
 
183
                self.checkProgress.setValue(progress)
 
184
                QApplication.processEvents()
 
185
        finally:
 
186
            # reenable updates of the list
 
187
            self.resultList.setSortingEnabled(True)
 
188
            self.resultList.setUpdatesEnabled(True)
 
189
        self.__resortResultList()
 
190
        
 
191
        # now do the summary stuff
 
192
        self.__createSummaryItem(self.trUtf8("function calls"), str(total_calls))
 
193
        if total_calls != prim_calls:
 
194
            self.__createSummaryItem(self.trUtf8("primitive calls"), str(prim_calls))
 
195
        self.__createSummaryItem(self.trUtf8("CPU seconds"), "%.3f" % total_tt)
 
196
        
 
197
    def start(self, pfn, fn=None):
 
198
        """
 
199
        Public slot to start the calculation of the profile data.
 
200
        
 
201
        @param pfn basename of the profiling file (string)
 
202
        @param fn file to display the profiling data for (string)
 
203
        """
 
204
        self.basename = os.path.splitext(pfn)[0]
 
205
        
 
206
        fname = "%s.profile" % self.basename
 
207
        if not os.path.exists(fname):
 
208
            KQMessageBox.warning(None,
 
209
                self.trUtf8("Profile Results"),
 
210
                self.trUtf8("""<p>There is no profiling data"""
 
211
                            """ available for <b>%1</b>.</p>""")
 
212
                    .arg(pfn))
 
213
            self.close()
 
214
            return
 
215
        try:
 
216
            f = open(fname, 'rb')
 
217
            self.stats = marshal.load(f)
 
218
            f.close()
 
219
        except:
 
220
            KQMessageBox.critical(None,
 
221
                self.trUtf8("Loading Profiling Data"),
 
222
                self.trUtf8("""<p>The profiling data could not be"""
 
223
                            """ read from file <b>%1</b>.</p>""")
 
224
                    .arg(fname))
 
225
            self.close()
 
226
            return
 
227
        
 
228
        self.file = fn
 
229
        self.__populateLists()
 
230
        self.__finish()
 
231
        
 
232
    def __finish(self):
 
233
        """
 
234
        Private slot called when the action finished or the user pressed the button.
 
235
        """
 
236
        self.cancelled = True
 
237
        self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True)
 
238
        self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False)
 
239
        self.buttonBox.button(QDialogButtonBox.Close).setDefault(True)
 
240
        QApplication.processEvents()
 
241
        self.resultList.header().resizeSections(QHeaderView.ResizeToContents)
 
242
        self.resultList.header().setStretchLastSection(True)
 
243
        self.summaryList.header().resizeSections(QHeaderView.ResizeToContents)
 
244
        self.summaryList.header().setStretchLastSection(True)
 
245
        
 
246
    def __unfinish(self):
 
247
        """
 
248
        Private slot called to revert the effects of the __finish slot.
 
249
        """
 
250
        self.cancelled = False
 
251
        self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False)
 
252
        self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(True)
 
253
        self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True)
 
254
        
 
255
    def on_buttonBox_clicked(self, button):
 
256
        """
 
257
        Private slot called by a button of the button box clicked.
 
258
        
 
259
        @param button button that was clicked (QAbstractButton)
 
260
        """
 
261
        if button == self.buttonBox.button(QDialogButtonBox.Close):
 
262
            self.close()
 
263
        elif button == self.buttonBox.button(QDialogButtonBox.Cancel):
 
264
            self.__finish()
 
265
        
 
266
    def __showContextMenu(self, coord):
 
267
        """
 
268
        Private slot to show the context menu of the listview.
 
269
        
 
270
        @param coord the position of the mouse pointer (QPoint)
 
271
        """
 
272
        self.__menu.popup(self.mapToGlobal(coord))
 
273
        
 
274
    def __eraseProfile(self):
 
275
        """
 
276
        Private slot to handle the Erase Profile context menu action.
 
277
        """
 
278
        fname = "%s.profile" % self.basename
 
279
        if os.path.exists(fname):
 
280
            os.remove(fname)
 
281
        
 
282
    def __eraseTiming(self):
 
283
        """
 
284
        Private slot to handle the Erase Timing context menu action.
 
285
        """
 
286
        fname = "%s.timings" % self.basename
 
287
        if os.path.exists(fname):
 
288
            os.remove(fname)
 
289
        
 
290
    def __eraseAll(self):
 
291
        """
 
292
        Private slot to handle the Erase All context menu action.
 
293
        """
 
294
        self.__eraseProfile()
 
295
        self.__eraseTiming()
 
296
        
 
297
    def __filter(self):
 
298
        """
 
299
        Private slot to handle the Exclude/Include Python Library context menu action.
 
300
        """
 
301
        self.__unfinish()
 
302
        if self.exclude:
 
303
            self.exclude = False
 
304
            self.filterItm.setText(self.trUtf8('Include Python Library'))
 
305
            self.__populateLists(True)
 
306
        else:
 
307
            self.exclude = True
 
308
            self.filterItm.setText(self.trUtf8('Exclude Python Library'))
 
309
            self.__populateLists(False)
 
310
        self.__finish()