| Total Complexity | 185 |
| Total Lines | 980 |
| Duplicated Lines | 0 % |
Complex classes like glances.outputs._GlancesCurses often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
| 1 | # -*- coding: utf-8 -*- |
||
| 47 | class _GlancesCurses(object): |
||
| 48 | |||
| 49 | """This class manages the curses display (and key pressed). |
||
| 50 | |||
| 51 | Note: It is a private class, use GlancesCursesClient or GlancesCursesBrowser. |
||
| 52 | """ |
||
| 53 | |||
| 54 | def __init__(self, args=None): |
||
| 55 | # Init args |
||
| 56 | self.args = args |
||
| 57 | |||
| 58 | # Init windows positions |
||
| 59 | self.term_w = 80 |
||
| 60 | self.term_h = 24 |
||
| 61 | |||
| 62 | # Space between stats |
||
| 63 | self.space_between_column = 3 |
||
| 64 | self.space_between_line = 2 |
||
| 65 | |||
| 66 | # Init the curses screen |
||
| 67 | self.screen = curses.initscr() |
||
| 68 | if not self.screen: |
||
| 69 | logger.critical("Cannot init the curses library.\n") |
||
| 70 | sys.exit(1) |
||
| 71 | |||
| 72 | # Init cursor |
||
| 73 | self._init_cursor() |
||
| 74 | |||
| 75 | # Init the colors |
||
| 76 | self._init_colors() |
||
| 77 | |||
| 78 | # Init main window |
||
| 79 | self.term_window = self.screen.subwin(0, 0) |
||
| 80 | |||
| 81 | # Init refresh time |
||
| 82 | self.__refresh_time = args.time |
||
| 83 | |||
| 84 | # Init edit filter tag |
||
| 85 | self.edit_filter = False |
||
| 86 | |||
| 87 | # Init the process min/max reset |
||
| 88 | self.args.reset_minmax_tag = False |
||
| 89 | |||
| 90 | # Catch key pressed with non blocking mode |
||
| 91 | self.no_flash_cursor() |
||
| 92 | self.term_window.nodelay(1) |
||
| 93 | self.pressedkey = -1 |
||
| 94 | |||
| 95 | # History tag |
||
| 96 | self._init_history() |
||
| 97 | |||
| 98 | def _init_history(self): |
||
| 99 | '''Init the history option''' |
||
| 100 | |||
| 101 | self.reset_history_tag = False |
||
| 102 | self.history_tag = False |
||
| 103 | if self.args.enable_history: |
||
| 104 | logger.info('Stats history enabled with output path %s' % |
||
| 105 | self.args.path_history) |
||
| 106 | from glances.exports.glances_history import GlancesHistory |
||
| 107 | self.glances_history = GlancesHistory(self.args.path_history) |
||
| 108 | if not self.glances_history.graph_enabled(): |
||
| 109 | self.args.enable_history = False |
||
| 110 | logger.error( |
||
| 111 | 'Stats history disabled because MatPlotLib is not installed') |
||
| 112 | |||
| 113 | def _init_cursor(self): |
||
| 114 | '''Init cursors''' |
||
| 115 | |||
| 116 | if hasattr(curses, 'noecho'): |
||
| 117 | curses.noecho() |
||
| 118 | if hasattr(curses, 'cbreak'): |
||
| 119 | curses.cbreak() |
||
| 120 | self.set_cursor(0) |
||
| 121 | |||
| 122 | def _init_colors(self): |
||
| 123 | '''Init the Curses color layout''' |
||
| 124 | |||
| 125 | # Set curses options |
||
| 126 | if hasattr(curses, 'start_color'): |
||
| 127 | curses.start_color() |
||
| 128 | if hasattr(curses, 'use_default_colors'): |
||
| 129 | curses.use_default_colors() |
||
| 130 | |||
| 131 | # Init colors |
||
| 132 | if self.args.disable_bold: |
||
| 133 | A_BOLD = 0 |
||
| 134 | self.args.disable_bg = True |
||
| 135 | else: |
||
| 136 | A_BOLD = curses.A_BOLD |
||
| 137 | |||
| 138 | self.title_color = A_BOLD |
||
| 139 | self.title_underline_color = A_BOLD | curses.A_UNDERLINE |
||
| 140 | self.help_color = A_BOLD |
||
| 141 | |||
| 142 | if curses.has_colors(): |
||
| 143 | # The screen is compatible with a colored design |
||
| 144 | if self.args.theme_white: |
||
| 145 | # White theme: black ==> white |
||
| 146 | curses.init_pair(1, curses.COLOR_BLACK, -1) |
||
| 147 | else: |
||
| 148 | curses.init_pair(1, curses.COLOR_WHITE, -1) |
||
| 149 | if self.args.disable_bg: |
||
| 150 | curses.init_pair(2, curses.COLOR_RED, -1) |
||
| 151 | curses.init_pair(3, curses.COLOR_GREEN, -1) |
||
| 152 | curses.init_pair(4, curses.COLOR_BLUE, -1) |
||
| 153 | curses.init_pair(5, curses.COLOR_MAGENTA, -1) |
||
| 154 | else: |
||
| 155 | curses.init_pair(2, curses.COLOR_WHITE, curses.COLOR_RED) |
||
| 156 | curses.init_pair(3, curses.COLOR_WHITE, curses.COLOR_GREEN) |
||
| 157 | curses.init_pair(4, curses.COLOR_WHITE, curses.COLOR_BLUE) |
||
| 158 | curses.init_pair(5, curses.COLOR_WHITE, curses.COLOR_MAGENTA) |
||
| 159 | curses.init_pair(6, curses.COLOR_RED, -1) |
||
| 160 | curses.init_pair(7, curses.COLOR_GREEN, -1) |
||
| 161 | curses.init_pair(8, curses.COLOR_BLUE, -1) |
||
| 162 | |||
| 163 | # Colors text styles |
||
| 164 | if curses.COLOR_PAIRS > 8: |
||
| 165 | try: |
||
| 166 | curses.init_pair(9, curses.COLOR_MAGENTA, -1) |
||
| 167 | except Exception: |
||
| 168 | if self.args.theme_white: |
||
| 169 | curses.init_pair(9, curses.COLOR_BLACK, -1) |
||
| 170 | else: |
||
| 171 | curses.init_pair(9, curses.COLOR_WHITE, -1) |
||
| 172 | try: |
||
| 173 | curses.init_pair(10, curses.COLOR_CYAN, -1) |
||
| 174 | except Exception: |
||
| 175 | if self.args.theme_white: |
||
| 176 | curses.init_pair(10, curses.COLOR_BLACK, -1) |
||
| 177 | else: |
||
| 178 | curses.init_pair(10, curses.COLOR_WHITE, -1) |
||
| 179 | |||
| 180 | self.ifWARNING_color2 = curses.color_pair(9) | A_BOLD |
||
| 181 | self.ifCRITICAL_color2 = curses.color_pair(6) | A_BOLD |
||
| 182 | self.filter_color = curses.color_pair(10) | A_BOLD |
||
| 183 | |||
| 184 | self.no_color = curses.color_pair(1) |
||
| 185 | self.default_color = curses.color_pair(3) | A_BOLD |
||
| 186 | self.nice_color = curses.color_pair(9) | A_BOLD |
||
| 187 | self.cpu_time_color = curses.color_pair(9) | A_BOLD |
||
| 188 | self.ifCAREFUL_color = curses.color_pair(4) | A_BOLD |
||
| 189 | self.ifWARNING_color = curses.color_pair(5) | A_BOLD |
||
| 190 | self.ifCRITICAL_color = curses.color_pair(2) | A_BOLD |
||
| 191 | self.default_color2 = curses.color_pair(7) | A_BOLD |
||
| 192 | self.ifCAREFUL_color2 = curses.color_pair(8) | A_BOLD |
||
| 193 | |||
| 194 | else: |
||
| 195 | # The screen is NOT compatible with a colored design |
||
| 196 | # switch to B&W text styles |
||
| 197 | self.no_color = curses.A_NORMAL |
||
| 198 | self.default_color = curses.A_NORMAL |
||
| 199 | self.nice_color = A_BOLD |
||
| 200 | self.cpu_time_color = A_BOLD |
||
| 201 | self.ifCAREFUL_color = curses.A_UNDERLINE |
||
| 202 | self.ifWARNING_color = A_BOLD |
||
| 203 | self.ifCRITICAL_color = curses.A_REVERSE |
||
| 204 | self.default_color2 = curses.A_NORMAL |
||
| 205 | self.ifCAREFUL_color2 = curses.A_UNDERLINE |
||
| 206 | self.ifWARNING_color2 = A_BOLD |
||
| 207 | self.ifCRITICAL_color2 = curses.A_REVERSE |
||
| 208 | self.filter_color = A_BOLD |
||
| 209 | |||
| 210 | # Define the colors list (hash table) for stats |
||
| 211 | self.colors_list = { |
||
| 212 | 'DEFAULT': self.no_color, |
||
| 213 | 'UNDERLINE': curses.A_UNDERLINE, |
||
| 214 | 'BOLD': A_BOLD, |
||
| 215 | 'SORT': A_BOLD, |
||
| 216 | 'OK': self.default_color2, |
||
| 217 | 'FILTER': self.filter_color, |
||
| 218 | 'TITLE': self.title_color, |
||
| 219 | 'PROCESS': self.default_color2, |
||
| 220 | 'STATUS': self.default_color2, |
||
| 221 | 'NICE': self.nice_color, |
||
| 222 | 'CPU_TIME': self.cpu_time_color, |
||
| 223 | 'CAREFUL': self.ifCAREFUL_color2, |
||
| 224 | 'WARNING': self.ifWARNING_color2, |
||
| 225 | 'CRITICAL': self.ifCRITICAL_color2, |
||
| 226 | 'OK_LOG': self.default_color, |
||
| 227 | 'CAREFUL_LOG': self.ifCAREFUL_color, |
||
| 228 | 'WARNING_LOG': self.ifWARNING_color, |
||
| 229 | 'CRITICAL_LOG': self.ifCRITICAL_color, |
||
| 230 | 'PASSWORD': curses.A_PROTECT |
||
| 231 | } |
||
| 232 | |||
| 233 | def flash_cursor(self): |
||
| 234 | self.term_window.keypad(1) |
||
| 235 | |||
| 236 | def no_flash_cursor(self): |
||
| 237 | self.term_window.keypad(0) |
||
| 238 | |||
| 239 | def set_cursor(self, value): |
||
| 240 | """Configure the curse cursor apparence. |
||
| 241 | |||
| 242 | 0: invisible |
||
| 243 | 1: visible |
||
| 244 | 2: very visible |
||
| 245 | """ |
||
| 246 | if hasattr(curses, 'curs_set'): |
||
| 247 | try: |
||
| 248 | curses.curs_set(value) |
||
| 249 | except Exception: |
||
| 250 | pass |
||
| 251 | |||
| 252 | def get_key(self, window): |
||
| 253 | # Catch ESC key AND numlock key (issue #163) |
||
| 254 | keycode = [0, 0] |
||
| 255 | keycode[0] = window.getch() |
||
| 256 | keycode[1] = window.getch() |
||
| 257 | |||
| 258 | if keycode != [-1, -1]: |
||
| 259 | logger.debug("Keypressed (code: %s)" % keycode) |
||
| 260 | |||
| 261 | if keycode[0] == 27 and keycode[1] != -1: |
||
| 262 | # Do not escape on specials keys |
||
| 263 | return -1 |
||
| 264 | else: |
||
| 265 | return keycode[0] |
||
| 266 | |||
| 267 | def __catch_key(self, return_to_browser=False): |
||
| 268 | # Catch the pressed key |
||
| 269 | self.pressedkey = self.get_key(self.term_window) |
||
| 270 | |||
| 271 | # Actions... |
||
| 272 | if self.pressedkey == ord('\x1b') or self.pressedkey == ord('q'): |
||
| 273 | # 'ESC'|'q' > Quit |
||
| 274 | if return_to_browser: |
||
| 275 | logger.info("Stop Glances client and return to the browser") |
||
| 276 | else: |
||
| 277 | self.end() |
||
| 278 | logger.info("Stop Glances") |
||
| 279 | sys.exit(0) |
||
| 280 | elif self.pressedkey == 10: |
||
| 281 | # 'ENTER' > Edit the process filter |
||
| 282 | self.edit_filter = not self.edit_filter |
||
| 283 | elif self.pressedkey == ord('0'): |
||
| 284 | # '0' > Switch between IRIX and Solaris mode |
||
| 285 | self.args.disable_irix = not self.args.disable_irix |
||
| 286 | elif self.pressedkey == ord('1'): |
||
| 287 | # '1' > Switch between CPU and PerCPU information |
||
| 288 | self.args.percpu = not self.args.percpu |
||
| 289 | elif self.pressedkey == ord('2'): |
||
| 290 | # '2' > Enable/disable left sidebar |
||
| 291 | self.args.disable_left_sidebar = not self.args.disable_left_sidebar |
||
| 292 | elif self.pressedkey == ord('3'): |
||
| 293 | # '3' > Enable/disable quicklook |
||
| 294 | self.args.disable_quicklook = not self.args.disable_quicklook |
||
| 295 | elif self.pressedkey == ord('4'): |
||
| 296 | # '4' > Enable/disable all but quick look and load |
||
| 297 | self.args.full_quicklook = not self.args.full_quicklook |
||
| 298 | if self.args.full_quicklook: |
||
| 299 | self.args.disable_quicklook = False |
||
| 300 | self.args.disable_cpu = True |
||
| 301 | self.args.disable_mem = True |
||
| 302 | self.args.disable_swap = True |
||
| 303 | else: |
||
| 304 | self.args.disable_quicklook = False |
||
| 305 | self.args.disable_cpu = False |
||
| 306 | self.args.disable_mem = False |
||
| 307 | self.args.disable_swap = False |
||
| 308 | elif self.pressedkey == ord('5'): |
||
| 309 | # '5' > Enable/disable top menu |
||
| 310 | logger.info(self.args.disable_top) |
||
| 311 | self.args.disable_top = not self.args.disable_top |
||
| 312 | if self.args.disable_top: |
||
| 313 | self.args.disable_quicklook = True |
||
| 314 | self.args.disable_cpu = True |
||
| 315 | self.args.disable_mem = True |
||
| 316 | self.args.disable_swap = True |
||
| 317 | self.args.disable_load = True |
||
| 318 | else: |
||
| 319 | self.args.disable_quicklook = False |
||
| 320 | self.args.disable_cpu = False |
||
| 321 | self.args.disable_mem = False |
||
| 322 | self.args.disable_swap = False |
||
| 323 | self.args.disable_load = False |
||
| 324 | elif self.pressedkey == ord('/'): |
||
| 325 | # '/' > Switch between short/long name for processes |
||
| 326 | self.args.process_short_name = not self.args.process_short_name |
||
| 327 | elif self.pressedkey == ord('a'): |
||
| 328 | # 'a' > Sort processes automatically and reset to 'cpu_percent' |
||
| 329 | glances_processes.auto_sort = True |
||
| 330 | glances_processes.sort_key = 'cpu_percent' |
||
| 331 | elif self.pressedkey == ord('b'): |
||
| 332 | # 'b' > Switch between bit/s and Byte/s for network IO |
||
| 333 | self.args.byte = not self.args.byte |
||
| 334 | elif self.pressedkey == ord('B'): |
||
| 335 | # 'B' > Switch between bit/s and IO/s for Disk IO |
||
| 336 | self.args.diskio_iops = not self.args.diskio_iops |
||
| 337 | elif self.pressedkey == ord('c'): |
||
| 338 | # 'c' > Sort processes by CPU usage |
||
| 339 | glances_processes.auto_sort = False |
||
| 340 | glances_processes.sort_key = 'cpu_percent' |
||
| 341 | elif self.pressedkey == ord('d'): |
||
| 342 | # 'd' > Show/hide disk I/O stats |
||
| 343 | self.args.disable_diskio = not self.args.disable_diskio |
||
| 344 | elif self.pressedkey == ord('D'): |
||
| 345 | # 'D' > Show/hide Docker stats |
||
| 346 | self.args.disable_docker = not self.args.disable_docker |
||
| 347 | elif self.pressedkey == ord('e'): |
||
| 348 | # 'e' > Enable/Disable extended stats for top process |
||
| 349 | self.args.enable_process_extended = not self.args.enable_process_extended |
||
| 350 | if not self.args.enable_process_extended: |
||
| 351 | glances_processes.disable_extended() |
||
| 352 | else: |
||
| 353 | glances_processes.enable_extended() |
||
| 354 | elif self.pressedkey == ord('E'): |
||
| 355 | # 'E' > Erase the process filter |
||
| 356 | logger.info("Erase process filter") |
||
| 357 | glances_processes.process_filter = None |
||
| 358 | elif self.pressedkey == ord('F'): |
||
| 359 | # 'F' > Switch between FS available and free space |
||
| 360 | self.args.fs_free_space = not self.args.fs_free_space |
||
| 361 | elif self.pressedkey == ord('f'): |
||
| 362 | # 'f' > Show/hide fs / folder stats |
||
| 363 | self.args.disable_fs = not self.args.disable_fs |
||
| 364 | self.args.disable_folder = not self.args.disable_folder |
||
| 365 | elif self.pressedkey == ord('g'): |
||
| 366 | # 'g' > History |
||
| 367 | self.history_tag = not self.history_tag |
||
| 368 | elif self.pressedkey == ord('h'): |
||
| 369 | # 'h' > Show/hide help |
||
| 370 | self.args.help_tag = not self.args.help_tag |
||
| 371 | elif self.pressedkey == ord('i'): |
||
| 372 | # 'i' > Sort processes by IO rate (not available on OS X) |
||
| 373 | glances_processes.auto_sort = False |
||
| 374 | glances_processes.sort_key = 'io_counters' |
||
| 375 | elif self.pressedkey == ord('I'): |
||
| 376 | # 'I' > Show/hide IP module |
||
| 377 | self.args.disable_ip = not self.args.disable_ip |
||
| 378 | elif self.pressedkey == ord('l'): |
||
| 379 | # 'l' > Show/hide log messages |
||
| 380 | self.args.disable_log = not self.args.disable_log |
||
| 381 | elif self.pressedkey == ord('m'): |
||
| 382 | # 'm' > Sort processes by MEM usage |
||
| 383 | glances_processes.auto_sort = False |
||
| 384 | glances_processes.sort_key = 'memory_percent' |
||
| 385 | elif self.pressedkey == ord('M'): |
||
| 386 | # 'M' > Reset processes summary min/max |
||
| 387 | self.args.reset_minmax_tag = not self.args.reset_minmax_tag |
||
| 388 | elif self.pressedkey == ord('n'): |
||
| 389 | # 'n' > Show/hide network stats |
||
| 390 | self.args.disable_network = not self.args.disable_network |
||
| 391 | elif self.pressedkey == ord('p'): |
||
| 392 | # 'p' > Sort processes by name |
||
| 393 | glances_processes.auto_sort = False |
||
| 394 | glances_processes.sort_key = 'name' |
||
| 395 | elif self.pressedkey == ord('r'): |
||
| 396 | # 'r' > Reset history |
||
| 397 | self.reset_history_tag = not self.reset_history_tag |
||
| 398 | elif self.pressedkey == ord('R'): |
||
| 399 | # 'R' > Hide RAID plugins |
||
| 400 | self.args.disable_raid = not self.args.disable_raid |
||
| 401 | elif self.pressedkey == ord('s'): |
||
| 402 | # 's' > Show/hide sensors stats (Linux-only) |
||
| 403 | self.args.disable_sensors = not self.args.disable_sensors |
||
| 404 | elif self.pressedkey == ord('t'): |
||
| 405 | # 't' > Sort processes by TIME usage |
||
| 406 | glances_processes.auto_sort = False |
||
| 407 | glances_processes.sort_key = 'cpu_times' |
||
| 408 | elif self.pressedkey == ord('T'): |
||
| 409 | # 'T' > View network traffic as sum Rx+Tx |
||
| 410 | self.args.network_sum = not self.args.network_sum |
||
| 411 | elif self.pressedkey == ord('u'): |
||
| 412 | # 'u' > Sort processes by USER |
||
| 413 | glances_processes.auto_sort = False |
||
| 414 | glances_processes.sort_key = 'username' |
||
| 415 | elif self.pressedkey == ord('U'): |
||
| 416 | # 'U' > View cumulative network I/O (instead of bitrate) |
||
| 417 | self.args.network_cumul = not self.args.network_cumul |
||
| 418 | elif self.pressedkey == ord('w'): |
||
| 419 | # 'w' > Delete finished warning logs |
||
| 420 | glances_logs.clean() |
||
| 421 | elif self.pressedkey == ord('x'): |
||
| 422 | # 'x' > Delete finished warning and critical logs |
||
| 423 | glances_logs.clean(critical=True) |
||
| 424 | elif self.pressedkey == ord('z'): |
||
| 425 | # 'z' > Enable/Disable processes stats (count + list + monitor) |
||
| 426 | # Enable/Disable display |
||
| 427 | self.args.disable_process = not self.args.disable_process |
||
| 428 | # Enable/Disable update |
||
| 429 | if self.args.disable_process: |
||
| 430 | glances_processes.disable() |
||
| 431 | else: |
||
| 432 | glances_processes.enable() |
||
| 433 | # Return the key code |
||
| 434 | return self.pressedkey |
||
| 435 | |||
| 436 | def end(self): |
||
| 437 | """Shutdown the curses window.""" |
||
| 438 | if hasattr(curses, 'echo'): |
||
| 439 | curses.echo() |
||
| 440 | if hasattr(curses, 'nocbreak'): |
||
| 441 | curses.nocbreak() |
||
| 442 | if hasattr(curses, 'curs_set'): |
||
| 443 | try: |
||
| 444 | curses.curs_set(1) |
||
| 445 | except Exception: |
||
| 446 | pass |
||
| 447 | curses.endwin() |
||
| 448 | |||
| 449 | def init_line_column(self): |
||
| 450 | """Init the line and column position for the curses inteface.""" |
||
| 451 | self.init_line() |
||
| 452 | self.init_column() |
||
| 453 | |||
| 454 | def init_line(self): |
||
| 455 | """Init the line position for the curses inteface.""" |
||
| 456 | self.line = 0 |
||
| 457 | self.next_line = 0 |
||
| 458 | |||
| 459 | def init_column(self): |
||
| 460 | """Init the column position for the curses inteface.""" |
||
| 461 | self.column = 0 |
||
| 462 | self.next_column = 0 |
||
| 463 | |||
| 464 | def new_line(self): |
||
| 465 | """New line in the curses interface.""" |
||
| 466 | self.line = self.next_line |
||
| 467 | |||
| 468 | def new_column(self): |
||
| 469 | """New column in the curses interface.""" |
||
| 470 | self.column = self.next_column |
||
| 471 | |||
| 472 | def display(self, stats, cs_status=None): |
||
| 473 | """Display stats on the screen. |
||
| 474 | |||
| 475 | stats: Stats database to display |
||
| 476 | cs_status: |
||
| 477 | "None": standalone or server mode |
||
| 478 | "Connected": Client is connected to a Glances server |
||
| 479 | "SNMP": Client is connected to a SNMP server |
||
| 480 | "Disconnected": Client is disconnected from the server |
||
| 481 | |||
| 482 | Return: |
||
| 483 | True if the stats have been displayed |
||
| 484 | False if the help have been displayed |
||
| 485 | """ |
||
| 486 | # Init the internal line/column for Glances Curses |
||
| 487 | self.init_line_column() |
||
| 488 | |||
| 489 | # Get the screen size |
||
| 490 | screen_x = self.screen.getmaxyx()[1] |
||
| 491 | screen_y = self.screen.getmaxyx()[0] |
||
| 492 | |||
| 493 | # No processes list in SNMP mode |
||
| 494 | if cs_status == 'SNMP': |
||
| 495 | # so... more space for others plugins |
||
| 496 | plugin_max_width = 43 |
||
| 497 | else: |
||
| 498 | plugin_max_width = None |
||
| 499 | |||
| 500 | # Update the stats messages |
||
| 501 | ########################### |
||
| 502 | |||
| 503 | # Update the client server status |
||
| 504 | self.args.cs_status = cs_status |
||
| 505 | stats_system = stats.get_plugin( |
||
| 506 | 'system').get_stats_display(args=self.args) |
||
| 507 | stats_uptime = stats.get_plugin('uptime').get_stats_display() |
||
| 508 | if self.args.percpu: |
||
| 509 | stats_cpu = stats.get_plugin('percpu').get_stats_display(args=self.args) |
||
| 510 | else: |
||
| 511 | stats_cpu = stats.get_plugin('cpu').get_stats_display(args=self.args) |
||
| 512 | stats_load = stats.get_plugin('load').get_stats_display(args=self.args) |
||
| 513 | stats_mem = stats.get_plugin('mem').get_stats_display(args=self.args) |
||
| 514 | stats_memswap = stats.get_plugin('memswap').get_stats_display(args=self.args) |
||
| 515 | stats_network = stats.get_plugin('network').get_stats_display( |
||
| 516 | args=self.args, max_width=plugin_max_width) |
||
| 517 | try: |
||
| 518 | stats_ip = stats.get_plugin('ip').get_stats_display(args=self.args) |
||
| 519 | except AttributeError: |
||
| 520 | stats_ip = None |
||
| 521 | stats_diskio = stats.get_plugin( |
||
| 522 | 'diskio').get_stats_display(args=self.args) |
||
| 523 | stats_fs = stats.get_plugin('fs').get_stats_display( |
||
| 524 | args=self.args, max_width=plugin_max_width) |
||
| 525 | stats_folders = stats.get_plugin('folders').get_stats_display( |
||
| 526 | args=self.args, max_width=plugin_max_width) |
||
| 527 | stats_raid = stats.get_plugin('raid').get_stats_display( |
||
| 528 | args=self.args) |
||
| 529 | stats_sensors = stats.get_plugin( |
||
| 530 | 'sensors').get_stats_display(args=self.args) |
||
| 531 | stats_now = stats.get_plugin('now').get_stats_display() |
||
| 532 | stats_docker = stats.get_plugin('docker').get_stats_display( |
||
| 533 | args=self.args) |
||
| 534 | stats_processcount = stats.get_plugin( |
||
| 535 | 'processcount').get_stats_display(args=self.args) |
||
| 536 | stats_monitor = stats.get_plugin( |
||
| 537 | 'monitor').get_stats_display(args=self.args) |
||
| 538 | stats_alert = stats.get_plugin( |
||
| 539 | 'alert').get_stats_display(args=self.args) |
||
| 540 | |||
| 541 | # Adapt number of processes to the available space |
||
| 542 | max_processes_displayed = screen_y - 11 - \ |
||
| 543 | self.get_stats_display_height(stats_alert) - \ |
||
| 544 | self.get_stats_display_height(stats_docker) |
||
| 545 | try: |
||
| 546 | if self.args.enable_process_extended and not self.args.process_tree: |
||
| 547 | max_processes_displayed -= 4 |
||
| 548 | except AttributeError: |
||
| 549 | pass |
||
| 550 | if max_processes_displayed < 0: |
||
| 551 | max_processes_displayed = 0 |
||
| 552 | if (glances_processes.max_processes is None or |
||
| 553 | glances_processes.max_processes != max_processes_displayed): |
||
| 554 | logger.debug("Set number of displayed processes to {0}".format(max_processes_displayed)) |
||
| 555 | glances_processes.max_processes = max_processes_displayed |
||
| 556 | |||
| 557 | stats_processlist = stats.get_plugin( |
||
| 558 | 'processlist').get_stats_display(args=self.args) |
||
| 559 | |||
| 560 | # Display the stats on the curses interface |
||
| 561 | ########################################### |
||
| 562 | |||
| 563 | # Help screen (on top of the other stats) |
||
| 564 | if self.args.help_tag: |
||
| 565 | # Display the stats... |
||
| 566 | self.display_plugin( |
||
| 567 | stats.get_plugin('help').get_stats_display(args=self.args)) |
||
| 568 | # ... and exit |
||
| 569 | return False |
||
| 570 | |||
| 571 | # ================================== |
||
| 572 | # Display first line (system+uptime) |
||
| 573 | # ================================== |
||
| 574 | # Space between column |
||
| 575 | self.space_between_column = 0 |
||
| 576 | self.new_line() |
||
| 577 | l_uptime = self.get_stats_display_width( |
||
| 578 | stats_system) + self.space_between_column + self.get_stats_display_width(stats_ip) + 3 + self.get_stats_display_width(stats_uptime) |
||
| 579 | self.display_plugin( |
||
| 580 | stats_system, display_optional=(screen_x >= l_uptime)) |
||
| 581 | self.new_column() |
||
| 582 | self.display_plugin(stats_ip) |
||
| 583 | # Space between column |
||
| 584 | self.space_between_column = 3 |
||
| 585 | self.new_column() |
||
| 586 | self.display_plugin(stats_uptime) |
||
| 587 | |||
| 588 | # ======================================================== |
||
| 589 | # Display second line (<SUMMARY>+CPU|PERCPU+LOAD+MEM+SWAP) |
||
| 590 | # ======================================================== |
||
| 591 | self.init_column() |
||
| 592 | self.new_line() |
||
| 593 | |||
| 594 | # Init quicklook |
||
| 595 | stats_quicklook = {'msgdict': []} |
||
| 596 | quicklook_width = 0 |
||
| 597 | |||
| 598 | # Get stats for CPU, MEM, SWAP and LOAD (if needed) |
||
| 599 | if self.args.disable_cpu: |
||
| 600 | cpu_width = 0 |
||
| 601 | else: |
||
| 602 | cpu_width = self.get_stats_display_width(stats_cpu) |
||
| 603 | if self.args.disable_mem: |
||
| 604 | mem_width = 0 |
||
| 605 | else: |
||
| 606 | mem_width = self.get_stats_display_width(stats_mem) |
||
| 607 | if self.args.disable_swap: |
||
| 608 | swap_width = 0 |
||
| 609 | else: |
||
| 610 | swap_width = self.get_stats_display_width(stats_memswap) |
||
| 611 | if self.args.disable_load: |
||
| 612 | load_width = 0 |
||
| 613 | else: |
||
| 614 | load_width = self.get_stats_display_width(stats_load) |
||
| 615 | |||
| 616 | # Size of plugins but quicklook |
||
| 617 | stats_width = cpu_width + mem_width + swap_width + load_width |
||
| 618 | |||
| 619 | # Number of plugin but quicklook |
||
| 620 | stats_number = ( |
||
| 621 | int(not self.args.disable_cpu and stats_cpu['msgdict'] != []) + |
||
| 622 | int(not self.args.disable_mem and stats_mem['msgdict'] != []) + |
||
| 623 | int(not self.args.disable_swap and stats_memswap['msgdict'] != []) + |
||
| 624 | int(not self.args.disable_load and stats_load['msgdict'] != [])) |
||
| 625 | |||
| 626 | if not self.args.disable_quicklook: |
||
| 627 | # Quick look is in the place ! |
||
| 628 | if self.args.full_quicklook: |
||
| 629 | quicklook_width = screen_x - (stats_width + 8 + stats_number * self.space_between_column) |
||
| 630 | else: |
||
| 631 | quicklook_width = min(screen_x - (stats_width + 8 + stats_number * self.space_between_column), 79) |
||
| 632 | try: |
||
| 633 | stats_quicklook = stats.get_plugin( |
||
| 634 | 'quicklook').get_stats_display(max_width=quicklook_width, args=self.args) |
||
| 635 | except AttributeError as e: |
||
| 636 | logger.debug("Quicklook plugin not available (%s)" % e) |
||
| 637 | else: |
||
| 638 | quicklook_width = self.get_stats_display_width(stats_quicklook) |
||
| 639 | stats_width += quicklook_width + 1 |
||
| 640 | self.space_between_column = 1 |
||
| 641 | self.display_plugin(stats_quicklook) |
||
| 642 | self.new_column() |
||
| 643 | |||
| 644 | # Compute spaces between plugins |
||
| 645 | # Note: Only one space between Quicklook and others |
||
| 646 | display_optional_cpu = True |
||
| 647 | display_optional_mem = True |
||
| 648 | if stats_number > 1: |
||
| 649 | self.space_between_column = max(1, int((screen_x - stats_width) / (stats_number - 1))) |
||
| 650 | # No space ? Remove optionnal MEM stats |
||
| 651 | if self.space_between_column < 3: |
||
| 652 | display_optional_mem = False |
||
| 653 | if self.args.disable_mem: |
||
| 654 | mem_width = 0 |
||
| 655 | else: |
||
| 656 | mem_width = self.get_stats_display_width(stats_mem, without_option=True) |
||
| 657 | stats_width = quicklook_width + 1 + cpu_width + mem_width + swap_width + load_width |
||
| 658 | self.space_between_column = max(1, int((screen_x - stats_width) / (stats_number - 1))) |
||
| 659 | # No space again ? Remove optionnal CPU stats |
||
| 660 | if self.space_between_column < 3: |
||
| 661 | display_optional_cpu = False |
||
| 662 | if self.args.disable_cpu: |
||
| 663 | cpu_width = 0 |
||
| 664 | else: |
||
| 665 | cpu_width = self.get_stats_display_width(stats_cpu, without_option=True) |
||
| 666 | stats_width = quicklook_width + 1 + cpu_width + mem_width + swap_width + load_width |
||
| 667 | self.space_between_column = max(1, int((screen_x - stats_width) / (stats_number - 1))) |
||
| 668 | else: |
||
| 669 | self.space_between_column = 0 |
||
| 670 | |||
| 671 | # Display CPU, MEM, SWAP and LOAD |
||
| 672 | self.display_plugin(stats_cpu, display_optional=display_optional_cpu) |
||
| 673 | self.new_column() |
||
| 674 | self.display_plugin(stats_mem, display_optional=display_optional_mem) |
||
| 675 | self.new_column() |
||
| 676 | self.display_plugin(stats_memswap) |
||
| 677 | self.new_column() |
||
| 678 | self.display_plugin(stats_load) |
||
| 679 | |||
| 680 | # Space between column |
||
| 681 | self.space_between_column = 3 |
||
| 682 | |||
| 683 | # Backup line position |
||
| 684 | self.saved_line = self.next_line |
||
| 685 | |||
| 686 | # ================================================================== |
||
| 687 | # Display left sidebar (NETWORK+DISKIO+FS+SENSORS+Current time) |
||
| 688 | # ================================================================== |
||
| 689 | self.init_column() |
||
| 690 | if not (self.args.disable_network and |
||
| 691 | self.args.disable_diskio and |
||
| 692 | self.args.disable_fs and |
||
| 693 | self.args.disable_folder and |
||
| 694 | self.args.disable_raid and |
||
| 695 | self.args.disable_sensors) and not self.args.disable_left_sidebar: |
||
| 696 | self.new_line() |
||
| 697 | self.display_plugin(stats_network) |
||
| 698 | self.new_line() |
||
| 699 | self.display_plugin(stats_diskio) |
||
| 700 | self.new_line() |
||
| 701 | self.display_plugin(stats_fs) |
||
| 702 | self.new_line() |
||
| 703 | self.display_plugin(stats_folders) |
||
| 704 | self.new_line() |
||
| 705 | self.display_plugin(stats_raid) |
||
| 706 | self.new_line() |
||
| 707 | self.display_plugin(stats_sensors) |
||
| 708 | self.new_line() |
||
| 709 | self.display_plugin(stats_now) |
||
| 710 | |||
| 711 | # ==================================== |
||
| 712 | # Display right stats (process and co) |
||
| 713 | # ==================================== |
||
| 714 | # If space available... |
||
| 715 | if screen_x > 52: |
||
| 716 | # Restore line position |
||
| 717 | self.next_line = self.saved_line |
||
| 718 | |||
| 719 | # Display right sidebar |
||
| 720 | # ((DOCKER)+PROCESS_COUNT+(MONITORED)+PROCESS_LIST+ALERT) |
||
| 721 | self.new_column() |
||
| 722 | self.new_line() |
||
| 723 | self.display_plugin(stats_docker) |
||
| 724 | self.new_line() |
||
| 725 | self.display_plugin(stats_processcount) |
||
| 726 | if glances_processes.process_filter is None and cs_status is None: |
||
| 727 | # Do not display stats monitor list if a filter exist |
||
| 728 | self.new_line() |
||
| 729 | self.display_plugin(stats_monitor) |
||
| 730 | self.new_line() |
||
| 731 | self.display_plugin(stats_processlist, |
||
| 732 | display_optional=(screen_x > 102), |
||
| 733 | display_additional=(not OSX), |
||
| 734 | max_y=(screen_y - self.get_stats_display_height(stats_alert) - 2)) |
||
| 735 | self.new_line() |
||
| 736 | self.display_plugin(stats_alert) |
||
| 737 | |||
| 738 | # History option |
||
| 739 | # Generate history graph |
||
| 740 | if self.history_tag and self.args.enable_history: |
||
| 741 | self.display_popup( |
||
| 742 | 'Generate graphs history in {0}\nPlease wait...'.format( |
||
| 743 | self.glances_history.get_output_folder())) |
||
| 744 | self.display_popup( |
||
| 745 | 'Generate graphs history in {0}\nDone: {1} graphs generated'.format( |
||
| 746 | self.glances_history.get_output_folder(), |
||
| 747 | self.glances_history.generate_graph(stats))) |
||
| 748 | elif self.reset_history_tag and self.args.enable_history: |
||
| 749 | self.display_popup('Reset history') |
||
| 750 | self.glances_history.reset(stats) |
||
| 751 | elif (self.history_tag or self.reset_history_tag) and not self.args.enable_history: |
||
| 752 | try: |
||
| 753 | self.glances_history.graph_enabled() |
||
| 754 | except Exception: |
||
| 755 | self.display_popup('History disabled\nEnable it using --enable-history') |
||
| 756 | else: |
||
| 757 | self.display_popup('History disabled\nPlease install matplotlib') |
||
| 758 | self.history_tag = False |
||
| 759 | self.reset_history_tag = False |
||
| 760 | |||
| 761 | # Display edit filter popup |
||
| 762 | # Only in standalone mode (cs_status is None) |
||
| 763 | if self.edit_filter and cs_status is None: |
||
| 764 | new_filter = self.display_popup( |
||
| 765 | 'Process filter pattern: ', is_input=True, |
||
| 766 | input_value=glances_processes.process_filter) |
||
| 767 | glances_processes.process_filter = new_filter |
||
| 768 | elif self.edit_filter and cs_status != 'None': |
||
| 769 | self.display_popup('Process filter only available in standalone mode') |
||
| 770 | self.edit_filter = False |
||
| 771 | |||
| 772 | return True |
||
| 773 | |||
| 774 | def display_popup(self, message, |
||
| 775 | size_x=None, size_y=None, |
||
| 776 | duration=3, |
||
| 777 | is_input=False, |
||
| 778 | input_size=30, |
||
| 779 | input_value=None): |
||
| 780 | """ |
||
| 781 | Display a centered popup. |
||
| 782 | |||
| 783 | If is_input is False: |
||
| 784 | Display a centered popup with the given message during duration seconds |
||
| 785 | If size_x and size_y: set the popup size |
||
| 786 | else set it automatically |
||
| 787 | Return True if the popup could be displayed |
||
| 788 | |||
| 789 | If is_input is True: |
||
| 790 | Display a centered popup with the given message and a input field |
||
| 791 | If size_x and size_y: set the popup size |
||
| 792 | else set it automatically |
||
| 793 | Return the input string or None if the field is empty |
||
| 794 | """ |
||
| 795 | # Center the popup |
||
| 796 | sentence_list = message.split('\n') |
||
| 797 | if size_x is None: |
||
| 798 | size_x = len(max(sentence_list, key=len)) + 4 |
||
| 799 | # Add space for the input field |
||
| 800 | if is_input: |
||
| 801 | size_x += input_size |
||
| 802 | if size_y is None: |
||
| 803 | size_y = len(sentence_list) + 4 |
||
| 804 | screen_x = self.screen.getmaxyx()[1] |
||
| 805 | screen_y = self.screen.getmaxyx()[0] |
||
| 806 | if size_x > screen_x or size_y > screen_y: |
||
| 807 | # No size to display the popup => abord |
||
| 808 | return False |
||
| 809 | pos_x = int((screen_x - size_x) / 2) |
||
| 810 | pos_y = int((screen_y - size_y) / 2) |
||
| 811 | |||
| 812 | # Create the popup |
||
| 813 | popup = curses.newwin(size_y, size_x, pos_y, pos_x) |
||
| 814 | |||
| 815 | # Fill the popup |
||
| 816 | popup.border() |
||
| 817 | |||
| 818 | # Add the message |
||
| 819 | for y, m in enumerate(message.split('\n')): |
||
| 820 | popup.addnstr(2 + y, 2, m, len(m)) |
||
| 821 | |||
| 822 | if is_input and not WINDOWS: |
||
| 823 | # Create a subwindow for the text field |
||
| 824 | subpop = popup.derwin(1, input_size, 2, 2 + len(m)) |
||
| 825 | subpop.attron(self.colors_list['FILTER']) |
||
| 826 | # Init the field with the current value |
||
| 827 | if input_value is not None: |
||
| 828 | subpop.addnstr(0, 0, input_value, len(input_value)) |
||
| 829 | # Display the popup |
||
| 830 | popup.refresh() |
||
| 831 | subpop.refresh() |
||
| 832 | # Create the textbox inside the subwindows |
||
| 833 | self.set_cursor(2) |
||
| 834 | self.flash_cursor() |
||
| 835 | textbox = GlancesTextbox(subpop, insert_mode=False) |
||
| 836 | textbox.edit() |
||
| 837 | self.set_cursor(0) |
||
| 838 | self.no_flash_cursor() |
||
| 839 | if textbox.gather() != '': |
||
| 840 | logger.debug( |
||
| 841 | "User enters the following string: %s" % textbox.gather()) |
||
| 842 | return textbox.gather()[:-1] |
||
| 843 | else: |
||
| 844 | logger.debug("User centers an empty string") |
||
| 845 | return None |
||
| 846 | else: |
||
| 847 | # Display the popup |
||
| 848 | popup.refresh() |
||
| 849 | self.wait(duration * 1000) |
||
| 850 | return True |
||
| 851 | |||
| 852 | def display_plugin(self, plugin_stats, |
||
| 853 | display_optional=True, |
||
| 854 | display_additional=True, |
||
| 855 | max_y=65535): |
||
| 856 | """Display the plugin_stats on the screen. |
||
| 857 | |||
| 858 | If display_optional=True display the optional stats |
||
| 859 | If display_additional=True display additionnal stats |
||
| 860 | max_y do not display line > max_y |
||
| 861 | """ |
||
| 862 | # Exit if: |
||
| 863 | # - the plugin_stats message is empty |
||
| 864 | # - the display tag = False |
||
| 865 | if plugin_stats is None or not plugin_stats['msgdict'] or not plugin_stats['display']: |
||
| 866 | # Exit |
||
| 867 | return 0 |
||
| 868 | |||
| 869 | # Get the screen size |
||
| 870 | screen_x = self.screen.getmaxyx()[1] |
||
| 871 | screen_y = self.screen.getmaxyx()[0] |
||
| 872 | |||
| 873 | # Set the upper/left position of the message |
||
| 874 | if plugin_stats['align'] == 'right': |
||
| 875 | # Right align (last column) |
||
| 876 | display_x = screen_x - self.get_stats_display_width(plugin_stats) |
||
| 877 | else: |
||
| 878 | display_x = self.column |
||
| 879 | if plugin_stats['align'] == 'bottom': |
||
| 880 | # Bottom (last line) |
||
| 881 | display_y = screen_y - self.get_stats_display_height(plugin_stats) |
||
| 882 | else: |
||
| 883 | display_y = self.line |
||
| 884 | |||
| 885 | # Display |
||
| 886 | x = display_x |
||
| 887 | x_max = x |
||
| 888 | y = display_y |
||
| 889 | for m in plugin_stats['msgdict']: |
||
| 890 | # New line |
||
| 891 | if m['msg'].startswith('\n'): |
||
| 892 | # Go to the next line |
||
| 893 | y += 1 |
||
| 894 | # Return to the first column |
||
| 895 | x = display_x |
||
| 896 | continue |
||
| 897 | # Do not display outside the screen |
||
| 898 | if x < 0: |
||
| 899 | continue |
||
| 900 | if not m['splittable'] and (x + len(m['msg']) > screen_x): |
||
| 901 | continue |
||
| 902 | if y < 0 or (y + 1 > screen_y) or (y > max_y): |
||
| 903 | break |
||
| 904 | # If display_optional = False do not display optional stats |
||
| 905 | if not display_optional and m['optional']: |
||
| 906 | continue |
||
| 907 | # If display_additional = False do not display additional stats |
||
| 908 | if not display_additional and m['additional']: |
||
| 909 | continue |
||
| 910 | # Is it possible to display the stat with the current screen size |
||
| 911 | # !!! Crach if not try/except... Why ??? |
||
| 912 | try: |
||
| 913 | self.term_window.addnstr(y, x, |
||
| 914 | m['msg'], |
||
| 915 | # Do not disply outside the screen |
||
| 916 | screen_x - x, |
||
| 917 | self.colors_list[m['decoration']]) |
||
| 918 | except Exception: |
||
| 919 | pass |
||
| 920 | else: |
||
| 921 | # New column |
||
| 922 | # Python 2: we need to decode to get real screen size because |
||
| 923 | # UTF-8 special tree chars occupy several bytes. |
||
| 924 | # Python 3: strings are strings and bytes are bytes, all is |
||
| 925 | # good. |
||
| 926 | try: |
||
| 927 | x += len(u(m['msg'])) |
||
| 928 | except UnicodeDecodeError: |
||
| 929 | # Quick and dirty hack for issue #745 |
||
| 930 | pass |
||
| 931 | if x > x_max: |
||
| 932 | x_max = x |
||
| 933 | |||
| 934 | # Compute the next Glances column/line position |
||
| 935 | self.next_column = max( |
||
| 936 | self.next_column, x_max + self.space_between_column) |
||
| 937 | self.next_line = max(self.next_line, y + self.space_between_line) |
||
| 938 | |||
| 939 | def erase(self): |
||
| 940 | """Erase the content of the screen.""" |
||
| 941 | self.term_window.erase() |
||
| 942 | |||
| 943 | def flush(self, stats, cs_status=None): |
||
| 944 | """Clear and update the screen. |
||
| 945 | |||
| 946 | stats: Stats database to display |
||
| 947 | cs_status: |
||
| 948 | "None": standalone or server mode |
||
| 949 | "Connected": Client is connected to the server |
||
| 950 | "Disconnected": Client is disconnected from the server |
||
| 951 | """ |
||
| 952 | self.erase() |
||
| 953 | self.display(stats, cs_status=cs_status) |
||
| 954 | |||
| 955 | def update(self, stats, cs_status=None, return_to_browser=False): |
||
| 956 | """Update the screen. |
||
| 957 | |||
| 958 | Wait for __refresh_time sec / catch key every 100 ms. |
||
| 959 | |||
| 960 | INPUT |
||
| 961 | stats: Stats database to display |
||
| 962 | cs_status: |
||
| 963 | "None": standalone or server mode |
||
| 964 | "Connected": Client is connected to the server |
||
| 965 | "Disconnected": Client is disconnected from the server |
||
| 966 | return_to_browser: |
||
| 967 | True: Do not exist, return to the browser list |
||
| 968 | False: Exit and return to the shell |
||
| 969 | |||
| 970 | OUPUT |
||
| 971 | True: Exit key has been pressed |
||
| 972 | False: Others cases... |
||
| 973 | """ |
||
| 974 | # Flush display |
||
| 975 | self.flush(stats, cs_status=cs_status) |
||
| 976 | |||
| 977 | # Wait |
||
| 978 | exitkey = False |
||
| 979 | countdown = Timer(self.__refresh_time) |
||
| 980 | while not countdown.finished() and not exitkey: |
||
| 981 | # Getkey |
||
| 982 | pressedkey = self.__catch_key(return_to_browser=return_to_browser) |
||
| 983 | # Is it an exit key ? |
||
| 984 | exitkey = (pressedkey == ord('\x1b') or pressedkey == ord('q')) |
||
| 985 | if not exitkey and pressedkey > -1: |
||
| 986 | # Redraw display |
||
| 987 | self.flush(stats, cs_status=cs_status) |
||
| 988 | # Wait 100ms... |
||
| 989 | self.wait() |
||
| 990 | |||
| 991 | return exitkey |
||
| 992 | |||
| 993 | def wait(self, delay=100): |
||
| 994 | """Wait delay in ms""" |
||
| 995 | curses.napms(100) |
||
| 996 | |||
| 997 | def get_stats_display_width(self, curse_msg, without_option=False): |
||
| 998 | """Return the width of the formatted curses message. |
||
| 999 | |||
| 1000 | The height is defined by the maximum line. |
||
| 1001 | """ |
||
| 1002 | try: |
||
| 1003 | if without_option: |
||
| 1004 | # Size without options |
||
| 1005 | c = len(max(''.join([(re.sub(r'[^\x00-\x7F]+', ' ', i['msg']) if not i['optional'] else "") |
||
| 1006 | for i in curse_msg['msgdict']]).split('\n'), key=len)) |
||
| 1007 | else: |
||
| 1008 | # Size with all options |
||
| 1009 | c = len(max(''.join([re.sub(r'[^\x00-\x7F]+', ' ', i['msg']) |
||
| 1010 | for i in curse_msg['msgdict']]).split('\n'), key=len)) |
||
| 1011 | except Exception: |
||
| 1012 | return 0 |
||
| 1013 | else: |
||
| 1014 | return c |
||
| 1015 | |||
| 1016 | def get_stats_display_height(self, curse_msg): |
||
| 1017 | r"""Return the height of the formatted curses message. |
||
| 1018 | |||
| 1019 | The height is defined by the number of '\n' (new line). |
||
| 1020 | """ |
||
| 1021 | try: |
||
| 1022 | c = [i['msg'] for i in curse_msg['msgdict']].count('\n') |
||
| 1023 | except Exception: |
||
| 1024 | return 0 |
||
| 1025 | else: |
||
| 1026 | return c + 1 |
||
| 1027 | |||
| 1055 |