Package node_manager_fkie :: Module settings'
[frames] | no frames]

Source Code for Module node_manager_fkie.settings'

  1  # Software License Agreement (BSD License) 
  2  # 
  3  # Copyright (c) 2012, Fraunhofer FKIE/US, Alexander Tiderko 
  4  # All rights reserved. 
  5  # 
  6  # Redistribution and use in source and binary forms, with or without 
  7  # modification, are permitted provided that the following conditions 
  8  # are met: 
  9  # 
 10  #  * Redistributions of source code must retain the above copyright 
 11  #    notice, this list of conditions and the following disclaimer. 
 12  #  * Redistributions in binary form must reproduce the above 
 13  #    copyright notice, this list of conditions and the following 
 14  #    disclaimer in the documentation and/or other materials provided 
 15  #    with the distribution. 
 16  #  * Neither the name of Fraunhofer nor the names of its 
 17  #    contributors may be used to endorse or promote products derived 
 18  #    from this software without specific prior written permission. 
 19  # 
 20  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 21  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 22  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 23  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 24  # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 25  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 26  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 27  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 28  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 29  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 30  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 31  # POSSIBILITY OF SUCH DAMAGE. 
 32   
 33  from python_qt_binding.QtGui import QColor 
 34  import os 
 35  import roslib 
 36  import rospy 
 37   
 38  from node_manager_fkie.common import get_ros_home, masteruri_from_ros 
39 40 41 -class LoggingConfig(object):
42 LOGLEVEL = 'DEFAULT' 43 LOGLEVEL_ROSCPP = 'INFO' 44 LOGLEVEL_SUPERDEBUG = 'WARN' 45 CONSOLE_FORMAT = 'DEFAULT' 46
47 - def __init__(self):
48 self.loglevel = self.LOGLEVEL 49 self.loglevel_roscpp = self.LOGLEVEL_ROSCPP 50 self.loglevel_superdebug = self.LOGLEVEL_SUPERDEBUG 51 self.console_format = self.CONSOLE_FORMAT
52
53 - def get_attributes(self):
54 return ['loglevel', 55 'loglevel_roscpp', 56 'loglevel_superdebug', 57 'console_format' 58 ]
59
60 - def is_default(self, attribute):
61 return getattr(self, attribute) == getattr(self, attribute.upper())
62
63 - def get_alternatives(self, attribute):
64 result = [] 65 if attribute == 'console_format': 66 result = [self.CONSOLE_FORMAT, 67 '[${severity}] [${time}]: ${message}', 68 '[${severity}] [${time}] [${logger}]: ${message}'] 69 elif attribute == 'loglevel': 70 result = ['DEFAULT', 'DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL'] 71 elif attribute == 'loglevel_roscpp': 72 result = ['INFO', 'DEBUG', 'WARN', 'ERROR', 'FATAL'] 73 elif attribute == 'loglevel_superdebug': 74 result = ['WARN', 'DEBUG', 'INFO', 'ERROR', 'FATAL'] 75 if not self.is_default(attribute): 76 result.insert(0, getattr(self, attribute)) 77 return result
78
79 80 -class Settings(object):
81 82 USER_DEFAULT = 'robot' 83 # set the cwd to the package of the node_manager_fkie to support the images 84 # in HTML descriptions of the robots and capabilities 85 PKG_NAME = 'node_manager_fkie' 86 PACKAGE_DIR = roslib.packages.get_pkg_dir(PKG_NAME) 87 ROBOTS_DIR = os.path.join(PACKAGE_DIR, 'images') 88 CFG_PATH = os.path.join('.node_manager', os.sep) 89 '''@ivar: configuration path to store the history.''' 90 HELP_FILE = os.path.join(PACKAGE_DIR, 'README.rst') 91 CURRENT_DIALOG_PATH = os.path.expanduser('~') 92 LOG_PATH = os.environ.get('ROS_LOG_DIR') if os.environ.get('ROS_LOG_DIR') else os.path.join(os.path.expanduser('~'), '.ros/log/') 93 94 LOG_VIEWER = "/usr/bin/less -fKLnQrSU" 95 STARTER_SCRIPT = 'rosrun node_manager_fkie remote_nm.py' 96 RESPAWN_SCRIPT = 'rosrun node_manager_fkie respawn' 97 ''' 98 the script used on remote hosts to start new ROS nodes 99 ''' 100 101 LAUNCH_HISTORY_FILE = 'launch.history' 102 LAUNCH_HISTORY_LENGTH = 5 103 104 PARAM_HISTORY_FILE = 'param.history' 105 PARAM_HISTORY_LENGTH = 12 106 107 CFG_REDIRECT_FILE = 'redirect' 108 CFG_FILE = 'settings.ini' 109 CFG_GUI_FILE = 'settings.ini' 110 111 TIMEOUT_CONTROL = 5 112 TIMEOUT_UPDATES = 20 113 114 SEARCH_IN_EXT = ['.launch', '.yaml', '.conf', '.cfg', '.iface', '.sync', '.test', '.xml'] 115 LAUNCH_VIEW_EXT = ['.yaml', '.conf', '.cfg', '.iface', '.sync', '.test'] 116 117 STORE_GEOMETRY = True 118 AUTOUPDATE = True 119 MAX_TIMEDIFF = 0.5 120 121 START_SYNC_WITH_DISCOVERY = False 122 CONFIRM_EXIT_WHEN_CLOSING = True 123 HIGHLIGHT_XML_BLOCKS = True 124 COLORIZE_HOSTS = True 125 126 SHOW_DOMAIN_SUFFIX = False 127 128 TRANSPOSE_PUB_SUB_DESCR = True 129 130 DEAFULT_HOST_COLORS = [QColor(255, 255, 235).rgb()] 131
132 - def __init__(self):
133 self.reload()
134
135 - def reload(self):
136 ''' 137 Loads the settings from file or sets default values if no one exists. 138 ''' 139 self._terminal_emulator = None 140 self._terminal_command_arg = 'e' 141 self._masteruri = masteruri_from_ros() 142 self.CFG_PATH = os.path.join(get_ros_home(), 'node_manager') 143 # loads the current configuration path. If the path was changed, a redirection 144 # file exists with new configuration folder 145 if not os.path.isdir(self.CFG_PATH): 146 os.makedirs(self.CFG_PATH) 147 self._cfg_path = self.CFG_PATH 148 else: 149 settings = self.qsettings(os.path.join(self.CFG_PATH, self.CFG_REDIRECT_FILE)) 150 self._cfg_path = settings.value('cfg_path', self.CFG_PATH) 151 # after the settings path was loaded, load other settings 152 self._robots_path = self.ROBOTS_DIR 153 settings = self.qsettings(self.CFG_FILE) 154 self._default_user = settings.value('default_user', self.USER_DEFAULT) 155 settings.beginGroup('default_user_hosts') 156 self._default_user_hosts = dict() 157 for k in settings.childKeys(): 158 self._default_user_hosts[k] = settings.value(k, self._default_user) 159 settings.endGroup() 160 try: 161 self._launch_history_length = int(settings.value('launch_history_length', self.LAUNCH_HISTORY_LENGTH)) 162 except: 163 self._launch_history_length = self.LAUNCH_HISTORY_LENGTH 164 try: 165 self._param_history_length = int(settings.value('param_history_length', self.PARAM_HISTORY_LENGTH)) 166 except: 167 self._param_history_length = self.PARAM_HISTORY_LENGTH 168 self._current_dialog_path = self.CURRENT_DIALOG_PATH 169 self._log_viewer = self.LOG_VIEWER 170 self._start_remote_script = self.STARTER_SCRIPT 171 self._respawn_script = self.RESPAWN_SCRIPT 172 self._launch_view_file_ext = self.str2list(settings.value('launch_view_file_ext', ', '.join(self.LAUNCH_VIEW_EXT))) 173 self._store_geometry = self.str2bool(settings.value('store_geometry', self.STORE_GEOMETRY)) 174 self.SEARCH_IN_EXT = list(set(self.SEARCH_IN_EXT) | set(self._launch_view_file_ext)) 175 self._autoupdate = self.str2bool(settings.value('autoupdate', self.AUTOUPDATE)) 176 self._max_timediff = float(settings.value('max_timediff', self.MAX_TIMEDIFF)) 177 self._rosconsole_cfg_file = 'rosconsole.config' 178 self.logging = LoggingConfig() 179 self.logging.loglevel = settings.value('logging/level', LoggingConfig.LOGLEVEL) 180 self.logging.loglevel_roscpp = settings.value('logging/level_roscpp', LoggingConfig.LOGLEVEL_ROSCPP) 181 self.logging.loglevel_superdebug = settings.value('logging/level_superdebug', LoggingConfig.LOGLEVEL_SUPERDEBUG) 182 self.logging.console_format = settings.value('logging/rosconsole_format', LoggingConfig.CONSOLE_FORMAT) 183 self._start_sync_with_discovery = self.str2bool(settings.value('start_sync_with_discovery', self.START_SYNC_WITH_DISCOVERY)) 184 self._confirm_exit_when_closing = self.str2bool(settings.value('confirm_exit_when_closing', self.CONFIRM_EXIT_WHEN_CLOSING)) 185 self._highlight_xml_blocks = self.str2bool(settings.value('highlight_xml_blocks', self.HIGHLIGHT_XML_BLOCKS)) 186 self._colorize_hosts = self.str2bool(settings.value('colorize_hosts', self.COLORIZE_HOSTS)) 187 self._show_domain_suffix = self.str2bool(settings.value('show_domain_suffix', self.SHOW_DOMAIN_SUFFIX)) 188 self._transpose_pub_sub_descr = self.str2bool(settings.value('transpose_pub_sub_descr', self.TRANSPOSE_PUB_SUB_DESCR)) 189 settings.beginGroup('host_colors') 190 self._host_colors = dict() 191 for k in settings.childKeys(): 192 self._host_colors[k] = settings.value(k, None) 193 settings.endGroup() 194 self.init_hosts_color_list()
195
196 - def masteruri(self):
197 return self._masteruri
198 199 @property
200 - def cfg_path(self):
201 return self._cfg_path
202 203 @cfg_path.setter
204 - def cfg_path(self, path):
205 if path: 206 abspath = os.path.abspath(path).rstrip(os.path.sep) 207 if not os.path.isdir(abspath): 208 os.makedirs(abspath) 209 self._cfg_path = abspath 210 if abspath != os.path.abspath(self.CFG_PATH).rstrip(os.path.sep): 211 settings = self.qsettings(os.path.join(self.CFG_PATH, self.CFG_REDIRECT_FILE)) 212 settings.setValue('cfg_path', abspath) 213 else: 214 # remove the redirection 215 settings = self.qsettings(os.path.join(self.CFG_PATH, self.CFG_REDIRECT_FILE)) 216 settings.remove('cfg_path') 217 self.reload()
218 219 @property
220 - def robots_path(self):
221 return self._robots_path
222 223 @robots_path.setter
224 - def robots_path(self, path):
225 if path: 226 if not os.path.isdir(path): 227 os.makedirs(path) 228 self._robots_path = path 229 settings = self.qsettings(self.CFG_FILE) 230 settings.setValue('robots_path', self._robots_path)
231 232 @property
233 - def default_user(self):
234 return self._default_user
235 236 @default_user.setter
237 - def default_user(self, user):
238 if user: 239 self._default_user = user 240 settings = self.qsettings(self.CFG_FILE) 241 settings.setValue('default_user', self._default_user)
242
243 - def host_user(self, host):
244 if host in self._default_user_hosts: 245 return self._default_user_hosts[host] 246 return self.default_user
247
248 - def set_host_user(self, host, user):
249 if host and user: 250 self._default_user_hosts[host] = user 251 settings = self.qsettings(self.CFG_FILE) 252 settings.setValue('default_user_hosts/%s' % host, user)
253 254 @property
255 - def launch_history_length(self):
256 return self._launch_history_length
257 258 @launch_history_length.setter
259 - def launch_history_length(self, length):
260 self._launch_history_length = length 261 settings = self.qsettings(self.CFG_FILE) 262 settings.setValue('launch_history_length', self._launch_history_length)
263 264 @property
265 - def param_history_length(self):
266 return self._param_history_length
267 268 @param_history_length.setter
269 - def param_history_length(self, length):
270 self._param_history_length = length 271 settings = self.qsettings(self.CFG_FILE) 272 settings.setValue('param_history_length', self._param_history_length)
273 274 @property
275 - def current_dialog_path(self):
276 return self._current_dialog_path
277 278 @current_dialog_path.setter
279 - def current_dialog_path(self, path):
280 self._current_dialog_path = path
281
282 - def robot_image_file(self, robot_name):
283 return os.path.join(self.ROBOTS_DIR, '%s.png' % robot_name)
284 285 @property
286 - def log_viewer(self):
287 return self._log_viewer
288 289 @log_viewer.setter
290 - def log_viewer(self, viewer):
291 self._log_viewer = viewer
292 293 @property
294 - def start_remote_script(self):
295 return self._start_remote_script
296 297 @start_remote_script.setter
298 - def start_remote_script(self, script):
299 self._start_remote_script = script
300 301 @property
302 - def respawn_script(self):
303 return self._respawn_script
304 305 @respawn_script.setter
306 - def respawn_script(self, script):
307 self._respawn_script = script
308 309 @property
310 - def launch_view_file_ext(self):
311 return self._launch_view_file_ext
312 313 @launch_view_file_ext.setter
314 - def launch_view_file_ext(self, exts):
315 self._launch_view_file_ext = self.str2list('%s' % exts) 316 settings = self.qsettings(self.CFG_FILE) 317 settings.setValue('launch_view_file_ext', self._launch_view_file_ext) 318 self.SEARCH_IN_EXT = list(set(self.SEARCH_IN_EXT) | set(self._launch_view_file_ext))
319 320 @property
321 - def store_geometry(self):
322 return self._store_geometry
323 324 @store_geometry.setter
325 - def store_geometry(self, value):
326 v = self.str2bool(value) 327 if self._store_geometry != v: 328 self._store_geometry = v 329 settings = self.qsettings(self.CFG_FILE) 330 settings.setValue('store_geometry', self._store_geometry)
331 332 @property
333 - def autoupdate(self):
334 return self._autoupdate
335 336 @autoupdate.setter
337 - def autoupdate(self, value):
338 v = self.str2bool(value) 339 if self._autoupdate != v: 340 self._autoupdate = v 341 settings = self.qsettings(self.CFG_FILE) 342 settings.setValue('autoupdate', self._autoupdate)
343 344 @property
345 - def max_timediff(self):
346 return self._max_timediff
347 348 @max_timediff.setter
349 - def max_timediff(self, value):
350 v = float(value) 351 if self._max_timediff != v: 352 self._max_timediff = v 353 settings = self.qsettings(self.CFG_FILE) 354 settings.setValue('max_timediff', self._max_timediff)
355
356 - def rosconsole_cfg_file(self, package):
357 result = os.path.join(self.LOG_PATH, '%s.%s' % (package, self._rosconsole_cfg_file)) 358 with open(result, 'w') as cfg_file: 359 cfg_file.write('log4j.logger.ros=%s\n' % self.logging.loglevel) 360 cfg_file.write('log4j.logger.ros.roscpp=%s\n' % self.logging.loglevel_roscpp) 361 cfg_file.write('log4j.logger.ros.roscpp.superdebug=%s\n' % self.logging.loglevel_superdebug) 362 return result
363
364 - def store_logging(self):
365 settings = self.qsettings(self.CFG_FILE) 366 settings.setValue('logging/level', self.logging.loglevel) 367 settings.setValue('logging/level_roscpp', self.logging.loglevel_roscpp) 368 settings.setValue('logging/level_superdebug', self.logging.loglevel_superdebug) 369 settings.setValue('logging/rosconsole_format', self.logging.console_format)
370 371 @property
373 return self._start_sync_with_discovery
374 375 @start_sync_with_discovery.setter
376 - def start_sync_with_discovery(self, value):
377 v = self.str2bool(value) 378 if self._start_sync_with_discovery != v: 379 self._start_sync_with_discovery = v 380 settings = self.qsettings(self.CFG_FILE) 381 settings.setValue('start_sync_with_discovery', self._start_sync_with_discovery)
382 383 @property
385 return self._confirm_exit_when_closing
386 387 @confirm_exit_when_closing.setter
388 - def confirm_exit_when_closing(self, value):
389 val = self.str2bool(value) 390 if self._confirm_exit_when_closing != val: 391 self._confirm_exit_when_closing = val 392 settings = self.qsettings(self.CFG_FILE) 393 settings.setValue('confirm_exit_when_closing', self._confirm_exit_when_closing)
394 395 @property
396 - def highlight_xml_blocks(self):
397 return self._highlight_xml_blocks
398 399 @highlight_xml_blocks.setter
400 - def highlight_xml_blocks(self, value):
401 val = self.str2bool(value) 402 if self._highlight_xml_blocks != val: 403 self._highlight_xml_blocks = val 404 settings = self.qsettings(self.CFG_FILE) 405 settings.setValue('highlight_xml_blocks', self._highlight_xml_blocks)
406 407 @property
408 - def colorize_hosts(self):
409 return self._colorize_hosts
410 411 @colorize_hosts.setter
412 - def colorize_hosts(self, value):
413 val = self.str2bool(value) 414 if self._colorize_hosts != val: 415 self._colorize_hosts = val 416 settings = self.qsettings(self.CFG_FILE) 417 settings.setValue('colorize_hosts', self._colorize_hosts)
418 419 @property
420 - def show_domain_suffix(self):
421 return self._show_domain_suffix
422 423 @show_domain_suffix.setter
424 - def show_domain_suffix(self, value):
425 val = self.str2bool(value) 426 if self._show_domain_suffix != val: 427 self._show_domain_suffix = val 428 settings = self.qsettings(self.CFG_FILE) 429 settings.setValue('show_domain_suffix', self._show_domain_suffix)
430 431 @property
432 - def transpose_pub_sub_descr(self):
433 return self._transpose_pub_sub_descr
434 435 @transpose_pub_sub_descr.setter
436 - def transpose_pub_sub_descr(self, value):
437 val = self.str2bool(value) 438 if self._transpose_pub_sub_descr != val: 439 self._transpose_pub_sub_descr = val 440 settings = self.qsettings(self.CFG_FILE) 441 settings.setValue('transpose_pub_sub_descr', self._transpose_pub_sub_descr)
442
443 - def host_color(self, host, default_color):
444 if self.colorize_hosts: 445 # get the color from settings file 446 if host in self._host_colors: 447 result = self._host_colors[host] 448 if isinstance(result, (unicode, str)): 449 return long(result) 450 return result 451 else: 452 # determine a color 453 index = abs(hash(host)) % len(self.DEAFULT_HOST_COLORS) 454 return self.DEAFULT_HOST_COLORS[index] 455 return default_color
456
457 - def set_host_color(self, host, color):
458 if host and color: 459 self._host_colors[host] = color 460 settings = self.qsettings(self.CFG_FILE) 461 settings.setValue('host_colors/%s' % host, color)
462
463 - def str2bool(self, v):
464 if isinstance(v, bool): 465 return v 466 return v.lower() in ("yes", "true", "t", "1")
467
468 - def str2list(self, l):
469 if isinstance(l, list): 470 return l 471 try: 472 l = l.strip('[]') 473 l = l.replace('u"', '') 474 l = l.replace('"', '') 475 l = l.replace("'", '') 476 l = l.replace(",", ' ') 477 return [str(i).strip() for i in l.split(' ') if i] 478 except: 479 return []
480
481 - def terminal_cmd(self, cmd, title, noclose=False):
482 ''' 483 Creates a command string to run with a terminal prefix 484 @param cmd: the list with a command and args 485 @type cmd: [str,..] 486 @param title: the title of the terminal 487 @type title: str 488 @return: command with a terminal prefix 489 @rtype: str 490 ''' 491 noclose_str = '-hold' 492 if self._terminal_emulator is None: 493 self._terminal_emulator = "" 494 for t in ['/usr/bin/x-terminal-emulator', '/usr/bin/xterm', '/opt/x11/bin/xterm']: 495 if os.path.isfile(t) and os.access(t, os.X_OK): 496 # workaround to support the command parameter in different terminal 497 if os.path.basename(os.path.realpath(t)) in ['terminator', 'gnome-terminal', 'xfce4-terminal']: 498 self._terminal_command_arg = 'x' 499 else: 500 self._terminal_command_arg = 'e' 501 if os.path.basename(os.path.realpath(t)) in ['terminator', 'gnome-terminal', 'gnome-terminal.wrapper']: 502 noclose_str = '--profile hold' 503 if noclose: 504 rospy.loginfo("If your terminal close after the execution, you can change this behavior in " 505 "profiles. You can also create a profile with name 'hold'. This profile will " 506 "be then load by node_manager.") 507 elif os.path.basename(os.path.realpath(t)) in ['xfce4-terminal']: 508 noclose_str = '' 509 self._terminal_emulator = t 510 break 511 if self._terminal_emulator == "": 512 raise Exception("No Terminal found! Please install one of ['/usr/bin/x-terminal-emulator', '/usr/bin/xterm', '/opt/x11/bin/xterm']") 513 noclose_str = noclose_str if noclose else "" 514 return '%s -T "%s" %s -%s %s' % (self._terminal_emulator, title, noclose_str, self._terminal_command_arg, ' '.join(cmd))
515
516 - def qsettings(self, settings_file):
517 from python_qt_binding.QtCore import QSettings 518 path = settings_file 519 if not settings_file.startswith(os.path.sep): 520 path = os.path.join(self.cfg_path, settings_file) 521 return QSettings(path, QSettings.IniFormat)
522
523 - def init_hosts_color_list(self):
524 self.DEAFULT_HOST_COLORS = [ 525 QColor(255, 255, 235).rgb(), 526 QColor(87, 93, 94).rgb(), 527 QColor(205, 186, 136).rgb(), 528 QColor(249, 168, 0).rgb(), 529 QColor(232, 140, 0).rgb(), 530 QColor(175, 128, 79).rgb(), 531 QColor(221, 175, 39).rgb(), 532 QColor(227, 217, 198).rgb(), 533 QColor(186, 72, 27).rgb(), 534 QColor(246, 120, 40).rgb(), 535 QColor(255, 77, 6).rgb(), 536 QColor(89, 25, 31).rgb(), 537 QColor(216, 160, 166).rgb(), 538 QColor(129, 97, 131).rgb(), 539 QColor(196, 97, 140).rgb(), 540 QColor(118, 104, 154).rgb(), 541 QColor(188, 64, 119).rgb(), 542 QColor(0, 56, 123).rgb(), 543 QColor(15, 76, 100).rgb(), 544 QColor(0, 137, 182).rgb(), 545 QColor(99, 125, 150).rgb(), 546 QColor(5, 139, 140).rgb(), 547 QColor(34, 45, 90).rgb(), 548 QColor(60, 116, 96).rgb(), 549 QColor(54, 103, 53).rgb(), 550 QColor(80, 83, 60).rgb(), 551 QColor(17, 66, 50).rgb(), 552 QColor(108, 124, 89).rgb(), 553 QColor(97, 153, 59).rgb(), 554 QColor(185, 206, 172).rgb(), 555 QColor(0, 131, 81).rgb(), 556 QColor(126, 186, 181).rgb(), 557 QColor(0, 181, 26).rgb(), 558 QColor(122, 136, 142).rgb(), 559 QColor(108, 110, 107).rgb(), 560 QColor(118, 106, 94).rgb(), 561 QColor(56, 62, 66).rgb(), 562 QColor(128, 128, 118).rgb(), 563 QColor(127, 130, 116).rgb(), 564 QColor(197, 199, 196).rgb(), 565 QColor(137, 105, 62).rgb(), 566 QColor(112, 69, 42).rgb(), 567 QColor(141, 73, 49).rgb(), 568 QColor(90, 56, 38).rgb(), 569 QColor(233, 224, 210).rgb(), 570 QColor(236, 236, 231).rgb(), 571 QColor(43, 43, 44).rgb(), 572 QColor(121, 123, 122) 573 ]
574