From 80a4b509c9cb9d850808ea2de73f41eca26399f8 Mon Sep 17 00:00:00 2001 From: Kevin Date: Wed, 12 Dec 2012 13:58:42 +0100 Subject: [PATCH 01/24] STL implementation --- src/controllers/debugcontroller.py | 18 + src/controllers/localscontroller.py | 7 + src/helpers/actions.py | 6 + src/helpers/distributedobjects.py | 6 +- src/helpers/gdbconnector.py | 21 +- src/helpers/gdbinit.py | 55 ++ src/resources.qrc | 1 + .../pretty_printer/libstdcxx/__init__.py | 1 + .../pretty_printer/libstdcxx/v6/__init__.py | 1 + .../pretty_printer/libstdcxx/v6/printers.py | 930 ++++++++++++++++++ src/variables/variablelist.py | 6 + src/variables/variablepool.py | 31 +- src/views/mainwindow.py | 7 +- 13 files changed, 1078 insertions(+), 12 deletions(-) create mode 100644 src/helpers/gdbinit.py create mode 100644 src/third_party/pretty_printer/libstdcxx/__init__.py create mode 100644 src/third_party/pretty_printer/libstdcxx/v6/__init__.py create mode 100644 src/third_party/pretty_printer/libstdcxx/v6/printers.py diff --git a/src/controllers/debugcontroller.py b/src/controllers/debugcontroller.py index 982782e..8b0259e 100644 --- a/src/controllers/debugcontroller.py +++ b/src/controllers/debugcontroller.py @@ -47,13 +47,22 @@ def __init__(self, distributedObjects): self.distributedObjects = distributedObjects self.connector = self.distributedObjects.gdb_connector + self.gdbinit = self.distributedObjects.gdb_init self.signalProxy = self.distributedObjects.signalProxy self.executableName = None self.lastCmdWasStep = False self.ptyhandler.start() + + self.gdbinit.writeFile() + self.connector.start() + + self.connector.initPrettyPrinter(self.gdbinit.getPath()) + self.connector.startPrettyPrinting() + + self.toggleBeautify = True self.connector.reader.asyncRecordReceived.connect(self.handleAsyncRecord, Qt.QueuedConnection) @@ -131,6 +140,15 @@ def finish(self): def until(self, file_, line): self.connector.until(file_, line) self.lastCmdWasStep = False + + def beautify(self): + if self.toggleBeautify: + self.connector.disablePrettyPrinter() + else: + self.connector.enablePrettyPrinter() + + self.toggleBeautify = not self.toggleBeautify + self.distributedObjects.localsController.reloadLocals() def evaluateExpression(self, exp): if exp == "": diff --git a/src/controllers/localscontroller.py b/src/controllers/localscontroller.py index 82145af..a843828 100644 --- a/src/controllers/localscontroller.py +++ b/src/controllers/localscontroller.py @@ -38,3 +38,10 @@ def getLocals(self): for vw in self.variableList.list: self.add(vw) + + def reloadLocals(self): + self.clear() + self.variableList.reloadLocals() + + for vw in self.variableList.list: + self.add(vw) diff --git a/src/helpers/actions.py b/src/helpers/actions.py index cc67518..7864348 100644 --- a/src/helpers/actions.py +++ b/src/helpers/actions.py @@ -129,6 +129,12 @@ def __init__(self): "Del var from Watch", "+", "Remove selected variable from watchview-window") + ############################################### + ## miscellaneous + ############################################### + self.Beautify = self.__createAction(":/icons/images/beautify.png", + "Beautify", None, "Pretty Print of Objects") + def getAddToWatchAction(self, name, slot): a = self.createEx(name) a.setText("Add '%s' to watch window" % name) diff --git a/src/helpers/distributedobjects.py b/src/helpers/distributedobjects.py index 38b9452..168da01 100644 --- a/src/helpers/distributedobjects.py +++ b/src/helpers/distributedobjects.py @@ -53,7 +53,7 @@ from views.threadview import ThreadView from models.threadmodel import ThreadModel from views.mitraceview import MiTraceView - +from helpers.gdbinit import GDBInit class DistributedObjects: def __init__(self, mainwindow): @@ -61,19 +61,17 @@ def __init__(self, mainwindow): self.settings = QSettings("fh-hagenberg", "ricodebug") self.configStore = ConfigStore(self.settings) self.gdb_connector = GdbConnector() + self.gdb_init = GDBInit() self.actions = Actions() self.signalProxy = SignalProxy(self) self.sessionManager = SessionManager(self) - self.breakpointModel, _ = self.buildModelAndView(BreakpointModel, BreakpointView, "Breakpoints") - self.debugController = DebugController(self) self.variablePool = VariablePool(self) self.editorController = EditorController(self) self.toolTipController = ToolTipController(self, ToolTipView(self, self.editorController.editor_view)) self.filelistController = FileListController(self) self.stackController = StackController(self) - self.threadModel, _ = self.buildModelAndView(ThreadModel, ThreadView, "Threads") self.watchView = WatchView() diff --git a/src/helpers/gdbconnector.py b/src/helpers/gdbconnector.py index 8ffa454..708ce4d 100644 --- a/src/helpers/gdbconnector.py +++ b/src/helpers/gdbconnector.py @@ -25,6 +25,7 @@ import subprocess import signal import logging +import os from .gdbreader import GdbReader from .gdboutput import GdbOutput from PyQt4.QtCore import QObject, pyqtSignal @@ -41,7 +42,7 @@ def __init__(self): def start(self): try: - self.gdb = subprocess.Popen(['gdb', '-i', 'mi', '-q'], + self.gdb = subprocess.Popen(['gdb', '-i', 'mi', '-q', '-nx'], \ shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE) except OSError as e: logging.critical("Could not start gdb. Error message: %s", e) @@ -79,6 +80,10 @@ def openFile(self, filename): self.executeAndRaiseIfFailed("-file-exec-and-symbols " + filename, "Could not open file!") + def startPrettyPrinting(self): + self.execute("-enable-pretty-printing", \ + "Enable MI PrettyPrint") + def getSources(self): res = self.executeAndRaiseIfFailed("-file-list-exec-source-files", "Could not get files.") @@ -268,8 +273,8 @@ def var_assign(self, exp, value): return self.execute("-var-assign \"" + exp + "\" " + value) def var_list_children(self, exp): - return self.execute("-var-list-children --all-values \"" + - str(exp) + "\"") + return self.execute("-var-list-children --all-values \"" + \ + str(exp) + "\" 0 100") def var_update(self, exp): return self.execute("-var-update --all-values \"" + exp + "\"") @@ -289,3 +294,13 @@ def threadInfo(self): def selectThread(self, id_): return self.executeAndRaiseIfFailed("-thread-select %s" % id_) + + def initPrettyPrinter(self,path): + command = "source" + path + self.executeAndRaiseIfFailed(command,"Can not load pretty printer module!") + + def enablePrettyPrinter(self): + return self.executeAndRaiseIfFailed("enable pretty-printer", "Enables Initialized Printers") + + def disablePrettyPrinter(self): + return self.executeAndRaiseIfFailed("disable pretty-printer", "Disables Initialized Printers") diff --git a/src/helpers/gdbinit.py b/src/helpers/gdbinit.py new file mode 100644 index 0000000..484a6de --- /dev/null +++ b/src/helpers/gdbinit.py @@ -0,0 +1,55 @@ +# ricodebug - A GDB frontend which focuses on visually supported +# debugging using data structure graphs and SystemC features. +# +# Copyright (C) 2011 The ricodebug project team at the +# Upper Austrian University Of Applied Sciences Hagenberg, +# Department Embedded Systems Design +# +# This file is part of ricodebug. +# +# ricodebug is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# For further information see . + +from PyQt4.QtCore import QObject +import os + + +class GDBInit(QObject): + def __init__(self): + self.path = "./third_party/" + self.fileName = "load_pretty_printer" + + self.file_content = [] + self.file_content.append("python\n") + self.file_content.append("import sys\n") + self.file_content.append("path=\"" + self.path + "pretty_printer\"\n") + self.file_content.append("sys.path.insert(0, path)\n") + self.file_content.append("from libstdcxx.v6.printers import register_libstdcxx_printers\n") + self.file_content.append("register_libstdcxx_printers (None)\n") + self.file_content.append("end\n") + + def writeFile(self): + if os.path.exists(self.path + self.fileName): + os.remove(self.path + self.fileName) + + init_script = open(self.path + self.fileName,'w') + + for i in self.file_content: + init_script.write(i) + + init_script.close() + + def getPath(self): + return self.path + self.fileName \ No newline at end of file diff --git a/src/resources.qrc b/src/resources.qrc index 65304f3..d301ecf 100644 --- a/src/resources.qrc +++ b/src/resources.qrc @@ -62,6 +62,7 @@ images/save-html.png images/minus.png images/arrow-right.png + images/beautify.png images/markers/exec_pos.png diff --git a/src/third_party/pretty_printer/libstdcxx/__init__.py b/src/third_party/pretty_printer/libstdcxx/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/third_party/pretty_printer/libstdcxx/__init__.py @@ -0,0 +1 @@ + diff --git a/src/third_party/pretty_printer/libstdcxx/v6/__init__.py b/src/third_party/pretty_printer/libstdcxx/v6/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/third_party/pretty_printer/libstdcxx/v6/__init__.py @@ -0,0 +1 @@ + diff --git a/src/third_party/pretty_printer/libstdcxx/v6/printers.py b/src/third_party/pretty_printer/libstdcxx/v6/printers.py new file mode 100644 index 0000000..0eac413 --- /dev/null +++ b/src/third_party/pretty_printer/libstdcxx/v6/printers.py @@ -0,0 +1,930 @@ +# Pretty-printers for libstc++. + +# Copyright (C) 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import gdb +import itertools +import re + +# Try to use the new-style pretty-printing if available. +_use_gdb_pp = True +try: + import gdb.printing +except ImportError: + _use_gdb_pp = False + +# Starting with the type ORIG, search for the member type NAME. This +# handles searching upward through superclasses. This is needed to +# work around http://sourceware.org/bugzilla/show_bug.cgi?id=13615. +def find_type(orig, name): + typ = orig.strip_typedefs() + while True: + search = str(typ) + '::' + name + try: + return gdb.lookup_type(search) + except RuntimeError: + pass + # The type was not found, so try the superclass. We only need + # to check the first superclass, so we don't bother with + # anything fancier here. + field = typ.fields()[0] + if not field.is_base_class: + raise ValueError, "Cannot find type %s::%s" % (str(orig), name) + typ = field.type + +class SharedPointerPrinter: + "Print a shared_ptr or weak_ptr" + + def __init__ (self, typename, val): + self.typename = typename + self.val = val + + def to_string (self): + state = 'empty' + refcounts = self.val['_M_refcount']['_M_pi'] + if refcounts != 0: + usecount = refcounts['_M_use_count'] + weakcount = refcounts['_M_weak_count'] + if usecount == 0: + state = 'expired, weak %d' % weakcount + else: + state = 'count %d, weak %d' % (usecount, weakcount - 1) + return '%s (%s) %s' % (self.typename, state, self.val['_M_ptr']) + +class UniquePointerPrinter: + "Print a unique_ptr" + + def __init__ (self, typename, val): + self.val = val + + def to_string (self): + v = self.val['_M_t']['_M_head_impl'] + return ('std::unique_ptr<%s> containing %s' % (str(v.type.target()), + str(v))) + +class StdListPrinter: + "Print a std::list" + + class _iterator: + def __init__(self, nodetype, head): + self.nodetype = nodetype + self.base = head['_M_next'] + self.head = head.address + self.count = 0 + + def __iter__(self): + return self + + def next(self): + if self.base == self.head: + raise StopIteration + elt = self.base.cast(self.nodetype).dereference() + self.base = elt['_M_next'] + count = self.count + self.count = self.count + 1 + return ('[%d]' % count, elt['_M_data']) + + def __init__(self, typename, val): + self.typename = typename + self.val = val + + def children(self): + nodetype = find_type(self.val.type, '_Node') + nodetype = nodetype.strip_typedefs().pointer() + return self._iterator(nodetype, self.val['_M_impl']['_M_node']) + + def to_string(self): + if self.val['_M_impl']['_M_node'].address == self.val['_M_impl']['_M_node']['_M_next']: + return 'empty %s' % (self.typename) + return '%s' % (self.typename) + +class StdListIteratorPrinter: + "Print std::list::iterator" + + def __init__(self, typename, val): + self.val = val + self.typename = typename + + def to_string(self): + nodetype = find_type(self.val.type, '_Node') + nodetype = nodetype.strip_typedefs().pointer() + return self.val['_M_node'].cast(nodetype).dereference()['_M_data'] + +class StdSlistPrinter: + "Print a __gnu_cxx::slist" + + class _iterator: + def __init__(self, nodetype, head): + self.nodetype = nodetype + self.base = head['_M_head']['_M_next'] + self.count = 0 + + def __iter__(self): + return self + + def next(self): + if self.base == 0: + raise StopIteration + elt = self.base.cast(self.nodetype).dereference() + self.base = elt['_M_next'] + count = self.count + self.count = self.count + 1 + return ('[%d]' % count, elt['_M_data']) + + def __init__(self, typename, val): + self.val = val + + def children(self): + nodetype = find_type(self.val.type, '_Node') + nodetype = nodetype.strip_typedefs().pointer() + return self._iterator(nodetype, self.val) + + def to_string(self): + if self.val['_M_head']['_M_next'] == 0: + return 'empty __gnu_cxx::slist' + return '__gnu_cxx::slist' + +class StdSlistIteratorPrinter: + "Print __gnu_cxx::slist::iterator" + + def __init__(self, typename, val): + self.val = val + + def to_string(self): + nodetype = find_type(self.val.type, '_Node') + nodetype = nodetype.strip_typedefs().pointer() + return self.val['_M_node'].cast(nodetype).dereference()['_M_data'] + +class StdVectorPrinter: + "Print a std::vector" + + class _iterator: + def __init__ (self, start, finish, bitvec): + self.bitvec = bitvec + if bitvec: + self.item = start['_M_p'] + self.so = start['_M_offset'] + self.finish = finish['_M_p'] + self.fo = finish['_M_offset'] + itype = self.item.dereference().type + self.isize = 8 * itype.sizeof + else: + self.item = start + self.finish = finish + self.count = 0 + + def __iter__(self): + return self + + def next(self): + count = self.count + self.count = self.count + 1 + if self.bitvec: + if self.item == self.finish and self.so >= self.fo: + raise StopIteration + elt = self.item.dereference() + if elt & (1 << self.so): + obit = 1 + else: + obit = 0 + self.so = self.so + 1 + if self.so >= self.isize: + self.item = self.item + 1 + self.so = 0 + return ('[%d]' % count, obit) + else: + if self.item == self.finish: + raise StopIteration + elt = self.item.dereference() + self.item = self.item + 1 + return ('[%d]' % count, elt) + + def __init__(self, typename, val): + self.typename = typename + self.val = val + self.is_bool = val.type.template_argument(0).code == gdb.TYPE_CODE_BOOL + + def children(self): + return self._iterator(self.val['_M_impl']['_M_start'], + self.val['_M_impl']['_M_finish'], + self.is_bool) + + def to_string(self): + start = self.val['_M_impl']['_M_start'] + finish = self.val['_M_impl']['_M_finish'] + end = self.val['_M_impl']['_M_end_of_storage'] + if self.is_bool: + start = self.val['_M_impl']['_M_start']['_M_p'] + so = self.val['_M_impl']['_M_start']['_M_offset'] + finish = self.val['_M_impl']['_M_finish']['_M_p'] + fo = self.val['_M_impl']['_M_finish']['_M_offset'] + itype = start.dereference().type + bl = 8 * itype.sizeof + length = (bl - so) + bl * ((finish - start) - 1) + fo + capacity = bl * (end - start) + return ('%s of length %d, capacity %d' + % (self.typename, int (length), int (capacity))) + else: + return ('%s of length %d, capacity %d' + % (self.typename, int (finish - start), int (end - start))) + + def display_hint(self): + return 'array' + +class StdVectorIteratorPrinter: + "Print std::vector::iterator" + + def __init__(self, typename, val): + self.val = val + + def to_string(self): + return self.val['_M_current'].dereference() + +class StdTuplePrinter: + "Print a std::tuple" + + class _iterator: + def __init__ (self, head): + self.head = head + + # Set the base class as the initial head of the + # tuple. + nodes = self.head.type.fields () + if len (nodes) == 1: + # Set the actual head to the first pair. + self.head = self.head.cast (nodes[0].type) + elif len (nodes) != 0: + raise ValueError, "Top of tuple tree does not consist of a single node." + self.count = 0 + + def __iter__ (self): + return self + + def next (self): + nodes = self.head.type.fields () + # Check for further recursions in the inheritance tree. + if len (nodes) == 0: + raise StopIteration + # Check that this iteration has an expected structure. + if len (nodes) != 2: + raise ValueError, "Cannot parse more than 2 nodes in a tuple tree." + + # - Left node is the next recursion parent. + # - Right node is the actual class contained in the tuple. + + # Process right node. + impl = self.head.cast (nodes[1].type) + + # Process left node and set it as head. + self.head = self.head.cast (nodes[0].type) + self.count = self.count + 1 + + # Finally, check the implementation. If it is + # wrapped in _M_head_impl return that, otherwise return + # the value "as is". + fields = impl.type.fields () + if len (fields) < 1 or fields[0].name != "_M_head_impl": + return ('[%d]' % self.count, impl) + else: + return ('[%d]' % self.count, impl['_M_head_impl']) + + def __init__ (self, typename, val): + self.typename = typename + self.val = val; + + def children (self): + return self._iterator (self.val) + + def to_string (self): + if len (self.val.type.fields ()) == 0: + return 'empty %s' % (self.typename) + return '%s containing' % (self.typename) + +class StdStackOrQueuePrinter: + "Print a std::stack or std::queue" + + def __init__ (self, typename, val): + self.typename = typename + self.visualizer = gdb.default_visualizer(val['c']) + + def children (self): + return self.visualizer.children() + + def to_string (self): + return '%s wrapping: %s' % (self.typename, + self.visualizer.to_string()) + + def display_hint (self): + if hasattr (self.visualizer, 'display_hint'): + return self.visualizer.display_hint () + return None + +class RbtreeIterator: + def __init__(self, rbtree): + self.size = rbtree['_M_t']['_M_impl']['_M_node_count'] + self.node = rbtree['_M_t']['_M_impl']['_M_header']['_M_left'] + self.count = 0 + + def __iter__(self): + return self + + def __len__(self): + return int (self.size) + + def next(self): + if self.count == self.size: + raise StopIteration + result = self.node + self.count = self.count + 1 + if self.count < self.size: + # Compute the next node. + node = self.node + if node.dereference()['_M_right']: + node = node.dereference()['_M_right'] + while node.dereference()['_M_left']: + node = node.dereference()['_M_left'] + else: + parent = node.dereference()['_M_parent'] + while node == parent.dereference()['_M_right']: + node = parent + parent = parent.dereference()['_M_parent'] + if node.dereference()['_M_right'] != parent: + node = parent + self.node = node + return result + +# This is a pretty printer for std::_Rb_tree_iterator (which is +# std::map::iterator), and has nothing to do with the RbtreeIterator +# class above. +class StdRbtreeIteratorPrinter: + "Print std::map::iterator" + + def __init__ (self, typename, val): + self.val = val + + def to_string (self): + typename = str(self.val.type.strip_typedefs()) + '::_Link_type' + nodetype = gdb.lookup_type(typename).strip_typedefs() + return self.val.cast(nodetype).dereference()['_M_value_field'] + +class StdDebugIteratorPrinter: + "Print a debug enabled version of an iterator" + + def __init__ (self, typename, val): + self.val = val + + # Just strip away the encapsulating __gnu_debug::_Safe_iterator + # and return the wrapped iterator value. + def to_string (self): + itype = self.val.type.template_argument(0) + return self.val['_M_current'].cast(itype) + +class StdMapPrinter: + "Print a std::map or std::multimap" + + # Turn an RbtreeIterator into a pretty-print iterator. + class _iter: + def __init__(self, rbiter, type): + self.rbiter = rbiter + self.count = 0 + self.type = type + + def __iter__(self): + return self + + def next(self): + if self.count % 2 == 0: + n = self.rbiter.next() + n = n.cast(self.type).dereference()['_M_value_field'] + self.pair = n + item = n['first'] + else: + item = self.pair['second'] + result = ('[%d]' % self.count, item) + self.count = self.count + 1 + return result + + def __init__ (self, typename, val): + self.typename = typename + self.val = val + + def to_string (self): + return '%s with %d elements' % (self.typename, + len (RbtreeIterator (self.val))) + + def children (self): + rep_type = find_type(self.val.type, '_Rep_type') + node = find_type(rep_type, '_Link_type') + node = node.strip_typedefs() + return self._iter (RbtreeIterator (self.val), node) + + def display_hint (self): + return 'map' + +class StdSetPrinter: + "Print a std::set or std::multiset" + + # Turn an RbtreeIterator into a pretty-print iterator. + class _iter: + def __init__(self, rbiter, type): + self.rbiter = rbiter + self.count = 0 + self.type = type + + def __iter__(self): + return self + + def next(self): + item = self.rbiter.next() + item = item.cast(self.type).dereference()['_M_value_field'] + # FIXME: this is weird ... what to do? + # Maybe a 'set' display hint? + result = ('[%d]' % self.count, item) + self.count = self.count + 1 + return result + + def __init__ (self, typename, val): + self.typename = typename + self.val = val + + def to_string (self): + return '%s with %d elements' % (self.typename, + len (RbtreeIterator (self.val))) + + def children (self): + rep_type = find_type(self.val.type, '_Rep_type') + node = find_type(rep_type, '_Link_type') + node = node.strip_typedefs() + return self._iter (RbtreeIterator (self.val), node) + +class StdBitsetPrinter: + "Print a std::bitset" + + def __init__(self, typename, val): + self.typename = typename + self.val = val + + def to_string (self): + # If template_argument handled values, we could print the + # size. Or we could use a regexp on the type. + return '%s' % (self.typename) + + def children (self): + words = self.val['_M_w'] + wtype = words.type + + # The _M_w member can be either an unsigned long, or an + # array. This depends on the template specialization used. + # If it is a single long, convert to a single element list. + if wtype.code == gdb.TYPE_CODE_ARRAY: + tsize = wtype.target ().sizeof + else: + words = [words] + tsize = wtype.sizeof + + nwords = wtype.sizeof / tsize + result = [] + byte = 0 + while byte < nwords: + w = words[byte] + bit = 0 + while w != 0: + if (w & 1) != 0: + # Another spot where we could use 'set'? + result.append(('[%d]' % (byte * tsize * 8 + bit), 1)) + bit = bit + 1 + w = w >> 1 + byte = byte + 1 + return result + +class StdDequePrinter: + "Print a std::deque" + + class _iter: + def __init__(self, node, start, end, last, buffer_size): + self.node = node + self.p = start + self.end = end + self.last = last + self.buffer_size = buffer_size + self.count = 0 + + def __iter__(self): + return self + + def next(self): + if self.p == self.last: + raise StopIteration + + result = ('[%d]' % self.count, self.p.dereference()) + self.count = self.count + 1 + + # Advance the 'cur' pointer. + self.p = self.p + 1 + if self.p == self.end: + # If we got to the end of this bucket, move to the + # next bucket. + self.node = self.node + 1 + self.p = self.node[0] + self.end = self.p + self.buffer_size + + return result + + def __init__(self, typename, val): + self.typename = typename + self.val = val + self.elttype = val.type.template_argument(0) + size = self.elttype.sizeof + if size < 512: + self.buffer_size = int (512 / size) + else: + self.buffer_size = 1 + + def to_string(self): + start = self.val['_M_impl']['_M_start'] + end = self.val['_M_impl']['_M_finish'] + + delta_n = end['_M_node'] - start['_M_node'] - 1 + delta_s = start['_M_last'] - start['_M_cur'] + delta_e = end['_M_cur'] - end['_M_first'] + + size = self.buffer_size * delta_n + delta_s + delta_e + + return '%s with %d elements' % (self.typename, long (size)) + + def children(self): + start = self.val['_M_impl']['_M_start'] + end = self.val['_M_impl']['_M_finish'] + return self._iter(start['_M_node'], start['_M_cur'], start['_M_last'], + end['_M_cur'], self.buffer_size) + + def display_hint (self): + return 'array' + +class StdDequeIteratorPrinter: + "Print std::deque::iterator" + + def __init__(self, typename, val): + self.val = val + + def to_string(self): + return self.val['_M_cur'].dereference() + +class StdStringPrinter: + "Print a std::basic_string of some kind" + + def __init__(self, typename, val): + self.val = val + + def to_string(self): + # Make sure &string works, too. + type = self.val.type + if type.code == gdb.TYPE_CODE_REF: + type = type.target () + + # Calculate the length of the string so that to_string returns + # the string according to length, not according to first null + # encountered. + ptr = self.val ['_M_dataplus']['_M_p'] + realtype = type.unqualified ().strip_typedefs () + reptype = gdb.lookup_type (str (realtype) + '::_Rep').pointer () + header = ptr.cast(reptype) - 1 + len = header.dereference ()['_M_length'] + if hasattr(ptr, "lazy_string"): + return ptr.lazy_string (length = len) + return ptr.string (length = len) + + def display_hint (self): + return 'string' + +class Tr1HashtableIterator: + def __init__ (self, hash): + self.node = hash['_M_before_begin']['_M_nxt'] + self.node_type = find_type(hash.type, '__node_type').pointer() + + def __iter__ (self): + return self + + def next (self): + if self.node == 0: + raise StopIteration + node = self.node.cast(self.node_type) + result = node.dereference()['_M_v'] + self.node = node.dereference()['_M_nxt'] + return result + +class Tr1UnorderedSetPrinter: + "Print a tr1::unordered_set" + + def __init__ (self, typename, val): + self.typename = typename + self.val = val + + def to_string (self): + return '%s with %d elements' % (self.typename, self.val['_M_element_count']) + + @staticmethod + def format_count (i): + return '[%d]' % i + + def children (self): + counter = itertools.imap (self.format_count, itertools.count()) + return itertools.izip (counter, Tr1HashtableIterator (self.val)) + +class Tr1UnorderedMapPrinter: + "Print a tr1::unordered_map" + + def __init__ (self, typename, val): + self.typename = typename + self.val = val + + def to_string (self): + return '%s with %d elements' % (self.typename, self.val['_M_element_count']) + + @staticmethod + def flatten (list): + for elt in list: + for i in elt: + yield i + + @staticmethod + def format_one (elt): + return (elt['first'], elt['second']) + + @staticmethod + def format_count (i): + return '[%d]' % i + + def children (self): + counter = itertools.imap (self.format_count, itertools.count()) + # Map over the hash table and flatten the result. + data = self.flatten (itertools.imap (self.format_one, Tr1HashtableIterator (self.val))) + # Zip the two iterators together. + return itertools.izip (counter, data) + + def display_hint (self): + return 'map' + +class StdForwardListPrinter: + "Print a std::forward_list" + + class _iterator: + def __init__(self, nodetype, head): + self.nodetype = nodetype + self.base = head['_M_next'] + self.count = 0 + + def __iter__(self): + return self + + def next(self): + if self.base == 0: + raise StopIteration + elt = self.base.cast(self.nodetype).dereference() + self.base = elt['_M_next'] + count = self.count + self.count = self.count + 1 + return ('[%d]' % count, elt['_M_value']) + + def __init__(self, typename, val): + self.val = val + self.typename = typename + + def children(self): + nodetype = find_type(self.val.type, '_Node') + nodetype = nodetype.strip_typedefs().pointer() + return self._iterator(nodetype, self.val['_M_impl']['_M_head']) + + def to_string(self): + if self.val['_M_impl']['_M_head']['_M_next'] == 0: + return 'empty %s' % (self.typename) + return '%s' % (self.typename) + + +# A "regular expression" printer which conforms to the +# "SubPrettyPrinter" protocol from gdb.printing. +class RxPrinter(object): + def __init__(self, name, function): + super(RxPrinter, self).__init__() + self.name = name + self.function = function + self.enabled = True + + def invoke(self, value): + if not self.enabled: + return None + return self.function(self.name, value) + +# A pretty-printer that conforms to the "PrettyPrinter" protocol from +# gdb.printing. It can also be used directly as an old-style printer. +class Printer(object): + def __init__(self, name): + super(Printer, self).__init__() + self.name = name + self.subprinters = [] + self.lookup = {} + self.enabled = True + self.compiled_rx = re.compile('^([a-zA-Z0-9_:]+)<.*>$') + + def add(self, name, function): + # A small sanity check. + # FIXME + if not self.compiled_rx.match(name + '<>'): + raise ValueError, 'libstdc++ programming error: "%s" does not match' % name + printer = RxPrinter(name, function) + self.subprinters.append(printer) + self.lookup[name] = printer + + # Add a name using _GLIBCXX_BEGIN_NAMESPACE_VERSION. + def add_version(self, base, name, function): + self.add(base + name, function) + self.add(base + '__7::' + name, function) + + # Add a name using _GLIBCXX_BEGIN_NAMESPACE_CONTAINER. + def add_container(self, base, name, function): + self.add_version(base, name, function) + self.add_version(base + '__cxx1998::', name, function) + + @staticmethod + def get_basic_type(type): + # If it points to a reference, get the reference. + if type.code == gdb.TYPE_CODE_REF: + type = type.target () + + # Get the unqualified type, stripped of typedefs. + type = type.unqualified ().strip_typedefs () + + return type.tag + + def __call__(self, val): + typename = self.get_basic_type(val.type) + if not typename: + return None + + # All the types we match are template types, so we can use a + # dictionary. + match = self.compiled_rx.match(typename) + if not match: + return None + + basename = match.group(1) + if basename in self.lookup: + return self.lookup[basename].invoke(val) + + # Cannot find a pretty printer. Return None. + return None + +libstdcxx_printer = None + +def register_libstdcxx_printers (obj): + "Register libstdc++ pretty-printers with objfile Obj." + + global _use_gdb_pp + global libstdcxx_printer + + if _use_gdb_pp: + gdb.printing.register_pretty_printer(obj, libstdcxx_printer) + else: + if obj is None: + obj = gdb + obj.pretty_printers.append(libstdcxx_printer) + +def build_libstdcxx_dictionary (): + global libstdcxx_printer + + libstdcxx_printer = Printer("libstdc++-v6") + + # For _GLIBCXX_BEGIN_NAMESPACE_VERSION. + vers = '(__7::)?' + # For _GLIBCXX_BEGIN_NAMESPACE_CONTAINER. + container = '(__cxx1998::' + vers + ')?' + + # libstdc++ objects requiring pretty-printing. + # In order from: + # http://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen/a01847.html + libstdcxx_printer.add_version('std::', 'basic_string', StdStringPrinter) + libstdcxx_printer.add_container('std::', 'bitset', StdBitsetPrinter) + libstdcxx_printer.add_container('std::', 'deque', StdDequePrinter) + libstdcxx_printer.add_container('std::', 'list', StdListPrinter) + libstdcxx_printer.add_container('std::', 'map', StdMapPrinter) + libstdcxx_printer.add_container('std::', 'multimap', StdMapPrinter) + libstdcxx_printer.add_container('std::', 'multiset', StdSetPrinter) + libstdcxx_printer.add_version('std::', 'priority_queue', + StdStackOrQueuePrinter) + libstdcxx_printer.add_version('std::', 'queue', StdStackOrQueuePrinter) + libstdcxx_printer.add_version('std::', 'tuple', StdTuplePrinter) + libstdcxx_printer.add_container('std::', 'set', StdSetPrinter) + libstdcxx_printer.add_version('std::', 'stack', StdStackOrQueuePrinter) + libstdcxx_printer.add_version('std::', 'unique_ptr', UniquePointerPrinter) + libstdcxx_printer.add_container('std::', 'vector', StdVectorPrinter) + # vector + + # Printer registrations for classes compiled with -D_GLIBCXX_DEBUG. + libstdcxx_printer.add('std::__debug::bitset', StdBitsetPrinter) + libstdcxx_printer.add('std::__debug::deque', StdDequePrinter) + libstdcxx_printer.add('std::__debug::list', StdListPrinter) + libstdcxx_printer.add('std::__debug::map', StdMapPrinter) + libstdcxx_printer.add('std::__debug::multimap', StdMapPrinter) + libstdcxx_printer.add('std::__debug::multiset', StdSetPrinter) + libstdcxx_printer.add('std::__debug::priority_queue', + StdStackOrQueuePrinter) + libstdcxx_printer.add('std::__debug::queue', StdStackOrQueuePrinter) + libstdcxx_printer.add('std::__debug::set', StdSetPrinter) + libstdcxx_printer.add('std::__debug::stack', StdStackOrQueuePrinter) + libstdcxx_printer.add('std::__debug::unique_ptr', UniquePointerPrinter) + libstdcxx_printer.add('std::__debug::vector', StdVectorPrinter) + + # These are the TR1 and C++0x printers. + # For array - the default GDB pretty-printer seems reasonable. + libstdcxx_printer.add_version('std::', 'shared_ptr', SharedPointerPrinter) + libstdcxx_printer.add_version('std::', 'weak_ptr', SharedPointerPrinter) + libstdcxx_printer.add_container('std::', 'unordered_map', + Tr1UnorderedMapPrinter) + libstdcxx_printer.add_container('std::', 'unordered_set', + Tr1UnorderedSetPrinter) + libstdcxx_printer.add_container('std::', 'unordered_multimap', + Tr1UnorderedMapPrinter) + libstdcxx_printer.add_container('std::', 'unordered_multiset', + Tr1UnorderedSetPrinter) + libstdcxx_printer.add_container('std::', 'forward_list', + StdForwardListPrinter) + + libstdcxx_printer.add_version('std::tr1::', 'shared_ptr', SharedPointerPrinter) + libstdcxx_printer.add_version('std::tr1::', 'weak_ptr', SharedPointerPrinter) + libstdcxx_printer.add_version('std::tr1::', 'unordered_map', + Tr1UnorderedMapPrinter) + libstdcxx_printer.add_version('std::tr1::', 'unordered_set', + Tr1UnorderedSetPrinter) + libstdcxx_printer.add_version('std::tr1::', 'unordered_multimap', + Tr1UnorderedMapPrinter) + libstdcxx_printer.add_version('std::tr1::', 'unordered_multiset', + Tr1UnorderedSetPrinter) + + # These are the C++0x printer registrations for -D_GLIBCXX_DEBUG cases. + # The tr1 namespace printers do not seem to have any debug + # equivalents, so do no register them. + libstdcxx_printer.add('std::__debug::unordered_map', + Tr1UnorderedMapPrinter) + libstdcxx_printer.add('std::__debug::unordered_set', + Tr1UnorderedSetPrinter) + libstdcxx_printer.add('std::__debug::unordered_multimap', + Tr1UnorderedMapPrinter) + libstdcxx_printer.add('std::__debug::unordered_multiset', + Tr1UnorderedSetPrinter) + libstdcxx_printer.add('std::__debug::forward_list', + StdForwardListPrinter) + + + # Extensions. + libstdcxx_printer.add_version('__gnu_cxx::', 'slist', StdSlistPrinter) + + if True: + # These shouldn't be necessary, if GDB "print *i" worked. + # But it often doesn't, so here they are. + libstdcxx_printer.add_container('std::', '_List_iterator', + StdListIteratorPrinter) + libstdcxx_printer.add_container('std::', '_List_const_iterator', + StdListIteratorPrinter) + libstdcxx_printer.add_version('std::', '_Rb_tree_iterator', + StdRbtreeIteratorPrinter) + libstdcxx_printer.add_version('std::', '_Rb_tree_const_iterator', + StdRbtreeIteratorPrinter) + libstdcxx_printer.add_container('std::', '_Deque_iterator', + StdDequeIteratorPrinter) + libstdcxx_printer.add_container('std::', '_Deque_const_iterator', + StdDequeIteratorPrinter) + libstdcxx_printer.add_version('__gnu_cxx::', '__normal_iterator', + StdVectorIteratorPrinter) + libstdcxx_printer.add_version('__gnu_cxx::', '_Slist_iterator', + StdSlistIteratorPrinter) + + # Debug (compiled with -D_GLIBCXX_DEBUG) printer + # registrations. The Rb_tree debug iterator when unwrapped + # from the encapsulating __gnu_debug::_Safe_iterator does not + # have the __norm namespace. Just use the existing printer + # registration for that. + libstdcxx_printer.add('__gnu_debug::_Safe_iterator', + StdDebugIteratorPrinter) + libstdcxx_printer.add('std::__norm::_List_iterator', + StdListIteratorPrinter) + libstdcxx_printer.add('std::__norm::_List_const_iterator', + StdListIteratorPrinter) + libstdcxx_printer.add('std::__norm::_Deque_const_iterator', + StdDequeIteratorPrinter) + libstdcxx_printer.add('std::__norm::_Deque_iterator', + StdDequeIteratorPrinter) + +build_libstdcxx_dictionary () diff --git a/src/variables/variablelist.py b/src/variables/variablelist.py index 886ae22..f404d89 100644 --- a/src/variables/variablelist.py +++ b/src/variables/variablelist.py @@ -53,6 +53,12 @@ def addVar(self, varWrapper): @param varWrapper variables.variablewrapper.VariableWrapper, VariableWrapper to add to the list """ self.list.append(varWrapper) + def reloadLocals(self): + self.list = [] + for var in self.varPool.reloadLocals(): + vw = var.makeWrapper(self.factory) + self.list.append(vw) + def addLocals(self): for var in self.varPool.addLocals(): vw = var.makeWrapper(self.factory) diff --git a/src/variables/variablepool.py b/src/variables/variablepool.py index 600626d..b167e3d 100644 --- a/src/variables/variablepool.py +++ b/src/variables/variablepool.py @@ -61,6 +61,20 @@ def clearVars(self): self.variables = {} + def reloadLocals(self): + """ function deletes all variables created within gdb and stored within ricodebug + after that the locals are read again + function is important for beautify (en/disable) + reason :: calling 'disable pretty-printer' disables access to vars with a saved + format like var4.4 -> after disable -> var24.private._M_dataplus + """ + for var in self.variables: + self.connector.var_delete(var) + + self.clearVars() + ret = self.addLocals() + return ret + def justUpdateValues(self): """ just update variables for tracepoints, dont signal changes to connected views this function is connected to the signal SignalProxy::tracepointOccured() @@ -211,15 +225,24 @@ def __createVariable(self, gdbVar, parentName=None, exp=None, access=None, child # Therefore, if the value looks like a size, treat it as an array. # * Everything else with children is a structure. # * Again, everything else is a normal variable. - if gdbVar.value.startswith('0x'): + if value.startswith('0x'): logging.debug("Creating a pointer variable for '%s'", exp) varReturn = PtrVariable(self, exp, gdbName, uniqueName, type_, value, inScope, haschildren, access) - elif re.match("\[\d+\]", gdbVar.value) and int(gdbVar.numchild) >= 1: - logging.debug("Creating a array variable for '%s'", exp) - varReturn = ArrayVariable(self, exp, gdbName, uniqueName, type_, value, inScope, haschildren, access) elif haschildren: logging.debug("Creating a struct variable for '%s'", exp) varReturn = StructVariable(self, exp, gdbName, uniqueName, type_, value, inScope, haschildren, access) + elif re.match("{...}", value): + # NOTE:this solution only works for variables that are not nested + # if the vector is uninitialized we get a negative length + # additionally we limit the children to 100 + checkError = self.connector.evaluate(exp); + if str(checkError).find("length -") is -1: + haschildren = True; + else: + haschildren = False + + logging.debug("Creating an array variable for '%s'", exp) + varReturn = ArrayVariable(self, exp, gdbName, uniqueName, type_, value, inScope, haschildren, access) else: logging.debug("Creating a normal variable for '%s'", exp) varReturn = StdVariable(self, exp, gdbName, uniqueName, type_, value, inScope, haschildren, access) diff --git a/src/views/mainwindow.py b/src/views/mainwindow.py index 9ab07bb..69746da 100644 --- a/src/views/mainwindow.py +++ b/src/views/mainwindow.py @@ -141,6 +141,7 @@ def __initActions(self): self.ui.menuDebug.addAction(self.act.Record) self.ui.menuDebug.addAction(self.act.ReverseNext) self.ui.menuDebug.addAction(self.act.ReverseStep) + self.ui.menuDebug.addAction(self.act.Beautify) # file actions self.ui.menuFile.insertAction(self.ui.actionSaveSession, @@ -168,6 +169,7 @@ def __initActions(self): self.ui.Main.addAction(self.act.ReverseStep) self.ui.Main.addAction(self.act.Finish) self.ui.Main.addAction(self.act.RunToCursor) + self.ui.Main.addAction(self.act.Beautify) self.ui.Main.addSeparator() self.ui.Main.addAction(self.act.Exit) @@ -190,7 +192,8 @@ def __connectActions(self): self.act.Record.triggered.connect(self.toggleRecord) self.act.ReverseStep.triggered.connect(self.debugController.reverse_step) self.act.ReverseNext.triggered.connect(self.debugController.reverse_next) - + self.act.Beautify.triggered.connect(self.debugController.beautify) + self.act.Interrupt.triggered.connect(self.debugController.interrupt) self.act.Finish.triggered.connect(self.debugController.finish) self.act.RunToCursor.triggered.connect(self.debugController.inferiorUntil) @@ -301,6 +304,7 @@ def enableButtons(self): self.act.Finish.setEnabled(True) self.act.RunToCursor.setEnabled(True) self.act.Record.setEnabled(True) + self.act.Beautify.setEnabled(True) def disableButtons(self): self.act.Continue.setEnabled(False) @@ -311,6 +315,7 @@ def disableButtons(self): self.act.RunToCursor.setEnabled(False) self.act.Record.setChecked(False) self.act.Record.setEnabled(False) + self.act.Beautify.setEnabled(False) def __observeWorkingBinary(self, filename): """ Private Method to Observe Debugged Binary """ From ae87d4a8cae05bdaa910227cb13ff666de1a70c3 Mon Sep 17 00:00:00 2001 From: Kevin Date: Wed, 19 Dec 2012 11:37:19 +0100 Subject: [PATCH 02/24] Added picture --- src/images/beautify.png | Bin 0 -> 548 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/images/beautify.png diff --git a/src/images/beautify.png b/src/images/beautify.png new file mode 100644 index 0000000000000000000000000000000000000000..cb4a7f63e03c36aabc5fb30c5e0fe5ebd647f7f8 GIT binary patch literal 548 zcmV+<0^9wGP)EKorL>tyF|UK@bWB zCq*1x3J!vF@LzDNA|mP{C^#rM2s#MuUco^foE^oAmbMDDom}c-7X`toAoNFaIlq^_ zlxt1WE){(6;UzD3pCtE^(}WOg5aDA3ivK4b^166&{OVYb?@jd-YqEK60*W!|0>j`6 zRNMxtH|&1mFz|y9rM3^Y=y)D1DAyu@gHNwDmck-<0|^kIdO#U7qKr+@*dm>~ii0GP zY=c>OYj8_X>Mop&X3BDji|hrgQjVo z2*DS*$wy6-Gh}9(Mge(uW_m8u&vzWAM%Yq6q<4^RbV+57B0oI8q+?yK2zlD#UrmE_ z8PaG=)*-dBRpUm=tC2pZra>C(A!S?`6WP-BxLYHAO4E|mkQ^kT9#ZQVgp@`m$x~Io z3VE_)uMZ~hKBV#*rynHl_)FXIn9mBsFj%wMw5ZqX7PXqpH7nPUTlEHOHh`|PRO(do z6wBCLUlZ~vIc^|tqU(0nnQrLqb3 Date: Tue, 8 Jan 2013 21:40:48 +0100 Subject: [PATCH 03/24] fixed dynamic path --- src/helpers/gdbinit.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/helpers/gdbinit.py b/src/helpers/gdbinit.py index 484a6de..a39dbe3 100644 --- a/src/helpers/gdbinit.py +++ b/src/helpers/gdbinit.py @@ -28,8 +28,9 @@ class GDBInit(QObject): def __init__(self): - self.path = "./third_party/" self.fileName = "load_pretty_printer" + filePath = repr(__file__) + self.path = filePath[1:filePath.find("helpers/gdbinit.py")] + "third_party/" self.file_content = [] self.file_content.append("python\n") From 99733b558301ad4e66c80b7c7e7ac8c9cf83a1d8 Mon Sep 17 00:00:00 2001 From: Kevin Date: Wed, 16 Jan 2013 09:19:01 +0100 Subject: [PATCH 04/24] rework toggle beautify --- src/controllers/debugcontroller.py | 4 +++- src/controllers/tooltipcontroller.py | 6 +++++- src/controllers/watchcontroller.py | 6 +++++- src/datagraph/datagraphcontroller.py | 7 +++++++ src/views/mainwindow.py | 17 ++++++++++++++++- 5 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/controllers/debugcontroller.py b/src/controllers/debugcontroller.py index 8b0259e..9e9c374 100644 --- a/src/controllers/debugcontroller.py +++ b/src/controllers/debugcontroller.py @@ -149,7 +149,9 @@ def beautify(self): self.toggleBeautify = not self.toggleBeautify self.distributedObjects.localsController.reloadLocals() - + self.distributedObjects.watchController.clearModel() + self.distributedObjects.datagraphController.clearDataGraphOnBeautify() + def evaluateExpression(self, exp): if exp == "": return None diff --git a/src/controllers/tooltipcontroller.py b/src/controllers/tooltipcontroller.py index e59872b..4368491 100644 --- a/src/controllers/tooltipcontroller.py +++ b/src/controllers/tooltipcontroller.py @@ -32,7 +32,10 @@ def __init__(self, distributedObjects, view): TreeItemController.__init__(self, distributedObjects, "Tooltip", view, VariableModel, False) def __setVar(self, watch): - self.clear() + try: + self.clear() + except: + self.model.clear() try: self.add(self.variableList.addVarByName(watch)) except VariableNotFoundException: @@ -46,3 +49,4 @@ def showToolTip(self, exp, pos, parent): def hideToolTip(self): self.view.hideLater() + \ No newline at end of file diff --git a/src/controllers/watchcontroller.py b/src/controllers/watchcontroller.py index 4ff518a..da5c846 100644 --- a/src/controllers/watchcontroller.py +++ b/src/controllers/watchcontroller.py @@ -80,4 +80,8 @@ def loadSession(self, xmlHandler): childnodes = watchParent.childNodes() for i in range(childnodes.size()): attr = xmlHandler.getAttributes(childnodes.at(i)) - self.addWatch(attr["exp"]) + self.addWatch(attr["exp"]) + + def clearModel(self): + self.model.clear() + \ No newline at end of file diff --git a/src/datagraph/datagraphcontroller.py b/src/datagraph/datagraphcontroller.py index 89eaade..dc5554b 100644 --- a/src/datagraph/datagraphcontroller.py +++ b/src/datagraph/datagraphcontroller.py @@ -142,6 +142,13 @@ def clearDataGraph(self): """ self.variableList.clear() self.data_graph_view.clear() + + def clearDataGraphOnBeautify(self): + """ clears the DataGraphView
+ in case pretty printer gets en/disabled we can't clear + the variableList, as it already contains new variables. + """ + self.data_graph_view.clear() def saveSession(self, xmlHandler): """ Insert session info to xml file diff --git a/src/views/mainwindow.py b/src/views/mainwindow.py index 69746da..e9632c9 100644 --- a/src/views/mainwindow.py +++ b/src/views/mainwindow.py @@ -130,6 +130,8 @@ def __initActions(self): self.act.ReverseNext.setEnabled(False) self.act.ReverseStep.setEnabled(False) self.act.SaveFile.setEnabled(False) + self.act.Beautify.setCheckable(True) + self.act.Beautify.setChecked(True) # debug actions self.ui.menuDebug.addAction(self.act.Run) self.ui.menuDebug.addAction(self.act.Continue) @@ -192,7 +194,7 @@ def __connectActions(self): self.act.Record.triggered.connect(self.toggleRecord) self.act.ReverseStep.triggered.connect(self.debugController.reverse_step) self.act.ReverseNext.triggered.connect(self.debugController.reverse_next) - self.act.Beautify.triggered.connect(self.debugController.beautify) + self.act.Beautify.triggered.connect(self.__askBeautify) self.act.Interrupt.triggered.connect(self.debugController.interrupt) self.act.Finish.triggered.connect(self.debugController.finish) @@ -333,3 +335,16 @@ def __binaryChanged(self): else: self.fileWatcher.removePath(self.binaryName) self.fileWatcher.addPath(self.binaryName) + + def __askBeautify(self): + """ Warn user before Beautify - Using QtMessagebox for interaction""" + box = QtGui.QMessageBox() + if box.question(self, "Warning!", "This deletes the objects in watch and datagraphview.", + QtGui.QMessageBox.Ok, QtGui.QMessageBox.Cancel) == QtGui.QMessageBox.Ok: + self.debugController.beautify() + else: + self.act.Beautify.toggle() + + + + From 98691710eb2b63fe3d36a49b4cadc0159fa61ddf Mon Sep 17 00:00:00 2001 From: Kevin Date: Wed, 16 Jan 2013 13:02:35 +0100 Subject: [PATCH 05/24] fixed beautify --- src/controllers/debugcontroller.py | 2 +- src/controllers/tooltipcontroller.py | 5 +---- src/datagraph/datagraphcontroller.py | 7 ------- src/variables/variablelist.py | 5 +++-- src/variables/variablepool.py | 7 ++++--- src/views/mainwindow.py | 6 +----- 6 files changed, 10 insertions(+), 22 deletions(-) diff --git a/src/controllers/debugcontroller.py b/src/controllers/debugcontroller.py index 9e9c374..76a0143 100644 --- a/src/controllers/debugcontroller.py +++ b/src/controllers/debugcontroller.py @@ -150,7 +150,7 @@ def beautify(self): self.toggleBeautify = not self.toggleBeautify self.distributedObjects.localsController.reloadLocals() self.distributedObjects.watchController.clearModel() - self.distributedObjects.datagraphController.clearDataGraphOnBeautify() + self.distributedObjects.datagraphController.clearDataGraph() def evaluateExpression(self, exp): if exp == "": diff --git a/src/controllers/tooltipcontroller.py b/src/controllers/tooltipcontroller.py index 4368491..59129a7 100644 --- a/src/controllers/tooltipcontroller.py +++ b/src/controllers/tooltipcontroller.py @@ -32,10 +32,7 @@ def __init__(self, distributedObjects, view): TreeItemController.__init__(self, distributedObjects, "Tooltip", view, VariableModel, False) def __setVar(self, watch): - try: - self.clear() - except: - self.model.clear() + self.clear() try: self.add(self.variableList.addVarByName(watch)) except VariableNotFoundException: diff --git a/src/datagraph/datagraphcontroller.py b/src/datagraph/datagraphcontroller.py index dc5554b..89eaade 100644 --- a/src/datagraph/datagraphcontroller.py +++ b/src/datagraph/datagraphcontroller.py @@ -142,13 +142,6 @@ def clearDataGraph(self): """ self.variableList.clear() self.data_graph_view.clear() - - def clearDataGraphOnBeautify(self): - """ clears the DataGraphView
- in case pretty printer gets en/disabled we can't clear - the variableList, as it already contains new variables. - """ - self.data_graph_view.clear() def saveSession(self, xmlHandler): """ Insert session info to xml file diff --git a/src/variables/variablelist.py b/src/variables/variablelist.py index f404d89..abc7a3a 100644 --- a/src/variables/variablelist.py +++ b/src/variables/variablelist.py @@ -67,8 +67,9 @@ def addLocals(self): def removeVar(self, varWrapper): """ removes VariableWrapper varWrapper from the list @param varWrapper variables.variablewrapper.VariableWrapper, VariableWrapper to remove from the list """ - self.list.remove(varWrapper) - varWrapper.die() + if varWrapper in self.list: + self.list.remove(varWrapper) + varWrapper.die() def clear(self): """ Clears the whole VariableList. """ diff --git a/src/variables/variablepool.py b/src/variables/variablepool.py index b167e3d..338eeab 100644 --- a/src/variables/variablepool.py +++ b/src/variables/variablepool.py @@ -150,9 +150,10 @@ def removeVar(self, variable): """ remove variable from variable pool @param variable Variable type instance """ - self.variables.pop(variable._gdbName) - self.connector.var_delete(variable._gdbName) - del variable + if variable._gdbName in self.variables: + self.variables.pop(variable._gdbName) + self.connector.var_delete(variable._gdbName) + del variable def getChildren(self, name, childList, access, parentName, childformat): """ diff --git a/src/views/mainwindow.py b/src/views/mainwindow.py index e9632c9..cc40ffa 100644 --- a/src/views/mainwindow.py +++ b/src/views/mainwindow.py @@ -343,8 +343,4 @@ def __askBeautify(self): QtGui.QMessageBox.Ok, QtGui.QMessageBox.Cancel) == QtGui.QMessageBox.Ok: self.debugController.beautify() else: - self.act.Beautify.toggle() - - - - + self.act.Beautify.toggle() \ No newline at end of file From 4c585fbaa02d638be4a6cbf93307142a2897c9b8 Mon Sep 17 00:00:00 2001 From: Kevin Date: Mon, 4 Feb 2013 04:30:23 +0100 Subject: [PATCH 06/24] fixed watch/datagraph --- src/controllers/debugcontroller.py | 5 ++-- src/controllers/treeitemcontroller.py | 6 +++-- src/datagraph/datagraphvw.py | 18 +++++++------- src/helpers/gdbinit.py | 36 ++++++++++++++++----------- src/variables/variable.py | 11 ++++++-- src/variables/variablelist.py | 6 ++--- src/variables/variablepool.py | 27 ++++++++------------ 7 files changed, 60 insertions(+), 49 deletions(-) diff --git a/src/controllers/debugcontroller.py b/src/controllers/debugcontroller.py index 76a0143..2fbf69b 100644 --- a/src/controllers/debugcontroller.py +++ b/src/controllers/debugcontroller.py @@ -28,6 +28,7 @@ from helpers.gdboutput import GdbOutput import logging from helpers.configstore import ConfigSet, ConfigItem +from helpers.gdbinit import GDBInit class DebugConfig(ConfigSet): @@ -55,11 +56,11 @@ def __init__(self, distributedObjects): self.ptyhandler.start() - self.gdbinit.writeFile() + GDBInit.writeFile(self.gdbinit.getPath(),self.gdbinit.getFileName()) self.connector.start() - self.connector.initPrettyPrinter(self.gdbinit.getPath()) + self.connector.initPrettyPrinter(self.gdbinit.getPath() + self.gdbinit.getFileName()) self.connector.startPrettyPrinting() self.toggleBeautify = True diff --git a/src/controllers/treeitemcontroller.py b/src/controllers/treeitemcontroller.py index 4d60326..e38e3de 100644 --- a/src/controllers/treeitemcontroller.py +++ b/src/controllers/treeitemcontroller.py @@ -90,12 +90,14 @@ def getChildren(self, factory): Get Children from VariableList for StructVariable @param factory derived from VarWrapperFactory, factory to look in VariableList for children """ - if len(self.childItems) == 0: + if (len(self.childItems) == 0) or (self._v.numChild != len(self.childItems)): + self.removeChildren() for child in self._v.childs: vwChild = child.makeWrapper(factory) vwChild.parent = self - vwChild.dataChanged.connect(vwChild.hasChanged) + vwChild.dataChanged.connect(vwChild.hasChanged) self.addChild(vwChild) + self._v.numChild = len(self.childItems) return self.childItems diff --git a/src/datagraph/datagraphvw.py b/src/datagraph/datagraphvw.py index 9f56d74..227bb8a 100644 --- a/src/datagraph/datagraphvw.py +++ b/src/datagraph/datagraphvw.py @@ -127,6 +127,7 @@ def prepareContextMenu(self, menu): action.setChecked(self.vertical) def render(self, role, **kwargs): + self.varWrapper.getChildren() return HtmlTemplateHandler.render(self, role, vertical=self.vertical, **kwargs) @@ -204,7 +205,7 @@ def render(self, role, **kwargs): def setFilter(self, f): VariableWrapper.setFilter(self, f) self.setDirty(True) - + class ComplexDataGraphVW(DataGraphVW): def __init__(self, variable, distributedObjects, vwFactory, templateHandler): @@ -221,14 +222,13 @@ def __init__(self, variable, distributedObjects, vwFactory, templateHandler): def setOpen(self, open_): self.isOpen = open_ self.setDirty(True) - + def getChildren(self): """ returns list of children as DataGraphVWs; creates the wrappers if they haven't yet been @return list of datagraph.datagraphvw.DataGraphVW """ - if not self.childrenWrapper: - self.childrenWrapper = [] - for childVar in self.childs: - wrapper = childVar.makeWrapper(self.vwFactory) - wrapper.setExistingView(self.getView(), self) - self.childrenWrapper.append(wrapper) - return self.childrenWrapper + self.childrenWrapper = [] + for childVar in self.childs: + wrapper = childVar.makeWrapper(self.vwFactory) + wrapper.setExistingView(self.getView(), self) + self.childrenWrapper.append(wrapper) + return self.childrenWrapper \ No newline at end of file diff --git a/src/helpers/gdbinit.py b/src/helpers/gdbinit.py index a39dbe3..e2b03f7 100644 --- a/src/helpers/gdbinit.py +++ b/src/helpers/gdbinit.py @@ -31,26 +31,32 @@ def __init__(self): self.fileName = "load_pretty_printer" filePath = repr(__file__) self.path = filePath[1:filePath.find("helpers/gdbinit.py")] + "third_party/" + + @staticmethod + def writeFile(path,fileName): + init = path + fileName - self.file_content = [] - self.file_content.append("python\n") - self.file_content.append("import sys\n") - self.file_content.append("path=\"" + self.path + "pretty_printer\"\n") - self.file_content.append("sys.path.insert(0, path)\n") - self.file_content.append("from libstdcxx.v6.printers import register_libstdcxx_printers\n") - self.file_content.append("register_libstdcxx_printers (None)\n") - self.file_content.append("end\n") - - def writeFile(self): - if os.path.exists(self.path + self.fileName): - os.remove(self.path + self.fileName) + file_content = [] + file_content.append("python\n") + file_content.append("import sys\n") + file_content.append("path=\"" + path + "pretty_printer\"\n") + file_content.append("sys.path.insert(0, path)\n") + file_content.append("from libstdcxx.v6.printers import register_libstdcxx_printers\n") + file_content.append("register_libstdcxx_printers (None)\n") + file_content.append("end\n") + + if os.path.exists(init): + os.remove(init) - init_script = open(self.path + self.fileName,'w') + init_script = open(init,'w') - for i in self.file_content: + for i in file_content: init_script.write(i) init_script.close() def getPath(self): - return self.path + self.fileName \ No newline at end of file + return self.path + + def getFileName(self): + return self.fileName \ No newline at end of file diff --git a/src/variables/variable.py b/src/variables/variable.py index 2ad1e06..c85542b 100644 --- a/src/variables/variable.py +++ b/src/variables/variable.py @@ -39,7 +39,7 @@ class Variable(QObject): def __init__(self, variablepool, exp, gdbName, uniqueName, type_, value, inScope, - hasChildren, access): + numChild, access): QObject.__init__(self) self.exp = exp self.type = type_ @@ -47,11 +47,13 @@ def __init__(self, variablepool, exp, gdbName, self.access = access self.uniqueName = uniqueName self.value = value - self.hasChildren = hasChildren + self.numChild = numChild self._vp = variablepool self._gdbName = gdbName self._childs = [] + + self.hasChildren = property(lambda self: self.numChild > 0) def _getChildrenFromGdb(self): """Load the children from GDB, if there are any.""" @@ -99,6 +101,11 @@ def emitChanged(self): def makeWrapper(self, factory): return factory.makeWrapper(self) + + def removeChildren(self): + for child in self._childs: + child.removeChildren() + del self._childs[:] def die(self): self._vp.removeVar(self) diff --git a/src/variables/variablelist.py b/src/variables/variablelist.py index abc7a3a..8c603eb 100644 --- a/src/variables/variablelist.py +++ b/src/variables/variablelist.py @@ -52,9 +52,9 @@ def addVar(self, varWrapper): """ adds VariableWrapper varWrapper to the list @param varWrapper variables.variablewrapper.VariableWrapper, VariableWrapper to add to the list """ self.list.append(varWrapper) - + def reloadLocals(self): - self.list = [] + self.clear() for var in self.varPool.reloadLocals(): vw = var.makeWrapper(self.factory) self.list.append(vw) @@ -63,7 +63,7 @@ def addLocals(self): for var in self.varPool.addLocals(): vw = var.makeWrapper(self.factory) self.list.append(vw) - + def removeVar(self, varWrapper): """ removes VariableWrapper varWrapper from the list @param varWrapper variables.variablewrapper.VariableWrapper, VariableWrapper to remove from the list """ diff --git a/src/variables/variablepool.py b/src/variables/variablepool.py index 338eeab..aa9ad60 100644 --- a/src/variables/variablepool.py +++ b/src/variables/variablepool.py @@ -60,7 +60,7 @@ def clearVars(self): """ self.variables = {} - + def reloadLocals(self): """ function deletes all variables created within gdb and stored within ricodebug after that the locals are read again @@ -106,6 +106,10 @@ def __updateVars(self, isTracePoint=None): for changed in res: var = self.variables[changed.name] var.inScope = (changed.in_scope == "true") + if hasattr(changed, "new_num_children"): + var.removeChildren() + var.numChild = changed.new_num_children + self.getChildren(var._gdbName, var._childs, var.access, var.uniqueName, var._childFormat) if hasattr(changed, "value"): var.value = changed.value if not isTracePoint: @@ -215,7 +219,7 @@ def __createVariable(self, gdbVar, parentName=None, exp=None, access=None, child type_ = gdbVar.type value = gdbVar.value inScope = True - haschildren = (int(gdbVar.numchild) > 0) + numChild = int(gdbVar.numchild) access = access # We use some heuristics to find out whether a type is a pointer, an @@ -228,25 +232,16 @@ def __createVariable(self, gdbVar, parentName=None, exp=None, access=None, child # * Again, everything else is a normal variable. if value.startswith('0x'): logging.debug("Creating a pointer variable for '%s'", exp) - varReturn = PtrVariable(self, exp, gdbName, uniqueName, type_, value, inScope, haschildren, access) - elif haschildren: + varReturn = PtrVariable(self, exp, gdbName, uniqueName, type_, value, inScope, numChild, access) + elif numChild > 0: logging.debug("Creating a struct variable for '%s'", exp) - varReturn = StructVariable(self, exp, gdbName, uniqueName, type_, value, inScope, haschildren, access) + varReturn = StructVariable(self, exp, gdbName, uniqueName, type_, value, inScope, numChild, access) elif re.match("{...}", value): - # NOTE:this solution only works for variables that are not nested - # if the vector is uninitialized we get a negative length - # additionally we limit the children to 100 - checkError = self.connector.evaluate(exp); - if str(checkError).find("length -") is -1: - haschildren = True; - else: - haschildren = False - logging.debug("Creating an array variable for '%s'", exp) - varReturn = ArrayVariable(self, exp, gdbName, uniqueName, type_, value, inScope, haschildren, access) + varReturn = ArrayVariable(self, exp, gdbName, uniqueName, type_, value, inScope, numChild, access) else: logging.debug("Creating a normal variable for '%s'", exp) - varReturn = StdVariable(self, exp, gdbName, uniqueName, type_, value, inScope, haschildren, access) + varReturn = StdVariable(self, exp, gdbName, uniqueName, type_, value, inScope, numChild, access) return varReturn From f69afdc6a6bbbe8929825a2c25c2a573b097a509 Mon Sep 17 00:00:00 2001 From: stefan cao Date: Tue, 27 Nov 2012 17:13:58 +0100 Subject: [PATCH 07/24] Add svg datagraph routines --- src/datagraph/datagraphcontroller.py | 30 +++-- src/datagraph/svgview.py | 172 +++++++++++++++++++++++++++ src/datagraph/templates/svgview.mako | 63 ++++++++++ src/helpers/actions.py | 9 ++ 4 files changed, 266 insertions(+), 8 deletions(-) create mode 100644 src/datagraph/svgview.py create mode 100644 src/datagraph/templates/svgview.mako diff --git a/src/datagraph/datagraphcontroller.py b/src/datagraph/datagraphcontroller.py index 89eaade..f9b7833 100644 --- a/src/datagraph/datagraphcontroller.py +++ b/src/datagraph/datagraphcontroller.py @@ -67,6 +67,7 @@ def __init__(self, distributedObjects): ## @var vwFactory # datagraph.datagraphvwfactory.DataGraphVWFactory, private, self-created DataGraphVWFactory self.vwFactory = DataGraphVWFactory(self.distributedObjects) + ## @var variableList # variables.variablelist.VariableList, private, self-created VariableList self.variableList = VariableList(self.vwFactory, self.distributedObjects) @@ -93,6 +94,15 @@ def addWatch(self, watch, xPos=0, yPos=0): except VariableNotFoundException: pass + def addSVG(self, svgWrapper, xPos=0, yPos=0): + """ adds the given VariableWrapper varWrapper to the DataGraph and - if addVarToList is true - + also to the VariableList + @param svgWrapper datagraph.svgwrapper.SVGWrapper class to add + @param xPos Integer, the X-Coordinate of the Position where to add the VariableWrapper + @param yPos Integer, the Y-Coordinate of the Position where to add the VariableWrapper + """ + self.addGraph(svgWrapper, xPos, yPos) + def addVar(self, varWrapper, xPos=0, yPos=0, addVarToList=True): """ adds the given VariableWrapper varWrapper to the DataGraph and - if addVarToList is true - also to the VariableList @@ -101,23 +111,27 @@ def addVar(self, varWrapper, xPos=0, yPos=0, addVarToList=True): @param yPos Integer, the Y-Coordinate of the Position where to add the VariableWrapper @param addVarToList Boolean, tells if varWrapper should be added to the VariableList too """ - varWrapper.createView() + self.addGraph(varWrapper, xPos, yPos) + if addVarToList: + self.variableList.addVar(varWrapper) + + def addGraph(self, wrapper, xPos=0, yPos=0): + wrapper.createView() try: - varWrapper.getView().render() + wrapper.getView().render() except: from mako import exceptions logging.error("Caught exception while rendering template: %s", exceptions.text_error_template().render()) - varWrapper.setXPos(xPos) - varWrapper.setYPos(yPos) - self.data_graph_view.addItem(varWrapper.getView()) - if addVarToList: - self.variableList.addVar(varWrapper) + wrapper.setXPos(xPos) + wrapper.setYPos(yPos) + self.data_graph_view.addItem(wrapper.getView()) def removeVar(self, varWrapper): """ removes the given varWrapper from the DataGraphView and the PointerList @param varWrapper variables.variablewrapper.VariableWrapper, the VariableWrapper to remove """ - self.variableList.removeVar(varWrapper) + if varWrapper in self.variableList: + self.variableList.removeVar(varWrapper) self.data_graph_view.removeItem(varWrapper.getView()) def addPointer(self, fromView, toView): diff --git a/src/datagraph/svgview.py b/src/datagraph/svgview.py new file mode 100644 index 0000000..a42dd9b --- /dev/null +++ b/src/datagraph/svgview.py @@ -0,0 +1,172 @@ +# ricodebug - A GDB frontend which focuses on visually supported +# debugging using data structure graphs and SystemC features. +# +# Copyright (C) 2011 The ricodebug project team at the +# Upper Austrian University Of Applied Sciences Hagenberg, +# Department Embedded Systems Design +# +# This file is part of ricodebug. +# +# ricodebug is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# For further information see . + + +from .datagraphvw import HtmlTemplateHandler, DataGraphVW + +from PyQt4.QtCore import QSize, QSizeF, pyqtSignal +from PyQt4.QtGui import QGraphicsItem, QCursor, QFileDialog, QIcon +from PyQt4.QtWebKit import QGraphicsWebView +from mako.template import Template +from PyQt4 import QtCore +import sys +import logging + +import cairo +import rsvg +import gtk + + +class SVGView(QGraphicsWebView): + """ the view to show variables in the DataGraph """ + + removing = pyqtSignal() + + def __init__(self, svgWrapper, distributedObjects): + """ Constructor + @param varWrapper datagraph.datagraphvw.DataGraphVW, holds the Data of the Variable to show + @param distributedObjects distributedobjects.DistributedObjects, the DistributedObjects-Instance + """ + QGraphicsWebView.__init__(self, None) + self.svgWrapper = svgWrapper + self.distributedObjects = distributedObjects + self.setFlags(QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsFocusable) + self.htmlTemplate = Template(filename=sys.path[0] + '/datagraph/templates/svgview.mako') + self.page().setPreferredContentsSize(QSize(0, 0)) + self.setPreferredSize(QSizeF(0, 0)) + self.setResizesToContents(True) + + self.source = None + + # ids for ourself and the template handlers we will eventually render + self.lastId = -1 + self.uniqueIds = {} + + self.dirty = True + + self.id = self.getUniqueId(self) + + self.distributedObjects.signalProxy.variableUpdateCompleted.connect(self.render) + + def setDirty(self, render_immediately): + self.dirty = True + if render_immediately: + self.render() + + def render(self): + if self.dirty: + # the page's viewport will not shrink if new content is set, so set it to it's minimum + self.page().setViewportSize(QSize(0, 0)) + try: + self.source = self.htmlTemplate.render(svgWrapper=self.svgWrapper, top=True, id=self.id) + self.setHtml(self.source) + + for template, id_ in self.uniqueIds.iteritems(): + self.page().mainFrame().addToJavaScriptWindowObject(id_, template) + except Exception as e: + logging.error("Rendering failed: %s", str(e)) + self.setHtml(str(e)) + raise + + # force an update of the scene that contains us, since sometimes setHtml + # will not cause the view to be redrawn immediately + if self.scene(): + self.scene().update() + + self.dirty = False + + return self.source + + def openContextMenu(self, menu): + menu.addAction(QIcon(":/icons/images/minus.png"), + "Remove %s" % self.varWrapper.exp, self.remove) + menu.addAction(QIcon(":/icons/images/save-html.png"), + "Save HTML for %s" % self.varWrapper.exp, self.saveHtml) + menu.exec_(QCursor.pos()) + + @QtCore.pyqtSlot() + def saveHtml(self): + name = QFileDialog.getSaveFileName(filter="HTML (*.html)") + if name != "": + out = file(name, 'w') + out.write(self.source) + out.close() + + def contextMenuEvent(self, event): + pass + + @QtCore.pyqtSlot() + def remove(self): + """remove the varWrapper from the datagraph""" + self.removing.emit() + self.distributedObjects.datagraphController.removeVar(self.svgWrapper) + + def getUniqueId(self, template): + if not template in self.uniqueIds: + self.lastId += 1 + self.uniqueIds[template] = "tmpl%d" % self.lastId + return self.uniqueIds[template] + + def paint(self, painter, option, widget): + from PyQt4.QtGui import QColor + painter.setPen(QColor(Qt.red)) + painter.drawRoundedRect(self.boundingRect(), 5, 5) + QGraphicsWebView.paint(self, painter, option, widget) + + +class SVGTemplateHandler(HtmlTemplateHandler): + """ TemplateHandler for SVG Images """ + + def __init__(self, svgWrapper, distributedObjects): + """ Constructor + @param svgWrapper datagraph.datagraphvw.DataGraphVW, holds the Data to show """ + HtmlTemplateHandler.__init__(self, svgWrapper, distributedObjects, 'svgview.mako') + + def prepareContextMenu(self, menu): + HtmlTemplateHandler.prepareContextMenu(self, menu) + # filters.add_actions_for_all_filters(menu.addMenu(QIcon(":/icons/images/filter.png"), "Set Filter for %s..." % self.varWrapper.exp), self.varWrapper) + + +class SVGDataGraphVW(DataGraphVW): + """ Wrapper for SVG Images """ + + def __init__(self, image, distributedObjects): + """ Constructor + @param image SVG image to wrap with the new DataGraphVW + @param distributedObjects distributedobjects.DistributedObjects, the DistributedObjects-Instance + """ + DataGraphVW.__init__(self, image, distributedObjects) + self.image = image + self.templateHandler = SVGTemplateHandler(self, + self.distributedObjects) + def showContent(self): + self.image.refresh() + return self.image.imageContent + + def showName(self): + return str(self.image) + + def createView(self): + self._view = SVGView(self, self.distributedObjects) + self.parentWrapper = self._view diff --git a/src/datagraph/templates/svgview.mako b/src/datagraph/templates/svgview.mako new file mode 100644 index 0000000..a1f8719 --- /dev/null +++ b/src/datagraph/templates/svgview.mako @@ -0,0 +1,63 @@ +<%! + from datagraph.datagraphvw import Role +%>\ + + + + + + + +
${svgWrapper.showName()}
${svgWrapper.showContent()}
+
+ +
+ + diff --git a/src/helpers/actions.py b/src/helpers/actions.py index 7864348..03da96f 100644 --- a/src/helpers/actions.py +++ b/src/helpers/actions.py @@ -161,6 +161,15 @@ def getAddToTracepointAction(self, varname, tpname, slot): a.triggeredEx.connect(slot) return a + def getAddSVGToDatagraphAction(self, name, slot): + a = self.createEx(name) + a.setText(str(name)) + a.setIcon(QtGui.QIcon(":/icons/images/insert.png")) + a.setIconVisibleInMenu(True) + a.triggeredEx.connect(slot) + return a + + def createEx(self, parameter): return self.ActionEx(parameter, self) From e3521e5ef37afdfe5af6ce1d7f90d55e7d5c7431 Mon Sep 17 00:00:00 2001 From: Alexander Preisinger Date: Tue, 27 Nov 2012 18:25:41 +0100 Subject: [PATCH 08/24] Add initial diagram plugin --- src/datagraph/SVGImage.py | 49 ++++ src/datagraph/svgview.py | 53 +++-- src/plugins/SysCDiagram/SysCDiagramPlugin.py | 233 +++++++++++++++++++ src/plugins/SysCDiagram/__init__.py | 27 +++ 4 files changed, 335 insertions(+), 27 deletions(-) create mode 100644 src/datagraph/SVGImage.py create mode 100644 src/plugins/SysCDiagram/SysCDiagramPlugin.py create mode 100644 src/plugins/SysCDiagram/__init__.py diff --git a/src/datagraph/SVGImage.py b/src/datagraph/SVGImage.py new file mode 100644 index 0000000..33358cf --- /dev/null +++ b/src/datagraph/SVGImage.py @@ -0,0 +1,49 @@ +# ricodebug - A GDB frontend which focuses on visually supported +# debugging using data structure graphs and SystemC features. +# +# Copyright (C) 2011 The ricodebug project team at the +# Upper Austrian University Of Applied Sciences Hagenberg, +# Department Embedded Systems Design +# +# This file is part of ricodebug. +# +# ricodebug is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# For further information see . + +from PyQt4.QtCore import QObject, pyqtSignal + + +MIME_TYPE = "application/x-variableexpresssion" + + +class SVGImage(QObject): + changed = pyqtSignal(str) + + def __init__(self, name, fileObject): + QObject.__init__(self) + self.name = name + self.fileObject = fileObject + self.imageContent = fileObject.read() + self.inScope = True + + def setFileObject(self, fo): + self.fileObject = fo + + def refresh(self): + self.fileObject.seek(0) + self.imageContent = self.fileObject.read() + + def __str__(self): + return self.name diff --git a/src/datagraph/svgview.py b/src/datagraph/svgview.py index a42dd9b..d83e58b 100644 --- a/src/datagraph/svgview.py +++ b/src/datagraph/svgview.py @@ -24,7 +24,6 @@ from .datagraphvw import HtmlTemplateHandler, DataGraphVW - from PyQt4.QtCore import QSize, QSizeF, pyqtSignal from PyQt4.QtGui import QGraphicsItem, QCursor, QFileDialog, QIcon from PyQt4.QtWebKit import QGraphicsWebView @@ -33,10 +32,6 @@ import sys import logging -import cairo -import rsvg -import gtk - class SVGView(QGraphicsWebView): """ the view to show variables in the DataGraph """ @@ -45,14 +40,16 @@ class SVGView(QGraphicsWebView): def __init__(self, svgWrapper, distributedObjects): """ Constructor - @param varWrapper datagraph.datagraphvw.DataGraphVW, holds the Data of the Variable to show - @param distributedObjects distributedobjects.DistributedObjects, the DistributedObjects-Instance + @param varWrapper holds the Data of the Variable to show + @param distributedObjects the DistributedObjects-Instance """ QGraphicsWebView.__init__(self, None) self.svgWrapper = svgWrapper self.distributedObjects = distributedObjects - self.setFlags(QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsFocusable) - self.htmlTemplate = Template(filename=sys.path[0] + '/datagraph/templates/svgview.mako') + self.setFlags(QGraphicsItem.ItemIsMovable | + QGraphicsItem.ItemIsFocusable) + self.htmlTemplate = Template(filename=sys.path[0] + + '/datagraph/templates/svgview.mako') self.page().setPreferredContentsSize(QSize(0, 0)) self.setPreferredSize(QSizeF(0, 0)) self.setResizesToContents(True) @@ -67,7 +64,8 @@ def __init__(self, svgWrapper, distributedObjects): self.id = self.getUniqueId(self) - self.distributedObjects.signalProxy.variableUpdateCompleted.connect(self.render) + self.distributedObjects.signalProxy.\ + variableUpdateCompleted.connect(self.render) def setDirty(self, render_immediately): self.dirty = True @@ -76,21 +74,25 @@ def setDirty(self, render_immediately): def render(self): if self.dirty: - # the page's viewport will not shrink if new content is set, so set it to it's minimum + # the page's viewport will not shrink if new content is set, + # so set it to it's minimum self.page().setViewportSize(QSize(0, 0)) try: - self.source = self.htmlTemplate.render(svgWrapper=self.svgWrapper, top=True, id=self.id) + self.source = \ + self.htmlTemplate.render(svgWrapper=self.svgWrapper, + top=True, id=self.id) self.setHtml(self.source) for template, id_ in self.uniqueIds.iteritems(): - self.page().mainFrame().addToJavaScriptWindowObject(id_, template) + self.page().mainFrame().\ + addToJavaScriptWindowObject(id_, template) except Exception as e: logging.error("Rendering failed: %s", str(e)) self.setHtml(str(e)) raise - # force an update of the scene that contains us, since sometimes setHtml - # will not cause the view to be redrawn immediately + # force an update of the scene that contains us, since sometimes + # setHtml will not cause the view to be redrawn immediately if self.scene(): self.scene().update() @@ -100,9 +102,9 @@ def render(self): def openContextMenu(self, menu): menu.addAction(QIcon(":/icons/images/minus.png"), - "Remove %s" % self.varWrapper.exp, self.remove) + "Remove %s" % self.varWrapper.exp, self.remove) menu.addAction(QIcon(":/icons/images/save-html.png"), - "Save HTML for %s" % self.varWrapper.exp, self.saveHtml) + "Save HTML for %s" % self.varWrapper.exp, self.saveHtml) menu.exec_(QCursor.pos()) @QtCore.pyqtSlot() @@ -128,24 +130,20 @@ def getUniqueId(self, template): self.uniqueIds[template] = "tmpl%d" % self.lastId return self.uniqueIds[template] - def paint(self, painter, option, widget): - from PyQt4.QtGui import QColor - painter.setPen(QColor(Qt.red)) - painter.drawRoundedRect(self.boundingRect(), 5, 5) - QGraphicsWebView.paint(self, painter, option, widget) - class SVGTemplateHandler(HtmlTemplateHandler): """ TemplateHandler for SVG Images """ def __init__(self, svgWrapper, distributedObjects): """ Constructor - @param svgWrapper datagraph.datagraphvw.DataGraphVW, holds the Data to show """ - HtmlTemplateHandler.__init__(self, svgWrapper, distributedObjects, 'svgview.mako') + @param svgWrapper holds the Data to show """ + HtmlTemplateHandler.__init__(self, + svgWrapper, + distributedObjects, + 'svgview.mako') def prepareContextMenu(self, menu): HtmlTemplateHandler.prepareContextMenu(self, menu) - # filters.add_actions_for_all_filters(menu.addMenu(QIcon(":/icons/images/filter.png"), "Set Filter for %s..." % self.varWrapper.exp), self.varWrapper) class SVGDataGraphVW(DataGraphVW): @@ -154,12 +152,13 @@ class SVGDataGraphVW(DataGraphVW): def __init__(self, image, distributedObjects): """ Constructor @param image SVG image to wrap with the new DataGraphVW - @param distributedObjects distributedobjects.DistributedObjects, the DistributedObjects-Instance + @param distributedObjects the DistributedObjects-Instance """ DataGraphVW.__init__(self, image, distributedObjects) self.image = image self.templateHandler = SVGTemplateHandler(self, self.distributedObjects) + def showContent(self): self.image.refresh() return self.image.imageContent diff --git a/src/plugins/SysCDiagram/SysCDiagramPlugin.py b/src/plugins/SysCDiagram/SysCDiagramPlugin.py new file mode 100644 index 0000000..b480af2 --- /dev/null +++ b/src/plugins/SysCDiagram/SysCDiagramPlugin.py @@ -0,0 +1,233 @@ +# ricodebug - A GDB frontend which focuses on visually supported +# debugging using data structure graphes and SystemC features. +# +# Copyright (C) 2011 The ricodebug project team at the +# Upper Austrian University Of Applied Sciences Hagenberg, +# Department Embedded Systems Design +# +# This file is part of ricodebug. +# +# ricodebug is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# For further information see . + +from PyQt4.QtCore import QThread +from datagraph.svgview import SVGDataGraphVW +from datagraph.SVGImage import SVGImage +from StringIO import StringIO +from pydot import Dot, Cluster, Node + +from variables.varwrapperfactory import VarWrapperFactory +from variables.variablelist import VariableList + + +class SysCDiagramPlugin(QThread): + + def __init__(self): + QThread.__init__(self, None) + + def initPlugin(self, signalproxy): + """Initialise the systemc block diagram plugin""" + + self.signalproxy = signalproxy + self.do = signalproxy.distributedObjects + + # systemc stuff and and pointer type strings needed for casting in gdb + # because systemc objects can be systemc modules, systemc ports, etc. + self.ctx = None + self.ctx_pointer = None + self.ctx_func = "sc_get_curr_simcontext()" + self.ctx_found = False + + self.ctx_type = "(sc_core::sc_simcontext*)" + self.port_type = "(sc_core::sc_port_base*)" + self.module_type = "(sc_core::sc_module*)" + self.object_type = "(sc_core::sc_object*)" + self.prim_channel_type = "(sc_core::sc_prim_channel*)" + + # dict with a string that represents the pointer as key and + # a nested dict with parent, children, etc as key-values + # dst_dict[ptr] = {"wrapper": vw, + # "name": None, + # "parent_ptr": None, + # "children": {}} + self.sysc_modules = {} + self.sysc_objects = {} + self.sysc_ports = {} + self.sysc_prim_channels = {} + + # because of how we built the interface for the tracepoints on the + # datagraph we first need to create an file obj. We choose StringIO + # because it uses a string as buffer and does not acces the filesystem + self._file_obj = StringIO() + self.image = SVGImage("SystemC Block Diagram", self._file_obj) + self.image_wrapper = SVGDataGraphVW(self.image, self.do) + + self.signalproxy.inferiorStoppedNormally.connect(self.update) + + # hook Datagraph variable wrapper into the datagraph controller + # after self.action.commit is called the image will be displayed at the + # datagraph + self.action = self.do.actions.\ + getAddSVGToDatagraphAction(self.image_wrapper, + self.do. + datagraphController.addSVG) + + # pydot graph visualization library + self.block_diagram = Dot(graph_type='digraph') + self.block_diagram.set_graph_defaults(compound='true', + splines='ortho', + rankdir='LR') + self.block_diagram.set_node_defaults(shape='box') + + # Needed for creating the right variables and variable wrappers from + # the systemc pointers + self.vwFactory = VarWrapperFactory() + self.variableList = VariableList(self.vwFactory, self.do) + + def deInitPlugin(self): + pass + + def evaluateExp(self, exp): + return self.signalproxy.gdbEvaluateExpression(exp) + + def showDiagram(self): + self.action.commit() + + def update(self): + if self.analysis_done: + # disconnect update function to stop calling it + self.signalproxy.inferiorStoppedNormally.disconnect(self.update) + return + + def update(self): + self.start() + + def run(self): + if not self.ctx_found: + self.__findSimContext() + else: + # don't try to analyze if elaboration is not done + if not self.ctx["m_elaboration_done"]: + return + + # prepare for easy information collection + object_vec = self.ctx["m_child_objects"] + module_vec = self.ctx["m_module_registry"]["*"]["m_module_vec"] + port_vec = self.ctx["m_port_registry"]["*"]["m_port_vec"] + prim_channel_vec = self.ctx["m_prim_channel_registry"]["*"]\ + ["m_prim_channel_vec"] + + # find all relevant information + self.__findSysCObjects(object_vec, self.object_type, self.sysc_objects) + self.__findSysCObjects(module_vec, self.module_type, self.sysc_modules) + self.__findSysCObjects(port_vec, self.port_type, self.sysc_ports) + self.__findSysCObjects(prim_channel_vec, + self.prim_channel_type, + self.sysc_prim_channels) + + # build hierachy + # pydot data + + clusters = {} + nodes = {} + edges = {} + + # build pydot hierachy and add all subgraphs and nodes to the main + # graph + self.__buildHierachy(self.sysc_objects, clusters, nodes) + for sptr in clusters: + self.block_diagram.add_subgraph(clusters[sptr]) + for sptr in nodes: + self.block_diagram.add_node(nodes[sptr]) + + self.block_diagram.write_raw("bd.gv") + self._file_obj.write(self.block_diagram.create_svg()) + self.signalproxy.inferiorStoppedNormally.disconnect(self.update) + + self.showDiagram() + + def __buildHierachy(self, obj_dict, clusters, nodes): + """ Build Cluster and Node hierachy for pydot + """ + for ptr in obj_dict: + obj = obj_dict[ptr] + if ptr in (self.sysc_ports.keys()+self.sysc_prim_channels.keys()): + continue + + if len(obj["children"].keys()) == 0: + node = Node(obj["name"]) + nodes[ptr] = node + else: + clust = Cluster(obj["name"].replace(".", "_"), + color='red', + label=obj["name"]) + c_clusters = {} + c_nodes = {} + self.__buildHierachy(obj["children"], c_clusters, c_nodes) + for sptr in c_clusters: + clust.add_subgraph(c_clusters[sptr]) + for sptr in c_nodes: + clust.add_node(c_nodes[sptr]) + clusters[ptr] = clust + + def __findSysCObjects(self, obj_vec, obj_type, dst_dict): + """ Find sc_object from module, port and prim channel registry + """ + for i in obj_vec.childs: + ptr = i.value + var = "(*{}{})".format(obj_type, ptr) + vw = self.variableList.addVarByName(var) + dst_dict[ptr] = {"wrapper": vw, + "name": None, + "parent_ptr": None, + "children": {}} + + for member in vw.childs: + if member.exp == "m_name": + dst_dict[ptr]["name"] = member.value.strip('"') + + elif member.exp == "m_child_objects": + children = {} + self.__findSysCObjects(vw["m_child_objects"], + obj_type, + children) + dst_dict[ptr]["children"] = children + # for obj in member.childs: + # dst_dict[ptr]["child_ptrs"].append(obj.value) + + elif member.exp == "m_parent": + dst_dict[ptr]["parent_ptr"] = member.value + + def __findSimContext(self): + """ Find systemc simulation context + """ + self.ctx_pointer = self.evaluateExp(self.ctx_func) + if self.ctx is None: + frame = 0 + depth = self.signalproxy.gdbGetStackDepth() + while (self.ctx_pointer is None) and frame <= depth: + frame += 1 + self.signalproxy.gdbSelectStackFrame(frame) + self.ctx_pointer = self.evaluateExp(self.ctx_func) + else: + if self.ctx_pointer is None: + return + else: + self.ctx_found = True + self.ctx = self.do.variablePool.getVar(self.ctx_func)["*"] + else: + self.ctx_found = True + self.ctx = self.do.variablePool.getVar(self.ctx_func)["*"] + diff --git a/src/plugins/SysCDiagram/__init__.py b/src/plugins/SysCDiagram/__init__.py new file mode 100644 index 0000000..bdc7acf --- /dev/null +++ b/src/plugins/SysCDiagram/__init__.py @@ -0,0 +1,27 @@ +# ricodebug - A GDB frontend which focuses on visually supported +# debugging using data structure graphes and SystemC features. +# +# Copyright (C) 2011 The ricodebug project team at the +# Upper Austrian University Of Applied Sciences Hagenberg, +# Department Embedded Systems Design +# +# This file is part of ricodebug. +# +# ricodebug is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# For further information see . + +#optional: define name of plugin, will be added to menu Help -> Plugins +#Pluginloader will call plugin like file if PluginName is not defined +PluginName = "SystemC Block Diagram" From cd84f7687d9d2e95b557177e67a766ad9bdd9316 Mon Sep 17 00:00:00 2001 From: Alexander Preisinger Date: Wed, 30 Jan 2013 14:28:01 +0100 Subject: [PATCH 09/24] remove left over update function --- src/plugins/SysCDiagram/SysCDiagramPlugin.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/plugins/SysCDiagram/SysCDiagramPlugin.py b/src/plugins/SysCDiagram/SysCDiagramPlugin.py index b480af2..622a726 100644 --- a/src/plugins/SysCDiagram/SysCDiagramPlugin.py +++ b/src/plugins/SysCDiagram/SysCDiagramPlugin.py @@ -105,12 +105,6 @@ def evaluateExp(self, exp): def showDiagram(self): self.action.commit() - def update(self): - if self.analysis_done: - # disconnect update function to stop calling it - self.signalproxy.inferiorStoppedNormally.disconnect(self.update) - return - def update(self): self.start() From 220d2c88ec482cc0c2c8a4852df4d83a3ce3fd4a Mon Sep 17 00:00:00 2001 From: Alexander Preisinger Date: Wed, 30 Jan 2013 14:31:18 +0100 Subject: [PATCH 10/24] remove unused variables and function calls --- src/plugins/SysCDiagram/SysCDiagramPlugin.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/plugins/SysCDiagram/SysCDiagramPlugin.py b/src/plugins/SysCDiagram/SysCDiagramPlugin.py index 622a726..3bb9414 100644 --- a/src/plugins/SysCDiagram/SysCDiagramPlugin.py +++ b/src/plugins/SysCDiagram/SysCDiagramPlugin.py @@ -118,25 +118,12 @@ def run(self): # prepare for easy information collection object_vec = self.ctx["m_child_objects"] - module_vec = self.ctx["m_module_registry"]["*"]["m_module_vec"] - port_vec = self.ctx["m_port_registry"]["*"]["m_port_vec"] - prim_channel_vec = self.ctx["m_prim_channel_registry"]["*"]\ - ["m_prim_channel_vec"] # find all relevant information self.__findSysCObjects(object_vec, self.object_type, self.sysc_objects) - self.__findSysCObjects(module_vec, self.module_type, self.sysc_modules) - self.__findSysCObjects(port_vec, self.port_type, self.sysc_ports) - self.__findSysCObjects(prim_channel_vec, - self.prim_channel_type, - self.sysc_prim_channels) - - # build hierachy - # pydot data clusters = {} nodes = {} - edges = {} # build pydot hierachy and add all subgraphs and nodes to the main # graph From 51d8b6b443f219e0064464617cbe68f8c6025678 Mon Sep 17 00:00:00 2001 From: Alexander Preisinger Date: Wed, 30 Jan 2013 14:32:59 +0100 Subject: [PATCH 11/24] remove commented out code --- src/plugins/SysCDiagram/SysCDiagramPlugin.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/plugins/SysCDiagram/SysCDiagramPlugin.py b/src/plugins/SysCDiagram/SysCDiagramPlugin.py index 3bb9414..ddd1503 100644 --- a/src/plugins/SysCDiagram/SysCDiagramPlugin.py +++ b/src/plugins/SysCDiagram/SysCDiagramPlugin.py @@ -185,8 +185,6 @@ def __findSysCObjects(self, obj_vec, obj_type, dst_dict): obj_type, children) dst_dict[ptr]["children"] = children - # for obj in member.childs: - # dst_dict[ptr]["child_ptrs"].append(obj.value) elif member.exp == "m_parent": dst_dict[ptr]["parent_ptr"] = member.value From d070f47aef78d93bcf65c7ff7e3c315ccbf39d1e Mon Sep 17 00:00:00 2001 From: Alexander Preisinger Date: Wed, 30 Jan 2013 14:39:19 +0100 Subject: [PATCH 12/24] remove addSVG from datagraph ctrl --- src/datagraph/SVGImage.py | 3 +++ src/datagraph/datagraphcontroller.py | 9 --------- src/plugins/SysCDiagram/SysCDiagramPlugin.py | 2 +- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/datagraph/SVGImage.py b/src/datagraph/SVGImage.py index 33358cf..0ab6cce 100644 --- a/src/datagraph/SVGImage.py +++ b/src/datagraph/SVGImage.py @@ -45,5 +45,8 @@ def refresh(self): self.fileObject.seek(0) self.imageContent = self.fileObject.read() + def die(self): + pass + def __str__(self): return self.name diff --git a/src/datagraph/datagraphcontroller.py b/src/datagraph/datagraphcontroller.py index f9b7833..df928aa 100644 --- a/src/datagraph/datagraphcontroller.py +++ b/src/datagraph/datagraphcontroller.py @@ -94,15 +94,6 @@ def addWatch(self, watch, xPos=0, yPos=0): except VariableNotFoundException: pass - def addSVG(self, svgWrapper, xPos=0, yPos=0): - """ adds the given VariableWrapper varWrapper to the DataGraph and - if addVarToList is true - - also to the VariableList - @param svgWrapper datagraph.svgwrapper.SVGWrapper class to add - @param xPos Integer, the X-Coordinate of the Position where to add the VariableWrapper - @param yPos Integer, the Y-Coordinate of the Position where to add the VariableWrapper - """ - self.addGraph(svgWrapper, xPos, yPos) - def addVar(self, varWrapper, xPos=0, yPos=0, addVarToList=True): """ adds the given VariableWrapper varWrapper to the DataGraph and - if addVarToList is true - also to the VariableList diff --git a/src/plugins/SysCDiagram/SysCDiagramPlugin.py b/src/plugins/SysCDiagram/SysCDiagramPlugin.py index ddd1503..928c946 100644 --- a/src/plugins/SysCDiagram/SysCDiagramPlugin.py +++ b/src/plugins/SysCDiagram/SysCDiagramPlugin.py @@ -82,7 +82,7 @@ def initPlugin(self, signalproxy): self.action = self.do.actions.\ getAddSVGToDatagraphAction(self.image_wrapper, self.do. - datagraphController.addSVG) + datagraphController.addVar) # pydot graph visualization library self.block_diagram = Dot(graph_type='digraph') From 592b6483680aa32901b5ea849c83e860f9481b95 Mon Sep 17 00:00:00 2001 From: Alexander Preisinger Date: Fri, 1 Feb 2013 11:42:35 +0100 Subject: [PATCH 13/24] renmae SVGImage to svgimage --- src/datagraph/{SVGImage.py => svgimage.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/datagraph/{SVGImage.py => svgimage.py} (100%) diff --git a/src/datagraph/SVGImage.py b/src/datagraph/svgimage.py similarity index 100% rename from src/datagraph/SVGImage.py rename to src/datagraph/svgimage.py From 58d3da69ebfe02007bedcf695fb450b44cb75b3d Mon Sep 17 00:00:00 2001 From: Alexander Preisinger Date: Fri, 1 Feb 2013 11:46:12 +0100 Subject: [PATCH 14/24] don't create widgets outside of the main thread This removes the flood of Pixmap error messages --- src/plugins/SysCDiagram/SysCDiagramPlugin.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/plugins/SysCDiagram/SysCDiagramPlugin.py b/src/plugins/SysCDiagram/SysCDiagramPlugin.py index 928c946..a79d4fe 100644 --- a/src/plugins/SysCDiagram/SysCDiagramPlugin.py +++ b/src/plugins/SysCDiagram/SysCDiagramPlugin.py @@ -22,9 +22,8 @@ # # For further information see . -from PyQt4.QtCore import QThread from datagraph.svgview import SVGDataGraphVW -from datagraph.SVGImage import SVGImage +from datagraph.svgimage import SVGImage from StringIO import StringIO from pydot import Dot, Cluster, Node @@ -32,10 +31,7 @@ from variables.variablelist import VariableList -class SysCDiagramPlugin(QThread): - - def __init__(self): - QThread.__init__(self, None) +class SysCDiagramPlugin(): def initPlugin(self, signalproxy): """Initialise the systemc block diagram plugin""" @@ -106,9 +102,6 @@ def showDiagram(self): self.action.commit() def update(self): - self.start() - - def run(self): if not self.ctx_found: self.__findSimContext() else: From 434786f7b9a88add9baa0fb8b4b71eb648058d90 Mon Sep 17 00:00:00 2001 From: Alexander Preisinger Date: Fri, 1 Feb 2013 11:58:03 +0100 Subject: [PATCH 15/24] update SysCSimCtx for pretty printer --- src/plugins/SysCSimCtx/SysCSimCtxPlugin.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/plugins/SysCSimCtx/SysCSimCtxPlugin.py b/src/plugins/SysCSimCtx/SysCSimCtxPlugin.py index 5a6984e..d0c6598 100644 --- a/src/plugins/SysCSimCtx/SysCSimCtxPlugin.py +++ b/src/plugins/SysCSimCtx/SysCSimCtxPlugin.py @@ -145,7 +145,8 @@ def _findSimVariables(self): self._setDeltaCycle(self._currDeltaCycles.value) if self._simContext["m_curr_proc_info"]["process_handle"]["*"] is not None: - self._currProcess = self._simContext["m_curr_proc_info"]["process_handle"]["*"]["m_name"]["_M_dataplus"]["_M_p"] + self._currProcess = self._simContext["m_curr_proc_info"]\ + ["process_handle"]["*"]["m_name"] self._currProcess.changed.connect(self._setProcess) self._setProcess(self._currProcess.value) else: @@ -201,17 +202,17 @@ def _changedProcessHandle(self, v): self._currProcess.changed.disconnect() self._currProcess.die() self._currProcess = self.__sp.distributedObjects.variablePool.\ - getVar("(sc_core::sc_process_b *)" + str(v))["*"]["m_name"]["_M_dataplus"]["_M_p"] + getVar("(sc_core::sc_process_b *)" + str(v))["*"]["m_name"] self._currProcess.changed.connect(self._setProcess) self._setProcess(self._currProcess.value) def _setProcessKind(self, v): t = { - "sc_core::SC_NO_PROC_": "No Process", - "sc_core::SC_METHOD_PROC_": "Method", - "sc_core::SC_THREAD_PROC_": "Thread", - "sc_core::SC_CTHREAD_PROC_": "CThread", - } + "sc_core::SC_NO_PROC_": "No Process", + "sc_core::SC_METHOD_PROC_": "Method", + "sc_core::SC_THREAD_PROC_": "Thread", + "sc_core::SC_CTHREAD_PROC_": "CThread", + } self.ui.processKindEdit.setText(t[str(v)]) @@ -228,7 +229,7 @@ def _setSimTime(self): "us": 6, "ms": 9, "s": 12 - } + } time /= 10 ** div[unit] From f1561f4d28b6182dc81634ddb7b05becfc29ba4c Mon Sep 17 00:00:00 2001 From: Alexander Preisinger Date: Fri, 1 Feb 2013 12:01:35 +0100 Subject: [PATCH 16/24] remove unused stuff from svgimage.py --- src/datagraph/svgimage.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/datagraph/svgimage.py b/src/datagraph/svgimage.py index 0ab6cce..d9e7728 100644 --- a/src/datagraph/svgimage.py +++ b/src/datagraph/svgimage.py @@ -25,9 +25,6 @@ from PyQt4.QtCore import QObject, pyqtSignal -MIME_TYPE = "application/x-variableexpresssion" - - class SVGImage(QObject): changed = pyqtSignal(str) @@ -38,9 +35,6 @@ def __init__(self, name, fileObject): self.imageContent = fileObject.read() self.inScope = True - def setFileObject(self, fo): - self.fileObject = fo - def refresh(self): self.fileObject.seek(0) self.imageContent = self.fileObject.read() From 327a2bbcc07e8f3977a32f0541e2d369adbd17c9 Mon Sep 17 00:00:00 2001 From: Alexander Preisinger Date: Fri, 1 Feb 2013 12:05:55 +0100 Subject: [PATCH 17/24] remove SVGTemplateHandler --- src/datagraph/svgview.py | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/src/datagraph/svgview.py b/src/datagraph/svgview.py index d83e58b..b102571 100644 --- a/src/datagraph/svgview.py +++ b/src/datagraph/svgview.py @@ -111,7 +111,7 @@ def openContextMenu(self, menu): def saveHtml(self): name = QFileDialog.getSaveFileName(filter="HTML (*.html)") if name != "": - out = file(name, 'w') + out = open(name, 'w') out.write(self.source) out.close() @@ -131,21 +131,6 @@ def getUniqueId(self, template): return self.uniqueIds[template] -class SVGTemplateHandler(HtmlTemplateHandler): - """ TemplateHandler for SVG Images """ - - def __init__(self, svgWrapper, distributedObjects): - """ Constructor - @param svgWrapper holds the Data to show """ - HtmlTemplateHandler.__init__(self, - svgWrapper, - distributedObjects, - 'svgview.mako') - - def prepareContextMenu(self, menu): - HtmlTemplateHandler.prepareContextMenu(self, menu) - - class SVGDataGraphVW(DataGraphVW): """ Wrapper for SVG Images """ @@ -156,8 +141,9 @@ def __init__(self, image, distributedObjects): """ DataGraphVW.__init__(self, image, distributedObjects) self.image = image - self.templateHandler = SVGTemplateHandler(self, - self.distributedObjects) + self.templateHandler = HtmlTemplateHandler(self, + self.distributedObjects, + "svgview.mako") def showContent(self): self.image.refresh() From 7f390fc6e05f6fe547143f405e0778ea9045d5f4 Mon Sep 17 00:00:00 2001 From: Alexander Preisinger Date: Mon, 4 Feb 2013 11:00:21 +0100 Subject: [PATCH 18/24] simplify svgview to svgvw --- src/datagraph/svgview.py | 157 --------------------------- src/datagraph/svgvw.py | 55 ++++++++++ src/datagraph/templates/svgview.mako | 65 +---------- 3 files changed, 57 insertions(+), 220 deletions(-) delete mode 100644 src/datagraph/svgview.py create mode 100644 src/datagraph/svgvw.py diff --git a/src/datagraph/svgview.py b/src/datagraph/svgview.py deleted file mode 100644 index b102571..0000000 --- a/src/datagraph/svgview.py +++ /dev/null @@ -1,157 +0,0 @@ -# ricodebug - A GDB frontend which focuses on visually supported -# debugging using data structure graphs and SystemC features. -# -# Copyright (C) 2011 The ricodebug project team at the -# Upper Austrian University Of Applied Sciences Hagenberg, -# Department Embedded Systems Design -# -# This file is part of ricodebug. -# -# ricodebug is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# For further information see . - - -from .datagraphvw import HtmlTemplateHandler, DataGraphVW -from PyQt4.QtCore import QSize, QSizeF, pyqtSignal -from PyQt4.QtGui import QGraphicsItem, QCursor, QFileDialog, QIcon -from PyQt4.QtWebKit import QGraphicsWebView -from mako.template import Template -from PyQt4 import QtCore -import sys -import logging - - -class SVGView(QGraphicsWebView): - """ the view to show variables in the DataGraph """ - - removing = pyqtSignal() - - def __init__(self, svgWrapper, distributedObjects): - """ Constructor - @param varWrapper holds the Data of the Variable to show - @param distributedObjects the DistributedObjects-Instance - """ - QGraphicsWebView.__init__(self, None) - self.svgWrapper = svgWrapper - self.distributedObjects = distributedObjects - self.setFlags(QGraphicsItem.ItemIsMovable | - QGraphicsItem.ItemIsFocusable) - self.htmlTemplate = Template(filename=sys.path[0] + - '/datagraph/templates/svgview.mako') - self.page().setPreferredContentsSize(QSize(0, 0)) - self.setPreferredSize(QSizeF(0, 0)) - self.setResizesToContents(True) - - self.source = None - - # ids for ourself and the template handlers we will eventually render - self.lastId = -1 - self.uniqueIds = {} - - self.dirty = True - - self.id = self.getUniqueId(self) - - self.distributedObjects.signalProxy.\ - variableUpdateCompleted.connect(self.render) - - def setDirty(self, render_immediately): - self.dirty = True - if render_immediately: - self.render() - - def render(self): - if self.dirty: - # the page's viewport will not shrink if new content is set, - # so set it to it's minimum - self.page().setViewportSize(QSize(0, 0)) - try: - self.source = \ - self.htmlTemplate.render(svgWrapper=self.svgWrapper, - top=True, id=self.id) - self.setHtml(self.source) - - for template, id_ in self.uniqueIds.iteritems(): - self.page().mainFrame().\ - addToJavaScriptWindowObject(id_, template) - except Exception as e: - logging.error("Rendering failed: %s", str(e)) - self.setHtml(str(e)) - raise - - # force an update of the scene that contains us, since sometimes - # setHtml will not cause the view to be redrawn immediately - if self.scene(): - self.scene().update() - - self.dirty = False - - return self.source - - def openContextMenu(self, menu): - menu.addAction(QIcon(":/icons/images/minus.png"), - "Remove %s" % self.varWrapper.exp, self.remove) - menu.addAction(QIcon(":/icons/images/save-html.png"), - "Save HTML for %s" % self.varWrapper.exp, self.saveHtml) - menu.exec_(QCursor.pos()) - - @QtCore.pyqtSlot() - def saveHtml(self): - name = QFileDialog.getSaveFileName(filter="HTML (*.html)") - if name != "": - out = open(name, 'w') - out.write(self.source) - out.close() - - def contextMenuEvent(self, event): - pass - - @QtCore.pyqtSlot() - def remove(self): - """remove the varWrapper from the datagraph""" - self.removing.emit() - self.distributedObjects.datagraphController.removeVar(self.svgWrapper) - - def getUniqueId(self, template): - if not template in self.uniqueIds: - self.lastId += 1 - self.uniqueIds[template] = "tmpl%d" % self.lastId - return self.uniqueIds[template] - - -class SVGDataGraphVW(DataGraphVW): - """ Wrapper for SVG Images """ - - def __init__(self, image, distributedObjects): - """ Constructor - @param image SVG image to wrap with the new DataGraphVW - @param distributedObjects the DistributedObjects-Instance - """ - DataGraphVW.__init__(self, image, distributedObjects) - self.image = image - self.templateHandler = HtmlTemplateHandler(self, - self.distributedObjects, - "svgview.mako") - - def showContent(self): - self.image.refresh() - return self.image.imageContent - - def showName(self): - return str(self.image) - - def createView(self): - self._view = SVGView(self, self.distributedObjects) - self.parentWrapper = self._view diff --git a/src/datagraph/svgvw.py b/src/datagraph/svgvw.py new file mode 100644 index 0000000..655c1f5 --- /dev/null +++ b/src/datagraph/svgvw.py @@ -0,0 +1,55 @@ +# ricodebug - A GDB frontend which focuses on visually supported +# debugging using data structure graphs and SystemC features. +# +# Copyright (C) 2011 The ricodebug project team at the +# Upper Austrian University Of Applied Sciences Hagenberg, +# Department Embedded Systems Design +# +# This file is part of ricodebug. +# +# ricodebug is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# For further information see . + +from .datagraphvw import HtmlTemplateHandler, DataGraphVW +from .htmlvariableview import HtmlVariableView + + +class SVGDataGraphVW(DataGraphVW): + """ Wrapper for SVG Images """ + + def __init__(self, image, distributedObjects): + """ Constructor + @param image SVG image to wrap with the new DataGraphVW + @param distributedObjects the DistributedObjects-Instance + """ + DataGraphVW.__init__(self, image, distributedObjects) + self.image = image + self.templateHandler = HtmlTemplateHandler(self, + self.distributedObjects, + "svgview.mako") + + def showContent(self): + self.image.refresh() + return self.image.imageContent + + def showName(self): + return str(self.image) + + def render(self, role, **kwargs): + return self.templateHandler.render(role) + + def createView(self): + self._view = HtmlVariableView(self, self.distributedObjects) + self.parentWrapper = self._view diff --git a/src/datagraph/templates/svgview.mako b/src/datagraph/templates/svgview.mako index a1f8719..0f4de27 100644 --- a/src/datagraph/templates/svgview.mako +++ b/src/datagraph/templates/svgview.mako @@ -1,63 +1,2 @@ -<%! - from datagraph.datagraphvw import Role -%>\ - - - - - - - -
${svgWrapper.showName()}
${svgWrapper.showContent()}
-
- -
- - +${varWrapper.showName()} +${varWrapper.showContent()} From 752131345f87bc543c8f78d52a4e429466f2e5ae Mon Sep 17 00:00:00 2001 From: Alexander Preisinger Date: Mon, 4 Feb 2013 11:27:38 +0100 Subject: [PATCH 19/24] fix import --- src/plugins/SysCDiagram/SysCDiagramPlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/SysCDiagram/SysCDiagramPlugin.py b/src/plugins/SysCDiagram/SysCDiagramPlugin.py index a79d4fe..a94079c 100644 --- a/src/plugins/SysCDiagram/SysCDiagramPlugin.py +++ b/src/plugins/SysCDiagram/SysCDiagramPlugin.py @@ -22,7 +22,7 @@ # # For further information see . -from datagraph.svgview import SVGDataGraphVW +from datagraph.svgvw import SVGDataGraphVW from datagraph.svgimage import SVGImage from StringIO import StringIO from pydot import Dot, Cluster, Node From 98623725937621c11e9b64c8a5b4403af352c89d Mon Sep 17 00:00:00 2001 From: Alexander Preisinger Date: Mon, 4 Feb 2013 12:32:00 +0100 Subject: [PATCH 20/24] use cpp2py to get real value --- src/plugins/SysCDiagram/SysCDiagramPlugin.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/plugins/SysCDiagram/SysCDiagramPlugin.py b/src/plugins/SysCDiagram/SysCDiagramPlugin.py index a94079c..95e23a9 100644 --- a/src/plugins/SysCDiagram/SysCDiagramPlugin.py +++ b/src/plugins/SysCDiagram/SysCDiagramPlugin.py @@ -27,6 +27,8 @@ from StringIO import StringIO from pydot import Dot, Cluster, Node +from helpers.tools import cpp2py + from variables.varwrapperfactory import VarWrapperFactory from variables.variablelist import VariableList @@ -102,11 +104,13 @@ def showDiagram(self): self.action.commit() def update(self): - if not self.ctx_found: + if not self.ctx_found and self.ctx is None: self.__findSimContext() + if self.ctx is None: + return else: # don't try to analyze if elaboration is not done - if not self.ctx["m_elaboration_done"]: + if not cpp2py(self.ctx["m_elaboration_done"].value): return # prepare for easy information collection @@ -115,6 +119,13 @@ def update(self): # find all relevant information self.__findSysCObjects(object_vec, self.object_type, self.sysc_objects) + # if there are no objects to draw than skip the drawing part + # this might happen if you set the breakpoint before any objects are + # created. This is actually catched above, but there might also be a + # design with no objects. + if len(self.sysc_objects.keys()) == 0: + return + clusters = {} nodes = {} @@ -195,6 +206,7 @@ def __findSimContext(self): self.ctx_pointer = self.evaluateExp(self.ctx_func) else: if self.ctx_pointer is None: + self.ctx_found = False return else: self.ctx_found = True From 3c16ab29ef4d42d1aa72138c6e61c295e72cd574 Mon Sep 17 00:00:00 2001 From: Alexander Preisinger Date: Mon, 4 Feb 2013 14:30:46 +0100 Subject: [PATCH 21/24] remove debugging leftovers --- src/plugins/SysCDiagram/SysCDiagramPlugin.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/plugins/SysCDiagram/SysCDiagramPlugin.py b/src/plugins/SysCDiagram/SysCDiagramPlugin.py index 95e23a9..edeb7c2 100644 --- a/src/plugins/SysCDiagram/SysCDiagramPlugin.py +++ b/src/plugins/SysCDiagram/SysCDiagramPlugin.py @@ -137,10 +137,8 @@ def update(self): for sptr in nodes: self.block_diagram.add_node(nodes[sptr]) - self.block_diagram.write_raw("bd.gv") self._file_obj.write(self.block_diagram.create_svg()) self.signalproxy.inferiorStoppedNormally.disconnect(self.update) - self.showDiagram() def __buildHierachy(self, obj_dict, clusters, nodes): From 5ed0a9e33fd2a662a777e91c0d8321a40eb07f35 Mon Sep 17 00:00:00 2001 From: num3n0r Date: Wed, 28 Nov 2012 13:01:08 +0100 Subject: [PATCH 22/24] Show Waveforms in Datagraph Shows Waveforms in the Datagraph using the newly added functionality for show SVG images in the datagraph --- src/controllers/tracepointwavecontroller.py | 83 ------ src/datagraph/svgview.py | 166 ++++++++++++ src/datagraph/templates/waveformview.mako | 56 +++++ src/datagraph/waveview.py | 123 +++++++++ src/helpers/distributedobjects.py | 4 +- src/helpers/svgdraw.py | 53 ++++ src/helpers/svgdrawdiagram.py | 15 ++ src/helpers/svgdrawwaveforms.py | 119 +++++++++ src/models/tracepointmodel.py | 2 +- src/models/tracepointwavemodel.py | 263 ++++++-------------- 10 files changed, 616 insertions(+), 268 deletions(-) delete mode 100644 src/controllers/tracepointwavecontroller.py create mode 100644 src/datagraph/svgview.py create mode 100644 src/datagraph/templates/waveformview.mako create mode 100644 src/datagraph/waveview.py create mode 100644 src/helpers/svgdraw.py create mode 100644 src/helpers/svgdrawdiagram.py create mode 100644 src/helpers/svgdrawwaveforms.py diff --git a/src/controllers/tracepointwavecontroller.py b/src/controllers/tracepointwavecontroller.py deleted file mode 100644 index 024a0d7..0000000 --- a/src/controllers/tracepointwavecontroller.py +++ /dev/null @@ -1,83 +0,0 @@ -# ricodebug - A GDB frontend which focuses on visually supported -# debugging using data structure graphs and SystemC features. -# -# Copyright (C) 2011 The ricodebug project team at the -# Upper Austrian University Of Applied Sciences Hagenberg, -# Department Embedded Systems Design -# -# This file is part of ricodebug. -# -# ricodebug is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# For further information see . - -from PyQt4.QtCore import QObject, Qt -from models.tracepointwavemodel import TracepointWaveModel, TracepointWaveDelegate -from views.tracepointwaveview import TracepointWaveView -import logging - - -class TracepointWaveController(QObject): - '''Controller for Tracepoint Wave widget. - Following datatypes can be displayed in waveform: bool, int, float, double - ''' - - def __init__(self, distributedObjects): - '''CTOR''' - QObject.__init__(self) - - self.distributedObjects = distributedObjects - self.model = TracepointWaveModel() - self.supportedTypes = ["bool", "int", "float", "double"] - self.view = TracepointWaveView() - self.view.setModel(self.model) - delegate = TracepointWaveDelegate() - - # TracepointWaveView using TracepointWaveDelegate to paint waveforms - self.view.setItemDelegate(delegate) - self.view.getZoomInButton().clicked.connect(self.zoomIn) - self.view.getZoomOutButton().clicked.connect(self.zoomOut) - self.distributedObjects.mainwindow.insertDockWidget(self.view.widget, "Tracepoint Wave", Qt.BottomDockWidgetArea, True) - self.distributedObjects.signalProxy.cleanupModels.connect(self.model.cleanUp) - - def updateTracepointWaveView(self, list_): - ''' Repaint tracepoint waves - @param list_: list of ValueList objects - ''' - self.model.cleanUp(False) # clean up without reseting zoom - for item in list_: - for v in item.values: - self.__updateTracepointWave(item.name, item.type, v) - - def zoomIn(self): - '''Zoom wave horizontally''' - self.model.zoomIn() - self.view.resizeColumnsToContents() - - def zoomOut(self): - '''Zoom wave horizontally''' - self.model.zoomOut() - self.view.resizeColumnsToContents() - - def __updateTracepointWave(self, name, type_, value): - ''' Append value to tracepoint wave. Creates new wave if model does not contain wave with name and value type - @param name: string with variable name - @param type_: string with type of variable ("bool", "int", "float", "double" supported). - @param value: value of variable - ''' - if type_ in self.supportedTypes: - self.model.updateTracepointWave(name, type_, value) - self.view.resizeColumnsToContents() - else: - logging.error("Could not update tracepoint wave. Illegal variable type.") diff --git a/src/datagraph/svgview.py b/src/datagraph/svgview.py new file mode 100644 index 0000000..1fd8d41 --- /dev/null +++ b/src/datagraph/svgview.py @@ -0,0 +1,166 @@ +# ricodebug - A GDB frontend which focuses on visually supported +# debugging using data structure graphs and SystemC features. +# +# Copyright (C) 2011 The ricodebug project team at the +# Upper Austrian University Of Applied Sciences Hagenberg, +# Department Embedded Systems Design +# +# This file is part of ricodebug. +# +# ricodebug is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# For further information see . + + +from .datagraphvw import HtmlTemplateHandler, DataGraphVW +from PyQt4.QtCore import QSize, QSizeF, pyqtSignal +from PyQt4.QtGui import QGraphicsItem, QCursor, QFileDialog, QIcon +from PyQt4.QtWebKit import QGraphicsWebView +from mako.template import Template +from PyQt4 import QtCore +import sys +import logging + + +class SVGView(QGraphicsWebView): + """ the view to show variables in the DataGraph """ + + removing = pyqtSignal() + + def __init__(self, svgWrapper, distributedObjects): + """ Constructor + @param varWrapper holds the Data of the Variable to show + @param distributedObjects the DistributedObjects-Instance + """ + QGraphicsWebView.__init__(self, None) + self.svgWrapper = svgWrapper + self.distributedObjects = distributedObjects + self.setFlags(QGraphicsItem.ItemIsMovable | + QGraphicsItem.ItemIsFocusable) + self.htmlTemplate = Template(filename=sys.path[0] + + '/datagraph/templates/svgview.mako') + self.page().setPreferredContentsSize(QSize(0, 0)) + self.setPreferredSize(QSizeF(0, 0)) + self.setResizesToContents(True) + + self.source = None + + # ids for ourself and the template handlers we will eventually render + self.lastId = -1 + self.uniqueIds = {} + + self.dirty = True + + self.id = self.getUniqueId(self) + + self.distributedObjects.signalProxy.\ + variableUpdateCompleted.connect(self.render) + + def setDirty(self, render_immediately): + self.dirty = True + if render_immediately: + self.render() + + def render(self): + if self.dirty: + # the page's viewport will not shrink if new content is set, + # so set it to it's minimum + self.page().setViewportSize(QSize(0, 0)) + try: + self.source = \ + self.htmlTemplate.render(svgWrapper=self.svgWrapper, + top=True, id=self.id) + self.setHtml(self.source) + + for template, id_ in self.uniqueIds.iteritems(): + self.page().mainFrame().\ + addToJavaScriptWindowObject(id_, template) + except Exception as e: + logging.error("Rendering failed: %s", str(e)) + self.setHtml(str(e)) + raise + + # force an update of the scene that contains us, since sometimes + # setHtml will not cause the view to be redrawn immediately + if self.scene(): + self.scene().update() + + self.dirty = False + + return self.source + + def openContextMenu(self, menu): + menu.addAction(QIcon(":/icons/images/minus.png"), + "Remove %s" % self.varWrapper.exp, self.remove) + menu.addAction(QIcon(":/icons/images/save-html.png"), + "Save HTML for %s" % self.varWrapper.exp, self.saveHtml) + menu.exec_(QCursor.pos()) + + @QtCore.pyqtSlot() + def saveHtml(self): + name = QFileDialog.getSaveFileName(filter="HTML (*.html)") + if name != "": + out = file(name, 'w') + out.write(self.source) + out.close() + + def contextMenuEvent(self, event): + pass + + @QtCore.pyqtSlot() + def remove(self): + """remove the varWrapper from the datagraph""" + self.removing.emit() + self.distributedObjects.datagraphController.removeVar(self.varWrapper) + + def getUniqueId(self, template): + if not template in self.uniqueIds: + self.lastId += 1 + self.uniqueIds[template] = "tmpl%d" % self.lastId + return self.uniqueIds[template] + + +class SVGTemplateHandler(HtmlTemplateHandler): + """ TemplateHandler for SVG Images """ + + def __init__(self, svgWrapper, distributedObjects): + """ Constructor + @param svgWrapper holds the Data to show """ + HtmlTemplateHandler.__init__(self, + svgWrapper, + distributedObjects, + 'svgview.mako') + + def prepareContextMenu(self, menu): + HtmlTemplateHandler.prepareContextMenu(self, menu) + + +class SVGDataGraphVW(DataGraphVW): + """ Wrapper for SVG Images """ + + def __init__(self, image, distributedObjects): + """ Constructor + @param image SVG image to wrap with the new DataGraphVW + @param distributedObjects the DistributedObjects-Instance + """ + DataGraphVW.__init__(self, image, distributedObjects) + self.image = image + self.templateHandler = SVGTemplateHandler(self, + self.distributedObjects) + self.imagePath = image.imagePath + self.name = str(image) + + def createView(self): + self._view = SVGView(self, self.distributedObjects) + self.parentWrapper = self._view diff --git a/src/datagraph/templates/waveformview.mako b/src/datagraph/templates/waveformview.mako new file mode 100644 index 0000000..f988150 --- /dev/null +++ b/src/datagraph/templates/waveformview.mako @@ -0,0 +1,56 @@ +<%! + from datagraph.datagraphvw import Role +%>\ + + + + +<% assert(top) %>\ +% if varWrapper.inScope: + +${varWrapper.render(Role.INCLUDE_HEADER)} +
+
+ +
+% endif + + diff --git a/src/datagraph/waveview.py b/src/datagraph/waveview.py new file mode 100644 index 0000000..510b4e8 --- /dev/null +++ b/src/datagraph/waveview.py @@ -0,0 +1,123 @@ +# ricodebug - A GDB frontend which focuses on visually supported +# debugging using data structure graphs and SystemC features. +# +# Copyright (C) 2011 The ricodebug project team at the +# Upper Austrian University Of Applied Sciences Hagenberg, +# Department Embedded Systems Design +# +# This file is part of ricodebug. +# +# ricodebug is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# For further information see . + +from PyQt4.QtCore import QSize, QSizeF, pyqtSignal +from PyQt4.QtGui import QGraphicsItem, QCursor, QFileDialog, QIcon +from PyQt4.QtWebKit import QGraphicsWebView +from mako.template import Template +from PyQt4 import QtCore +import sys +import logging + + +class WaveView(QGraphicsWebView): + """ the view to show waveforms in the datagraph """ + + removing = pyqtSignal() + + def __init__(self, varWrapper, distributedObjects): + """ Constructor + @param varWrapper datagraph.datagraphvw.DataGraphVW, holds the Data of the Variable to show + @param distributedObjects distributedobjects.DistributedObjects, the DistributedObjects-Instance + """ + QGraphicsWebView.__init__(self, None) + self.varWrapper = varWrapper + self.distributedObjects = distributedObjects + self.setFlags(QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsFocusable) + self.htmlTemplate = Template(filename=sys.path[0] + '/datagraph/templates/waveformview.mako') + self.page().setPreferredContentsSize(QSize(0, 0)) + self.setPreferredSize(QSizeF(0, 0)) + self.setResizesToContents(True) + + + self.source = None + + # ids for ourself and the template handlers we will eventually render + self.lastId = -1 + self.uniqueIds = {} + + self.dirty = True + + self.id = self.getUniqueId(self) + + self.distributedObjects.signalProxy.variableUpdateCompleted.connect(self.render) + + def setDirty(self, render_immediately): + self.dirty = True + if render_immediately: + self.render() + + def render(self): + if self.dirty: + # the page's viewport will not shrink if new content is set, so set it to it's minimum + self.page().setViewportSize(QSize(0, 0)) + try: + self.source = self.htmlTemplate.render(varWrapper=self.varWrapper, top=True, id=self.id) + self.setHtml(self.source) + + for template, id_ in self.uniqueIds.iteritems(): + self.page().mainFrame().addToJavaScriptWindowObject(id_, template) + except Exception as e: + logging.error("Rendering failed: %s", str(e)) + self.setHtml(str(e)) + raise + + # force an update of the scene that contains us, since sometimes setHtml + # will not cause the view to be redrawn immediately + if self.scene(): + self.scene().update() + + self.dirty = False + + return self.source + + def openContextMenu(self, menu): + menu.addAction(QIcon(":/icons/images/minus.png"), + "Remove %s" % self.varWrapper.exp, self.remove) + menu.addAction(QIcon(":/icons/images/save-html.png"), + "Save HTML for %s" % self.varWrapper.exp, self.saveHtml) + menu.exec_(QCursor.pos()) + + @QtCore.pyqtSlot() + def saveHtml(self): + name = QFileDialog.getSaveFileName(filter="HTML (*.html)") + if name != "": + out = file(name, 'w') + out.write(self.source) + out.close() + + def contextMenuEvent(self, event): + pass + + @QtCore.pyqtSlot() + def remove(self): + """remove the varWrapper from the datagraph""" + self.removing.emit() + self.distributedObjects.datagraphController.removeVar(self.varWrapper) + + def getUniqueId(self, template): + if not template in self.uniqueIds: + self.lastId += 1 + self.uniqueIds[template] = "tmpl%d" % self.lastId + return self.uniqueIds[template] diff --git a/src/helpers/distributedobjects.py b/src/helpers/distributedobjects.py index 168da01..b1bbcd0 100644 --- a/src/helpers/distributedobjects.py +++ b/src/helpers/distributedobjects.py @@ -36,7 +36,7 @@ from datagraph.datagraphcontroller import DataGraphController from variables.variablepool import VariablePool from .stlvectorparser import StlVectorParser -from controllers.tracepointwavecontroller import TracepointWaveController +from models.tracepointwavemodel import TracepointWaveModel from .sessionmanager import SessionManager from helpers.actions import Actions from helpers.configstore import ConfigStore @@ -88,7 +88,7 @@ def __init__(self, mainwindow): self.datagraphController = DataGraphController(self) self.stlvectorParser = StlVectorParser(self) - self.tracepointwaveController = TracepointWaveController(self) + self.tracepointwaveModel = TracepointWaveModel(self) self.miView = self.buildView(MiTraceView, "MI Trace") diff --git a/src/helpers/svgdraw.py b/src/helpers/svgdraw.py new file mode 100644 index 0000000..beb44c2 --- /dev/null +++ b/src/helpers/svgdraw.py @@ -0,0 +1,53 @@ +import cairo +import rsvg +import math + + +class SvgDraw(): + '''Draws simple objects in svg''' + + def __init__(self, fileObject, picWidth, picHeight): + self.fo = fileObject + SvgDraw.refresh(self, picWidth, picHeight) + + # settings for text + #self.ctx.set_font_size(20.0) + #self.ctx.set_line_width(3.0) + self.ctx.set_source_rgb(0.0, 0.0, 0.0) + self.ctx.select_font_face("Arial", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL) + + # reset coordinates + self.currentX = 0 + self.currentY = 0 + + def refresh(self, width, height): + self.__width = width + self.__height = height + self.resize(self.__width, self.__height) + + def getWidth(self): + return self.__width + + def getHeight(self): + return self.__height + + def drawLine(self, xPos, yPos): + self.ctx.line_to(xPos, yPos) + self.currentX = xPos + self.currentY = yPos + + def finish(self): + self.ctx.stroke() + self.surface.finish() + + def drawText(self, xPos, yPos, text): + self.ctx.move_to(xPos, yPos) + self.ctx.show_text(text) + + def resize(self, width, height): + self.surface = cairo.SVGSurface(self.fo, self.__width, self.__height) + self.ctx = cairo.Context(self.surface) + + + + diff --git a/src/helpers/svgdrawdiagram.py b/src/helpers/svgdrawdiagram.py new file mode 100644 index 0000000..1054188 --- /dev/null +++ b/src/helpers/svgdrawdiagram.py @@ -0,0 +1,15 @@ +from svgdraw import SvgDraw + +class SvgDrawDiagram(SvgDraw): + '''draw diagrams in svg''' + + def __init__(self, filename, picWidth, picHeight): + SvgDraw.__init__(self, filename, picWidth, picHeight) + # constants + self.cEntityheight = 0.6 + self.cEntitylength = 0.5 + + + def drawEntity(self, xPos, yPos, name): + self.drawRectangle(xPos, yPos+0.05, self.cEntitylength, self.cEntityheight) + self.drawText(xPos, yPos, name) \ No newline at end of file diff --git a/src/helpers/svgdrawwaveforms.py b/src/helpers/svgdrawwaveforms.py new file mode 100644 index 0000000..a637f72 --- /dev/null +++ b/src/helpers/svgdrawwaveforms.py @@ -0,0 +1,119 @@ +from svgdraw import SvgDraw + + +class SvgDrawWaveform(SvgDraw): + """draw waveforms in svg""" + + def __init__(self, fileObject, picWidth, picHeight, type): + SvgDraw.__init__(self, fileObject, picWidth, picHeight) + self.period = 80 + self.waveheight = 50 + self.height = 40 + self.distance = self.period / 4 + self.type = type + self.pic_height = picHeight + self.pic_width = picWidth + self.no_waves = 1 + self.values = [] + self.wave_list = [{"values" : [], "position" : (0, 0)}] + + + def refresh(self, no_waves): + + self.no_waves = no_waves + self.pic_height = no_waves * (self.waveheight + 5) + self.pic_width = self.currentX + 150 + self.wave_list = [{"values" : [], "position": (5, 1 + (self.waveheight+5)*i)} for i in range(no_waves)] + SvgDraw.refresh(self, self.pic_width, self.pic_height) + + def setBoolStartLevel(self, level): + self.currentX += self.distance / 2 + self.ctx.move_to(self.currentX, self.currentY) + if not level: + self.currentY += self.height + self.ctx.move_to(self.currentX, self.currentY) + + def drawPeriod(self, type_="bool"): + if (type_ == "bool"): + self.currentX = self.currentX + self.period + self.distance + self.ctx.line_to(self.currentX, self.currentY) + else: + self.ctx.move_to(self.currentX, self.currentY) + self.ctx.line_to(self.currentX + self.period, self.currentY) + self.ctx.move_to(self.currentX, self.currentY + self.height) + self.ctx.line_to(self.currentX + self.period, self.currentY + self.height) + self.currentX += self.period + + def drawChangeValue(self): + self.ctx.move_to(self.currentX, self.currentY) + self.ctx.line_to(self.currentX + self.distance, self.currentY + self.height) + self.ctx.move_to(self.currentX, self.currentY + self.height) + self.ctx.line_to(self.currentX + self.distance, self.currentY) + self.currentX = self.currentX + self.distance + + def drawNoChangeValue(self): + self.ctx.line_to(self.currentX + self.distance, self.currentY) + self.ctx.move_to(self.currentX, self.currentY + self.height) + self.ctx.line_to(self.currentX + self.distance, self.currentY + self.height) + self.currentX += self.distance + + def drawChangeLevelToHigh(self): + self.ctx.line_to(self.currentX, self.currentY - self.height) + self.currentY = self.currentY - self.height + + def drawChangeLevelToLow(self): + self.ctx.line_to(self.currentX, self.currentY + self.height) + self.currentY = self.currentY + self.height + + def drawBoolWaveform(self, currVal): + self.ctx.move_to(self.currentX, self.currentY) + + if not len(self.values): + lastVal = currVal + self.setBoolStartLevel(currVal) + else: + lastVal = self.values[-1] + + if lastVal == currVal: + self.drawPeriod() + + elif lastVal == 0 and currVal == 1: + self.drawChangeLevelToHigh() + self.drawPeriod() + + elif lastVal == 1 and currVal == 0: + self.drawChangeLevelToLow() + self.drawPeriod() + + self.values.append(currVal) + + def drawValueWaveform(self, currVal): + self.ctx.move_to(self.currentX, self.currentY) + lastVal = self.values[-1] if len(self.values) else 0 + + if lastVal == currVal: + self.drawNoChangeValue() + self.drawPeriod("int") + + elif lastVal != currVal: + self.drawChangeValue() + self.drawPeriod("int") + self.drawText(self.currentX - self.period, + self.currentY + self.height / 1.5, + str(currVal)) + + def drawWaveform(self, i, currVal, type_, name): + self.values = self.wave_list[i]["values"] + self.currentX, self.currentY = self.wave_list[i]["position"] + self.ctx.move_to(self.currentX, self.currentY) + + if (self.currentX == 5): + self.drawText(self.currentX, self.currentY+self.height*0.6, name) + self.currentX += 40 + + if (type_ == "bool"): + self.drawBoolWaveform(currVal) + else: + self.drawValueWaveform(currVal) + self.wave_list[i]["position"] = (self.currentX, self.currentY) + diff --git a/src/models/tracepointmodel.py b/src/models/tracepointmodel.py index 9798e72..be8e386 100644 --- a/src/models/tracepointmodel.py +++ b/src/models/tracepointmodel.py @@ -408,4 +408,4 @@ def selectionMade(self, index): @param index: index of row that was clicked """ tp = self.tracepoints[index.row()] - self.distObjects.tracepointwaveController.updateTracepointWaveView(tp.wave) + self.distObjects.tracepointwaveModel.updateTracepointWave(tp.wave) diff --git a/src/models/tracepointwavemodel.py b/src/models/tracepointwavemodel.py index f1fdf93..85e45e2 100644 --- a/src/models/tracepointwavemodel.py +++ b/src/models/tracepointwavemodel.py @@ -22,149 +22,59 @@ # # For further information see . -from PyQt4.QtCore import Qt, QAbstractTableModel, QModelIndex, QPointF, QLineF, QRectF -from PyQt4 import QtGui +from PyQt4.QtCore import Qt, QAbstractTableModel, QObject, QModelIndex +#from PyQt4 import QtGui from operator import attrgetter +from helpers.svgdrawwaveforms import SvgDrawWaveform +from datagraph.SVGImage import SVGImage +from datagraph.svgview import SVGDataGraphVW +from StringIO import StringIO +import traceback +import logging +class TracepointWaveDrawing(QObject): -class TracepointWaveDelegate(QtGui.QItemDelegate): - '''Delegate for painting waveform to table - overwrites methods for waveform column - ''' - def __init__(self, parent=None): - QtGui.QItemDelegate.__init__(self, parent) + def __init__(self, distributedObjects): + self.distributedObjects = distributedObjects + self.f = StringIO("") - def createEditor(self, parent, option, index): - editor = TracepointWaveGraphicsView() - return editor + # Datagraph image + self.svg_image = SVGImage("Tracepoint", self.f) + self.svg_image_wrapper = SVGDataGraphVW(self.svg_image, self.distributedObjects) - def setEditorData(self, editor, index): - value = index.model().data(index, Qt.DisplayRole) - editor.setScene(value) + # Drawing library + self.svg = SvgDrawWaveform(self.f, 0, 0, "int") - def setModelData(self, editor, model, index): - value = editor - model.setData(index, value, Qt.EditRole) + self.action = self.distributedObjects.actions.\ + getAddSVGToDatagraphAction(self.svg_image_wrapper, + self.distributedObjects. + datagraphController.addSVG) - def updateEditorGeometry(self, editor, option, index): - editor.setGeometry(option.rect) - def paint(self, painter, option, index): - if index.column() == 1: - value = index.model().data(index, Qt.DisplayRole) - painter.setRenderHint(QtGui.QPainter.HighQualityAntialiasing, True) - value.scene().render(painter, QRectF(option.rect.x(), option.rect.y(), value.scene().sceneRect().width(), option.rect.height() - 5), value.scene().sceneRect(), Qt.IgnoreAspectRatio) - else: - QtGui.QItemDelegate.paint(self, painter, option, index) - - def sizeHint(self, option, index): - size = QtGui.QItemDelegate.sizeHint(self, option, index) - if (index.column() == 1): - size.setWidth(index.model().data(index, Qt.DisplayRole).scene().width) - return size + def refresh(self, no_waves): + self.svg.refresh(no_waves) + def display(self): + self.f.seek(0) + self.svg.finish() -class TracepointWaveGraphicsView(QtGui.QGraphicsView): - '''QGraphicsView for wave''' - def __init__(self, name): - '''CTOR - @param name: string, shown as name of variable - ''' - QtGui.QGraphicsView.__init__(self) - self.setAlignment(Qt.AlignLeft | Qt.AlignTop) - self.name = name - - def getName(self): - return self.name - - -class TracepointWaveScene(QtGui.QGraphicsScene): - '''QGraphicsView for wave''' - def __init__(self, type_, values, duration): - '''CTOR - @param type_: string, must be in supported types - @param values: list of values of variable - @param duration: integer, holds stepwidth of wave - ''' - QtGui.QGraphicsScene.__init__(self) - self.supportedTypes = ["bool", "int", "float", "double"] - self.vSpace = 10 - self.values = [] - self.curPos = QPointF(0, 2) - self.type = type_ - self.width = duration - self.valFont = QtGui.QFont("Arial", 7) - self.valFontColor = QtGui.QColor() - self.valFontColor.setGreen(100) - self.setSceneRect(0, 0, self.width, 15) - - for v in values: - self.appendValue(v, duration) - - def getSupportedTypes(self): - ''' Returns supported waveform types - @return list[string] - ''' - return self.supportedTypes - - def getType(self): - ''' @return string identifying type of TracepointWaveScene''' - return self.type - - def appendValue(self, value, duration): - ''' Append value to wave - @param value: value to add - @param duration: integer, defines duration(length) of value in wave - ''' - drawEdge = len(self.values) > 0 and self.values[len(self.values) - 1] != value - if drawEdge: - self.__drawEdge() - - self.values.append(value) - self.__drawLine(value, duration, drawEdge or len(self.values) == 1) - self.setSceneRect(0, 0, self.width, 15) - - def __drawEdge(self): - ''' Draws an edge depending on the type of the waveform. ''' - if self.type == "bool": - self.addItem(QtGui.QGraphicsLineItem(QLineF(self.curPos, QPointF(self.curPos.x(), self.curPos.y() + self.vSpace)))) - elif self.type in self.supportedTypes: - self.addItem(QtGui.QGraphicsLineItem(QLineF(self.curPos, QPointF(self.curPos.x() + 2, self.curPos.y() + self.vSpace)))) - self.addItem(QtGui.QGraphicsLineItem(QLineF(QPointF(self.curPos.x() + 2, self.curPos.y()), QPointF(self.curPos.x(), self.curPos.y() + self.vSpace)))) - - def __drawLine(self, value, duration, printvalue=True): - ''' Draws a line depending on the type of the waveform. - @param value: value to add to wave - @param duration: integer, defines duration(length) of value in wave - @param printvalue: bool, add values to waveform (for value-type waveforms only) - ''' - self.width = self.width + duration - tmp = self.curPos - self.curPos = QPointF(self.curPos.x() + duration, self.curPos.y()) - if self.type == "bool": - if value: - self.addItem(QtGui.QGraphicsLineItem(QLineF(tmp, self.curPos))) - else: - self.addItem(QtGui.QGraphicsLineItem(tmp.x(), tmp.y() + self.vSpace, self.curPos.x(), self.curPos.y() + self.vSpace)) - elif self.type in self.supportedTypes: - if printvalue: - text = QtGui.QGraphicsTextItem(str(value)) - text.setFont(self.valFont) - text.setDefaultTextColor(self.valFontColor) - text.setPos(QPointF(tmp.x() + 4, tmp.y() - 5)) - self.addItem(text) - - self.addItem(QtGui.QGraphicsLineItem(QLineF(tmp, self.curPos))) - self.addItem(QtGui.QGraphicsLineItem(tmp.x(), tmp.y() + self.vSpace, self.curPos.x(), self.curPos.y() + self.vSpace)) + def __del__(self): + self.f.close() class TracepointWaveModel(QAbstractTableModel): - '''TableModel for TracepointWaveView. - Holds a list of TracepointWaveGraphicsViews. - Every TracepointWaveGraphicsView holds a TracepointWaveScene which represents a waveform. - ''' - def __init__(self): + """TableModel for TracepointWaveView. + Holds a list of TracepointWaveGraphicsViews. + Every TracepointWaveGraphicsView holds a TracepointWaveScene + which represents a waveform. + """ + + def __init__(self, distributedObjects): QAbstractTableModel.__init__(self) + + self.supportedTypes = ["bool", "int", "float", "double"] + self.distributedObjects = distributedObjects + self.waveform = TracepointWaveDrawing(distributedObjects) # each TracepointWaveGraphicsView in list holds one TracepointWaveScene self.waveforms = [] @@ -172,79 +82,61 @@ def __init__(self): # factor for zoom in/out functions self.zoomfactor = 1.05 - # stepwidth in waveform + # stepwidth self.duration = 30 # const column of waveform self.wavecolumn = 1 + + self.distributedObjects.signalProxy.\ + cleanupModels.connect(self.cleanUp) - def cleanUp(self, resetzoomvalue=True): - '''Remove all waveforms and reset model''' - while self.rowCount(parent=None) > 0: - idx = len(self.waveforms) - 1 - self.beginRemoveRows(QModelIndex(), idx, idx) - self.waveforms.pop() - self.endRemoveRows() + def cleanUp(self): + pass - self.waveforms = [] - if resetzoomvalue: - self.duration = 30 - - def updateTracepointWave(self, name, type_, value): - '''Update existing tracepoint wave. If model does not contain a wave with given name and type a new wave is created. - @param name: string, name of wave in tableview - @param type_: string, type of wave (must be supported by TracepointWaveScene) - @param value: value to append to wave + def updateTracepointWave(self, list_): + ''' Repaint tracepoint waves + @param list_: list of ValueList objects ''' - i = self.__getTracepointWaveIndex(name, type_) + # TODO: write log message + if len(list_) == 0: + return + + self.waveform.refresh(len(list_)) + + for item in list_: + if item.type in self.supportedTypes: + i = self.__getTracepointWaveIndex(item.name, item.type) + + for v in item.values: + self.waveform.svg.drawWaveform(i, v, item.type, item.name) + + if i is not None: + self.waveform.svg_image_wrapper.setDirty(True) # render immediatly + else: + self.waveform.action.commit() + self.waveforms.append({"name" : item.name, + "type" : item.type}) + else: + logging.error("Could not update tracepoint wave. Illegal variable type.") + + self.waveform.display() - if i != None: - #append value to existing wave - wave = self.data(i, Qt.EditRole) - wave.scene().appendValue(value, self.duration) - self.setData(i, wave) - else: - #insert new wave - waveform = TracepointWaveGraphicsView(name) - waveform.setScene(TracepointWaveScene(type_, [value], self.duration)) - self.beginInsertRows(QModelIndex(), len(self.waveforms), len(self.waveforms)) - self.waveforms.append(waveform) - self.endInsertRows() def __getTracepointWaveIndex(self, name, type_): - for i in range(len(self.waveforms)): - if self.waveforms[i].getName() == name and self.waveforms[i].scene().getType() == type_: - return self.createIndex(i, self.wavecolumn, None) + for wf in self.waveforms: + if wf["name"] == name and wf["type"] == type_: + return self.waveforms.index(wf) return None def zoomIn(self): '''Zoom wave horizontally''' self.duration = self.duration * self.zoomfactor - for i in range(len(self.waveforms)): - prevScene = self.waveforms[i].scene() - waveform = TracepointWaveGraphicsView(self.waveforms[i].name) - waveform.setScene(TracepointWaveScene(prevScene.type, prevScene.values, self.duration)) - self.waveforms[i] = waveform - index = self.createIndex(i, self.wavecolumn, None) - self.dataChanged(index, index) def zoomOut(self): '''Zoom wave horizontally''' self.duration = self.duration / self.zoomfactor - for i in range(len(self.waveforms)): - prevScene = self.waveforms[i].scene() - waveform = TracepointWaveGraphicsView(self.waveforms[i].name) - waveform.setScene(TracepointWaveScene(prevScene.type, prevScene.values, self.duration)) - self.waveforms[i] = waveform - index = self.createIndex(i, self.wavecolumn, None) - self.dataChanged.emit(index, index) - - def rowCount(self, parent): - return len(self.waveforms) - - def columnCount(self, parent): - return 2 def data(self, index, role): ret = None @@ -282,3 +174,10 @@ def headerData(self, section, orientation, role): if section == self.wavecolumn: ret = "Wave" return ret + + + + + + + From fb95695cab463891d107864cd1703043c389ab3c Mon Sep 17 00:00:00 2001 From: Alexander Preisinger Date: Mon, 4 Feb 2013 14:54:06 +0100 Subject: [PATCH 23/24] fixes for svg changes --- src/models/tracepointwavemodel.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/models/tracepointwavemodel.py b/src/models/tracepointwavemodel.py index 85e45e2..c835e23 100644 --- a/src/models/tracepointwavemodel.py +++ b/src/models/tracepointwavemodel.py @@ -22,16 +22,17 @@ # # For further information see . -from PyQt4.QtCore import Qt, QAbstractTableModel, QObject, QModelIndex +from PyQt4.QtCore import Qt, QAbstractTableModel, QObject #from PyQt4 import QtGui from operator import attrgetter from helpers.svgdrawwaveforms import SvgDrawWaveform -from datagraph.SVGImage import SVGImage -from datagraph.svgview import SVGDataGraphVW +from datagraph.svgimage import SVGImage +from datagraph.svgvw import SVGDataGraphVW from StringIO import StringIO -import traceback + import logging + class TracepointWaveDrawing(QObject): def __init__(self, distributedObjects): @@ -48,8 +49,7 @@ def __init__(self, distributedObjects): self.action = self.distributedObjects.actions.\ getAddSVGToDatagraphAction(self.svg_image_wrapper, self.distributedObjects. - datagraphController.addSVG) - + datagraphController.addVar) def refresh(self, no_waves): self.svg.refresh(no_waves) @@ -71,7 +71,7 @@ class TracepointWaveModel(QAbstractTableModel): def __init__(self, distributedObjects): QAbstractTableModel.__init__(self) - + self.supportedTypes = ["bool", "int", "float", "double"] self.distributedObjects = distributedObjects self.waveform = TracepointWaveDrawing(distributedObjects) @@ -87,7 +87,7 @@ def __init__(self, distributedObjects): # const column of waveform self.wavecolumn = 1 - + self.distributedObjects.signalProxy.\ cleanupModels.connect(self.cleanUp) From 9e3a55977e113035274fbc418c1845640fe8722f Mon Sep 17 00:00:00 2001 From: Alexander Preisinger Date: Mon, 4 Feb 2013 14:59:18 +0100 Subject: [PATCH 24/24] Remove unused files --- src/datagraph/svgview.py | 166 ---------------------- src/datagraph/templates/waveformview.mako | 56 -------- src/datagraph/waveview.py | 123 ---------------- src/helpers/svgdrawdiagram.py | 15 -- 4 files changed, 360 deletions(-) delete mode 100644 src/datagraph/svgview.py delete mode 100644 src/datagraph/templates/waveformview.mako delete mode 100644 src/datagraph/waveview.py delete mode 100644 src/helpers/svgdrawdiagram.py diff --git a/src/datagraph/svgview.py b/src/datagraph/svgview.py deleted file mode 100644 index 1fd8d41..0000000 --- a/src/datagraph/svgview.py +++ /dev/null @@ -1,166 +0,0 @@ -# ricodebug - A GDB frontend which focuses on visually supported -# debugging using data structure graphs and SystemC features. -# -# Copyright (C) 2011 The ricodebug project team at the -# Upper Austrian University Of Applied Sciences Hagenberg, -# Department Embedded Systems Design -# -# This file is part of ricodebug. -# -# ricodebug is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# For further information see . - - -from .datagraphvw import HtmlTemplateHandler, DataGraphVW -from PyQt4.QtCore import QSize, QSizeF, pyqtSignal -from PyQt4.QtGui import QGraphicsItem, QCursor, QFileDialog, QIcon -from PyQt4.QtWebKit import QGraphicsWebView -from mako.template import Template -from PyQt4 import QtCore -import sys -import logging - - -class SVGView(QGraphicsWebView): - """ the view to show variables in the DataGraph """ - - removing = pyqtSignal() - - def __init__(self, svgWrapper, distributedObjects): - """ Constructor - @param varWrapper holds the Data of the Variable to show - @param distributedObjects the DistributedObjects-Instance - """ - QGraphicsWebView.__init__(self, None) - self.svgWrapper = svgWrapper - self.distributedObjects = distributedObjects - self.setFlags(QGraphicsItem.ItemIsMovable | - QGraphicsItem.ItemIsFocusable) - self.htmlTemplate = Template(filename=sys.path[0] + - '/datagraph/templates/svgview.mako') - self.page().setPreferredContentsSize(QSize(0, 0)) - self.setPreferredSize(QSizeF(0, 0)) - self.setResizesToContents(True) - - self.source = None - - # ids for ourself and the template handlers we will eventually render - self.lastId = -1 - self.uniqueIds = {} - - self.dirty = True - - self.id = self.getUniqueId(self) - - self.distributedObjects.signalProxy.\ - variableUpdateCompleted.connect(self.render) - - def setDirty(self, render_immediately): - self.dirty = True - if render_immediately: - self.render() - - def render(self): - if self.dirty: - # the page's viewport will not shrink if new content is set, - # so set it to it's minimum - self.page().setViewportSize(QSize(0, 0)) - try: - self.source = \ - self.htmlTemplate.render(svgWrapper=self.svgWrapper, - top=True, id=self.id) - self.setHtml(self.source) - - for template, id_ in self.uniqueIds.iteritems(): - self.page().mainFrame().\ - addToJavaScriptWindowObject(id_, template) - except Exception as e: - logging.error("Rendering failed: %s", str(e)) - self.setHtml(str(e)) - raise - - # force an update of the scene that contains us, since sometimes - # setHtml will not cause the view to be redrawn immediately - if self.scene(): - self.scene().update() - - self.dirty = False - - return self.source - - def openContextMenu(self, menu): - menu.addAction(QIcon(":/icons/images/minus.png"), - "Remove %s" % self.varWrapper.exp, self.remove) - menu.addAction(QIcon(":/icons/images/save-html.png"), - "Save HTML for %s" % self.varWrapper.exp, self.saveHtml) - menu.exec_(QCursor.pos()) - - @QtCore.pyqtSlot() - def saveHtml(self): - name = QFileDialog.getSaveFileName(filter="HTML (*.html)") - if name != "": - out = file(name, 'w') - out.write(self.source) - out.close() - - def contextMenuEvent(self, event): - pass - - @QtCore.pyqtSlot() - def remove(self): - """remove the varWrapper from the datagraph""" - self.removing.emit() - self.distributedObjects.datagraphController.removeVar(self.varWrapper) - - def getUniqueId(self, template): - if not template in self.uniqueIds: - self.lastId += 1 - self.uniqueIds[template] = "tmpl%d" % self.lastId - return self.uniqueIds[template] - - -class SVGTemplateHandler(HtmlTemplateHandler): - """ TemplateHandler for SVG Images """ - - def __init__(self, svgWrapper, distributedObjects): - """ Constructor - @param svgWrapper holds the Data to show """ - HtmlTemplateHandler.__init__(self, - svgWrapper, - distributedObjects, - 'svgview.mako') - - def prepareContextMenu(self, menu): - HtmlTemplateHandler.prepareContextMenu(self, menu) - - -class SVGDataGraphVW(DataGraphVW): - """ Wrapper for SVG Images """ - - def __init__(self, image, distributedObjects): - """ Constructor - @param image SVG image to wrap with the new DataGraphVW - @param distributedObjects the DistributedObjects-Instance - """ - DataGraphVW.__init__(self, image, distributedObjects) - self.image = image - self.templateHandler = SVGTemplateHandler(self, - self.distributedObjects) - self.imagePath = image.imagePath - self.name = str(image) - - def createView(self): - self._view = SVGView(self, self.distributedObjects) - self.parentWrapper = self._view diff --git a/src/datagraph/templates/waveformview.mako b/src/datagraph/templates/waveformview.mako deleted file mode 100644 index f988150..0000000 --- a/src/datagraph/templates/waveformview.mako +++ /dev/null @@ -1,56 +0,0 @@ -<%! - from datagraph.datagraphvw import Role -%>\ - - - - -<% assert(top) %>\ -% if varWrapper.inScope: - -${varWrapper.render(Role.INCLUDE_HEADER)} -
-
- -
-% endif - - diff --git a/src/datagraph/waveview.py b/src/datagraph/waveview.py deleted file mode 100644 index 510b4e8..0000000 --- a/src/datagraph/waveview.py +++ /dev/null @@ -1,123 +0,0 @@ -# ricodebug - A GDB frontend which focuses on visually supported -# debugging using data structure graphs and SystemC features. -# -# Copyright (C) 2011 The ricodebug project team at the -# Upper Austrian University Of Applied Sciences Hagenberg, -# Department Embedded Systems Design -# -# This file is part of ricodebug. -# -# ricodebug is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# For further information see . - -from PyQt4.QtCore import QSize, QSizeF, pyqtSignal -from PyQt4.QtGui import QGraphicsItem, QCursor, QFileDialog, QIcon -from PyQt4.QtWebKit import QGraphicsWebView -from mako.template import Template -from PyQt4 import QtCore -import sys -import logging - - -class WaveView(QGraphicsWebView): - """ the view to show waveforms in the datagraph """ - - removing = pyqtSignal() - - def __init__(self, varWrapper, distributedObjects): - """ Constructor - @param varWrapper datagraph.datagraphvw.DataGraphVW, holds the Data of the Variable to show - @param distributedObjects distributedobjects.DistributedObjects, the DistributedObjects-Instance - """ - QGraphicsWebView.__init__(self, None) - self.varWrapper = varWrapper - self.distributedObjects = distributedObjects - self.setFlags(QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsFocusable) - self.htmlTemplate = Template(filename=sys.path[0] + '/datagraph/templates/waveformview.mako') - self.page().setPreferredContentsSize(QSize(0, 0)) - self.setPreferredSize(QSizeF(0, 0)) - self.setResizesToContents(True) - - - self.source = None - - # ids for ourself and the template handlers we will eventually render - self.lastId = -1 - self.uniqueIds = {} - - self.dirty = True - - self.id = self.getUniqueId(self) - - self.distributedObjects.signalProxy.variableUpdateCompleted.connect(self.render) - - def setDirty(self, render_immediately): - self.dirty = True - if render_immediately: - self.render() - - def render(self): - if self.dirty: - # the page's viewport will not shrink if new content is set, so set it to it's minimum - self.page().setViewportSize(QSize(0, 0)) - try: - self.source = self.htmlTemplate.render(varWrapper=self.varWrapper, top=True, id=self.id) - self.setHtml(self.source) - - for template, id_ in self.uniqueIds.iteritems(): - self.page().mainFrame().addToJavaScriptWindowObject(id_, template) - except Exception as e: - logging.error("Rendering failed: %s", str(e)) - self.setHtml(str(e)) - raise - - # force an update of the scene that contains us, since sometimes setHtml - # will not cause the view to be redrawn immediately - if self.scene(): - self.scene().update() - - self.dirty = False - - return self.source - - def openContextMenu(self, menu): - menu.addAction(QIcon(":/icons/images/minus.png"), - "Remove %s" % self.varWrapper.exp, self.remove) - menu.addAction(QIcon(":/icons/images/save-html.png"), - "Save HTML for %s" % self.varWrapper.exp, self.saveHtml) - menu.exec_(QCursor.pos()) - - @QtCore.pyqtSlot() - def saveHtml(self): - name = QFileDialog.getSaveFileName(filter="HTML (*.html)") - if name != "": - out = file(name, 'w') - out.write(self.source) - out.close() - - def contextMenuEvent(self, event): - pass - - @QtCore.pyqtSlot() - def remove(self): - """remove the varWrapper from the datagraph""" - self.removing.emit() - self.distributedObjects.datagraphController.removeVar(self.varWrapper) - - def getUniqueId(self, template): - if not template in self.uniqueIds: - self.lastId += 1 - self.uniqueIds[template] = "tmpl%d" % self.lastId - return self.uniqueIds[template] diff --git a/src/helpers/svgdrawdiagram.py b/src/helpers/svgdrawdiagram.py deleted file mode 100644 index 1054188..0000000 --- a/src/helpers/svgdrawdiagram.py +++ /dev/null @@ -1,15 +0,0 @@ -from svgdraw import SvgDraw - -class SvgDrawDiagram(SvgDraw): - '''draw diagrams in svg''' - - def __init__(self, filename, picWidth, picHeight): - SvgDraw.__init__(self, filename, picWidth, picHeight) - # constants - self.cEntityheight = 0.6 - self.cEntitylength = 0.5 - - - def drawEntity(self, xPos, yPos, name): - self.drawRectangle(xPos, yPos+0.05, self.cEntitylength, self.cEntityheight) - self.drawText(xPos, yPos, name) \ No newline at end of file