1041 |
1116 |
1042 # Update subplots |
1117 # Update subplots |
1043 self.Figure.subplots_adjust() |
1118 self.Figure.subplots_adjust() |
1044 |
1119 |
1045 def RefreshViewer(self, refresh_graphics=True): |
1120 def RefreshViewer(self, refresh_graphics=True): |
1046 |
1121 """ |
|
1122 Function called to refresh displayed by matplotlib canvas |
|
1123 @param refresh_graphics: Flag indicating that graphs have to be |
|
1124 refreshed (False: only label values have to be refreshed) |
|
1125 """ |
|
1126 # Refresh graphs if needed |
1047 if refresh_graphics: |
1127 if refresh_graphics: |
|
1128 # Get tick range of values to display |
1048 start_tick, end_tick = self.ParentWindow.GetRange() |
1129 start_tick, end_tick = self.ParentWindow.GetRange() |
1049 |
1130 |
|
1131 # Graph is parallel |
1050 if self.GraphType == GRAPH_PARALLEL: |
1132 if self.GraphType == GRAPH_PARALLEL: |
1051 min_value = max_value = None |
1133 # Init list of data range for each variable displayed |
1052 |
1134 ranges = [] |
|
1135 |
|
1136 # Get data and range for each variable displayed |
1053 for idx, item in enumerate(self.Items): |
1137 for idx, item in enumerate(self.Items): |
1054 data = item.GetData(start_tick, end_tick) |
1138 data, min_value, max_value = item.GetDataAndValueRange( |
|
1139 start_tick, end_tick, not self.ZoomFit) |
|
1140 |
|
1141 # Check that data is not empty |
1055 if data is not None: |
1142 if data is not None: |
1056 item_min_value, item_max_value = item.GetValueRange() |
1143 # Add variable range to list of variable data range |
1057 if min_value is None: |
1144 ranges.append((min_value, max_value)) |
1058 min_value = item_min_value |
|
1059 elif item_min_value is not None: |
|
1060 min_value = min(min_value, item_min_value) |
|
1061 if max_value is None: |
|
1062 max_value = item_max_value |
|
1063 elif item_max_value is not None: |
|
1064 max_value = max(max_value, item_max_value) |
|
1065 |
1145 |
|
1146 # Add plot to canvas if not yet created |
1066 if len(self.Plots) <= idx: |
1147 if len(self.Plots) <= idx: |
1067 self.Plots.append( |
1148 self.Plots.append( |
1068 self.Axes.plot(data[:, 0], data[:, 1])[0]) |
1149 self.Axes.plot(data[:, 0], data[:, 1])[0]) |
|
1150 |
|
1151 # Set data to already created plot in canvas |
1069 else: |
1152 else: |
1070 self.Plots[idx].set_data(data[:, 0], data[:, 1]) |
1153 self.Plots[idx].set_data(data[:, 0], data[:, 1]) |
|
1154 |
|
1155 # Get X and Y axis ranges |
|
1156 x_min, x_max = start_tick, end_tick |
|
1157 y_min, y_max = merge_ranges(ranges) |
|
1158 |
|
1159 # Display cursor in canvas if a cursor tick is defined and it is |
|
1160 # include in values tick range |
|
1161 if (self.CursorTick is not None and |
|
1162 start_tick <= self.CursorTick <= end_tick): |
1071 |
1163 |
1072 if min_value is not None and max_value is not None: |
1164 # Define a vertical line to display cursor position if no |
1073 y_center = (min_value + max_value) / 2. |
1165 # line is already defined |
1074 y_range = max(1.0, max_value - min_value) |
|
1075 else: |
|
1076 y_center = 0.5 |
|
1077 y_range = 1.0 |
|
1078 x_min, x_max = start_tick, end_tick |
|
1079 y_min, y_max = y_center - y_range * 0.55, y_center + y_range * 0.55 |
|
1080 |
|
1081 if self.CursorTick is not None and start_tick <= self.CursorTick <= end_tick: |
|
1082 if self.VLine is None: |
1166 if self.VLine is None: |
1083 self.VLine = self.Axes.axvline(self.CursorTick, color=CURSOR_COLOR) |
1167 self.VLine = self.Axes.axvline(self.CursorTick, |
|
1168 color=CURSOR_COLOR) |
|
1169 |
|
1170 # Set value of vertical line if already defined |
1084 else: |
1171 else: |
1085 self.VLine.set_xdata((self.CursorTick, self.CursorTick)) |
1172 self.VLine.set_xdata((self.CursorTick, self.CursorTick)) |
1086 self.VLine.set_visible(True) |
1173 self.VLine.set_visible(True) |
1087 else: |
1174 |
1088 if self.VLine is not None: |
1175 # Hide vertical line if cursor tick is not defined or reset |
1089 self.VLine.set_visible(False) |
1176 elif self.VLine is not None: |
|
1177 self.VLine.set_visible(False) |
|
1178 |
|
1179 # Graph is orthogonal |
1090 else: |
1180 else: |
1091 min_start_tick = max(start_tick, self.GetItemsMinCommonTick()) |
1181 # Update tick range, removing ticks that don't have a value for |
1092 start_tick = max(start_tick, min_start_tick) |
1182 # each variable |
1093 end_tick = max(end_tick, min_start_tick) |
1183 start_tick = max(start_tick, self.GetItemsMinCommonTick()) |
|
1184 end_tick = max(end_tick, start_tick) |
1094 items = self.ItemsDict.values() |
1185 items = self.ItemsDict.values() |
1095 x_data, x_min, x_max = items[0].OrthogonalDataAndRange(start_tick, end_tick) |
1186 |
1096 y_data, y_min, y_max = items[1].OrthogonalDataAndRange(start_tick, end_tick) |
1187 # Get data and range for first variable (X coordinate) |
|
1188 x_data, x_min, x_max = items[0].GetDataAndValueRange( |
|
1189 start_tick, end_tick, not self.ZoomFit) |
|
1190 # Get data and range for second variable (Y coordinate) |
|
1191 y_data, y_min, y_max = items[1].GetDataAndValueRange( |
|
1192 start_tick, end_tick, not self.ZoomFit) |
|
1193 |
|
1194 # Normalize X and Y coordinates value range |
|
1195 x_min, x_max = merge_ranges([(x_min, x_max)]) |
|
1196 y_min, y_max = merge_ranges([(y_min, y_max)]) |
|
1197 |
|
1198 # Get X and Y coordinates for cursor if cursor tick is defined |
1097 if self.CursorTick is not None: |
1199 if self.CursorTick is not None: |
1098 x_cursor, x_forced = items[0].GetValue(self.CursorTick, raw=True) |
1200 x_cursor, x_forced = items[0].GetValue( |
1099 y_cursor, y_forced = items[1].GetValue(self.CursorTick, raw=True) |
1201 self.CursorTick, raw=True) |
1100 length = 0 |
1202 y_cursor, y_forced = items[1].GetValue( |
1101 if x_data is not None and y_data is not None: |
1203 self.CursorTick, raw=True) |
1102 length = min(len(x_data), len(y_data)) |
1204 |
|
1205 # Get common data length so that each value has an x and y |
|
1206 # coordinate |
|
1207 length = (min(len(x_data), len(y_data)) |
|
1208 if x_data is not None and y_data is not None |
|
1209 else 0) |
|
1210 |
|
1211 # Graph is orthogonal 2D |
1103 if len(self.Items) < 3: |
1212 if len(self.Items) < 3: |
|
1213 |
|
1214 # Check that x and y data are not empty |
1104 if x_data is not None and y_data is not None: |
1215 if x_data is not None and y_data is not None: |
|
1216 |
|
1217 # Add plot to canvas if not yet created |
1105 if len(self.Plots) == 0: |
1218 if len(self.Plots) == 0: |
1106 self.Plots.append( |
1219 self.Plots.append( |
1107 self.Axes.plot(x_data[:, 1][:length], |
1220 self.Axes.plot(x_data[:, 1][:length], |
1108 y_data[:, 1][:length])[0]) |
1221 y_data[:, 1][:length])[0]) |
|
1222 |
|
1223 # Set data to already created plot in canvas |
1109 else: |
1224 else: |
1110 self.Plots[0].set_data( |
1225 self.Plots[0].set_data( |
1111 x_data[:, 1][:length], |
1226 x_data[:, 1][:length], |
1112 y_data[:, 1][:length]) |
1227 y_data[:, 1][:length]) |
1113 |
1228 |
1114 if self.CursorTick is not None and start_tick <= self.CursorTick <= end_tick: |
1229 # Display cursor in canvas if a cursor tick is defined and it is |
|
1230 # include in values tick range |
|
1231 if (self.CursorTick is not None and |
|
1232 start_tick <= self.CursorTick <= end_tick): |
|
1233 |
|
1234 # Define a vertical line to display cursor x coordinate |
|
1235 # if no line is already defined |
1115 if self.VLine is None: |
1236 if self.VLine is None: |
1116 self.VLine = self.Axes.axvline(x_cursor, color=CURSOR_COLOR) |
1237 self.VLine = self.Axes.axvline(x_cursor, |
|
1238 color=CURSOR_COLOR) |
|
1239 # Set value of vertical line if already defined |
1117 else: |
1240 else: |
1118 self.VLine.set_xdata((x_cursor, x_cursor)) |
1241 self.VLine.set_xdata((x_cursor, x_cursor)) |
|
1242 |
|
1243 |
|
1244 # Define a horizontal line to display cursor y |
|
1245 # coordinate if no line is already defined |
1119 if self.HLine is None: |
1246 if self.HLine is None: |
1120 self.HLine = self.Axes.axhline(y_cursor, color=CURSOR_COLOR) |
1247 self.HLine = self.Axes.axhline(y_cursor, |
|
1248 color=CURSOR_COLOR) |
|
1249 # Set value of horizontal line if already defined |
1121 else: |
1250 else: |
1122 self.HLine.set_ydata((y_cursor, y_cursor)) |
1251 self.HLine.set_ydata((y_cursor, y_cursor)) |
|
1252 |
1123 self.VLine.set_visible(True) |
1253 self.VLine.set_visible(True) |
1124 self.HLine.set_visible(True) |
1254 self.HLine.set_visible(True) |
|
1255 |
|
1256 # Hide vertical and horizontal line if cursor tick is not |
|
1257 # defined or reset |
1125 else: |
1258 else: |
1126 if self.VLine is not None: |
1259 if self.VLine is not None: |
1127 self.VLine.set_visible(False) |
1260 self.VLine.set_visible(False) |
1128 if self.HLine is not None: |
1261 if self.HLine is not None: |
1129 self.HLine.set_visible(False) |
1262 self.HLine.set_visible(False) |
|
1263 |
|
1264 # Graph is orthogonal 3D |
1130 else: |
1265 else: |
|
1266 # Remove all plots already defined in 3D canvas |
1131 while len(self.Axes.lines) > 0: |
1267 while len(self.Axes.lines) > 0: |
1132 self.Axes.lines.pop() |
1268 self.Axes.lines.pop() |
1133 z_data, z_min, z_max = items[2].OrthogonalDataAndRange(start_tick, end_tick) |
1269 |
1134 if self.CursorTick is not None: |
1270 # Get data and range for third variable (Z coordinate) |
1135 z_cursor, z_forced = items[2].GetValue(self.CursorTick, raw=True) |
1271 z_data, z_min, z_max = items[2].GetDataAndValueRange( |
1136 if x_data is not None and y_data is not None and z_data is not None: |
1272 start_tick, end_tick, not self.ZoomFit) |
|
1273 |
|
1274 # Normalize Z coordinate value range |
|
1275 z_min, z_max = merge_ranges([(z_min, z_max)]) |
|
1276 |
|
1277 # Check that x, y and z data are not empty |
|
1278 if (x_data is not None and y_data is not None and |
|
1279 z_data is not None): |
|
1280 |
|
1281 # Get common data length so that each value has an x, y |
|
1282 # and z coordinate |
1137 length = min(length, len(z_data)) |
1283 length = min(length, len(z_data)) |
|
1284 |
|
1285 # Add plot to canvas |
1138 self.Axes.plot(x_data[:, 1][:length], |
1286 self.Axes.plot(x_data[:, 1][:length], |
1139 y_data[:, 1][:length], |
1287 y_data[:, 1][:length], |
1140 zs = z_data[:, 1][:length]) |
1288 zs = z_data[:, 1][:length]) |
1141 self.Axes.set_zlim(z_min, z_max) |
1289 |
1142 if self.CursorTick is not None and start_tick <= self.CursorTick <= end_tick: |
1290 # Display cursor in canvas if a cursor tick is defined and |
|
1291 # it is include in values tick range |
|
1292 if (self.CursorTick is not None and |
|
1293 start_tick <= self.CursorTick <= end_tick): |
|
1294 |
|
1295 # Get Z coordinate for cursor |
|
1296 z_cursor, z_forced = items[2].GetValue( |
|
1297 self.CursorTick, raw=True) |
|
1298 |
|
1299 # Add 3 lines parallel to x, y and z axis to display |
|
1300 # cursor position in 3D |
1143 for kwargs in [{"xs": numpy.array([x_min, x_max])}, |
1301 for kwargs in [{"xs": numpy.array([x_min, x_max])}, |
1144 {"ys": numpy.array([y_min, y_max])}, |
1302 {"ys": numpy.array([y_min, y_max])}, |
1145 {"zs": numpy.array([z_min, z_max])}]: |
1303 {"zs": numpy.array([z_min, z_max])}]: |
1146 for param, value in [("xs", numpy.array([x_cursor, x_cursor])), |
1304 for param, value in [ |
1147 ("ys", numpy.array([y_cursor, y_cursor])), |
1305 ("xs", numpy.array([x_cursor, x_cursor])), |
1148 ("zs", numpy.array([z_cursor, z_cursor]))]: |
1306 ("ys", numpy.array([y_cursor, y_cursor])), |
|
1307 ("zs", numpy.array([z_cursor, z_cursor]))]: |
1149 kwargs.setdefault(param, value) |
1308 kwargs.setdefault(param, value) |
1150 kwargs["color"] = CURSOR_COLOR |
1309 kwargs["color"] = CURSOR_COLOR |
1151 self.Axes.plot(**kwargs) |
1310 self.Axes.plot(**kwargs) |
1152 |
1311 |
|
1312 # Set Z axis limits |
|
1313 self.Axes.set_zlim(z_min, z_max) |
|
1314 |
|
1315 # Set X and Y axis limits |
1153 self.Axes.set_xlim(x_min, x_max) |
1316 self.Axes.set_xlim(x_min, x_max) |
1154 self.Axes.set_ylim(y_min, y_max) |
1317 self.Axes.set_ylim(y_min, y_max) |
1155 |
1318 |
1156 variable_name_mask = self.ParentWindow.GetVariableNameMask() |
1319 # Get value and forced flag for each variable displayed in graph |
1157 if self.CursorTick is not None: |
1320 # If cursor tick is not defined get value and flag of last received |
1158 values, forced = apply(zip, [item.GetValue(self.CursorTick) for item in self.Items]) |
1321 # or get value and flag of variable at cursor tick |
1159 else: |
1322 values, forced = apply(zip, [ |
1160 values, forced = apply(zip, [(item.GetValue(), item.IsForced()) for item in self.Items]) |
1323 (item.GetValue(self.CursorTick) |
1161 labels = [item.GetVariable(variable_name_mask) for item in self.Items] |
1324 if self.CursorTick is not None |
|
1325 else (item.GetValue(), item.IsForced())) |
|
1326 for item in self.Items]) |
|
1327 |
|
1328 # Get path of each variable displayed simplified using panel variable |
|
1329 # name mask |
|
1330 labels = [item.GetVariable(self.ParentWindow.GetVariableNameMask()) |
|
1331 for item in self.Items] |
|
1332 |
|
1333 # Get style for each variable according to |
1162 styles = map(lambda x: {True: 'italic', False: 'normal'}[x], forced) |
1334 styles = map(lambda x: {True: 'italic', False: 'normal'}[x], forced) |
|
1335 |
|
1336 # Graph is orthogonal 3D, set variables path as 3D axis label |
1163 if self.Is3DCanvas(): |
1337 if self.Is3DCanvas(): |
1164 for idx, label_func in enumerate([self.Axes.set_xlabel, |
1338 for idx, label_func in enumerate([self.Axes.set_xlabel, |
1165 self.Axes.set_ylabel, |
1339 self.Axes.set_ylabel, |
1166 self.Axes.set_zlabel]): |
1340 self.Axes.set_zlabel]): |
1167 label_func(labels[idx], fontdict={'size': 'small','color': COLOR_CYCLE[idx]}) |
1341 label_func(labels[idx], fontdict={'size': 'small', |
|
1342 'color': COLOR_CYCLE[idx]}) |
|
1343 |
|
1344 # Graph is not orthogonal 3D, set variables path in axes labels |
1168 else: |
1345 else: |
1169 for label, text in zip(self.AxesLabels, labels): |
1346 for label, text in zip(self.AxesLabels, labels): |
1170 label.set_text(text) |
1347 label.set_text(text) |
|
1348 |
|
1349 # Set value label text and style according to value and forced flag for |
|
1350 # each variable displayed |
1171 for label, value, style in zip(self.Labels, values, styles): |
1351 for label, value, style in zip(self.Labels, values, styles): |
1172 label.set_text(value) |
1352 label.set_text(value) |
1173 label.set_style(style) |
1353 label.set_style(style) |
1174 |
1354 |
|
1355 # Refresh figure |
1175 self.draw() |
1356 self.draw() |
1176 |
1357 |
1177 def draw(self, drawDC=None): |
1358 def draw(self, drawDC=None): |
1178 """ |
1359 """ |
1179 Render the figure. |
1360 Render the figure. |