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, \
00113                         'Unknown severity type: %s' % msg.severity
00114                     return MessageDataModel.severity_colors[msg.severity]
00115 
00116                 if role == Qt.ToolTipRole and column != 'severity':
00117                     if column == 'stamp':
00118                         data = msg.get_stamp_string()
00119                     elif column == 'topics':
00120                         data = ', '.join(msg.topics)
00121                     else:
00122                         data = getattr(msg, column)
00123                     # <font> tag enables word wrap by forcing rich text
00124                     return '<font>' + data + '<br/><br/>' + \
00125                         self.tr('Right click for menu.') + '</font>'
00126 
00127     def headerData(self, section, orientation, role=None):
00128         if role is None:
00129             role = Qt.DisplayRole
00130         if orientation == Qt.Horizontal:
00131             if role == Qt.DisplayRole:
00132                 if section == 0:
00133                     return self.tr('#')
00134                 else:
00135                     return MessageDataModel.columns[section - 1].capitalize()
00136             if role == Qt.ToolTipRole:
00137                 if section == 0:
00138                     return self.tr('Sort the rows by serial number in descendig order')
00139                 else:
00140                     return self.tr(
00141                         'Sorting the table by a column other then the serial number slows down the '
00142                         'interaction especially when recording high frequency data')
00143 
00144     # END Required implementations of QAbstractTableModel functions
00145 
00146     def get_message_limit(self):
00147         return self._message_limit
00148 
00149     def set_message_limit(self, new_limit):
00150         self._message_limit = new_limit
00151         self._enforce_message_limit(self._message_limit)
00152 
00153     def _enforce_message_limit(self, limit):
00154         if len(self._messages) > limit:
00155             self.beginRemoveRows(QModelIndex(), limit, len(self._messages) - 1)
00156             del self._messages[limit:len(self._messages)]
00157             self.endRemoveRows()
00158 
00159     def insert_rows(self, msgs):
00160         # never try to insert more message than the limit
00161         if len(msgs) > self._message_limit:
00162             msgs = msgs[-self._message_limit:]
00163         # reduce model before insert
00164         limit = self._message_limit - len(msgs)
00165         self._enforce_message_limit(limit)
00166         # insert newest messages
00167         self.beginInsertRows(QModelIndex(), 0, len(msgs) - 1)
00168         self._messages.extend(msgs)
00169         self.endInsertRows()
00170 
00171     def remove_rows(self, rowlist):
00172         """
00173         :param rowlist: list of row indexes, ''list(int)''
00174         :returns: True if the indexes were removed successfully, ''bool''
00175         """
00176         if len(rowlist) == 0:
00177             if len(self._messages) > 0:
00178                 try:
00179                     self.beginRemoveRows(QModelIndex(), 0, len(self._messages))
00180                     del self._messages[0:len(self._messages)]
00181                     self.endRemoveRows()
00182                 except:
00183                     return False
00184         else:
00185             rowlist = list(set(rowlist))
00186             rowlist.sort(reverse=True)
00187             dellist = [rowlist[0]]
00188             for row in rowlist[1:]:
00189                 if dellist[-1] - 1 > row:
00190                     try:
00191                         self.beginRemoveRows(QModelIndex(), dellist[-1], dellist[0])
00192                         del self._messages[dellist[-1]:dellist[0] + 1]
00193                         self.endRemoveRows()
00194                     except:
00195                         return False
00196                     dellist = []
00197                 dellist.append(row)
00198             if len(dellist) > 0:
00199                 try:
00200                     self.beginRemoveRows(QModelIndex(), dellist[-1], dellist[0])
00201                     del self._messages[dellist[-1]:dellist[0] + 1]
00202                     self.endRemoveRows()
00203                 except:
00204                     return False
00205         return True
00206 
00207     def get_selected_text(self, rowlist):
00208         """
00209         Returns an easily readable block of text for the currently selected rows
00210         :param rowlist: list of row indexes, ''list(int)''
00211         :returns: the text from those indexes, ''str''
00212         """
00213         text = None
00214         if len(rowlist) != 0:
00215             text = ''
00216             rowlist = list(set(rowlist))
00217             for row in rowlist:
00218                 text += self._messages[row].pretty_print()
00219         return text
00220 
00221     def get_time_range(self, rowlist):
00222         """
00223         :param rowlist: a list of row indexes, ''list''
00224         :returns: a tuple of min and max times in a rowlist in
00225                   '(unix timestamp).(fraction of second)' format, ''tuple(str,str)''
00226         """
00227         min_ = float("inf")
00228         max_ = float("-inf")
00229         for row in rowlist:
00230             item = self._messages[row].time_as_datestamp()
00231             if float(item) > float(max_):
00232                 max_ = item
00233             if float(item) < float(min_):
00234                 min_ = item
00235         return min_, max_
00236 
00237     def get_unique_nodes(self):
00238         nodes = set()
00239         for message in self._messages:
00240             nodes.add(message.node)
00241         return nodes
00242 
00243     def get_unique_severities(self):
00244         severities = set()
00245         for message in self._messages:
00246             severities.add(message.severity)
00247         return severities
00248 
00249     def get_unique_topics(self):
00250         topics = set()
00251         for message in self._messages:
00252             for topic in message.topics:
00253                 topics.add(topic)
00254         return topics
00255 
00256     def get_severity_dict(self):
00257         return Message.SEVERITY_LABELS
00258 
00259     def get_message_between(self, start_time, end_time=None):
00260         """
00261         :param start_time: time to start in timestamp form (including decimal
00262         fractions of a second is acceptable, ''unixtimestamp''
00263         :param end_time: time to end in timestamp form (including decimal
00264         fractions of a second is acceptable, ''unixtimestamp'' (Optional)
00265         :returns: list of messages in the time range ''list[message]''
00266         """
00267         msgs = []
00268         for message in self._messages:
00269             msg_time = message.stamp[0] + float(message.stamp[1]) / 10**9
00270             if msg_time >= float(start_time) and (end_time is None or msg_time <= float(end_time)):
00271                 msgs.append(message)
00272         return msgs


rqt_console
Author(s): Aaron Blasdel
autogenerated on Sat Jun 8 2019 20:58:08