summaryrefslogtreecommitdiffstats
path: root/Src/PyCatcher
diff options
context:
space:
mode:
Diffstat (limited to 'Src/PyCatcher')
-rw-r--r--Src/PyCatcher/.project17
-rw-r--r--Src/PyCatcher/.pydevproject10
-rw-r--r--Src/PyCatcher/GUI/catcher_main.glade493
-rw-r--r--Src/PyCatcher/GUI/mainWindow.glade293
-rw-r--r--Src/PyCatcher/src/driverConnector.py118
-rw-r--r--Src/PyCatcher/src/driverConnector.pycbin5536 -> 0 bytes
-rw-r--r--Src/PyCatcher/src/filterDialog.pycbin1793 -> 0 bytes
-rw-r--r--Src/PyCatcher/src/filters.py31
-rw-r--r--Src/PyCatcher/src/filters.pycbin2363 -> 0 bytes
-rw-r--r--Src/PyCatcher/src/main.py7
-rw-r--r--Src/PyCatcher/src/main.pycbin456 -> 0 bytes
-rw-r--r--Src/PyCatcher/src/pyCatcherController.py69
-rw-r--r--Src/PyCatcher/src/pyCatcherController.pycbin4529 -> 0 bytes
-rw-r--r--Src/PyCatcher/src/pyCatcherModel.py83
-rw-r--r--Src/PyCatcher/src/pyCatcherModel.pycbin3544 -> 0 bytes
-rw-r--r--Src/PyCatcher/src/pyCatcherSettings.py18
-rw-r--r--Src/PyCatcher/src/pyCatcherSettings.pycbin717 -> 0 bytes
-rw-r--r--Src/PyCatcher/src/pyCatcherView.py166
-rw-r--r--Src/PyCatcher/src/pyCatcherView.pycbin8559 -> 0 bytes
-rw-r--r--Src/PyCatcher/src/xdot.py2212
-rw-r--r--Src/PyCatcher/src/xdot.pycbin97140 -> 0 bytes
21 files changed, 0 insertions, 3517 deletions
diff --git a/Src/PyCatcher/.project b/Src/PyCatcher/.project
deleted file mode 100644
index e2975d0..0000000
--- a/Src/PyCatcher/.project
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>PyCatcher</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>org.python.pydev.PyDevBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>org.python.pydev.pythonNature</nature>
- </natures>
-</projectDescription>
diff --git a/Src/PyCatcher/.pydevproject b/Src/PyCatcher/.pydevproject
deleted file mode 100644
index 5662323..0000000
--- a/Src/PyCatcher/.pydevproject
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?eclipse-pydev version="1.0"?>
-
-<pydev_project>
-<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
-<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property>
-<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
-<path>/PyCatcher/src</path>
-</pydev_pathproperty>
-</pydev_project>
diff --git a/Src/PyCatcher/GUI/catcher_main.glade b/Src/PyCatcher/GUI/catcher_main.glade
deleted file mode 100644
index 5fd5626..0000000
--- a/Src/PyCatcher/GUI/catcher_main.glade
+++ /dev/null
@@ -1,493 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<interface>
- <!-- interface-requires gtk+ 3.0 -->
- <object class="GtkDialog" id="filter_window">
- <property name="can_focus">False</property>
- <property name="border_width">5</property>
- <property name="type">popup</property>
- <property name="modal">True</property>
- <property name="window_position">center-on-parent</property>
- <property name="destroy_with_parent">True</property>
- <property name="type_hint">dialog</property>
- <property name="transient_for">main_window</property>
- <property name="has_resize_grip">False</property>
- <child internal-child="vbox">
- <object class="GtkBox" id="dialog-vbox1">
- <property name="can_focus">False</property>
- <property name="orientation">vertical</property>
- <property name="spacing">2</property>
- <child internal-child="action_area">
- <object class="GtkButtonBox" id="dialog-action_area1">
- <property name="can_focus">False</property>
- <property name="layout_style">end</property>
- <child>
- <placeholder/>
- </child>
- <child>
- <object class="GtkButton" id="btn_close">
- <property name="label" translatable="yes">Cancel</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="use_action_appearance">False</property>
- <signal name="clicked" handler="_on_filter_close_clicked" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="pack_type">end</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkVBox" id="vBox1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkCheckButton" id="cb_filter_by_provider">
- <property name="label" translatable="yes">Filter by provider name (csv, whitelist)</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
- <property name="xalign">0</property>
- <property name="draw_indicator">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkHBox" id="hBox3">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkLabel" id="label3">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes"> </property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkEntry" id="te_filter_provider">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="invisible_char">•</property>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkHSeparator" id="Separator1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkCheckButton" id="cb_filter_by_arfcn">
- <property name="label" translatable="yes">Filter by ARFCN</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
- <property name="xalign">0</property>
- <property name="draw_indicator">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">3</property>
- </packing>
- </child>
- <child>
- <object class="GtkHBox" id="hBox1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkLabel" id="label1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes"> From: </property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkEntry" id="te_filter_arfcn_from">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="invisible_char">•</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">4</property>
- </packing>
- </child>
- <child>
- <object class="GtkHBox" id="hBox2">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkLabel" id="label2">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes"> To: </property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkEntry" id="te_filter_arfcn_to">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="invisible_char">•</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">5</property>
- </packing>
- </child>
- <child>
- <object class="GtkHSeparator" id="hSeparator2">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">6</property>
- </packing>
- </child>
- <child>
- <object class="GtkCheckButton" id="cb_only_scanned_bs">
- <property name="label" translatable="yes">Show only BS, that have been found</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
- <property name="xalign">0</property>
- <property name="draw_indicator">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">7</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- </child>
- <action-widgets>
- <action-widget response="0">btn_close</action-widget>
- </action-widgets>
- </object>
- <object class="GtkWindow" id="main_window">
- <property name="can_focus">False</property>
- <property name="default_width">800</property>
- <property name="default_height">600</property>
- <property name="has_resize_grip">False</property>
- <signal name="destroy" handler="_on_main_window_destroy" swapped="no"/>
- <child>
- <object class="GtkVBox" id="vbox1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkToolbar" id="toolbar_main">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="toolbar_style">both</property>
- <child>
- <object class="GtkToggleToolButton" id="tbtn_firmware">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="use_action_appearance">False</property>
- <property name="label" translatable="yes">Firmware</property>
- <property name="use_underline">True</property>
- <property name="icon_name">modem</property>
- <signal name="toggled" handler="_on_firmware_toggled" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkToggleToolButton" id="tbtn_scan">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="use_action_appearance">False</property>
- <property name="label" translatable="yes">Scan</property>
- <property name="use_underline">True</property>
- <property name="icon_name">network-wireless</property>
- <signal name="toggled" handler="_on_scan_toggled" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkToolButton" id="btn_show_filters">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="use_action_appearance">False</property>
- <property name="label" translatable="yes">Filters</property>
- <property name="use_underline">True</property>
- <property name="icon_name">edit-find</property>
- <signal name="clicked" handler="_on_filter_clicked" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkHSeparator" id="hSeparator1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkHBox" id="hbox1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkVBox" id="vbox2">
- <property name="width_request">500</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkToolbar" id="toolbar_graph">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkToolButton" id="btn_zoom_in">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="use_action_appearance">False</property>
- <property name="label" translatable="yes">Zoom In</property>
- <property name="use_underline">True</property>
- <property name="icon_name">zoom-in</property>
- <signal name="clicked" handler="_on_zoon_in_clicked" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkToolButton" id="btn_zoom_out">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="use_action_appearance">False</property>
- <property name="label" translatable="yes">Zoom Out</property>
- <property name="use_underline">True</property>
- <property name="icon_name">zoom-out</property>
- <signal name="clicked" handler="_on_zoon_out_clicked" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkToolButton" id="btn_best_fit">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="use_action_appearance">False</property>
- <property name="label" translatable="yes">Best Fit</property>
- <property name="use_underline">True</property>
- <property name="icon_name">zoom-best-fit</property>
- <signal name="clicked" handler="_on_zoon_fit_clicked" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkToolButton" id="btn_original">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="use_action_appearance">False</property>
- <property name="label" translatable="yes">Original</property>
- <property name="use_underline">True</property>
- <property name="icon_name">zoom-original</property>
- <signal name="clicked" handler="_on_zoon_original_clicked" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkToolButton" id="btn_open_dot">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="use_action_appearance">False</property>
- <property name="label" translatable="yes">Open File</property>
- <property name="use_underline">True</property>
- <property name="icon_name">document-open</property>
- <signal name="clicked" handler="_on_open_file_clicked" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <placeholder/>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkVSeparator" id="vSeparator2">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkVBox" id="vbox3">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkTreeView" id="tv_stations">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <child internal-child="selection">
- <object class="GtkTreeSelection" id="treeview-selection"/>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkHSeparator" id="hSeparator3">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkTextView" id="te_log">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="editable">False</property>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
-</interface>
diff --git a/Src/PyCatcher/GUI/mainWindow.glade b/Src/PyCatcher/GUI/mainWindow.glade
deleted file mode 100644
index 1b7da70..0000000
--- a/Src/PyCatcher/GUI/mainWindow.glade
+++ /dev/null
@@ -1,293 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<glade-interface>
- <!-- interface-requires gtk+ 2.24 -->
- <!-- interface-naming-policy project-wide -->
- <widget class="GtkWindow" id="main_window">
- <property name="can_focus">False</property>
- <property name="title" translatable="yes">IMSI Catcher Detector</property>
- <property name="default_width">1024</property>
- <property name="default_height">700</property>
- <signal name="destroy" handler="on_main_window_destroy" swapped="no"/>
- <child>
- <widget class="GtkVBox" id="vbox1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <widget class="GtkToolbar" id="main_toolbar">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="toolbar_style">both</property>
- <child>
- <widget class="GtkToggleToolButton" id="firmware_toggle">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="use_action_appearance">False</property>
- <property name="label" translatable="yes">Toggle Firmware</property>
- <property name="use_underline">True</property>
- <property name="icon_name">modem</property>
- <signal name="toggled" handler="on_firmware_toggle_toggled" swapped="no"/>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <child>
- <widget class="GtkToggleToolButton" id="scan_toggle">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="use_action_appearance">False</property>
- <property name="label" translatable="yes">Toggle Scanning</property>
- <property name="use_underline">True</property>
- <property name="icon_name">network-wireless</property>
- <signal name="toggled" handler="on_scan_toggle_toggled" swapped="no"/>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <child>
- <widget class="GtkToolButton" id="open_file">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="use_action_appearance">False</property>
- <property name="label" translatable="yes">Open File</property>
- <property name="use_underline">True</property>
- <property name="icon_name">document-open</property>
- <signal name="clicked" handler="on_open_file_clicked" swapped="no"/>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <child>
- <widget class="GtkToolButton" id="save_project">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="use_action_appearance">False</property>
- <property name="label" translatable="yes">Save Project</property>
- <property name="use_underline">True</property>
- <property name="icon_name">document-save</property>
- <signal name="clicked" handler="on_save_project_clicked" swapped="no"/>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <widget class="GtkHBox" id="hbox1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <widget class="GtkVBox" id="vbox4">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <placeholder/>
- </child>
- <child>
- <widget class="GtkToolbar" id="graph_control">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <widget class="GtkToolButton" id="graph_zoom_in">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="use_action_appearance">False</property>
- <property name="label" translatable="yes">Zoom In</property>
- <property name="use_underline">True</property>
- <property name="icon_name">zoom-in</property>
- <signal name="clicked" handler="on_graph_zoom_in_clicked" swapped="no"/>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <child>
- <widget class="GtkToolButton" id="graph_zoom_out">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="use_action_appearance">False</property>
- <property name="label" translatable="yes">Zoom Out</property>
- <property name="use_underline">True</property>
- <property name="icon_name">zoom-out</property>
- <signal name="clicked" handler="on_graph_zoom_out_clicked" swapped="no"/>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <child>
- <widget class="GtkToolButton" id="graph_fit">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="use_action_appearance">False</property>
- <property name="label" translatable="yes">Fit To Screen</property>
- <property name="use_underline">True</property>
- <property name="icon_name">zoom-best-fit</property>
- <signal name="clicked" handler="on_graph_fit_clicked" swapped="no"/>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <child>
- <widget class="GtkToolButton" id="graph_zoom_default">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="use_action_appearance">False</property>
- <property name="label" translatable="yes">100%</property>
- <property name="use_underline">True</property>
- <property name="icon_name">zoom-original</property>
- <signal name="clicked" handler="on_graph_zoom_default_clicked" swapped="no"/>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <widget class="GtkVSeparator" id="vseparator1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <widget class="GtkVBox" id="vbox2">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <widget class="GtkScrolledWindow" id="scrolledwindow2">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="hscrollbar_policy">never</property>
- <property name="vscrollbar_policy">automatic</property>
- <child>
- <widget class="GtkTreeView" id="bs_table">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- </widget>
- </child>
- </widget>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <widget class="GtkHSeparator" id="hseparator1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <widget class="GtkScrolledWindow" id="scrolledwindow1">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="hscrollbar_policy">automatic</property>
- <child>
- <widget class="GtkTextView" id="log_output">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="editable">False</property>
- </widget>
- </child>
- </widget>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <widget class="GtkStatusbar" id="statusbar">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="spacing">2</property>
- <child>
- <widget class="GtkLabel" id="status_firmware">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Firmware: Not Loaded</property>
- </widget>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="status_scanner">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Scanner: Not scanning</property>
- </widget>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- </widget>
- </child>
- </widget>
-</glade-interface>
diff --git a/Src/PyCatcher/src/driverConnector.py b/Src/PyCatcher/src/driverConnector.py
deleted file mode 100644
index 2167219..0000000
--- a/Src/PyCatcher/src/driverConnector.py
+++ /dev/null
@@ -1,118 +0,0 @@
-from pyCatcherModel import BaseStationInformation
-import subprocess
-import threading
-import re
-from pyCatcherSettings import Commands
-import time
-import gtk
-
-class DriverConnector:
- def __init__ (self):
- self._scan_thread_break = False
- self._firmware_thread_break = False
- self._firmware_waiting_callback = None
- self._firmware_loaded_callback = None
- self._base_station_found_callback = None
- self._firmware_thread = None
- self._scan_thread = None
-
- def start_scanning (self, base_station_found_callback):
- self._base_station_found_callback = base_station_found_callback
- self._scan_thread = ScanThread(self._base_station_found_callback)
- self._scan_thread.start()
-
- def start_firmware(self, firmware_waiting_callback, firmware_loaded_callback):
- self._firmware_waiting_callback = firmware_waiting_callback
- self._firmware_loaded_callback = firmware_loaded_callback
- self._firmware_thread = FirmwareThread(self._firmware_waiting_callback, self._firmware_loaded_callback)
- self._firmware_thread.start()
-
- def stop_scanning (self):
- self._scan_thread.terminate()
-
- def stop_firmware(self):
- self._firmware_thread_break = True
-
- def shutdown(self):
- if self._firmware_thread:
- self._firmware_thread.join(3)
- if self._scan_thread:
- self._scan_thread.join(3)
-
-class FirmwareThread(threading.Thread):
- def __init__(self, firmware_waiting_callback, firmware_loaded_callback):
- gtk.gdk.threads_init()
- threading.Thread.__init__(self)
- self._firmware_waiting_callback = firmware_waiting_callback
- self._firmware_loaded_callback = firmware_loaded_callback
- self._thread_break = False
-
- def terminate(self):
- self._thread_break = True
-
- def run(self):
- loader_process_object = subprocess.Popen(Commands['osmocon_command'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- time.sleep(3)
- self._firmware_waiting_callback()
- while not self._thread_break:
- line = loader_process_object.stdout.readline()
- #if line:
- # print line
- if line.strip() == 'Finishing download phase':
- self._firmware_loaded_callback()
- #time.sleep(0.5)
- print 'killing firmware'
- loader_process_object.terminate()
-
-class ScanThread(threading.Thread):
- def __init__(self, base_station_found_callback):
- gtk.gdk.threads_init()
- threading.Thread.__init__(self)
- self._base_station_found_callback = base_station_found_callback
- self._thread_break = False
-
- def terminate(self):
- self._thread_break = True
-
- def run(self):
- scan_process = subprocess.Popen(Commands['scan_command'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- time.sleep(2)
- while not self._thread_break:
- line = scan_process.stdout.readline()
- if line:
- #print line
- #sys.stdout.flush()
- if re.search('SysInfo', line):
- base_station = BaseStationInformation()
- #get country
- line = line = scan_process.stdout.readline()
- match = re.search(r'country\s(\w+)',line)
- if match:
- base_station.country = match.group(1)
- #get provider
- line = line = scan_process.stdout.readline()
- match = re.search(r'provider\s(.+)',line)
- if match:
- base_station.provider = match.group(1)
- #get arfcn
- line = line = scan_process.stdout.readline()
- match = re.search(r'arfcn\s(\d+)',line)
- if match:
- base_station.arfcn = int(match.group(1))
- #get rxlev
- line = line = scan_process.stdout.readline()
- match = re.search(r'rxlev\s(.\d+)',line)
- if match:
- base_station.rxlev = match.group(1)
- #get neighbours
- line = line = scan_process.stdout.readline()
- match = re.search(r'si2\s(.+)',line)
- if match:
- base_station.system_info_t2 = match.group(1).split(' ')
- #endinfo
- line = line = scan_process.stdout.readline()
-
- self._base_station_found_callback(base_station)
- print 'killing scan'
- scan_process.terminate()
- \ No newline at end of file
diff --git a/Src/PyCatcher/src/driverConnector.pyc b/Src/PyCatcher/src/driverConnector.pyc
deleted file mode 100644
index 7fc6e0c..0000000
--- a/Src/PyCatcher/src/driverConnector.pyc
+++ /dev/null
Binary files differ
diff --git a/Src/PyCatcher/src/filterDialog.pyc b/Src/PyCatcher/src/filterDialog.pyc
deleted file mode 100644
index bdd619c..0000000
--- a/Src/PyCatcher/src/filterDialog.pyc
+++ /dev/null
Binary files differ
diff --git a/Src/PyCatcher/src/filters.py b/Src/PyCatcher/src/filters.py
deleted file mode 100644
index 6247aac..0000000
--- a/Src/PyCatcher/src/filters.py
+++ /dev/null
@@ -1,31 +0,0 @@
-class Filter:
- def __init__(self):
- self.is_active = False
- self.params = {}
-
- def execute(self, station_list):
- raise NotImplementedError('Filter not yet implemented')
-
-class ARFCNFilter(Filter):
- def execute(self, station_list):
- filtered_list = []
- low = self.params['from']
- high = self.params['to']
- for station in station_list:
- if station.arfcn <= high and station.arfcn >= low:
- filtered_list.append(station)
- return filtered_list
-
-class ProviderFilter(Filter):
- def execute(self, station_list):
- filtered_list = []
- providers = [x.strip() for x in self.params['providers'].split(',')]
- for station in station_list:
- if station.provider in providers:
- filtered_list.append(station)
- return filtered_list
-
-#FIXME: provide extra abstraction for parameterless filters (on/off filters)
-class FoundFilter(Filter):
- def execute(self, station_list):
- pass \ No newline at end of file
diff --git a/Src/PyCatcher/src/filters.pyc b/Src/PyCatcher/src/filters.pyc
deleted file mode 100644
index 08d5e0e..0000000
--- a/Src/PyCatcher/src/filters.pyc
+++ /dev/null
Binary files differ
diff --git a/Src/PyCatcher/src/main.py b/Src/PyCatcher/src/main.py
deleted file mode 100644
index 0bbda10..0000000
--- a/Src/PyCatcher/src/main.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from pyCatcherController import PyCatcherController
-
-def main ():
- controller = PyCatcherController()
-
-if __name__ == '__main__':
- main() \ No newline at end of file
diff --git a/Src/PyCatcher/src/main.pyc b/Src/PyCatcher/src/main.pyc
deleted file mode 100644
index 93f3da3..0000000
--- a/Src/PyCatcher/src/main.pyc
+++ /dev/null
Binary files differ
diff --git a/Src/PyCatcher/src/pyCatcherController.py b/Src/PyCatcher/src/pyCatcherController.py
deleted file mode 100644
index 0810e97..0000000
--- a/Src/PyCatcher/src/pyCatcherController.py
+++ /dev/null
@@ -1,69 +0,0 @@
-import sys
-import pygtk
-import gtk
-import gtk.glade
-from driverConnector import DriverConnector
-from pyCatcherModel import BaseStationInformation, BaseStationInformationList
-from pyCatcherView import PyCatcherGUI
-from filters import ARFCNFilter,FoundFilter,ProviderFilter
-
-class PyCatcherController:
- def __init__(self):
- self._base_station_list = BaseStationInformationList()
- store = gtk.ListStore(str,str,str,str)
- store.append(('-','-','-','-'))
- self.bs_tree_list_data = store
- self._gui = PyCatcherGUI(self)
- self._driver_connector = DriverConnector()
- self._gui.log_line("GUI initialized")
-
- self.arfcn_filter = ARFCNFilter()
- self.provider_filter = ProviderFilter()
- self.found_filter = FoundFilter()
-
- self._filters = [self.arfcn_filter, self.provider_filter]
-
- gtk.main()
-
- def log_message(self, message):
- self._gui.log_line(message)
-
- def start_scan(self):
- self._gui.log_line("start scan")
- self._driver_connector.start_scanning(self._found_base_station_callback)
-
- def stop_scan(self):
- self._gui.log_line("stop scan")
- self._driver_connector.stop_scanning()
-
- def start_firmware(self):
- self._gui.log_line("start firmware")
- self._driver_connector.start_firmware(self._firmware_waiting_callback, self._firmware_done_callback)
-
- def stop_firmware(self):
- self._gui.log_line("stop firmware")
- print 'stop firmwares'
- self._driver_connector.stop_firmware()
-
- def shutdown(self):
- self._driver_connector.shutdown()
-
- def _found_base_station_callback(self, base_station):
- self._gui.log_line("found " + base_station.provider + ' (' + str(base_station.arfcn) + ')')
- self._base_station_list.add_station(base_station)
- self._base_station_list.refill_store(self.bs_tree_list_data)
- dotcode = self._base_station_list.get_dot_code(self._filters,self.found_filter)
- self._gui.load_dot(dotcode)
-
- def trigger_redraw(self):
- dotcode = self._base_station_list.get_dot_code(self._filters,self.found_filter)
- self._gui.load_dot(dotcode)
-
- def _firmware_waiting_callback(self):
- self._gui.log_line("firmware waiting for device")
- self._gui.show_info('Switch on the phone now.', 'Firmware')
-
- def _firmware_done_callback(self):
- self._gui.log_line("firmware loaded, ready for scanning")
- self._gui.show_info('Firmware load completed', 'Firmware')
- \ No newline at end of file
diff --git a/Src/PyCatcher/src/pyCatcherController.pyc b/Src/PyCatcher/src/pyCatcherController.pyc
deleted file mode 100644
index 87994f1..0000000
--- a/Src/PyCatcher/src/pyCatcherController.pyc
+++ /dev/null
Binary files differ
diff --git a/Src/PyCatcher/src/pyCatcherModel.py b/Src/PyCatcher/src/pyCatcherModel.py
deleted file mode 100644
index a857760..0000000
--- a/Src/PyCatcher/src/pyCatcherModel.py
+++ /dev/null
@@ -1,83 +0,0 @@
-import datetime
-import gtk
-import math
-
-class BaseStationInformation:
- #TODO: examine plmn permitted byte
- #TODO: examine emergency call capability
- def __init__ (self):
- self.country = 'Nowhere'
- self.provider = 'Carry'
- self.arfcn = 0
- self.rxlev = 0
- self.system_info_t2 = []
- self.discovery_time = datetime.datetime.now().strftime('%T')
- self.found = False
-
- def get_list_model(self):
- return (self.provider, str(self.arfcn), str(self.rxlev), self.discovery_time)
-
- def get_neighbour_arfcn(self):
- #TODO: implement this for bis/tar sysinfo
- neighbours = self.system_info_t2[3:19]
- bin_representation = ''
- neighbour_arfcn = []
-
- for value in neighbours:
- bin_representation += str(bin(int(value, 16))[2:].zfill(8))
-
- '''
- >>> for i, bit in enumerate(reversed(a)):
-... if bit == '1':
-... print i
- '''
-
- for x in xrange(1,125):
- index = 0-x
- bit = bin_representation[index]
- if bit == '1':
- neighbour_arfcn.append(abs(index))
- return neighbour_arfcn
-
-class BaseStationInformationList:
- def __init__(self):
- self._base_station_list = []
-
- def add_station(self, base_station):
- for item in self._base_station_list:
- if item.arfcn == base_station.arfcn:
- item.discovery_time = datetime.datetime.now().strftime('%T')
- break
- else:
- self._base_station_list.append(base_station)
-
- def get_dot_code(self, filters=None, found_filter=None):
- preamble = r'digraph bsnetwork { '
- postamble = r'}'
- code = ''
- filtered_list = self._base_station_list
-
- if found_filter == None:
- print_neighbours = True
- else:
- print_neighbours = not found_filter.is_active
-
- if filters != None:
- for filter in filters:
- if filter.is_active:
- filtered_list = filter.execute(filtered_list)
-
- for station in filtered_list:
- code += str(station.arfcn) + r' [color=red]; '
- if(print_neighbours):
- for neighbour in station.get_neighbour_arfcn():
- code += str(station.arfcn) + r' -> ' + str(neighbour) + r'; '
- #TODO: make printing the source a fixed option
- #print preamble + code + postamble
- return preamble + code + postamble
-
- def refill_store(self, store):
- store.clear()
- for item in self._base_station_list:
- store.append(item.get_list_model())
- \ No newline at end of file
diff --git a/Src/PyCatcher/src/pyCatcherModel.pyc b/Src/PyCatcher/src/pyCatcherModel.pyc
deleted file mode 100644
index c4e2eb2..0000000
--- a/Src/PyCatcher/src/pyCatcherModel.pyc
+++ /dev/null
Binary files differ
diff --git a/Src/PyCatcher/src/pyCatcherSettings.py b/Src/PyCatcher/src/pyCatcherSettings.py
deleted file mode 100644
index 7ced6b3..0000000
--- a/Src/PyCatcher/src/pyCatcherSettings.py
+++ /dev/null
@@ -1,18 +0,0 @@
-#needed commands with full path to applications
-
-PyCatcher_settings = {'debug' : False,
- }
-
-Device_settings = { 'mobile_device' : '/dev/ttyUSB0',
- 'xor_type' : 'c123xor',
- 'firmware' : 'compal_e88',
- }
-
-Osmocon_lib = '/home/tom/Documents/imsi-catcher-detection/Src/osmocom-bb/src'
-
-Commands = {'osmocon_command' : [Osmocon_lib + '/host/osmocon/osmocon',
- '-p', Device_settings['mobile_device'],
- '-m', Device_settings['xor_type'],
- Osmocon_lib + '/target/firmware/board/' + Device_settings['firmware'] + '/layer1.compalram.bin'],
- 'scan_command' : [Osmocon_lib + '/host/layer23/src/misc/catcher'],
- }
diff --git a/Src/PyCatcher/src/pyCatcherSettings.pyc b/Src/PyCatcher/src/pyCatcherSettings.pyc
deleted file mode 100644
index 3f86577..0000000
--- a/Src/PyCatcher/src/pyCatcherSettings.pyc
+++ /dev/null
Binary files differ
diff --git a/Src/PyCatcher/src/pyCatcherView.py b/Src/PyCatcher/src/pyCatcherView.py
deleted file mode 100644
index 4daa22e..0000000
--- a/Src/PyCatcher/src/pyCatcherView.py
+++ /dev/null
@@ -1,166 +0,0 @@
-import locale
-import gtk
-from xdot import DotWidget
-import datetime
-import time
-from filters import ARFCNFilter, FoundFilter, ProviderFilter
-
-class PyCatcherGUI:
-
- def __init__(self, catcher_controller):
- encoding = locale.getlocale()[1]
- self._utf8conv = lambda x : unicode(x, encoding).encode('utf8')
-
- self._builder = gtk.Builder()
- self._builder.add_from_file('../GUI/catcher_main.glade')
- self._main_window = self._builder.get_object('main_window')
- self._main_window.show()
-
- self._filter_window = self._builder.get_object('filter_window')
-
- self._catcher_controller = catcher_controller
-
- self._bs_tree_view = self._builder.get_object('tv_stations')
- self._add_column("Provider", 0)
- self._add_column("ARFCN", 1)
- self._add_column("Strength",2)
- self._add_column("Last seen", 3)
- self._bs_tree_view.set_model(self._catcher_controller.bs_tree_list_data)
-
- self._horizontal_container = self._builder.get_object('vbox2')
- self._dot_widget = DotWidget()
- self._horizontal_container.pack_start_defaults(self._dot_widget)
- self._dot_widget.set_filter('neato')
- self._dot_widget.show()
- self._dot_widget.connect('clicked', self._on_graph_node_clicked)
-
- self._builder.connect_signals(self)
-
- log_view = self._builder.get_object('te_log')
- self._log_buffer = log_view.get_buffer()
- self._log_buffer.insert(self._log_buffer.get_end_iter(),self._utf8conv("-- Log execution on " + datetime.datetime.now().strftime("%A, %d. %B %Y %I:%M %p") + " --\n\n"))
-
- self._main_window.show()
-
- def _add_column(self, name, index):
- column = gtk.TreeViewColumn(name, gtk.CellRendererText(), text=index)
- column.set_resizable(True)
- column.set_sort_column_id(index)
- self._bs_tree_view.append_column(column)
-
- def _update_filters(self):
- if self._builder.get_object('cb_filter_by_provider').get_active():
- self._catcher_controller.provider_filter.params = {'providers': self._builder.get_object('te_filter_provider').get_text()}
- self._catcher_controller.provider_filter.is_active = True
- print 'provider active'
- else:
- self._catcher_controller.provider_filter.is_active = False
- print 'provider off'
-
- if self._builder.get_object('cb_filter_by_arfcn').get_active():
- self._catcher_controller.arfcn_filter.params = {'from':int(self._builder.get_object('te_filter_arfcn_from').get_text()),
- 'to':int(self._builder.get_object('te_filter_arfcn_to').get_text())}
- self._catcher_controller.arfcn_filter.is_active = True
- print 'arfcn active'
- else:
- self._catcher_controller.arfcn_filter.is_active = False
- print 'arfcn off'
-
- if self._builder.get_object('cb_only_scanned_bs').get_active():
- self._catcher_controller.found_filter.is_active = True
- print 'scanned active'
- else:
- self._catcher_controller.found_filter.is_active = False
- print 'scanned off'
-
- self._catcher_controller.trigger_redraw()
-
- def _on_graph_node_clicked (self, widget, url, event):
- print 'NODE CLICKED'
-
- def _on_main_window_destroy(self, widget):
- self._catcher_controller.shutdown()
- gtk.main_quit()
-
- def _on_scan_toggled(self, widget):
- if(widget.get_active()):
- self._catcher_controller.start_scan()
- else:
- self._catcher_controller.stop_scan()
-
- def _on_firmware_toggled(self, widget):
- if(widget.get_active()):
- self._catcher_controller.start_firmware()
- else:
- self._catcher_controller.stop_firmware()
-
- def _on_filter_clicked(self,widget):
- self._filter_window.show()
-
- def _on_filter_close_clicked(self, widget):
- self._update_filters()
- self._filter_window.hide()
-
- def _on_open_file_clicked(self, widget):
- chooser = gtk.FileChooserDialog(title="Open dot File",
- action=gtk.FILE_CHOOSER_ACTION_OPEN,
- buttons=(gtk.STOCK_CANCEL,
- gtk.RESPONSE_CANCEL,
- gtk.STOCK_OPEN,
- gtk.RESPONSE_OK))
- chooser.set_default_response(gtk.RESPONSE_OK)
- filter = gtk.FileFilter()
- filter.set_name("Graphviz dot files")
- filter.add_pattern("*.dot")
- chooser.add_filter(filter)
- filter = gtk.FileFilter()
- filter.set_name("All files")
- filter.add_pattern("*")
- chooser.add_filter(filter)
- if chooser.run() == gtk.RESPONSE_OK:
- filename = chooser.get_filename()
- chooser.destroy()
- self.load_dot_from_file(filename)
- else:
- chooser.destroy()
-
- def _on_zoon_in_clicked(self,widget):
- self._dot_widget.on_zoom_in(None)
-
- def _on_zoon_out_clicked(self,widget):
- self._dot_widget.on_zoom_out(None)
-
- def _on_zoon_fit_clicked(self,widget):
- self._dot_widget.on_zoom_fit(None)
-
- def _on_zoon_original_clicked(self,widget):
- self._dot_widget.on_zoom_100(None)
-
- def load_dot_from_file(self, filename):
- try:
- fp = file(filename, 'rt')
- self.load_dot(fp.read(), filename)
- fp.close()
- except IOError, ex:
- self.show_info(ex)
-
- def load_dot(self, dotcode, filename="<stdin>"):
- if self._dot_widget.set_dotcode(dotcode, filename):
- #self._dot_widget.zoom_to_fit()
- pass
-
- def show_info(self, message, title='PyCatcher', time_to_sleep=3, type='INFO'):
- gtk_type = {'INFO' : gtk.MESSAGE_INFO,
- 'ERROR': gtk.MESSAGE_ERROR}
-
- dlg = gtk.MessageDialog(type=gtk.MESSAGE_INFO,
- message_format=str(message)
- )
-
- dlg.set_title(title)
- dlg.show()
- time.sleep(time_to_sleep)
- dlg.destroy()
-
- def log_line(self, line):
- self._log_buffer.insert(self._log_buffer.get_end_iter(),self._utf8conv(datetime.datetime.now().strftime("%I:%M:%S %p")+ ": " + line + "\n")) \ No newline at end of file
diff --git a/Src/PyCatcher/src/pyCatcherView.pyc b/Src/PyCatcher/src/pyCatcherView.pyc
deleted file mode 100644
index fe0075b..0000000
--- a/Src/PyCatcher/src/pyCatcherView.pyc
+++ /dev/null
Binary files differ
diff --git a/Src/PyCatcher/src/xdot.py b/Src/PyCatcher/src/xdot.py
deleted file mode 100644
index d1f9762..0000000
--- a/Src/PyCatcher/src/xdot.py
+++ /dev/null
@@ -1,2212 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2008 Jose Fonseca
-#
-# This program is free software: you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published
-# by the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-
-'''Visualize dot graphs via the xdot format.'''
-
-__author__ = "Jose Fonseca"
-
-__version__ = "0.4"
-
-
-import os
-import sys
-import subprocess
-import math
-import colorsys
-import time
-import re
-
-import gobject
-import gtk
-import gtk.gdk
-import gtk.keysyms
-import cairo
-import pango
-import pangocairo
-
-
-# See http://www.graphviz.org/pub/scm/graphviz-cairo/plugin/cairo/gvrender_cairo.c
-
-# For pygtk inspiration and guidance see:
-# - http://mirageiv.berlios.de/
-# - http://comix.sourceforge.net/
-
-
-class Pen:
- """Store pen attributes."""
-
- def __init__(self):
- # set default attributes
- self.color = (0.0, 0.0, 0.0, 1.0)
- self.fillcolor = (0.0, 0.0, 0.0, 1.0)
- self.linewidth = 1.0
- self.fontsize = 14.0
- self.fontname = "Times-Roman"
- self.dash = ()
-
- def copy(self):
- """Create a copy of this pen."""
- pen = Pen()
- pen.__dict__ = self.__dict__.copy()
- return pen
-
- def highlighted(self):
- pen = self.copy()
- pen.color = (1, 0, 0, 1)
- pen.fillcolor = (1, .8, .8, 1)
- return pen
-
-
-class Shape:
- """Abstract base class for all the drawing shapes."""
-
- def __init__(self):
- pass
-
- def draw(self, cr, highlight=False):
- """Draw this shape with the given cairo context"""
- raise NotImplementedError
-
- def select_pen(self, highlight):
- if highlight:
- if not hasattr(self, 'highlight_pen'):
- self.highlight_pen = self.pen.highlighted()
- return self.highlight_pen
- else:
- return self.pen
-
-
-class TextShape(Shape):
-
- #fontmap = pangocairo.CairoFontMap()
- #fontmap.set_resolution(72)
- #context = fontmap.create_context()
-
- LEFT, CENTER, RIGHT = -1, 0, 1
-
- def __init__(self, pen, x, y, j, w, t):
- Shape.__init__(self)
- self.pen = pen.copy()
- self.x = x
- self.y = y
- self.j = j
- self.w = w
- self.t = t
-
- def draw(self, cr, highlight=False):
-
- try:
- layout = self.layout
- except AttributeError:
- layout = cr.create_layout()
-
- # set font options
- # see http://lists.freedesktop.org/archives/cairo/2007-February/009688.html
- context = layout.get_context()
- fo = cairo.FontOptions()
- fo.set_antialias(cairo.ANTIALIAS_DEFAULT)
- fo.set_hint_style(cairo.HINT_STYLE_NONE)
- fo.set_hint_metrics(cairo.HINT_METRICS_OFF)
- try:
- pangocairo.context_set_font_options(context, fo)
- except TypeError:
- # XXX: Some broken pangocairo bindings show the error
- # 'TypeError: font_options must be a cairo.FontOptions or None'
- pass
-
- # set font
- font = pango.FontDescription()
- font.set_family(self.pen.fontname)
- font.set_absolute_size(self.pen.fontsize*pango.SCALE)
- layout.set_font_description(font)
-
- # set text
- layout.set_text(self.t)
-
- # cache it
- self.layout = layout
- else:
- cr.update_layout(layout)
-
- descent = 2 # XXX get descender from font metrics
-
- width, height = layout.get_size()
- width = float(width)/pango.SCALE
- height = float(height)/pango.SCALE
- # we know the width that dot thinks this text should have
- # we do not necessarily have a font with the same metrics
- # scale it so that the text fits inside its box
- if width > self.w:
- f = self.w / width
- width = self.w # equivalent to width *= f
- height *= f
- descent *= f
- else:
- f = 1.0
-
- if self.j == self.LEFT:
- x = self.x
- elif self.j == self.CENTER:
- x = self.x - 0.5*width
- elif self.j == self.RIGHT:
- x = self.x - width
- else:
- assert 0
-
- y = self.y - height + descent
-
- cr.move_to(x, y)
-
- cr.save()
- cr.scale(f, f)
- cr.set_source_rgba(*self.select_pen(highlight).color)
- cr.show_layout(layout)
- cr.restore()
-
- if 0: # DEBUG
- # show where dot thinks the text should appear
- cr.set_source_rgba(1, 0, 0, .9)
- if self.j == self.LEFT:
- x = self.x
- elif self.j == self.CENTER:
- x = self.x - 0.5*self.w
- elif self.j == self.RIGHT:
- x = self.x - self.w
- cr.move_to(x, self.y)
- cr.line_to(x+self.w, self.y)
- cr.stroke()
-
-
-class ImageShape(Shape):
-
- def __init__(self, pen, x0, y0, w, h, path):
- Shape.__init__(self)
- self.pen = pen.copy()
- self.x0 = x0
- self.y0 = y0
- self.w = w
- self.h = h
- self.path = path
-
- def draw(self, cr, highlight=False):
- cr2 = gtk.gdk.CairoContext(cr)
- pixbuf = gtk.gdk.pixbuf_new_from_file(self.path)
- sx = float(self.w)/float(pixbuf.get_width())
- sy = float(self.h)/float(pixbuf.get_height())
- cr.save()
- cr.translate(self.x0, self.y0 - self.h)
- cr.scale(sx, sy)
- cr2.set_source_pixbuf(pixbuf, 0, 0)
- cr2.paint()
- cr.restore()
-
-
-class EllipseShape(Shape):
-
- def __init__(self, pen, x0, y0, w, h, filled=False):
- Shape.__init__(self)
- self.pen = pen.copy()
- self.x0 = x0
- self.y0 = y0
- self.w = w
- self.h = h
- self.filled = filled
-
- def draw(self, cr, highlight=False):
- cr.save()
- cr.translate(self.x0, self.y0)
- cr.scale(self.w, self.h)
- cr.move_to(1.0, 0.0)
- cr.arc(0.0, 0.0, 1.0, 0, 2.0*math.pi)
- cr.restore()
- pen = self.select_pen(highlight)
- if self.filled:
- cr.set_source_rgba(*pen.fillcolor)
- cr.fill()
- else:
- cr.set_dash(pen.dash)
- cr.set_line_width(pen.linewidth)
- cr.set_source_rgba(*pen.color)
- cr.stroke()
-
-
-class PolygonShape(Shape):
-
- def __init__(self, pen, points, filled=False):
- Shape.__init__(self)
- self.pen = pen.copy()
- self.points = points
- self.filled = filled
-
- def draw(self, cr, highlight=False):
- x0, y0 = self.points[-1]
- cr.move_to(x0, y0)
- for x, y in self.points:
- cr.line_to(x, y)
- cr.close_path()
- pen = self.select_pen(highlight)
- if self.filled:
- cr.set_source_rgba(*pen.fillcolor)
- cr.fill_preserve()
- cr.fill()
- else:
- cr.set_dash(pen.dash)
- cr.set_line_width(pen.linewidth)
- cr.set_source_rgba(*pen.color)
- cr.stroke()
-
-
-class LineShape(Shape):
-
- def __init__(self, pen, points):
- Shape.__init__(self)
- self.pen = pen.copy()
- self.points = points
-
- def draw(self, cr, highlight=False):
- x0, y0 = self.points[0]
- cr.move_to(x0, y0)
- for x1, y1 in self.points[1:]:
- cr.line_to(x1, y1)
- pen = self.select_pen(highlight)
- cr.set_dash(pen.dash)
- cr.set_line_width(pen.linewidth)
- cr.set_source_rgba(*pen.color)
- cr.stroke()
-
-
-class BezierShape(Shape):
-
- def __init__(self, pen, points, filled=False):
- Shape.__init__(self)
- self.pen = pen.copy()
- self.points = points
- self.filled = filled
-
- def draw(self, cr, highlight=False):
- x0, y0 = self.points[0]
- cr.move_to(x0, y0)
- for i in xrange(1, len(self.points), 3):
- x1, y1 = self.points[i]
- x2, y2 = self.points[i + 1]
- x3, y3 = self.points[i + 2]
- cr.curve_to(x1, y1, x2, y2, x3, y3)
- pen = self.select_pen(highlight)
- if self.filled:
- cr.set_source_rgba(*pen.fillcolor)
- cr.fill_preserve()
- cr.fill()
- else:
- cr.set_dash(pen.dash)
- cr.set_line_width(pen.linewidth)
- cr.set_source_rgba(*pen.color)
- cr.stroke()
-
-
-class CompoundShape(Shape):
-
- def __init__(self, shapes):
- Shape.__init__(self)
- self.shapes = shapes
-
- def draw(self, cr, highlight=False):
- for shape in self.shapes:
- shape.draw(cr, highlight=highlight)
-
-
-class Url(object):
-
- def __init__(self, item, url, highlight=None):
- self.item = item
- self.url = url
- if highlight is None:
- highlight = set([item])
- self.highlight = highlight
-
-
-class Jump(object):
-
- def __init__(self, item, x, y, highlight=None):
- self.item = item
- self.x = x
- self.y = y
- if highlight is None:
- highlight = set([item])
- self.highlight = highlight
-
-
-class Element(CompoundShape):
- """Base class for graph nodes and edges."""
-
- def __init__(self, shapes):
- CompoundShape.__init__(self, shapes)
-
- def get_url(self, x, y):
- return None
-
- def get_jump(self, x, y):
- return None
-
-
-class Node(Element):
-
- def __init__(self, x, y, w, h, shapes, url):
- Element.__init__(self, shapes)
-
- self.x = x
- self.y = y
-
- self.x1 = x - 0.5*w
- self.y1 = y - 0.5*h
- self.x2 = x + 0.5*w
- self.y2 = y + 0.5*h
-
- self.url = url
-
- def is_inside(self, x, y):
- return self.x1 <= x and x <= self.x2 and self.y1 <= y and y <= self.y2
-
- def get_url(self, x, y):
- if self.url is None:
- return None
- #print (x, y), (self.x1, self.y1), "-", (self.x2, self.y2)
- if self.is_inside(x, y):
- return Url(self, self.url)
- return None
-
- def get_jump(self, x, y):
- if self.is_inside(x, y):
- return Jump(self, self.x, self.y)
- return None
-
-
-def square_distance(x1, y1, x2, y2):
- deltax = x2 - x1
- deltay = y2 - y1
- return deltax*deltax + deltay*deltay
-
-
-class Edge(Element):
-
- def __init__(self, src, dst, points, shapes):
- Element.__init__(self, shapes)
- self.src = src
- self.dst = dst
- self.points = points
-
- RADIUS = 10
-
- def get_jump(self, x, y):
- if square_distance(x, y, *self.points[0]) <= self.RADIUS*self.RADIUS:
- return Jump(self, self.dst.x, self.dst.y, highlight=set([self, self.dst]))
- if square_distance(x, y, *self.points[-1]) <= self.RADIUS*self.RADIUS:
- return Jump(self, self.src.x, self.src.y, highlight=set([self, self.src]))
- return None
-
-
-class Graph(Shape):
-
- def __init__(self, width=1, height=1, shapes=(), nodes=(), edges=()):
- Shape.__init__(self)
-
- self.width = width
- self.height = height
- self.shapes = shapes
- self.nodes = nodes
- self.edges = edges
-
- def get_size(self):
- return self.width, self.height
-
- def draw(self, cr, highlight_items=None):
- if highlight_items is None:
- highlight_items = ()
- cr.set_source_rgba(0.0, 0.0, 0.0, 1.0)
-
- cr.set_line_cap(cairo.LINE_CAP_BUTT)
- cr.set_line_join(cairo.LINE_JOIN_MITER)
-
- for shape in self.shapes:
- shape.draw(cr)
- for edge in self.edges:
- edge.draw(cr, highlight=(edge in highlight_items))
- for node in self.nodes:
- node.draw(cr, highlight=(node in highlight_items))
-
- def get_url(self, x, y):
- for node in self.nodes:
- url = node.get_url(x, y)
- if url is not None:
- return url
- return None
-
- def get_jump(self, x, y):
- for edge in self.edges:
- jump = edge.get_jump(x, y)
- if jump is not None:
- return jump
- for node in self.nodes:
- jump = node.get_jump(x, y)
- if jump is not None:
- return jump
- return None
-
-
-class XDotAttrParser:
- """Parser for xdot drawing attributes.
- See also:
- - http://www.graphviz.org/doc/info/output.html#d:xdot
- """
-
- def __init__(self, parser, buf):
- self.parser = parser
- self.buf = self.unescape(buf)
- self.pos = 0
-
- self.pen = Pen()
- self.shapes = []
-
- def __nonzero__(self):
- return self.pos < len(self.buf)
-
- def unescape(self, buf):
- buf = buf.replace('\\"', '"')
- buf = buf.replace('\\n', '\n')
- return buf
-
- def read_code(self):
- pos = self.buf.find(" ", self.pos)
- res = self.buf[self.pos:pos]
- self.pos = pos + 1
- while self.pos < len(self.buf) and self.buf[self.pos].isspace():
- self.pos += 1
- return res
-
- def read_number(self):
- return int(self.read_code())
-
- def read_float(self):
- return float(self.read_code())
-
- def read_point(self):
- x = self.read_number()
- y = self.read_number()
- return self.transform(x, y)
-
- def read_text(self):
- num = self.read_number()
- pos = self.buf.find("-", self.pos) + 1
- self.pos = pos + num
- res = self.buf[pos:self.pos]
- while self.pos < len(self.buf) and self.buf[self.pos].isspace():
- self.pos += 1
- return res
-
- def read_polygon(self):
- n = self.read_number()
- p = []
- for i in range(n):
- x, y = self.read_point()
- p.append((x, y))
- return p
-
- def read_color(self):
- # See http://www.graphviz.org/doc/info/attrs.html#k:color
- c = self.read_text()
- c1 = c[:1]
- if c1 == '#':
- hex2float = lambda h: float(int(h, 16)/255.0)
- r = hex2float(c[1:3])
- g = hex2float(c[3:5])
- b = hex2float(c[5:7])
- try:
- a = hex2float(c[7:9])
- except (IndexError, ValueError):
- a = 1.0
- return r, g, b, a
- elif c1.isdigit() or c1 == ".":
- # "H,S,V" or "H S V" or "H, S, V" or any other variation
- h, s, v = map(float, c.replace(",", " ").split())
- r, g, b = colorsys.hsv_to_rgb(h, s, v)
- a = 1.0
- return r, g, b, a
- else:
- return self.lookup_color(c)
-
- def lookup_color(self, c):
- try:
- color = gtk.gdk.color_parse(c)
- except ValueError:
- pass
- else:
- s = 1.0/65535.0
- r = color.red*s
- g = color.green*s
- b = color.blue*s
- a = 1.0
- return r, g, b, a
-
- try:
- dummy, scheme, index = c.split('/')
- r, g, b = brewer_colors[scheme][int(index)]
- except (ValueError, KeyError):
- pass
- else:
- s = 1.0/255.0
- r = r*s
- g = g*s
- b = b*s
- a = 1.0
- return r, g, b, a
-
- sys.stderr.write("unknown color '%s'\n" % c)
- return None
-
- def parse(self):
- s = self
-
- while s:
- op = s.read_code()
- if op == "c":
- color = s.read_color()
- if color is not None:
- self.handle_color(color, filled=False)
- elif op == "C":
- color = s.read_color()
- if color is not None:
- self.handle_color(color, filled=True)
- elif op == "S":
- # http://www.graphviz.org/doc/info/attrs.html#k:style
- style = s.read_text()
- if style.startswith("setlinewidth("):
- lw = style.split("(")[1].split(")")[0]
- lw = float(lw)
- self.handle_linewidth(lw)
- elif style in ("solid", "dashed", "dotted"):
- self.handle_linestyle(style)
- elif op == "F":
- size = s.read_float()
- name = s.read_text()
- self.handle_font(size, name)
- elif op == "T":
- x, y = s.read_point()
- j = s.read_number()
- w = s.read_number()
- t = s.read_text()
- self.handle_text(x, y, j, w, t)
- elif op == "E":
- x0, y0 = s.read_point()
- w = s.read_number()
- h = s.read_number()
- self.handle_ellipse(x0, y0, w, h, filled=True)
- elif op == "e":
- x0, y0 = s.read_point()
- w = s.read_number()
- h = s.read_number()
- self.handle_ellipse(x0, y0, w, h, filled=False)
- elif op == "L":
- points = self.read_polygon()
- self.handle_line(points)
- elif op == "B":
- points = self.read_polygon()
- self.handle_bezier(points, filled=False)
- elif op == "b":
- points = self.read_polygon()
- self.handle_bezier(points, filled=True)
- elif op == "P":
- points = self.read_polygon()
- self.handle_polygon(points, filled=True)
- elif op == "p":
- points = self.read_polygon()
- self.handle_polygon(points, filled=False)
- elif op == "I":
- x0, y0 = s.read_point()
- w = s.read_number()
- h = s.read_number()
- path = s.read_text()
- self.handle_image(x0, y0, w, h, path)
- else:
- sys.stderr.write("unknown xdot opcode '%s'\n" % op)
- break
-
- return self.shapes
-
- def transform(self, x, y):
- return self.parser.transform(x, y)
-
- def handle_color(self, color, filled=False):
- if filled:
- self.pen.fillcolor = color
- else:
- self.pen.color = color
-
- def handle_linewidth(self, linewidth):
- self.pen.linewidth = linewidth
-
- def handle_linestyle(self, style):
- if style == "solid":
- self.pen.dash = ()
- elif style == "dashed":
- self.pen.dash = (6, ) # 6pt on, 6pt off
- elif style == "dotted":
- self.pen.dash = (2, 4) # 2pt on, 4pt off
-
- def handle_font(self, size, name):
- self.pen.fontsize = size
- self.pen.fontname = name
-
- def handle_text(self, x, y, j, w, t):
- self.shapes.append(TextShape(self.pen, x, y, j, w, t))
-
- def handle_ellipse(self, x0, y0, w, h, filled=False):
- if filled:
- # xdot uses this to mean "draw a filled shape with an outline"
- self.shapes.append(EllipseShape(self.pen, x0, y0, w, h, filled=True))
- self.shapes.append(EllipseShape(self.pen, x0, y0, w, h))
-
- def handle_image(self, x0, y0, w, h, path):
- self.shapes.append(ImageShape(self.pen, x0, y0, w, h, path))
-
- def handle_line(self, points):
- self.shapes.append(LineShape(self.pen, points))
-
- def handle_bezier(self, points, filled=False):
- if filled:
- # xdot uses this to mean "draw a filled shape with an outline"
- self.shapes.append(BezierShape(self.pen, points, filled=True))
- self.shapes.append(BezierShape(self.pen, points))
-
- def handle_polygon(self, points, filled=False):
- if filled:
- # xdot uses this to mean "draw a filled shape with an outline"
- self.shapes.append(PolygonShape(self.pen, points, filled=True))
- self.shapes.append(PolygonShape(self.pen, points))
-
-
-EOF = -1
-SKIP = -2
-
-
-class ParseError(Exception):
-
- def __init__(self, msg=None, filename=None, line=None, col=None):
- self.msg = msg
- self.filename = filename
- self.line = line
- self.col = col
-
- def __str__(self):
- return ':'.join([str(part) for part in (self.filename, self.line, self.col, self.msg) if part != None])
-
-
-class Scanner:
- """Stateless scanner."""
-
- # should be overriden by derived classes
- tokens = []
- symbols = {}
- literals = {}
- ignorecase = False
-
- def __init__(self):
- flags = re.DOTALL
- if self.ignorecase:
- flags |= re.IGNORECASE
- self.tokens_re = re.compile(
- '|'.join(['(' + regexp + ')' for type, regexp, test_lit in self.tokens]),
- flags
- )
-
- def next(self, buf, pos):
- if pos >= len(buf):
- return EOF, '', pos
- mo = self.tokens_re.match(buf, pos)
- if mo:
- text = mo.group()
- type, regexp, test_lit = self.tokens[mo.lastindex - 1]
- pos = mo.end()
- if test_lit:
- type = self.literals.get(text, type)
- return type, text, pos
- else:
- c = buf[pos]
- return self.symbols.get(c, None), c, pos + 1
-
-
-class Token:
-
- def __init__(self, type, text, line, col):
- self.type = type
- self.text = text
- self.line = line
- self.col = col
-
-
-class Lexer:
-
- # should be overriden by derived classes
- scanner = None
- tabsize = 8
-
- newline_re = re.compile(r'\r\n?|\n')
-
- def __init__(self, buf = None, pos = 0, filename = None, fp = None):
- if fp is not None:
- try:
- fileno = fp.fileno()
- length = os.path.getsize(fp.name)
- import mmap
- except:
- # read whole file into memory
- buf = fp.read()
- pos = 0
- else:
- # map the whole file into memory
- if length:
- # length must not be zero
- buf = mmap.mmap(fileno, length, access = mmap.ACCESS_READ)
- pos = os.lseek(fileno, 0, 1)
- else:
- buf = ''
- pos = 0
-
- if filename is None:
- try:
- filename = fp.name
- except AttributeError:
- filename = None
-
- self.buf = buf
- self.pos = pos
- self.line = 1
- self.col = 1
- self.filename = filename
-
- def next(self):
- while True:
- # save state
- pos = self.pos
- line = self.line
- col = self.col
-
- type, text, endpos = self.scanner.next(self.buf, pos)
- assert pos + len(text) == endpos
- self.consume(text)
- type, text = self.filter(type, text)
- self.pos = endpos
-
- if type == SKIP:
- continue
- elif type is None:
- msg = 'unexpected char '
- if text >= ' ' and text <= '~':
- msg += "'%s'" % text
- else:
- msg += "0x%X" % ord(text)
- raise ParseError(msg, self.filename, line, col)
- else:
- break
- return Token(type = type, text = text, line = line, col = col)
-
- def consume(self, text):
- # update line number
- pos = 0
- for mo in self.newline_re.finditer(text, pos):
- self.line += 1
- self.col = 1
- pos = mo.end()
-
- # update column number
- while True:
- tabpos = text.find('\t', pos)
- if tabpos == -1:
- break
- self.col += tabpos - pos
- self.col = ((self.col - 1)//self.tabsize + 1)*self.tabsize + 1
- pos = tabpos + 1
- self.col += len(text) - pos
-
-
-class Parser:
-
- def __init__(self, lexer):
- self.lexer = lexer
- self.lookahead = self.lexer.next()
-
- def match(self, type):
- if self.lookahead.type != type:
- raise ParseError(
- msg = 'unexpected token %r' % self.lookahead.text,
- filename = self.lexer.filename,
- line = self.lookahead.line,
- col = self.lookahead.col)
-
- def skip(self, type):
- while self.lookahead.type != type:
- self.consume()
-
- def consume(self):
- token = self.lookahead
- self.lookahead = self.lexer.next()
- return token
-
-
-ID = 0
-STR_ID = 1
-HTML_ID = 2
-EDGE_OP = 3
-
-LSQUARE = 4
-RSQUARE = 5
-LCURLY = 6
-RCURLY = 7
-COMMA = 8
-COLON = 9
-SEMI = 10
-EQUAL = 11
-PLUS = 12
-
-STRICT = 13
-GRAPH = 14
-DIGRAPH = 15
-NODE = 16
-EDGE = 17
-SUBGRAPH = 18
-
-
-class DotScanner(Scanner):
-
- # token regular expression table
- tokens = [
- # whitespace and comments
- (SKIP,
- r'[ \t\f\r\n\v]+|'
- r'//[^\r\n]*|'
- r'/\*.*?\*/|'
- r'#[^\r\n]*',
- False),
-
- # Alphanumeric IDs
- (ID, r'[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*', True),
-
- # Numeric IDs
- (ID, r'-?(?:\.[0-9]+|[0-9]+(?:\.[0-9]*)?)', False),
-
- # String IDs
- (STR_ID, r'"[^"\\]*(?:\\.[^"\\]*)*"', False),
-
- # HTML IDs
- (HTML_ID, r'<[^<>]*(?:<[^<>]*>[^<>]*)*>', False),
-
- # Edge operators
- (EDGE_OP, r'-[>-]', False),
- ]
-
- # symbol table
- symbols = {
- '[': LSQUARE,
- ']': RSQUARE,
- '{': LCURLY,
- '}': RCURLY,
- ',': COMMA,
- ':': COLON,
- ';': SEMI,
- '=': EQUAL,
- '+': PLUS,
- }
-
- # literal table
- literals = {
- 'strict': STRICT,
- 'graph': GRAPH,
- 'digraph': DIGRAPH,
- 'node': NODE,
- 'edge': EDGE,
- 'subgraph': SUBGRAPH,
- }
-
- ignorecase = True
-
-
-class DotLexer(Lexer):
-
- scanner = DotScanner()
-
- def filter(self, type, text):
- # TODO: handle charset
- if type == STR_ID:
- text = text[1:-1]
-
- # line continuations
- text = text.replace('\\\r\n', '')
- text = text.replace('\\\r', '')
- text = text.replace('\\\n', '')
-
- text = text.replace('\\r', '\r')
- text = text.replace('\\n', '\n')
- text = text.replace('\\t', '\t')
- text = text.replace('\\', '')
-
- type = ID
-
- elif type == HTML_ID:
- text = text[1:-1]
- type = ID
-
- return type, text
-
-
-class DotParser(Parser):
-
- def __init__(self, lexer):
- Parser.__init__(self, lexer)
- self.graph_attrs = {}
- self.node_attrs = {}
- self.edge_attrs = {}
-
- def parse(self):
- self.parse_graph()
- self.match(EOF)
-
- def parse_graph(self):
- if self.lookahead.type == STRICT:
- self.consume()
- self.skip(LCURLY)
- self.consume()
- while self.lookahead.type != RCURLY:
- self.parse_stmt()
- self.consume()
-
- def parse_subgraph(self):
- id = None
- if self.lookahead.type == SUBGRAPH:
- self.consume()
- if self.lookahead.type == ID:
- id = self.lookahead.text
- self.consume()
- if self.lookahead.type == LCURLY:
- self.consume()
- while self.lookahead.type != RCURLY:
- self.parse_stmt()
- self.consume()
- return id
-
- def parse_stmt(self):
- if self.lookahead.type == GRAPH:
- self.consume()
- attrs = self.parse_attrs()
- self.graph_attrs.update(attrs)
- self.handle_graph(attrs)
- elif self.lookahead.type == NODE:
- self.consume()
- self.node_attrs.update(self.parse_attrs())
- elif self.lookahead.type == EDGE:
- self.consume()
- self.edge_attrs.update(self.parse_attrs())
- elif self.lookahead.type in (SUBGRAPH, LCURLY):
- self.parse_subgraph()
- else:
- id = self.parse_node_id()
- if self.lookahead.type == EDGE_OP:
- self.consume()
- node_ids = [id, self.parse_node_id()]
- while self.lookahead.type == EDGE_OP:
- node_ids.append(self.parse_node_id())
- attrs = self.parse_attrs()
- for i in range(0, len(node_ids) - 1):
- self.handle_edge(node_ids[i], node_ids[i + 1], attrs)
- elif self.lookahead.type == EQUAL:
- self.consume()
- self.parse_id()
- else:
- attrs = self.parse_attrs()
- self.handle_node(id, attrs)
- if self.lookahead.type == SEMI:
- self.consume()
-
- def parse_attrs(self):
- attrs = {}
- while self.lookahead.type == LSQUARE:
- self.consume()
- while self.lookahead.type != RSQUARE:
- name, value = self.parse_attr()
- attrs[name] = value
- if self.lookahead.type == COMMA:
- self.consume()
- self.consume()
- return attrs
-
- def parse_attr(self):
- name = self.parse_id()
- if self.lookahead.type == EQUAL:
- self.consume()
- value = self.parse_id()
- else:
- value = 'true'
- return name, value
-
- def parse_node_id(self):
- node_id = self.parse_id()
- if self.lookahead.type == COLON:
- self.consume()
- port = self.parse_id()
- if self.lookahead.type == COLON:
- self.consume()
- compass_pt = self.parse_id()
- else:
- compass_pt = None
- else:
- port = None
- compass_pt = None
- # XXX: we don't really care about port and compass point values when parsing xdot
- return node_id
-
- def parse_id(self):
- self.match(ID)
- id = self.lookahead.text
- self.consume()
- return id
-
- def handle_graph(self, attrs):
- pass
-
- def handle_node(self, id, attrs):
- pass
-
- def handle_edge(self, src_id, dst_id, attrs):
- pass
-
-
-class XDotParser(DotParser):
-
- def __init__(self, xdotcode):
- lexer = DotLexer(buf = xdotcode)
- DotParser.__init__(self, lexer)
-
- self.nodes = []
- self.edges = []
- self.shapes = []
- self.node_by_name = {}
- self.top_graph = True
-
- def handle_graph(self, attrs):
- if self.top_graph:
- try:
- bb = attrs['bb']
- except KeyError:
- return
-
- if not bb:
- return
-
- xmin, ymin, xmax, ymax = map(float, bb.split(","))
-
- self.xoffset = -xmin
- self.yoffset = -ymax
- self.xscale = 1.0
- self.yscale = -1.0
- # FIXME: scale from points to pixels
-
- self.width = max(xmax - xmin, 1)
- self.height = max(ymax - ymin, 1)
-
- self.top_graph = False
-
- for attr in ("_draw_", "_ldraw_", "_hdraw_", "_tdraw_", "_hldraw_", "_tldraw_"):
- if attr in attrs:
- parser = XDotAttrParser(self, attrs[attr])
- self.shapes.extend(parser.parse())
-
- def handle_node(self, id, attrs):
- try:
- pos = attrs['pos']
- except KeyError:
- return
-
- x, y = self.parse_node_pos(pos)
- w = float(attrs.get('width', 0))*72
- h = float(attrs.get('height', 0))*72
- shapes = []
- for attr in ("_draw_", "_ldraw_"):
- if attr in attrs:
- parser = XDotAttrParser(self, attrs[attr])
- shapes.extend(parser.parse())
- url = attrs.get('URL', None)
- node = Node(x, y, w, h, shapes, url)
- self.node_by_name[id] = node
- if shapes:
- self.nodes.append(node)
-
- def handle_edge(self, src_id, dst_id, attrs):
- try:
- pos = attrs['pos']
- except KeyError:
- return
-
- points = self.parse_edge_pos(pos)
- shapes = []
- for attr in ("_draw_", "_ldraw_", "_hdraw_", "_tdraw_", "_hldraw_", "_tldraw_"):
- if attr in attrs:
- parser = XDotAttrParser(self, attrs[attr])
- shapes.extend(parser.parse())
- if shapes:
- src = self.node_by_name[src_id]
- dst = self.node_by_name[dst_id]
- self.edges.append(Edge(src, dst, points, shapes))
-
- def parse(self):
- DotParser.parse(self)
-
- return Graph(self.width, self.height, self.shapes, self.nodes, self.edges)
-
- def parse_node_pos(self, pos):
- x, y = pos.split(",")
- return self.transform(float(x), float(y))
-
- def parse_edge_pos(self, pos):
- points = []
- for entry in pos.split(' '):
- fields = entry.split(',')
- try:
- x, y = fields
- except ValueError:
- # TODO: handle start/end points
- continue
- else:
- points.append(self.transform(float(x), float(y)))
- return points
-
- def transform(self, x, y):
- # XXX: this is not the right place for this code
- x = (x + self.xoffset)*self.xscale
- y = (y + self.yoffset)*self.yscale
- return x, y
-
-
-class Animation(object):
-
- step = 0.03 # seconds
-
- def __init__(self, dot_widget):
- self.dot_widget = dot_widget
- self.timeout_id = None
-
- def start(self):
- self.timeout_id = gobject.timeout_add(int(self.step * 1000), self.tick)
-
- def stop(self):
- self.dot_widget.animation = NoAnimation(self.dot_widget)
- if self.timeout_id is not None:
- gobject.source_remove(self.timeout_id)
- self.timeout_id = None
-
- def tick(self):
- self.stop()
-
-
-class NoAnimation(Animation):
-
- def start(self):
- pass
-
- def stop(self):
- pass
-
-
-class LinearAnimation(Animation):
-
- duration = 0.6
-
- def start(self):
- self.started = time.time()
- Animation.start(self)
-
- def tick(self):
- t = (time.time() - self.started) / self.duration
- self.animate(max(0, min(t, 1)))
- return (t < 1)
-
- def animate(self, t):
- pass
-
-
-class MoveToAnimation(LinearAnimation):
-
- def __init__(self, dot_widget, target_x, target_y):
- Animation.__init__(self, dot_widget)
- self.source_x = dot_widget.x
- self.source_y = dot_widget.y
- self.target_x = target_x
- self.target_y = target_y
-
- def animate(self, t):
- sx, sy = self.source_x, self.source_y
- tx, ty = self.target_x, self.target_y
- self.dot_widget.x = tx * t + sx * (1-t)
- self.dot_widget.y = ty * t + sy * (1-t)
- self.dot_widget.queue_draw()
-
-
-class ZoomToAnimation(MoveToAnimation):
-
- def __init__(self, dot_widget, target_x, target_y):
- MoveToAnimation.__init__(self, dot_widget, target_x, target_y)
- self.source_zoom = dot_widget.zoom_ratio
- self.target_zoom = self.source_zoom
- self.extra_zoom = 0
-
- middle_zoom = 0.5 * (self.source_zoom + self.target_zoom)
-
- distance = math.hypot(self.source_x - self.target_x,
- self.source_y - self.target_y)
- rect = self.dot_widget.get_allocation()
- visible = min(rect.width, rect.height) / self.dot_widget.zoom_ratio
- visible *= 0.9
- if distance > 0:
- desired_middle_zoom = visible / distance
- self.extra_zoom = min(0, 4 * (desired_middle_zoom - middle_zoom))
-
- def animate(self, t):
- a, b, c = self.source_zoom, self.extra_zoom, self.target_zoom
- self.dot_widget.zoom_ratio = c*t + b*t*(1-t) + a*(1-t)
- self.dot_widget.zoom_to_fit_on_resize = False
- MoveToAnimation.animate(self, t)
-
-
-class DragAction(object):
-
- def __init__(self, dot_widget):
- self.dot_widget = dot_widget
-
- def on_button_press(self, event):
- self.startmousex = self.prevmousex = event.x
- self.startmousey = self.prevmousey = event.y
- self.start()
-
- def on_motion_notify(self, event):
- if event.is_hint:
- x, y, state = event.window.get_pointer()
- else:
- x, y, state = event.x, event.y, event.state
- deltax = self.prevmousex - x
- deltay = self.prevmousey - y
- self.drag(deltax, deltay)
- self.prevmousex = x
- self.prevmousey = y
-
- def on_button_release(self, event):
- self.stopmousex = event.x
- self.stopmousey = event.y
- self.stop()
-
- def draw(self, cr):
- pass
-
- def start(self):
- pass
-
- def drag(self, deltax, deltay):
- pass
-
- def stop(self):
- pass
-
- def abort(self):
- pass
-
-
-class NullAction(DragAction):
-
- def on_motion_notify(self, event):
- if event.is_hint:
- x, y, state = event.window.get_pointer()
- else:
- x, y, state = event.x, event.y, event.state
- dot_widget = self.dot_widget
- item = dot_widget.get_url(x, y)
- if item is None:
- item = dot_widget.get_jump(x, y)
- if item is not None:
- dot_widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND2))
- dot_widget.set_highlight(item.highlight)
- else:
- dot_widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.ARROW))
- dot_widget.set_highlight(None)
-
-
-class PanAction(DragAction):
-
- def start(self):
- self.dot_widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.FLEUR))
-
- def drag(self, deltax, deltay):
- self.dot_widget.x += deltax / self.dot_widget.zoom_ratio
- self.dot_widget.y += deltay / self.dot_widget.zoom_ratio
- self.dot_widget.queue_draw()
-
- def stop(self):
- self.dot_widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.ARROW))
-
- abort = stop
-
-
-class ZoomAction(DragAction):
-
- def drag(self, deltax, deltay):
- self.dot_widget.zoom_ratio *= 1.005 ** (deltax + deltay)
- self.dot_widget.zoom_to_fit_on_resize = False
- self.dot_widget.queue_draw()
-
- def stop(self):
- self.dot_widget.queue_draw()
-
-
-class ZoomAreaAction(DragAction):
-
- def drag(self, deltax, deltay):
- self.dot_widget.queue_draw()
-
- def draw(self, cr):
- cr.save()
- cr.set_source_rgba(.5, .5, 1.0, 0.25)
- cr.rectangle(self.startmousex, self.startmousey,
- self.prevmousex - self.startmousex,
- self.prevmousey - self.startmousey)
- cr.fill()
- cr.set_source_rgba(.5, .5, 1.0, 1.0)
- cr.set_line_width(1)
- cr.rectangle(self.startmousex - .5, self.startmousey - .5,
- self.prevmousex - self.startmousex + 1,
- self.prevmousey - self.startmousey + 1)
- cr.stroke()
- cr.restore()
-
- def stop(self):
- x1, y1 = self.dot_widget.window2graph(self.startmousex,
- self.startmousey)
- x2, y2 = self.dot_widget.window2graph(self.stopmousex,
- self.stopmousey)
- self.dot_widget.zoom_to_area(x1, y1, x2, y2)
-
- def abort(self):
- self.dot_widget.queue_draw()
-
-
-class DotWidget(gtk.DrawingArea):
- """PyGTK widget that draws dot graphs."""
-
- __gsignals__ = {
- 'expose-event': 'override',
- 'clicked' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_STRING, gtk.gdk.Event))
- }
-
- filter = 'dot'
-
- def __init__(self):
- gtk.DrawingArea.__init__(self)
-
- self.graph = Graph()
- self.openfilename = None
-
- self.set_flags(gtk.CAN_FOCUS)
-
- self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK)
- self.connect("button-press-event", self.on_area_button_press)
- self.connect("button-release-event", self.on_area_button_release)
- self.add_events(gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.POINTER_MOTION_HINT_MASK | gtk.gdk.BUTTON_RELEASE_MASK)
- self.connect("motion-notify-event", self.on_area_motion_notify)
- self.connect("scroll-event", self.on_area_scroll_event)
- self.connect("size-allocate", self.on_area_size_allocate)
-
- self.connect('key-press-event', self.on_key_press_event)
-
- self.x, self.y = 0.0, 0.0
- self.zoom_ratio = 1.0
- self.zoom_to_fit_on_resize = False
- self.animation = NoAnimation(self)
- self.drag_action = NullAction(self)
- self.presstime = None
- self.highlight = None
-
- def set_filter(self, filter):
- self.filter = filter
-
- def run_filter(self, dotcode):
- if not self.filter:
- return dotcode
- p = subprocess.Popen(
- [self.filter, '-Txdot', '-Goverlap=scale'],
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- shell=False,
- universal_newlines=True
- )
- xdotcode, error = p.communicate(dotcode)
- sys.stderr.write(error)
- if p.returncode != 0:
- dialog = gtk.MessageDialog(type=gtk.MESSAGE_ERROR,
- message_format=error,
- buttons=gtk.BUTTONS_OK)
- dialog.set_title('Dot Viewer')
- dialog.run()
- dialog.destroy()
- return None
- return xdotcode
-
- def set_dotcode(self, dotcode, filename='<stdin>'):
- if isinstance(dotcode, unicode):
- dotcode = dotcode.encode('utf8')
- xdotcode = self.run_filter(dotcode)
- if xdotcode is None:
- return False
- try:
- self.set_xdotcode(xdotcode)
- except ParseError, ex:
- dialog = gtk.MessageDialog(type=gtk.MESSAGE_ERROR,
- message_format=str(ex),
- buttons=gtk.BUTTONS_OK)
- dialog.set_title('Dot Viewer')
- dialog.run()
- dialog.destroy()
- return False
- else:
- self.openfilename = filename
- return True
-
- def set_xdotcode(self, xdotcode):
- #print xdotcode
- parser = XDotParser(xdotcode)
- self.graph = parser.parse()
- self.zoom_image(self.zoom_ratio, center=True)
-
- def reload(self):
- if self.openfilename is not None:
- try:
- fp = file(self.openfilename, 'rt')
- self.set_dotcode(fp.read(), self.openfilename)
- fp.close()
- except IOError:
- pass
-
- def do_expose_event(self, event):
- cr = self.window.cairo_create()
-
- # set a clip region for the expose event
- cr.rectangle(
- event.area.x, event.area.y,
- event.area.width, event.area.height
- )
- cr.clip()
-
- cr.set_source_rgba(1.0, 1.0, 1.0, 1.0)
- cr.paint()
-
- cr.save()
- rect = self.get_allocation()
- cr.translate(0.5*rect.width, 0.5*rect.height)
- cr.scale(self.zoom_ratio, self.zoom_ratio)
- cr.translate(-self.x, -self.y)
-
- self.graph.draw(cr, highlight_items=self.highlight)
- cr.restore()
-
- self.drag_action.draw(cr)
-
- return False
-
- def get_current_pos(self):
- return self.x, self.y
-
- def set_current_pos(self, x, y):
- self.x = x
- self.y = y
- self.queue_draw()
-
- def set_highlight(self, items):
- if self.highlight != items:
- self.highlight = items
- self.queue_draw()
-
- def zoom_image(self, zoom_ratio, center=False, pos=None):
- if center:
- self.x = self.graph.width/2
- self.y = self.graph.height/2
- elif pos is not None:
- rect = self.get_allocation()
- x, y = pos
- x -= 0.5*rect.width
- y -= 0.5*rect.height
- self.x += x / self.zoom_ratio - x / zoom_ratio
- self.y += y / self.zoom_ratio - y / zoom_ratio
- self.zoom_ratio = zoom_ratio
- self.zoom_to_fit_on_resize = False
- self.queue_draw()
-
- def zoom_to_area(self, x1, y1, x2, y2):
- rect = self.get_allocation()
- width = abs(x1 - x2)
- height = abs(y1 - y2)
- self.zoom_ratio = min(
- float(rect.width)/float(width),
- float(rect.height)/float(height)
- )
- self.zoom_to_fit_on_resize = False
- self.x = (x1 + x2) / 2
- self.y = (y1 + y2) / 2
- self.queue_draw()
-
- def zoom_to_fit(self):
- rect = self.get_allocation()
- rect.x += self.ZOOM_TO_FIT_MARGIN
- rect.y += self.ZOOM_TO_FIT_MARGIN
- rect.width -= 2 * self.ZOOM_TO_FIT_MARGIN
- rect.height -= 2 * self.ZOOM_TO_FIT_MARGIN
- zoom_ratio = min(
- float(rect.width)/float(self.graph.width),
- float(rect.height)/float(self.graph.height)
- )
- self.zoom_image(zoom_ratio, center=True)
- self.zoom_to_fit_on_resize = True
-
- ZOOM_INCREMENT = 1.25
- ZOOM_TO_FIT_MARGIN = 12
-
- def on_zoom_in(self, action):
- self.zoom_image(self.zoom_ratio * self.ZOOM_INCREMENT)
-
- def on_zoom_out(self, action):
- self.zoom_image(self.zoom_ratio / self.ZOOM_INCREMENT)
-
- def on_zoom_fit(self, action):
- self.zoom_to_fit()
-
- def on_zoom_100(self, action):
- self.zoom_image(1.0)
-
- POS_INCREMENT = 100
-
- def on_key_press_event(self, widget, event):
- if event.keyval == gtk.keysyms.Left:
- self.x -= self.POS_INCREMENT/self.zoom_ratio
- self.queue_draw()
- return True
- if event.keyval == gtk.keysyms.Right:
- self.x += self.POS_INCREMENT/self.zoom_ratio
- self.queue_draw()
- return True
- if event.keyval == gtk.keysyms.Up:
- self.y -= self.POS_INCREMENT/self.zoom_ratio
- self.queue_draw()
- return True
- if event.keyval == gtk.keysyms.Down:
- self.y += self.POS_INCREMENT/self.zoom_ratio
- self.queue_draw()
- return True
- if event.keyval in (gtk.keysyms.Page_Up,
- gtk.keysyms.plus,
- gtk.keysyms.equal,
- gtk.keysyms.KP_Add):
- self.zoom_image(self.zoom_ratio * self.ZOOM_INCREMENT)
- self.queue_draw()
- return True
- if event.keyval in (gtk.keysyms.Page_Down,
- gtk.keysyms.minus,
- gtk.keysyms.KP_Subtract):
- self.zoom_image(self.zoom_ratio / self.ZOOM_INCREMENT)
- self.queue_draw()
- return True
- if event.keyval == gtk.keysyms.Escape:
- self.drag_action.abort()
- self.drag_action = NullAction(self)
- return True
- if event.keyval == gtk.keysyms.r:
- self.reload()
- return True
- if event.keyval == gtk.keysyms.q:
- gtk.main_quit()
- return True
- return False
-
- def get_drag_action(self, event):
- state = event.state
- if event.button in (1, 2): # left or middle button
- if state & gtk.gdk.CONTROL_MASK:
- return ZoomAction
- elif state & gtk.gdk.SHIFT_MASK:
- return ZoomAreaAction
- else:
- return PanAction
- return NullAction
-
- def on_area_button_press(self, area, event):
- self.animation.stop()
- self.drag_action.abort()
- action_type = self.get_drag_action(event)
- self.drag_action = action_type(self)
- self.drag_action.on_button_press(event)
- self.presstime = time.time()
- self.pressx = event.x
- self.pressy = event.y
- return False
-
- def is_click(self, event, click_fuzz=4, click_timeout=1.0):
- assert event.type == gtk.gdk.BUTTON_RELEASE
- if self.presstime is None:
- # got a button release without seeing the press?
- return False
- # XXX instead of doing this complicated logic, shouldn't we listen
- # for gtk's clicked event instead?
- deltax = self.pressx - event.x
- deltay = self.pressy - event.y
- return (time.time() < self.presstime + click_timeout
- and math.hypot(deltax, deltay) < click_fuzz)
-
- def on_area_button_release(self, area, event):
- self.drag_action.on_button_release(event)
- self.drag_action = NullAction(self)
- if event.button == 1 and self.is_click(event):
- x, y = int(event.x), int(event.y)
- url = self.get_url(x, y)
- if url is not None:
- self.emit('clicked', unicode(url.url), event)
- else:
- jump = self.get_jump(x, y)
- if jump is not None:
- self.animate_to(jump.x, jump.y)
-
- return True
- if event.button == 1 or event.button == 2:
- return True
- return False
-
- def on_area_scroll_event(self, area, event):
- if event.direction == gtk.gdk.SCROLL_UP:
- self.zoom_image(self.zoom_ratio * self.ZOOM_INCREMENT,
- pos=(event.x, event.y))
- return True
- if event.direction == gtk.gdk.SCROLL_DOWN:
- self.zoom_image(self.zoom_ratio / self.ZOOM_INCREMENT,
- pos=(event.x, event.y))
- return True
- return False
-
- def on_area_motion_notify(self, area, event):
- self.drag_action.on_motion_notify(event)
- return True
-
- def on_area_size_allocate(self, area, allocation):
- if self.zoom_to_fit_on_resize:
- self.zoom_to_fit()
-
- def animate_to(self, x, y):
- self.animation = ZoomToAnimation(self, x, y)
- self.animation.start()
-
- def window2graph(self, x, y):
- rect = self.get_allocation()
- x -= 0.5*rect.width
- y -= 0.5*rect.height
- x /= self.zoom_ratio
- y /= self.zoom_ratio
- x += self.x
- y += self.y
- return x, y
-
- def get_url(self, x, y):
- x, y = self.window2graph(x, y)
- return self.graph.get_url(x, y)
-
- def get_jump(self, x, y):
- x, y = self.window2graph(x, y)
- return self.graph.get_jump(x, y)
-
-
-class DotWindow(gtk.Window):
-
- ui = '''
- <ui>
- <toolbar name="ToolBar">
- <toolitem action="Open"/>
- <toolitem action="Reload"/>
- <separator/>
- <toolitem action="ZoomIn"/>
- <toolitem action="ZoomOut"/>
- <toolitem action="ZoomFit"/>
- <toolitem action="Zoom100"/>
- </toolbar>
- </ui>
- '''
-
- def __init__(self):
- gtk.Window.__init__(self)
-
- self.graph = Graph()
-
- window = self
-
- window.set_title('Dot Viewer')
- window.set_default_size(512, 512)
- vbox = gtk.VBox()
- window.add(vbox)
-
- self.widget = DotWidget()
-
- # Create a UIManager instance
- uimanager = self.uimanager = gtk.UIManager()
-
- # Add the accelerator group to the toplevel window
- accelgroup = uimanager.get_accel_group()
- window.add_accel_group(accelgroup)
-
- # Create an ActionGroup
- actiongroup = gtk.ActionGroup('Actions')
- self.actiongroup = actiongroup
-
- # Create actions
- actiongroup.add_actions((
- ('Open', gtk.STOCK_OPEN, None, None, None, self.on_open),
- ('Reload', gtk.STOCK_REFRESH, None, None, None, self.on_reload),
- ('ZoomIn', gtk.STOCK_ZOOM_IN, None, None, None, self.widget.on_zoom_in),
- ('ZoomOut', gtk.STOCK_ZOOM_OUT, None, None, None, self.widget.on_zoom_out),
- ('ZoomFit', gtk.STOCK_ZOOM_FIT, None, None, None, self.widget.on_zoom_fit),
- ('Zoom100', gtk.STOCK_ZOOM_100, None, None, None, self.widget.on_zoom_100),
- ))
-
- # Add the actiongroup to the uimanager
- uimanager.insert_action_group(actiongroup, 0)
-
- # Add a UI descrption
- uimanager.add_ui_from_string(self.ui)
-
- # Create a Toolbar
- toolbar = uimanager.get_widget('/ToolBar')
- vbox.pack_start(toolbar, False)
-
- vbox.pack_start(self.widget)
-
- self.set_focus(self.widget)
-
- self.show_all()
-
- def update(self, filename):
- import os
- if not hasattr(self, "last_mtime"):
- self.last_mtime = None
-
- current_mtime = os.stat(filename).st_mtime
- if current_mtime != self.last_mtime:
- self.last_mtime = current_mtime
- self.open_file(filename)
-
- return True
-
- def set_filter(self, filter):
- self.widget.set_filter(filter)
-
- def set_dotcode(self, dotcode, filename='<stdin>'):
- if self.widget.set_dotcode(dotcode, filename):
- self.set_title(os.path.basename(filename) + ' - Dot Viewer')
- self.widget.zoom_to_fit()
-
- def set_xdotcode(self, xdotcode, filename='<stdin>'):
- if self.widget.set_xdotcode(xdotcode):
- self.set_title(os.path.basename(filename) + ' - Dot Viewer')
- self.widget.zoom_to_fit()
-
- def open_file(self, filename):
- try:
- fp = file(filename, 'rt')
- self.set_dotcode(fp.read(), filename)
- fp.close()
- except IOError, ex:
- dlg = gtk.MessageDialog(type=gtk.MESSAGE_ERROR,
- message_format=str(ex),
- buttons=gtk.BUTTONS_OK)
- dlg.set_title('Dot Viewer')
- dlg.run()
- dlg.destroy()
-
- def on_open(self, action):
- chooser = gtk.FileChooserDialog(title="Open dot File",
- action=gtk.FILE_CHOOSER_ACTION_OPEN,
- buttons=(gtk.STOCK_CANCEL,
- gtk.RESPONSE_CANCEL,
- gtk.STOCK_OPEN,
- gtk.RESPONSE_OK))
- chooser.set_default_response(gtk.RESPONSE_OK)
- filter = gtk.FileFilter()
- filter.set_name("Graphviz dot files")
- filter.add_pattern("*.dot")
- chooser.add_filter(filter)
- filter = gtk.FileFilter()
- filter.set_name("All files")
- filter.add_pattern("*")
- chooser.add_filter(filter)
- if chooser.run() == gtk.RESPONSE_OK:
- filename = chooser.get_filename()
- chooser.destroy()
- self.open_file(filename)
- else:
- chooser.destroy()
-
- def on_reload(self, action):
- self.widget.reload()
-
-
-def main():
- import optparse
-
- parser = optparse.OptionParser(
- usage='\n\t%prog [file]',
- version='%%prog %s' % __version__)
- parser.add_option(
- '-f', '--filter',
- type='choice', choices=('dot', 'neato', 'twopi', 'circo', 'fdp'),
- dest='filter', default='dot',
- help='graphviz filter: dot, neato, twopi, circo, or fdp [default: %default]')
- parser.add_option(
- '-n', '--no-filter',
- action='store_const', const=None, dest='filter',
- help='assume input is already filtered into xdot format (use e.g. dot -Txdot)')
-
- (options, args) = parser.parse_args(sys.argv[1:])
- if len(args) > 1:
- parser.error('incorrect number of arguments')
-
- win = DotWindow()
- win.connect('destroy', gtk.main_quit)
- win.set_filter(options.filter)
- if len(args) >= 1:
- if args[0] == '-':
- win.set_dotcode(sys.stdin.read())
- else:
- win.open_file(args[0])
- gobject.timeout_add(1000, win.update, args[0])
- gtk.main()
-
-
-# Apache-Style Software License for ColorBrewer software and ColorBrewer Color
-# Schemes, Version 1.1
-#
-# Copyright (c) 2002 Cynthia Brewer, Mark Harrower, and The Pennsylvania State
-# University. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# 1. Redistributions as source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-#
-# 2. The end-user documentation included with the redistribution, if any,
-# must include the following acknowledgment:
-#
-# This product includes color specifications and designs developed by
-# Cynthia Brewer (http://colorbrewer.org/).
-#
-# Alternately, this acknowledgment may appear in the software itself, if and
-# wherever such third-party acknowledgments normally appear.
-#
-# 3. The name "ColorBrewer" must not be used to endorse or promote products
-# derived from this software without prior written permission. For written
-# permission, please contact Cynthia Brewer at cbrewer@psu.edu.
-#
-# 4. Products derived from this software may not be called "ColorBrewer",
-# nor may "ColorBrewer" appear in their name, without prior written
-# permission of Cynthia Brewer.
-#
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES,
-# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
-# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CYNTHIA
-# BREWER, MARK HARROWER, OR THE PENNSYLVANIA STATE UNIVERSITY BE LIABLE FOR ANY
-# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-brewer_colors = {
- 'accent3': [(127, 201, 127), (190, 174, 212), (253, 192, 134)],
- 'accent4': [(127, 201, 127), (190, 174, 212), (253, 192, 134), (255, 255, 153)],
- 'accent5': [(127, 201, 127), (190, 174, 212), (253, 192, 134), (255, 255, 153), (56, 108, 176)],
- 'accent6': [(127, 201, 127), (190, 174, 212), (253, 192, 134), (255, 255, 153), (56, 108, 176), (240, 2, 127)],
- 'accent7': [(127, 201, 127), (190, 174, 212), (253, 192, 134), (255, 255, 153), (56, 108, 176), (240, 2, 127), (191, 91, 23)],
- 'accent8': [(127, 201, 127), (190, 174, 212), (253, 192, 134), (255, 255, 153), (56, 108, 176), (240, 2, 127), (191, 91, 23), (102, 102, 102)],
- 'blues3': [(222, 235, 247), (158, 202, 225), (49, 130, 189)],
- 'blues4': [(239, 243, 255), (189, 215, 231), (107, 174, 214), (33, 113, 181)],
- 'blues5': [(239, 243, 255), (189, 215, 231), (107, 174, 214), (49, 130, 189), (8, 81, 156)],
- 'blues6': [(239, 243, 255), (198, 219, 239), (158, 202, 225), (107, 174, 214), (49, 130, 189), (8, 81, 156)],
- 'blues7': [(239, 243, 255), (198, 219, 239), (158, 202, 225), (107, 174, 214), (66, 146, 198), (33, 113, 181), (8, 69, 148)],
- 'blues8': [(247, 251, 255), (222, 235, 247), (198, 219, 239), (158, 202, 225), (107, 174, 214), (66, 146, 198), (33, 113, 181), (8, 69, 148)],
- 'blues9': [(247, 251, 255), (222, 235, 247), (198, 219, 239), (158, 202, 225), (107, 174, 214), (66, 146, 198), (33, 113, 181), (8, 81, 156), (8, 48, 107)],
- 'brbg10': [(84, 48, 5), (0, 60, 48), (140, 81, 10), (191, 129, 45), (223, 194, 125), (246, 232, 195), (199, 234, 229), (128, 205, 193), (53, 151, 143), (1, 102, 94)],
- 'brbg11': [(84, 48, 5), (1, 102, 94), (0, 60, 48), (140, 81, 10), (191, 129, 45), (223, 194, 125), (246, 232, 195), (245, 245, 245), (199, 234, 229), (128, 205, 193), (53, 151, 143)],
- 'brbg3': [(216, 179, 101), (245, 245, 245), (90, 180, 172)],
- 'brbg4': [(166, 97, 26), (223, 194, 125), (128, 205, 193), (1, 133, 113)],
- 'brbg5': [(166, 97, 26), (223, 194, 125), (245, 245, 245), (128, 205, 193), (1, 133, 113)],
- 'brbg6': [(140, 81, 10), (216, 179, 101), (246, 232, 195), (199, 234, 229), (90, 180, 172), (1, 102, 94)],
- 'brbg7': [(140, 81, 10), (216, 179, 101), (246, 232, 195), (245, 245, 245), (199, 234, 229), (90, 180, 172), (1, 102, 94)],
- 'brbg8': [(140, 81, 10), (191, 129, 45), (223, 194, 125), (246, 232, 195), (199, 234, 229), (128, 205, 193), (53, 151, 143), (1, 102, 94)],
- 'brbg9': [(140, 81, 10), (191, 129, 45), (223, 194, 125), (246, 232, 195), (245, 245, 245), (199, 234, 229), (128, 205, 193), (53, 151, 143), (1, 102, 94)],
- 'bugn3': [(229, 245, 249), (153, 216, 201), (44, 162, 95)],
- 'bugn4': [(237, 248, 251), (178, 226, 226), (102, 194, 164), (35, 139, 69)],
- 'bugn5': [(237, 248, 251), (178, 226, 226), (102, 194, 164), (44, 162, 95), (0, 109, 44)],
- 'bugn6': [(237, 248, 251), (204, 236, 230), (153, 216, 201), (102, 194, 164), (44, 162, 95), (0, 109, 44)],
- 'bugn7': [(237, 248, 251), (204, 236, 230), (153, 216, 201), (102, 194, 164), (65, 174, 118), (35, 139, 69), (0, 88, 36)],
- 'bugn8': [(247, 252, 253), (229, 245, 249), (204, 236, 230), (153, 216, 201), (102, 194, 164), (65, 174, 118), (35, 139, 69), (0, 88, 36)],
- 'bugn9': [(247, 252, 253), (229, 245, 249), (204, 236, 230), (153, 216, 201), (102, 194, 164), (65, 174, 118), (35, 139, 69), (0, 109, 44), (0, 68, 27)],
- 'bupu3': [(224, 236, 244), (158, 188, 218), (136, 86, 167)],
- 'bupu4': [(237, 248, 251), (179, 205, 227), (140, 150, 198), (136, 65, 157)],
- 'bupu5': [(237, 248, 251), (179, 205, 227), (140, 150, 198), (136, 86, 167), (129, 15, 124)],
- 'bupu6': [(237, 248, 251), (191, 211, 230), (158, 188, 218), (140, 150, 198), (136, 86, 167), (129, 15, 124)],
- 'bupu7': [(237, 248, 251), (191, 211, 230), (158, 188, 218), (140, 150, 198), (140, 107, 177), (136, 65, 157), (110, 1, 107)],
- 'bupu8': [(247, 252, 253), (224, 236, 244), (191, 211, 230), (158, 188, 218), (140, 150, 198), (140, 107, 177), (136, 65, 157), (110, 1, 107)],
- 'bupu9': [(247, 252, 253), (224, 236, 244), (191, 211, 230), (158, 188, 218), (140, 150, 198), (140, 107, 177), (136, 65, 157), (129, 15, 124), (77, 0, 75)],
- 'dark23': [(27, 158, 119), (217, 95, 2), (117, 112, 179)],
- 'dark24': [(27, 158, 119), (217, 95, 2), (117, 112, 179), (231, 41, 138)],
- 'dark25': [(27, 158, 119), (217, 95, 2), (117, 112, 179), (231, 41, 138), (102, 166, 30)],
- 'dark26': [(27, 158, 119), (217, 95, 2), (117, 112, 179), (231, 41, 138), (102, 166, 30), (230, 171, 2)],
- 'dark27': [(27, 158, 119), (217, 95, 2), (117, 112, 179), (231, 41, 138), (102, 166, 30), (230, 171, 2), (166, 118, 29)],
- 'dark28': [(27, 158, 119), (217, 95, 2), (117, 112, 179), (231, 41, 138), (102, 166, 30), (230, 171, 2), (166, 118, 29), (102, 102, 102)],
- 'gnbu3': [(224, 243, 219), (168, 221, 181), (67, 162, 202)],
- 'gnbu4': [(240, 249, 232), (186, 228, 188), (123, 204, 196), (43, 140, 190)],
- 'gnbu5': [(240, 249, 232), (186, 228, 188), (123, 204, 196), (67, 162, 202), (8, 104, 172)],
- 'gnbu6': [(240, 249, 232), (204, 235, 197), (168, 221, 181), (123, 204, 196), (67, 162, 202), (8, 104, 172)],
- 'gnbu7': [(240, 249, 232), (204, 235, 197), (168, 221, 181), (123, 204, 196), (78, 179, 211), (43, 140, 190), (8, 88, 158)],
- 'gnbu8': [(247, 252, 240), (224, 243, 219), (204, 235, 197), (168, 221, 181), (123, 204, 196), (78, 179, 211), (43, 140, 190), (8, 88, 158)],
- 'gnbu9': [(247, 252, 240), (224, 243, 219), (204, 235, 197), (168, 221, 181), (123, 204, 196), (78, 179, 211), (43, 140, 190), (8, 104, 172), (8, 64, 129)],
- 'greens3': [(229, 245, 224), (161, 217, 155), (49, 163, 84)],
- 'greens4': [(237, 248, 233), (186, 228, 179), (116, 196, 118), (35, 139, 69)],
- 'greens5': [(237, 248, 233), (186, 228, 179), (116, 196, 118), (49, 163, 84), (0, 109, 44)],
- 'greens6': [(237, 248, 233), (199, 233, 192), (161, 217, 155), (116, 196, 118), (49, 163, 84), (0, 109, 44)],
- 'greens7': [(237, 248, 233), (199, 233, 192), (161, 217, 155), (116, 196, 118), (65, 171, 93), (35, 139, 69), (0, 90, 50)],
- 'greens8': [(247, 252, 245), (229, 245, 224), (199, 233, 192), (161, 217, 155), (116, 196, 118), (65, 171, 93), (35, 139, 69), (0, 90, 50)],
- 'greens9': [(247, 252, 245), (229, 245, 224), (199, 233, 192), (161, 217, 155), (116, 196, 118), (65, 171, 93), (35, 139, 69), (0, 109, 44), (0, 68, 27)],
- 'greys3': [(240, 240, 240), (189, 189, 189), (99, 99, 99)],
- 'greys4': [(247, 247, 247), (204, 204, 204), (150, 150, 150), (82, 82, 82)],
- 'greys5': [(247, 247, 247), (204, 204, 204), (150, 150, 150), (99, 99, 99), (37, 37, 37)],
- 'greys6': [(247, 247, 247), (217, 217, 217), (189, 189, 189), (150, 150, 150), (99, 99, 99), (37, 37, 37)],
- 'greys7': [(247, 247, 247), (217, 217, 217), (189, 189, 189), (150, 150, 150), (115, 115, 115), (82, 82, 82), (37, 37, 37)],
- 'greys8': [(255, 255, 255), (240, 240, 240), (217, 217, 217), (189, 189, 189), (150, 150, 150), (115, 115, 115), (82, 82, 82), (37, 37, 37)],
- 'greys9': [(255, 255, 255), (240, 240, 240), (217, 217, 217), (189, 189, 189), (150, 150, 150), (115, 115, 115), (82, 82, 82), (37, 37, 37), (0, 0, 0)],
- 'oranges3': [(254, 230, 206), (253, 174, 107), (230, 85, 13)],
- 'oranges4': [(254, 237, 222), (253, 190, 133), (253, 141, 60), (217, 71, 1)],
- 'oranges5': [(254, 237, 222), (253, 190, 133), (253, 141, 60), (230, 85, 13), (166, 54, 3)],
- 'oranges6': [(254, 237, 222), (253, 208, 162), (253, 174, 107), (253, 141, 60), (230, 85, 13), (166, 54, 3)],
- 'oranges7': [(254, 237, 222), (253, 208, 162), (253, 174, 107), (253, 141, 60), (241, 105, 19), (217, 72, 1), (140, 45, 4)],
- 'oranges8': [(255, 245, 235), (254, 230, 206), (253, 208, 162), (253, 174, 107), (253, 141, 60), (241, 105, 19), (217, 72, 1), (140, 45, 4)],
- 'oranges9': [(255, 245, 235), (254, 230, 206), (253, 208, 162), (253, 174, 107), (253, 141, 60), (241, 105, 19), (217, 72, 1), (166, 54, 3), (127, 39, 4)],
- 'orrd3': [(254, 232, 200), (253, 187, 132), (227, 74, 51)],
- 'orrd4': [(254, 240, 217), (253, 204, 138), (252, 141, 89), (215, 48, 31)],
- 'orrd5': [(254, 240, 217), (253, 204, 138), (252, 141, 89), (227, 74, 51), (179, 0, 0)],
- 'orrd6': [(254, 240, 217), (253, 212, 158), (253, 187, 132), (252, 141, 89), (227, 74, 51), (179, 0, 0)],
- 'orrd7': [(254, 240, 217), (253, 212, 158), (253, 187, 132), (252, 141, 89), (239, 101, 72), (215, 48, 31), (153, 0, 0)],
- 'orrd8': [(255, 247, 236), (254, 232, 200), (253, 212, 158), (253, 187, 132), (252, 141, 89), (239, 101, 72), (215, 48, 31), (153, 0, 0)],
- 'orrd9': [(255, 247, 236), (254, 232, 200), (253, 212, 158), (253, 187, 132), (252, 141, 89), (239, 101, 72), (215, 48, 31), (179, 0, 0), (127, 0, 0)],
- 'paired10': [(166, 206, 227), (106, 61, 154), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153), (227, 26, 28), (253, 191, 111), (255, 127, 0), (202, 178, 214)],
- 'paired11': [(166, 206, 227), (106, 61, 154), (255, 255, 153), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153), (227, 26, 28), (253, 191, 111), (255, 127, 0), (202, 178, 214)],
- 'paired12': [(166, 206, 227), (106, 61, 154), (255, 255, 153), (177, 89, 40), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153), (227, 26, 28), (253, 191, 111), (255, 127, 0), (202, 178, 214)],
- 'paired3': [(166, 206, 227), (31, 120, 180), (178, 223, 138)],
- 'paired4': [(166, 206, 227), (31, 120, 180), (178, 223, 138), (51, 160, 44)],
- 'paired5': [(166, 206, 227), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153)],
- 'paired6': [(166, 206, 227), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153), (227, 26, 28)],
- 'paired7': [(166, 206, 227), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153), (227, 26, 28), (253, 191, 111)],
- 'paired8': [(166, 206, 227), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153), (227, 26, 28), (253, 191, 111), (255, 127, 0)],
- 'paired9': [(166, 206, 227), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153), (227, 26, 28), (253, 191, 111), (255, 127, 0), (202, 178, 214)],
- 'pastel13': [(251, 180, 174), (179, 205, 227), (204, 235, 197)],
- 'pastel14': [(251, 180, 174), (179, 205, 227), (204, 235, 197), (222, 203, 228)],
- 'pastel15': [(251, 180, 174), (179, 205, 227), (204, 235, 197), (222, 203, 228), (254, 217, 166)],
- 'pastel16': [(251, 180, 174), (179, 205, 227), (204, 235, 197), (222, 203, 228), (254, 217, 166), (255, 255, 204)],
- 'pastel17': [(251, 180, 174), (179, 205, 227), (204, 235, 197), (222, 203, 228), (254, 217, 166), (255, 255, 204), (229, 216, 189)],
- 'pastel18': [(251, 180, 174), (179, 205, 227), (204, 235, 197), (222, 203, 228), (254, 217, 166), (255, 255, 204), (229, 216, 189), (253, 218, 236)],
- 'pastel19': [(251, 180, 174), (179, 205, 227), (204, 235, 197), (222, 203, 228), (254, 217, 166), (255, 255, 204), (229, 216, 189), (253, 218, 236), (242, 242, 242)],
- 'pastel23': [(179, 226, 205), (253, 205, 172), (203, 213, 232)],
- 'pastel24': [(179, 226, 205), (253, 205, 172), (203, 213, 232), (244, 202, 228)],
- 'pastel25': [(179, 226, 205), (253, 205, 172), (203, 213, 232), (244, 202, 228), (230, 245, 201)],
- 'pastel26': [(179, 226, 205), (253, 205, 172), (203, 213, 232), (244, 202, 228), (230, 245, 201), (255, 242, 174)],
- 'pastel27': [(179, 226, 205), (253, 205, 172), (203, 213, 232), (244, 202, 228), (230, 245, 201), (255, 242, 174), (241, 226, 204)],
- 'pastel28': [(179, 226, 205), (253, 205, 172), (203, 213, 232), (244, 202, 228), (230, 245, 201), (255, 242, 174), (241, 226, 204), (204, 204, 204)],
- 'piyg10': [(142, 1, 82), (39, 100, 25), (197, 27, 125), (222, 119, 174), (241, 182, 218), (253, 224, 239), (230, 245, 208), (184, 225, 134), (127, 188, 65), (77, 146, 33)],
- 'piyg11': [(142, 1, 82), (77, 146, 33), (39, 100, 25), (197, 27, 125), (222, 119, 174), (241, 182, 218), (253, 224, 239), (247, 247, 247), (230, 245, 208), (184, 225, 134), (127, 188, 65)],
- 'piyg3': [(233, 163, 201), (247, 247, 247), (161, 215, 106)],
- 'piyg4': [(208, 28, 139), (241, 182, 218), (184, 225, 134), (77, 172, 38)],
- 'piyg5': [(208, 28, 139), (241, 182, 218), (247, 247, 247), (184, 225, 134), (77, 172, 38)],
- 'piyg6': [(197, 27, 125), (233, 163, 201), (253, 224, 239), (230, 245, 208), (161, 215, 106), (77, 146, 33)],
- 'piyg7': [(197, 27, 125), (233, 163, 201), (253, 224, 239), (247, 247, 247), (230, 245, 208), (161, 215, 106), (77, 146, 33)],
- 'piyg8': [(197, 27, 125), (222, 119, 174), (241, 182, 218), (253, 224, 239), (230, 245, 208), (184, 225, 134), (127, 188, 65), (77, 146, 33)],
- 'piyg9': [(197, 27, 125), (222, 119, 174), (241, 182, 218), (253, 224, 239), (247, 247, 247), (230, 245, 208), (184, 225, 134), (127, 188, 65), (77, 146, 33)],
- 'prgn10': [(64, 0, 75), (0, 68, 27), (118, 42, 131), (153, 112, 171), (194, 165, 207), (231, 212, 232), (217, 240, 211), (166, 219, 160), (90, 174, 97), (27, 120, 55)],
- 'prgn11': [(64, 0, 75), (27, 120, 55), (0, 68, 27), (118, 42, 131), (153, 112, 171), (194, 165, 207), (231, 212, 232), (247, 247, 247), (217, 240, 211), (166, 219, 160), (90, 174, 97)],
- 'prgn3': [(175, 141, 195), (247, 247, 247), (127, 191, 123)],
- 'prgn4': [(123, 50, 148), (194, 165, 207), (166, 219, 160), (0, 136, 55)],
- 'prgn5': [(123, 50, 148), (194, 165, 207), (247, 247, 247), (166, 219, 160), (0, 136, 55)],
- 'prgn6': [(118, 42, 131), (175, 141, 195), (231, 212, 232), (217, 240, 211), (127, 191, 123), (27, 120, 55)],
- 'prgn7': [(118, 42, 131), (175, 141, 195), (231, 212, 232), (247, 247, 247), (217, 240, 211), (127, 191, 123), (27, 120, 55)],
- 'prgn8': [(118, 42, 131), (153, 112, 171), (194, 165, 207), (231, 212, 232), (217, 240, 211), (166, 219, 160), (90, 174, 97), (27, 120, 55)],
- 'prgn9': [(118, 42, 131), (153, 112, 171), (194, 165, 207), (231, 212, 232), (247, 247, 247), (217, 240, 211), (166, 219, 160), (90, 174, 97), (27, 120, 55)],
- 'pubu3': [(236, 231, 242), (166, 189, 219), (43, 140, 190)],
- 'pubu4': [(241, 238, 246), (189, 201, 225), (116, 169, 207), (5, 112, 176)],
- 'pubu5': [(241, 238, 246), (189, 201, 225), (116, 169, 207), (43, 140, 190), (4, 90, 141)],
- 'pubu6': [(241, 238, 246), (208, 209, 230), (166, 189, 219), (116, 169, 207), (43, 140, 190), (4, 90, 141)],
- 'pubu7': [(241, 238, 246), (208, 209, 230), (166, 189, 219), (116, 169, 207), (54, 144, 192), (5, 112, 176), (3, 78, 123)],
- 'pubu8': [(255, 247, 251), (236, 231, 242), (208, 209, 230), (166, 189, 219), (116, 169, 207), (54, 144, 192), (5, 112, 176), (3, 78, 123)],
- 'pubu9': [(255, 247, 251), (236, 231, 242), (208, 209, 230), (166, 189, 219), (116, 169, 207), (54, 144, 192), (5, 112, 176), (4, 90, 141), (2, 56, 88)],
- 'pubugn3': [(236, 226, 240), (166, 189, 219), (28, 144, 153)],
- 'pubugn4': [(246, 239, 247), (189, 201, 225), (103, 169, 207), (2, 129, 138)],
- 'pubugn5': [(246, 239, 247), (189, 201, 225), (103, 169, 207), (28, 144, 153), (1, 108, 89)],
- 'pubugn6': [(246, 239, 247), (208, 209, 230), (166, 189, 219), (103, 169, 207), (28, 144, 153), (1, 108, 89)],
- 'pubugn7': [(246, 239, 247), (208, 209, 230), (166, 189, 219), (103, 169, 207), (54, 144, 192), (2, 129, 138), (1, 100, 80)],
- 'pubugn8': [(255, 247, 251), (236, 226, 240), (208, 209, 230), (166, 189, 219), (103, 169, 207), (54, 144, 192), (2, 129, 138), (1, 100, 80)],
- 'pubugn9': [(255, 247, 251), (236, 226, 240), (208, 209, 230), (166, 189, 219), (103, 169, 207), (54, 144, 192), (2, 129, 138), (1, 108, 89), (1, 70, 54)],
- 'puor10': [(127, 59, 8), (45, 0, 75), (179, 88, 6), (224, 130, 20), (253, 184, 99), (254, 224, 182), (216, 218, 235), (178, 171, 210), (128, 115, 172), (84, 39, 136)],
- 'puor11': [(127, 59, 8), (84, 39, 136), (45, 0, 75), (179, 88, 6), (224, 130, 20), (253, 184, 99), (254, 224, 182), (247, 247, 247), (216, 218, 235), (178, 171, 210), (128, 115, 172)],
- 'puor3': [(241, 163, 64), (247, 247, 247), (153, 142, 195)],
- 'puor4': [(230, 97, 1), (253, 184, 99), (178, 171, 210), (94, 60, 153)],
- 'puor5': [(230, 97, 1), (253, 184, 99), (247, 247, 247), (178, 171, 210), (94, 60, 153)],
- 'puor6': [(179, 88, 6), (241, 163, 64), (254, 224, 182), (216, 218, 235), (153, 142, 195), (84, 39, 136)],
- 'puor7': [(179, 88, 6), (241, 163, 64), (254, 224, 182), (247, 247, 247), (216, 218, 235), (153, 142, 195), (84, 39, 136)],
- 'puor8': [(179, 88, 6), (224, 130, 20), (253, 184, 99), (254, 224, 182), (216, 218, 235), (178, 171, 210), (128, 115, 172), (84, 39, 136)],
- 'puor9': [(179, 88, 6), (224, 130, 20), (253, 184, 99), (254, 224, 182), (247, 247, 247), (216, 218, 235), (178, 171, 210), (128, 115, 172), (84, 39, 136)],
- 'purd3': [(231, 225, 239), (201, 148, 199), (221, 28, 119)],
- 'purd4': [(241, 238, 246), (215, 181, 216), (223, 101, 176), (206, 18, 86)],
- 'purd5': [(241, 238, 246), (215, 181, 216), (223, 101, 176), (221, 28, 119), (152, 0, 67)],
- 'purd6': [(241, 238, 246), (212, 185, 218), (201, 148, 199), (223, 101, 176), (221, 28, 119), (152, 0, 67)],
- 'purd7': [(241, 238, 246), (212, 185, 218), (201, 148, 199), (223, 101, 176), (231, 41, 138), (206, 18, 86), (145, 0, 63)],
- 'purd8': [(247, 244, 249), (231, 225, 239), (212, 185, 218), (201, 148, 199), (223, 101, 176), (231, 41, 138), (206, 18, 86), (145, 0, 63)],
- 'purd9': [(247, 244, 249), (231, 225, 239), (212, 185, 218), (201, 148, 199), (223, 101, 176), (231, 41, 138), (206, 18, 86), (152, 0, 67), (103, 0, 31)],
- 'purples3': [(239, 237, 245), (188, 189, 220), (117, 107, 177)],
- 'purples4': [(242, 240, 247), (203, 201, 226), (158, 154, 200), (106, 81, 163)],
- 'purples5': [(242, 240, 247), (203, 201, 226), (158, 154, 200), (117, 107, 177), (84, 39, 143)],
- 'purples6': [(242, 240, 247), (218, 218, 235), (188, 189, 220), (158, 154, 200), (117, 107, 177), (84, 39, 143)],
- 'purples7': [(242, 240, 247), (218, 218, 235), (188, 189, 220), (158, 154, 200), (128, 125, 186), (106, 81, 163), (74, 20, 134)],
- 'purples8': [(252, 251, 253), (239, 237, 245), (218, 218, 235), (188, 189, 220), (158, 154, 200), (128, 125, 186), (106, 81, 163), (74, 20, 134)],
- 'purples9': [(252, 251, 253), (239, 237, 245), (218, 218, 235), (188, 189, 220), (158, 154, 200), (128, 125, 186), (106, 81, 163), (84, 39, 143), (63, 0, 125)],
- 'rdbu10': [(103, 0, 31), (5, 48, 97), (178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (209, 229, 240), (146, 197, 222), (67, 147, 195), (33, 102, 172)],
- 'rdbu11': [(103, 0, 31), (33, 102, 172), (5, 48, 97), (178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (247, 247, 247), (209, 229, 240), (146, 197, 222), (67, 147, 195)],
- 'rdbu3': [(239, 138, 98), (247, 247, 247), (103, 169, 207)],
- 'rdbu4': [(202, 0, 32), (244, 165, 130), (146, 197, 222), (5, 113, 176)],
- 'rdbu5': [(202, 0, 32), (244, 165, 130), (247, 247, 247), (146, 197, 222), (5, 113, 176)],
- 'rdbu6': [(178, 24, 43), (239, 138, 98), (253, 219, 199), (209, 229, 240), (103, 169, 207), (33, 102, 172)],
- 'rdbu7': [(178, 24, 43), (239, 138, 98), (253, 219, 199), (247, 247, 247), (209, 229, 240), (103, 169, 207), (33, 102, 172)],
- 'rdbu8': [(178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (209, 229, 240), (146, 197, 222), (67, 147, 195), (33, 102, 172)],
- 'rdbu9': [(178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (247, 247, 247), (209, 229, 240), (146, 197, 222), (67, 147, 195), (33, 102, 172)],
- 'rdgy10': [(103, 0, 31), (26, 26, 26), (178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (224, 224, 224), (186, 186, 186), (135, 135, 135), (77, 77, 77)],
- 'rdgy11': [(103, 0, 31), (77, 77, 77), (26, 26, 26), (178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (255, 255, 255), (224, 224, 224), (186, 186, 186), (135, 135, 135)],
- 'rdgy3': [(239, 138, 98), (255, 255, 255), (153, 153, 153)],
- 'rdgy4': [(202, 0, 32), (244, 165, 130), (186, 186, 186), (64, 64, 64)],
- 'rdgy5': [(202, 0, 32), (244, 165, 130), (255, 255, 255), (186, 186, 186), (64, 64, 64)],
- 'rdgy6': [(178, 24, 43), (239, 138, 98), (253, 219, 199), (224, 224, 224), (153, 153, 153), (77, 77, 77)],
- 'rdgy7': [(178, 24, 43), (239, 138, 98), (253, 219, 199), (255, 255, 255), (224, 224, 224), (153, 153, 153), (77, 77, 77)],
- 'rdgy8': [(178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (224, 224, 224), (186, 186, 186), (135, 135, 135), (77, 77, 77)],
- 'rdgy9': [(178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (255, 255, 255), (224, 224, 224), (186, 186, 186), (135, 135, 135), (77, 77, 77)],
- 'rdpu3': [(253, 224, 221), (250, 159, 181), (197, 27, 138)],
- 'rdpu4': [(254, 235, 226), (251, 180, 185), (247, 104, 161), (174, 1, 126)],
- 'rdpu5': [(254, 235, 226), (251, 180, 185), (247, 104, 161), (197, 27, 138), (122, 1, 119)],
- 'rdpu6': [(254, 235, 226), (252, 197, 192), (250, 159, 181), (247, 104, 161), (197, 27, 138), (122, 1, 119)],
- 'rdpu7': [(254, 235, 226), (252, 197, 192), (250, 159, 181), (247, 104, 161), (221, 52, 151), (174, 1, 126), (122, 1, 119)],
- 'rdpu8': [(255, 247, 243), (253, 224, 221), (252, 197, 192), (250, 159, 181), (247, 104, 161), (221, 52, 151), (174, 1, 126), (122, 1, 119)],
- 'rdpu9': [(255, 247, 243), (253, 224, 221), (252, 197, 192), (250, 159, 181), (247, 104, 161), (221, 52, 151), (174, 1, 126), (122, 1, 119), (73, 0, 106)],
- 'rdylbu10': [(165, 0, 38), (49, 54, 149), (215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 144), (224, 243, 248), (171, 217, 233), (116, 173, 209), (69, 117, 180)],
- 'rdylbu11': [(165, 0, 38), (69, 117, 180), (49, 54, 149), (215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 144), (255, 255, 191), (224, 243, 248), (171, 217, 233), (116, 173, 209)],
- 'rdylbu3': [(252, 141, 89), (255, 255, 191), (145, 191, 219)],
- 'rdylbu4': [(215, 25, 28), (253, 174, 97), (171, 217, 233), (44, 123, 182)],
- 'rdylbu5': [(215, 25, 28), (253, 174, 97), (255, 255, 191), (171, 217, 233), (44, 123, 182)],
- 'rdylbu6': [(215, 48, 39), (252, 141, 89), (254, 224, 144), (224, 243, 248), (145, 191, 219), (69, 117, 180)],
- 'rdylbu7': [(215, 48, 39), (252, 141, 89), (254, 224, 144), (255, 255, 191), (224, 243, 248), (145, 191, 219), (69, 117, 180)],
- 'rdylbu8': [(215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 144), (224, 243, 248), (171, 217, 233), (116, 173, 209), (69, 117, 180)],
- 'rdylbu9': [(215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 144), (255, 255, 191), (224, 243, 248), (171, 217, 233), (116, 173, 209), (69, 117, 180)],
- 'rdylgn10': [(165, 0, 38), (0, 104, 55), (215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 139), (217, 239, 139), (166, 217, 106), (102, 189, 99), (26, 152, 80)],
- 'rdylgn11': [(165, 0, 38), (26, 152, 80), (0, 104, 55), (215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 139), (255, 255, 191), (217, 239, 139), (166, 217, 106), (102, 189, 99)],
- 'rdylgn3': [(252, 141, 89), (255, 255, 191), (145, 207, 96)],
- 'rdylgn4': [(215, 25, 28), (253, 174, 97), (166, 217, 106), (26, 150, 65)],
- 'rdylgn5': [(215, 25, 28), (253, 174, 97), (255, 255, 191), (166, 217, 106), (26, 150, 65)],
- 'rdylgn6': [(215, 48, 39), (252, 141, 89), (254, 224, 139), (217, 239, 139), (145, 207, 96), (26, 152, 80)],
- 'rdylgn7': [(215, 48, 39), (252, 141, 89), (254, 224, 139), (255, 255, 191), (217, 239, 139), (145, 207, 96), (26, 152, 80)],
- 'rdylgn8': [(215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 139), (217, 239, 139), (166, 217, 106), (102, 189, 99), (26, 152, 80)],
- 'rdylgn9': [(215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 139), (255, 255, 191), (217, 239, 139), (166, 217, 106), (102, 189, 99), (26, 152, 80)],
- 'reds3': [(254, 224, 210), (252, 146, 114), (222, 45, 38)],
- 'reds4': [(254, 229, 217), (252, 174, 145), (251, 106, 74), (203, 24, 29)],
- 'reds5': [(254, 229, 217), (252, 174, 145), (251, 106, 74), (222, 45, 38), (165, 15, 21)],
- 'reds6': [(254, 229, 217), (252, 187, 161), (252, 146, 114), (251, 106, 74), (222, 45, 38), (165, 15, 21)],
- 'reds7': [(254, 229, 217), (252, 187, 161), (252, 146, 114), (251, 106, 74), (239, 59, 44), (203, 24, 29), (153, 0, 13)],
- 'reds8': [(255, 245, 240), (254, 224, 210), (252, 187, 161), (252, 146, 114), (251, 106, 74), (239, 59, 44), (203, 24, 29), (153, 0, 13)],
- 'reds9': [(255, 245, 240), (254, 224, 210), (252, 187, 161), (252, 146, 114), (251, 106, 74), (239, 59, 44), (203, 24, 29), (165, 15, 21), (103, 0, 13)],
- 'set13': [(228, 26, 28), (55, 126, 184), (77, 175, 74)],
- 'set14': [(228, 26, 28), (55, 126, 184), (77, 175, 74), (152, 78, 163)],
- 'set15': [(228, 26, 28), (55, 126, 184), (77, 175, 74), (152, 78, 163), (255, 127, 0)],
- 'set16': [(228, 26, 28), (55, 126, 184), (77, 175, 74), (152, 78, 163), (255, 127, 0), (255, 255, 51)],
- 'set17': [(228, 26, 28), (55, 126, 184), (77, 175, 74), (152, 78, 163), (255, 127, 0), (255, 255, 51), (166, 86, 40)],
- 'set18': [(228, 26, 28), (55, 126, 184), (77, 175, 74), (152, 78, 163), (255, 127, 0), (255, 255, 51), (166, 86, 40), (247, 129, 191)],
- 'set19': [(228, 26, 28), (55, 126, 184), (77, 175, 74), (152, 78, 163), (255, 127, 0), (255, 255, 51), (166, 86, 40), (247, 129, 191), (153, 153, 153)],
- 'set23': [(102, 194, 165), (252, 141, 98), (141, 160, 203)],
- 'set24': [(102, 194, 165), (252, 141, 98), (141, 160, 203), (231, 138, 195)],
- 'set25': [(102, 194, 165), (252, 141, 98), (141, 160, 203), (231, 138, 195), (166, 216, 84)],
- 'set26': [(102, 194, 165), (252, 141, 98), (141, 160, 203), (231, 138, 195), (166, 216, 84), (255, 217, 47)],
- 'set27': [(102, 194, 165), (252, 141, 98), (141, 160, 203), (231, 138, 195), (166, 216, 84), (255, 217, 47), (229, 196, 148)],
- 'set28': [(102, 194, 165), (252, 141, 98), (141, 160, 203), (231, 138, 195), (166, 216, 84), (255, 217, 47), (229, 196, 148), (179, 179, 179)],
- 'set310': [(141, 211, 199), (188, 128, 189), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211), (253, 180, 98), (179, 222, 105), (252, 205, 229), (217, 217, 217)],
- 'set311': [(141, 211, 199), (188, 128, 189), (204, 235, 197), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211), (253, 180, 98), (179, 222, 105), (252, 205, 229), (217, 217, 217)],
- 'set312': [(141, 211, 199), (188, 128, 189), (204, 235, 197), (255, 237, 111), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211), (253, 180, 98), (179, 222, 105), (252, 205, 229), (217, 217, 217)],
- 'set33': [(141, 211, 199), (255, 255, 179), (190, 186, 218)],
- 'set34': [(141, 211, 199), (255, 255, 179), (190, 186, 218), (251, 128, 114)],
- 'set35': [(141, 211, 199), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211)],
- 'set36': [(141, 211, 199), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211), (253, 180, 98)],
- 'set37': [(141, 211, 199), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211), (253, 180, 98), (179, 222, 105)],
- 'set38': [(141, 211, 199), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211), (253, 180, 98), (179, 222, 105), (252, 205, 229)],
- 'set39': [(141, 211, 199), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211), (253, 180, 98), (179, 222, 105), (252, 205, 229), (217, 217, 217)],
- 'spectral10': [(158, 1, 66), (94, 79, 162), (213, 62, 79), (244, 109, 67), (253, 174, 97), (254, 224, 139), (230, 245, 152), (171, 221, 164), (102, 194, 165), (50, 136, 189)],
- 'spectral11': [(158, 1, 66), (50, 136, 189), (94, 79, 162), (213, 62, 79), (244, 109, 67), (253, 174, 97), (254, 224, 139), (255, 255, 191), (230, 245, 152), (171, 221, 164), (102, 194, 165)],
- 'spectral3': [(252, 141, 89), (255, 255, 191), (153, 213, 148)],
- 'spectral4': [(215, 25, 28), (253, 174, 97), (171, 221, 164), (43, 131, 186)],
- 'spectral5': [(215, 25, 28), (253, 174, 97), (255, 255, 191), (171, 221, 164), (43, 131, 186)],
- 'spectral6': [(213, 62, 79), (252, 141, 89), (254, 224, 139), (230, 245, 152), (153, 213, 148), (50, 136, 189)],
- 'spectral7': [(213, 62, 79), (252, 141, 89), (254, 224, 139), (255, 255, 191), (230, 245, 152), (153, 213, 148), (50, 136, 189)],
- 'spectral8': [(213, 62, 79), (244, 109, 67), (253, 174, 97), (254, 224, 139), (230, 245, 152), (171, 221, 164), (102, 194, 165), (50, 136, 189)],
- 'spectral9': [(213, 62, 79), (244, 109, 67), (253, 174, 97), (254, 224, 139), (255, 255, 191), (230, 245, 152), (171, 221, 164), (102, 194, 165), (50, 136, 189)],
- 'ylgn3': [(247, 252, 185), (173, 221, 142), (49, 163, 84)],
- 'ylgn4': [(255, 255, 204), (194, 230, 153), (120, 198, 121), (35, 132, 67)],
- 'ylgn5': [(255, 255, 204), (194, 230, 153), (120, 198, 121), (49, 163, 84), (0, 104, 55)],
- 'ylgn6': [(255, 255, 204), (217, 240, 163), (173, 221, 142), (120, 198, 121), (49, 163, 84), (0, 104, 55)],
- 'ylgn7': [(255, 255, 204), (217, 240, 163), (173, 221, 142), (120, 198, 121), (65, 171, 93), (35, 132, 67), (0, 90, 50)],
- 'ylgn8': [(255, 255, 229), (247, 252, 185), (217, 240, 163), (173, 221, 142), (120, 198, 121), (65, 171, 93), (35, 132, 67), (0, 90, 50)],
- 'ylgn9': [(255, 255, 229), (247, 252, 185), (217, 240, 163), (173, 221, 142), (120, 198, 121), (65, 171, 93), (35, 132, 67), (0, 104, 55), (0, 69, 41)],
- 'ylgnbu3': [(237, 248, 177), (127, 205, 187), (44, 127, 184)],
- 'ylgnbu4': [(255, 255, 204), (161, 218, 180), (65, 182, 196), (34, 94, 168)],
- 'ylgnbu5': [(255, 255, 204), (161, 218, 180), (65, 182, 196), (44, 127, 184), (37, 52, 148)],
- 'ylgnbu6': [(255, 255, 204), (199, 233, 180), (127, 205, 187), (65, 182, 196), (44, 127, 184), (37, 52, 148)],
- 'ylgnbu7': [(255, 255, 204), (199, 233, 180), (127, 205, 187), (65, 182, 196), (29, 145, 192), (34, 94, 168), (12, 44, 132)],
- 'ylgnbu8': [(255, 255, 217), (237, 248, 177), (199, 233, 180), (127, 205, 187), (65, 182, 196), (29, 145, 192), (34, 94, 168), (12, 44, 132)],
- 'ylgnbu9': [(255, 255, 217), (237, 248, 177), (199, 233, 180), (127, 205, 187), (65, 182, 196), (29, 145, 192), (34, 94, 168), (37, 52, 148), (8, 29, 88)],
- 'ylorbr3': [(255, 247, 188), (254, 196, 79), (217, 95, 14)],
- 'ylorbr4': [(255, 255, 212), (254, 217, 142), (254, 153, 41), (204, 76, 2)],
- 'ylorbr5': [(255, 255, 212), (254, 217, 142), (254, 153, 41), (217, 95, 14), (153, 52, 4)],
- 'ylorbr6': [(255, 255, 212), (254, 227, 145), (254, 196, 79), (254, 153, 41), (217, 95, 14), (153, 52, 4)],
- 'ylorbr7': [(255, 255, 212), (254, 227, 145), (254, 196, 79), (254, 153, 41), (236, 112, 20), (204, 76, 2), (140, 45, 4)],
- 'ylorbr8': [(255, 255, 229), (255, 247, 188), (254, 227, 145), (254, 196, 79), (254, 153, 41), (236, 112, 20), (204, 76, 2), (140, 45, 4)],
- 'ylorbr9': [(255, 255, 229), (255, 247, 188), (254, 227, 145), (254, 196, 79), (254, 153, 41), (236, 112, 20), (204, 76, 2), (153, 52, 4), (102, 37, 6)],
- 'ylorrd3': [(255, 237, 160), (254, 178, 76), (240, 59, 32)],
- 'ylorrd4': [(255, 255, 178), (254, 204, 92), (253, 141, 60), (227, 26, 28)],
- 'ylorrd5': [(255, 255, 178), (254, 204, 92), (253, 141, 60), (240, 59, 32), (189, 0, 38)],
- 'ylorrd6': [(255, 255, 178), (254, 217, 118), (254, 178, 76), (253, 141, 60), (240, 59, 32), (189, 0, 38)],
- 'ylorrd7': [(255, 255, 178), (254, 217, 118), (254, 178, 76), (253, 141, 60), (252, 78, 42), (227, 26, 28), (177, 0, 38)],
- 'ylorrd8': [(255, 255, 204), (255, 237, 160), (254, 217, 118), (254, 178, 76), (253, 141, 60), (252, 78, 42), (227, 26, 28), (177, 0, 38)],
-}
-
-
-if __name__ == '__main__':
- main() \ No newline at end of file
diff --git a/Src/PyCatcher/src/xdot.pyc b/Src/PyCatcher/src/xdot.pyc
deleted file mode 100644
index 6b140a4..0000000
--- a/Src/PyCatcher/src/xdot.pyc
+++ /dev/null
Binary files differ