00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
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
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
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
00088 if role == Qt.DisplayRole and column == 'severity':
00089 data = Message.SEVERITY_LABELS[data]
00090
00091 if column == 'topics':
00092 data = ', '.join(data)
00093
00094 if role == Qt.UserRole:
00095
00096
00097
00098 data = str(data) + ' %08x' % index.row()
00099 return data
00100
00101
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
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
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
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
00157 if len(msgs) > self._message_limit:
00158 msgs = msgs[-self._message_limit:]
00159
00160 limit = self._message_limit - len(msgs)
00161 self._enforce_message_limit(limit)
00162
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