message_data_model.py
Go to the documentation of this file.
00001 # Software License Agreement (BSD License)
00002 #
00003 # Copyright (c) 2012, Willow Garage, Inc.
00004 # All rights reserved.
00005 #
00006 # Redistribution and use in source and binary forms, with or without
00007 # modification, are permitted provided that the following conditions
00008 # are met:
00009 #
00010 #  * Redistributions of source code must retain the above copyright
00011 #    notice, this list of conditions and the following disclaimer.
00012 #  * Redistributions in binary form must reproduce the above
00013 #    copyright notice, this list of conditions and the following
00014 #    disclaimer in the documentation and/or other materials provided
00015 #    with the distribution.
00016 #  * Neither the name of Willow Garage, Inc. nor the names of its
00017 #    contributors may be used to endorse or promote products derived
00018 #    from this software without specific prior written permission.
00019 #
00020 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00021 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00022 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00023 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00024 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00025 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00026 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00027 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00028 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00029 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00030 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00031 # POSSIBILITY OF SUCH DAMAGE.
00032 
00033 from python_qt_binding.QtCore import QAbstractTableModel, QModelIndex, Qt, qWarning
00034 from python_qt_binding.QtGui import QBrush, QIcon
00035 
00036 from .message import Message
00037 from .message_list import MessageList
00038 
00039 
00040 class MessageDataModel(QAbstractTableModel):
00041 
00042     # the column names must match the message attributes
00043     columns = ['message', 'severity', 'node', 'stamp', 'topics', 'location']
00044 
00045     severity_colors = {
00046         Message.DEBUG: QBrush(Qt.darkCyan),
00047         Message.INFO: QBrush(Qt.darkBlue),
00048         Message.WARN: QBrush(Qt.darkYellow),
00049         Message.ERROR: QBrush(Qt.darkRed),
00050         Message.FATAL: QBrush(Qt.red),
00051     }
00052 
00053     def __init__(self):
00054         super(MessageDataModel, self).__init__()
00055         self._messages = MessageList()
00056         self._message_limit = 20000
00057         self._info_icon = QIcon.fromTheme('dialog-information')
00058         self._warning_icon = QIcon.fromTheme('dialog-warning')
00059         self._error_icon = QIcon.fromTheme('dialog-error')
00060 
00061     # BEGIN Required implementations of QAbstractTableModel functions
00062 
00063     def rowCount(self, parent=None):
00064         return len(self._messages)
00065 
00066     def columnCount(self, parent=None):
00067         return len(MessageDataModel.columns) + 1
00068 
00069     def data(self, index, role=None):
00070         if role is None:
00071             role = Qt.DisplayRole
00072         if index.row() >= 0 and index.row() < len(self._messages):
00073             msg = self._messages[index.row()]
00074             if index.column() == 0:
00075                 if role == Qt.DisplayRole:
00076                     return '#%d' % msg.id
00077             elif index.column() > 0 and index.column() < len(MessageDataModel.columns) + 1:
00078                 column = MessageDataModel.columns[index.column() - 1]
00079                 if role == Qt.DisplayRole or role == Qt.UserRole:
00080                     if column == 'stamp':
00081                         if role != Qt.UserRole:
00082                             data = msg.get_stamp_string()
00083                         else:
00084                             data = msg.get_stamp_for_compare()
00085                     else:
00086                         data = getattr(msg, column)
00087                     # map severity enum to label
00088                     if role == Qt.DisplayRole and column == 'severity':
00089                         data = Message.SEVERITY_LABELS[data]
00090                     # implode topic names
00091                     if column == 'topics':
00092                         data = ', '.join(data)
00093                     # append row number to define strict order
00094                     if role == Qt.UserRole:
00095                         # append row number to define strict order
00096                         # shortest string representation to compare stamps
00097                         #print(column, data, str(index.row()).zfill(len(str(len(self._messages)))))
00098                         data = str(data) + ' %08x' % index.row()
00099                     return data
00100 
00101                 # decorate message column with severity icon
00102                 if role == Qt.DecorationRole and column == 'message':
00103                     if msg.severity in [Message.DEBUG, Message.INFO]:
00104                         return self._info_icon
00105                     elif msg.severity in [Message.WARN]:
00106                         return self._warning_icon
00107                     elif msg.severity in [Message.ERROR, Message.FATAL]:
00108                         return self._error_icon
00109 
00110                 # colorize severity label
00111                 if role == Qt.ForegroundRole and column =='severity':
00112                     assert msg.severity in MessageDataModel.severity_colors, 'Unknown severity type: %s' % msg.severity
00113                     return MessageDataModel.severity_colors[msg.severity]
00114 
00115                 if role == Qt.ToolTipRole and column != 'severity':
00116                     if column == 'stamp':
00117                         data = msg.get_stamp_string()
00118                     elif column == 'topics':
00119                         data = ', '.join(msg.topics)
00120                     else:
00121                         data = getattr(msg, column)
00122                     # <font> tag enables word wrap by forcing rich text
00123                     return '<font>' + data + '<br/><br/>' + self.tr('Right click for menu.') + '</font>'
00124 
00125     def headerData(self, section, orientation, role=None):
00126         if role is None:
00127             role = Qt.DisplayRole
00128         if orientation == Qt.Horizontal:
00129             if role == Qt.DisplayRole:
00130                 if section == 0:
00131                     return self.tr('#')
00132                 else:
00133                     return MessageDataModel.columns[section - 1].capitalize()
00134             if role == Qt.ToolTipRole:
00135                 if section == 0:
00136                     return self.tr('Sort the rows by serial number in descendig order')
00137                 else:
00138                     return self.tr('Sorting the table by a column other then the serial number slows down the interaction especially when recording high frequency data')
00139 
00140     # END Required implementations of QAbstractTableModel functions
00141 
00142     def get_message_limit(self):
00143         return self._message_limit
00144 
00145     def set_message_limit(self, new_limit):
00146         self._message_limit = new_limit
00147         self._enforce_message_limit(self._message_limit)
00148 
00149     def _enforce_message_limit(self, limit):
00150         if len(self._messages) > limit:
00151             self.beginRemoveRows(QModelIndex(), limit, len(self._messages) - 1)
00152             del self._messages[limit:len(self._messages)]
00153             self.endRemoveRows()
00154 
00155     def insert_rows(self, msgs):
00156         # never try to insert more message than the limit
00157         if len(msgs) > self._message_limit:
00158             msgs = msgs[-self._message_limit:]
00159         # reduce model before insert
00160         limit = self._message_limit - len(msgs)
00161         self._enforce_message_limit(limit)
00162         # insert newest messages
00163         self.beginInsertRows(QModelIndex(), 0, len(msgs) - 1)
00164         self._messages.extend(msgs)
00165         self.endInsertRows()
00166 
00167     def remove_rows(self, rowlist):
00168         """
00169         :param rowlist: list of row indexes, ''list(int)''
00170         :returns: True if the indexes were removed successfully, ''bool''
00171         """
00172         if len(rowlist) == 0:
00173             if len(self._messages) > 0:
00174                 try:
00175                     self.beginRemoveRows(QModelIndex(), 0, len(self._messages))
00176                     del self._messages[0:len(self._messages)]
00177                     self.endRemoveRows()
00178                 except:
00179                     return False
00180         else:
00181             rowlist = list(set(rowlist))
00182             rowlist.sort(reverse=True)
00183             dellist = [rowlist[0]]
00184             for row in rowlist[1:]:
00185                 if dellist[-1] - 1 > row:
00186                     try:
00187                         self.beginRemoveRows(QModelIndex(), dellist[-1], dellist[0])
00188                         del self._messages[dellist[-1]:dellist[0] + 1]
00189                         self.endRemoveRows()
00190                     except:
00191                         return False
00192                     dellist = []
00193                 dellist.append(row)
00194             if len(dellist) > 0:
00195                 try:
00196                     self.beginRemoveRows(QModelIndex(), dellist[-1], dellist[0])
00197                     del self._messages[dellist[-1]:dellist[0] + 1]
00198                     self.endRemoveRows()
00199                 except:
00200                     return False
00201         return True
00202 
00203     def get_selected_text(self, rowlist):
00204         """
00205         Returns an easily readable block of text for the currently selected rows
00206         :param rowlist: list of row indexes, ''list(int)''
00207         :returns: the text from those indexes, ''str''
00208         """
00209         text = None
00210         if len(rowlist) != 0:
00211             text = ''
00212             rowlist = list(set(rowlist))
00213             for row in rowlist:
00214                 text += self._messages[row].pretty_print()
00215         return text
00216 
00217     def get_time_range(self, rowlist):
00218         """
00219         :param rowlist: a list of row indexes, ''list''
00220         :returns: a tuple of min and max times in a rowlist in '(unix timestamp).(fraction of second)' format, ''tuple(str,str)''
00221         """
00222         min_ = float("inf")
00223         max_ = float("-inf")
00224         for row in rowlist:
00225             item = self._messages[row].time_as_datestamp()
00226             if float(item) > float(max_):
00227                 max_ = item
00228             if float(item) < float(min_):
00229                 min_ = item
00230         return min_, max_
00231 
00232     def get_unique_nodes(self):
00233         nodes = set([])
00234         for message in self._messages:
00235             nodes.add(message.node)
00236         return nodes
00237 
00238     def get_unique_severities(self):
00239         severities = set([])
00240         for message in self._messages:
00241             severities.add(message.severity)
00242         return severities
00243 
00244     def get_unique_topics(self):
00245         topics = set([])
00246         for message in self._messages:
00247             for topic in message.topics:
00248                 topics.add(topic)
00249         return topics
00250 
00251     def get_severity_dict(self):
00252         return Message.SEVERITY_LABELS
00253 
00254     def get_message_between(self, start_time, end_time=None):
00255         """
00256         :param start_time: time to start in timestamp form (including decimal
00257         fractions of a second is acceptable, ''unixtimestamp''
00258         :param end_time: time to end in timestamp form (including decimal
00259         fractions of a second is acceptable, ''unixtimestamp'' (Optional)
00260         :returns: list of messages in the time range ''list[message]''
00261         """
00262         msgs = []
00263         for message in self._messages:
00264             msg_time = message.stamp[0] + float(message.stamp[1]) / 10**9
00265             if msg_time >= float(start_time) and (end_time is None or msg_time <= float(end_time)):
00266                 msgs.append(message)
00267         return msgs


rqt_console
Author(s): Aaron Blasdel
autogenerated on Mon Oct 6 2014 07:15:09