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 message_list import MessageList
00034
00035 from python_qt_binding.QtCore import QAbstractTableModel, QDateTime, QModelIndex, Qt, qWarning
00036 from python_qt_binding.QtGui import QIcon
00037
00038
00039 class MessageDataModel(QAbstractTableModel):
00040 def __init__(self):
00041 super(MessageDataModel, self).__init__()
00042 self._messages = MessageList()
00043
00044 self._time_format = 'hh:mm:ss.zzz (yyyy-MM-dd)'
00045 self._insert_message_queue = []
00046 self._paused = False
00047 self._message_limit = 20000
00048 self._error_icon = QIcon.fromTheme('dialog-error')
00049 self._warning_icon = QIcon.fromTheme('dialog-warning')
00050 self._info_icon = QIcon.fromTheme('dialog-information')
00051
00052
00053
00054 def rowCount(self, parent=None):
00055 return len(self._messages.get_message_list())
00056
00057 def columnCount(self, parent=None):
00058 return self._messages.column_count()
00059
00060 def data(self, index, role=None):
00061 if role is None:
00062 role = Qt.DisplayRole
00063 messagelist = self._messages.get_message_list()
00064 if index.row() >= 0 and index.row() < len(messagelist):
00065 if index.column() >= 0 and index.column() < messagelist[index.row()].count():
00066 if role == Qt.DisplayRole:
00067 elements = self._messages.message_members()
00068 if elements[index.column()] == '_time':
00069 return self.timedata_to_timestring(messagelist[index.row()].time_in_seconds())
00070 else:
00071 return getattr(messagelist[index.row()], elements[index.column()])
00072 elif role == Qt.DecorationRole and index.column() == 0:
00073 msgseverity = messagelist[index.row()].get_data(1)
00074 if msgseverity in (self.tr('Debug'), self.tr('Info')):
00075 return self._info_icon
00076 elif msgseverity in (self.tr('Warn')):
00077 return self._warning_icon
00078 elif msgseverity in (self.tr('Error'), self.tr('Fatal')):
00079 return self._error_icon
00080 elif role == Qt.ToolTipRole:
00081 return self.tr('Right click for menu.')
00082
00083 def headerData(self, section, orientation, role=None):
00084 if role is None:
00085 role = Qt.DisplayRole
00086 if role == Qt.DisplayRole:
00087 if orientation == Qt.Horizontal:
00088 sections = self._messages.message_members()
00089 retval = sections[section][1:].capitalize()
00090 return retval
00091 elif orientation == Qt.Vertical:
00092 return '#%d' % (section + 1)
00093
00094
00095 def timestring_to_timedata(self, timestring):
00096 """
00097 Converts a time string in the format of _time_format into a string
00098 of format '(unix timestamp).(fraction of second)'
00099 :param timestring: formatted time string ''str''
00100 :returns: seconds and fractions thereof ''str''
00101 """
00102 timeval = QDateTime.fromString(timestring, self._time_format).toTime_t()
00103 return str(timeval) + '.' + timestring[9:12]
00104
00105 def timedata_to_timestring(self, timedata):
00106 """
00107 Converts a string in the format of '(unix timestamp).(fraction of second)'
00108 into a string of format _time_format
00109 :param timedata: seconds and fractions thereof ''str''
00110 :returns: formatted time string''str''
00111 """
00112 sec, fraction = timedata.split('.')
00113 if len(fraction) < 3:
00114 raise RuntimeError('Malformed timestring in timedata_to_timestring()')
00115 micro = int(fraction[:3])
00116 return QDateTime.fromTime_t(long(sec)).addMSecs(micro).toString(self._time_format)
00117
00118 def insert_rows(self, msgs):
00119 """
00120 Wraps the insert_row function to minimize gui notification calls
00121 """
00122 if len(msgs) == 0:
00123 return
00124 self.beginInsertRows(QModelIndex(), len(self._messages.get_message_list()), len(self._messages.get_message_list()) + len(msgs) - 1)
00125 for msg in msgs:
00126 self.insert_row(msg, False)
00127 self.endInsertRows()
00128
00129 if len(self.get_message_list()) > self._message_limit:
00130 self.beginRemoveRows(QModelIndex(), 0, len(self.get_message_list()) - self._message_limit - 1)
00131 del self.get_message_list()[0:len(self.get_message_list()) - self._message_limit]
00132 self.endRemoveRows()
00133
00134 def insert_row(self, msg, notify_model=True):
00135 if notify_model:
00136 self.beginInsertRows(QModelIndex(), len(self._messages.get_message_list()), len(self._messages.get_message_list()))
00137 self._messages.add_message(msg)
00138 if notify_model:
00139 self.endInsertRows()
00140
00141 def remove_rows(self, rowlist):
00142 """
00143 :param rowlist: list of row indexes, ''list(int)''
00144 :returns: True if the indexes were removed successfully, ''bool''
00145 OR
00146 :returns: False if there was an exception removing the rows, ''bool''
00147 """
00148 if len(rowlist) == 0:
00149 if len(self.get_message_list()) > 0:
00150 try:
00151 self.beginRemoveRows(QModelIndex(), 0, len(self.get_message_list()))
00152 del self.get_message_list()[0:len(self.get_message_list())]
00153 self.endRemoveRows()
00154 except:
00155 return False
00156 else:
00157 rowlist = list(set(rowlist))
00158 rowlist.sort(reverse=True)
00159 dellist = [rowlist[0]]
00160 for row in rowlist[1:]:
00161 if dellist[-1] - 1 > row:
00162 try:
00163 self.beginRemoveRows(QModelIndex(), dellist[-1], dellist[0])
00164 del self.get_message_list()[dellist[-1]:dellist[0] + 1]
00165 self.endRemoveRows()
00166 except:
00167 return False
00168 dellist = []
00169 dellist.append(row)
00170 if len(dellist) > 0:
00171 try:
00172 self.beginRemoveRows(QModelIndex(), dellist[-1], dellist[0])
00173 del self.get_message_list()[dellist[-1]:dellist[0] + 1]
00174 self.endRemoveRows()
00175 except:
00176 return False
00177 return True
00178
00179 def get_selected_text(self, rowlist):
00180 """
00181 Returns an easily readable block of text for the currently selected rows
00182 :param rowlist: list of row indexes, ''list(int)''
00183 :returns: the text from those indexes, ''str''
00184 """
00185 text = None
00186 if len(rowlist) != 0:
00187 text = ''
00188 rowlist = list(set(rowlist))
00189 for row in rowlist:
00190 text += self.get_message_list()[row].pretty_print()
00191 return text
00192
00193 def get_time_range(self, rowlist):
00194 """
00195 :param rowlist: a list of row indexes, ''list''
00196 :returns: a tuple of min and max times in a rowlist in '(unix timestamp).(fraction of second)' format, ''tuple(str,str)''
00197 """
00198 min_ = float("inf")
00199 max_ = float("-inf")
00200 for row in rowlist:
00201 item = self.get_message_list()[row].time_in_seconds()
00202 if float(item) > float(max_):
00203 max_ = item
00204 if float(item) < float(min_):
00205 min_ = item
00206 return min_, max_
00207
00208 def get_unique_col_data(self, index, separate_topics=True):
00209 """
00210 :param index: column index, ''int''
00211 :param separate_topics: if true separates comma delimited strings into
00212 unique rows, ''bool''
00213 :returns: list of unique strings in the column, ''list[str]''
00214 """
00215 if index == 4 and separate_topics:
00216 unique_list = set()
00217 for topiclists in self._messages.get_unique_col_data(index):
00218 for item in topiclists.split(','):
00219 unique_list.add(item.strip())
00220 return list(unique_list)
00221 return self._messages.get_unique_col_data(index)
00222
00223 def get_severity_list(self):
00224 return [self.tr('Debug'), self.tr('Info'), self.tr('Warn'), self.tr('Error'), self.tr('Fatal')]
00225
00226 def get_data(self, row, col):
00227 return self._messages.get_data(row, col)
00228
00229 def message_members(self):
00230 return self._messages.message_members()
00231
00232 def load_from_file(self, filehandle):
00233 """
00234 Saves to an already open filehandle.
00235 :returns: True if loaded successfully, ''bool''
00236 OR
00237 :returns: False if load fails, ''bool''
00238 """
00239 line = filehandle.readline()
00240 lines = []
00241 if line == self._messages.header_print():
00242 while 1:
00243 line = filehandle.readline()
00244 if not line:
00245 break
00246 while line[-2] != "\"":
00247 newline = filehandle.readline()
00248 if not line:
00249 qWarning('File does not appear to be a rqt_console message file: missing " at end of file')
00250 return False
00251 line = line + newline
00252 lines.append(line)
00253 self.beginInsertRows(QModelIndex(), len(self._messages.get_message_list()), len(self._messages.get_message_list()) + len(lines) - 1)
00254 for line in lines:
00255 try:
00256 self._messages.append_from_text(line)
00257 except ValueError:
00258 qWarning('File does not appear to be a rqt_console message file: File line is malformed %s' % line)
00259 self.endInsertRows()
00260 self._paused = True
00261 return True
00262 else:
00263 qWarning('File does not appear to be a rqt_console message file: missing file header.')
00264 return False
00265
00266 def get_message_list(self, start_time=None, end_time=None):
00267 """
00268 :param start_time: time to start in timestamp form (including decimal
00269 fractions of a second is acceptable, ''unixtimestamp'' (Optional)
00270 :param end_time: time to end in timestamp form (including decimal
00271 fractions of a second is acceptable, ''unixtimestamp'' (Optional)
00272 :returns: list of messages in the time range ''list[message]''
00273 """
00274 if start_time is not None:
00275 return self._messages.get_messages_in_time_range(start_time, end_time)
00276 else:
00277 return self._messages.get_message_list()