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, \
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
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
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
00161 if len(msgs) > self._message_limit:
00162 msgs = msgs[-self._message_limit:]
00163
00164 limit = self._message_limit - len(msgs)
00165 self._enforce_message_limit(limit)
00166
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