+#!/usr/bin/env python
+#
+# An app to interactively change RadialSymmetryDiagram properies
+#
+# Copyright (C) 2015 Antonio Ospite <ao2@ao2.it>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+from gi.repository import Gtk, Gio
+import sys
+import math
+
+import RadialSymmetryDiagram
+
+APPLICATION_ID = "it.ao2.RadialSymmetryApp"
+WINDOW_TITLE = "Radial Symmetry"
+CANVAS_WIDTH = 600
+CANVAS_HEIGHT = 600
+
+
+class RadialSymmetryWindow(Gtk.ApplicationWindow):
+
+ def __init__(self):
+ Gtk.Window.__init__(self, title=WINDOW_TITLE)
+ self.set_border_width(10)
+ self.set_property('resizable', False)
+
+ self.model = model = RadialSymmetryDiagram.RadialSymmetryModel()
+ self.diagram = RadialSymmetryDiagram.RadialSymmetryDiagram(CANVAS_WIDTH,
+ CANVAS_HEIGHT)
+ model.base_polygon_sides = 6
+ ad = Gtk.Adjustment(model.base_polygon_sides, 1, 32, 1, 0, 0)
+ self.spin_base_polygon_sides = Gtk.SpinButton(adjustment=ad, climb_rate=1, digits=0)
+ self.spin_base_polygon_sides.connect("value-changed", self.get_base_polygon_sides)
+
+ model.element_sides = 4
+ ad = Gtk.Adjustment(model.element_sides, 2, 16, 1, 0, 0)
+ self.spin_elem_sides = Gtk.SpinButton(adjustment=ad, climb_rate=1, digits=0)
+ self.spin_elem_sides.connect("value-changed", self.get_elem_sides)
+
+ max_element_radius = min(CANVAS_WIDTH, CANVAS_HEIGHT)
+ model.element_radius = max_element_radius / 4.0
+ ad = Gtk.Adjustment(model.element_radius, 0, max_element_radius, 1, 0, 0)
+ self.spin_element_radius = Gtk.SpinButton(adjustment=ad, climb_rate=1, digits=0)
+ self.spin_element_radius.connect("value-changed", self.get_element_radius)
+
+ model.radial_orientate = True
+ self.checkbox_radial_orientate = Gtk.CheckButton(label="Radial orientation")
+ self.checkbox_radial_orientate.set_active(model.radial_orientate)
+ self.checkbox_radial_orientate.connect("toggled", self.get_radial_orientate)
+
+ model.show_base_polygon = False
+ self.checkbox_base_polygon = Gtk.CheckButton(label="Draw base polygon")
+ self.checkbox_base_polygon.set_active(model.show_base_polygon)
+ self.checkbox_base_polygon.connect("toggled", self.get_base_polygon)
+
+ max_base_polygon_radius = min(CANVAS_WIDTH, CANVAS_HEIGHT)
+ model.base_polygon_radius = max_base_polygon_radius / 4.0
+ ad = Gtk.Adjustment(model.base_polygon_radius, 0, max_base_polygon_radius, 1, 0, 0)
+ self.spin_base_polygon_radius = Gtk.SpinButton(adjustment=ad, climb_rate=1, digits=0)
+ self.spin_base_polygon_radius.connect("value-changed", self.get_base_polygon_radius)
+
+ model.show_stars = False
+ self.checkbox_stars = Gtk.CheckButton(label="Draw stars")
+ self.checkbox_stars.set_active(model.show_stars)
+ self.checkbox_stars.connect("toggled", self.get_draw_stars)
+
+ model.show_elements = True
+ self.checkbox_elements = Gtk.CheckButton(label="Draw elements")
+ self.checkbox_elements.set_active(model.show_elements)
+ self.checkbox_elements.connect("toggled", self.get_draw_elements)
+
+ model.show_labels = False
+ self.checkbox_labels = Gtk.CheckButton(label="Draw labels")
+ self.checkbox_labels.set_active(model.show_labels)
+ self.checkbox_labels.connect("toggled", self.get_draw_labels)
+
+ model.element_angle_offset = 0
+ ad2 = Gtk.Adjustment(math.degrees(model.element_angle_offset), -1, 360, 1, 0, 0)
+ self.spin_element_angle = Gtk.SpinButton(adjustment=ad2, climb_rate=1, digits=0)
+ self.spin_element_angle.connect("value-changed", self.get_element_angle)
+
+ model.base_polygon_angle = 0
+ ad = Gtk.Adjustment(math.degrees(model.base_polygon_angle), -1, 360, 1, 0, 0)
+ self.spin_global_angle = Gtk.SpinButton(adjustment=ad, climb_rate=1, digits=0)
+ self.spin_global_angle.connect("value-changed", self.get_global_angle)
+
+ self.darea = Gtk.DrawingArea()
+ self.darea.connect("draw", self.draw)
+ self.darea.set_size_request(CANVAS_WIDTH, CANVAS_HEIGHT)
+
+ self.export_button = Gtk.Button(label="Export diagram")
+ self.export_button.connect("clicked", self.on_export_diagram)
+
+ controls = Gtk.VBox(spacing=10)
+
+ vbox = Gtk.VBox()
+ vbox.pack_start(Gtk.Label("Elements"), False, False, 0)
+ vbox.pack_start(self.spin_base_polygon_sides, False, False, 0)
+ controls.pack_start(vbox, False, False, 0)
+
+ vbox = Gtk.VBox()
+ vbox.pack_start(Gtk.Label("Element sides"), False, False, 0)
+ vbox.pack_start(self.spin_elem_sides, False, False, 0)
+ controls.pack_start(vbox, False, False, 0)
+
+ vbox = Gtk.VBox()
+ vbox.pack_start(Gtk.Label("Element radius"), False, False, 0)
+ vbox.pack_start(self.spin_element_radius, False, False, 0)
+ controls.pack_start(vbox, False, False, 0)
+
+ controls.pack_start(self.checkbox_radial_orientate, False, False, 0)
+
+ vbox = Gtk.VBox()
+ vbox.pack_start(Gtk.Label("Element angle"), False, False, 0)
+ vbox.pack_start(self.spin_element_angle, False, False, 0)
+ controls.pack_start(vbox, False, False, 0)
+
+ vbox = Gtk.VBox()
+ vbox.pack_start(Gtk.Label("Global angle"), False, False, 0)
+ vbox.pack_start(self.spin_global_angle, False, False, 0)
+ controls.pack_start(vbox, False, False, 0)
+
+ controls.pack_start(self.checkbox_base_polygon, False, False, 0)
+
+ vbox = Gtk.VBox()
+ vbox.pack_start(Gtk.Label("Base polygon radius"), False, False, 0)
+ vbox.pack_start(self.spin_base_polygon_radius, False, False, 0)
+ controls.pack_start(vbox, False, False, 0)
+
+ controls.pack_start(self.checkbox_elements, False, False, 0)
+ controls.pack_start(self.checkbox_stars, False, False, 0)
+ controls.pack_start(self.checkbox_labels, False, False, 0)
+ controls.pack_end(self.export_button, False, False, 0)
+
+ main_container = Gtk.HBox(spacing=10)
+ main_container.add(controls)
+ main_container.add(self.darea)
+
+ self.add(main_container)
+
+ def normalize_angle(self, angle):
+ if angle == 360:
+ angle = 0
+ elif angle == -1:
+ angle = 359
+
+ return angle
+
+ def get_base_polygon_sides(self, event):
+ self.model.base_polygon_sides = self.spin_base_polygon_sides.get_value_as_int()
+ self.darea.queue_draw()
+
+ def get_elem_sides(self, event):
+ self.model.element_sides = self.spin_elem_sides.get_value_as_int()
+ self.darea.queue_draw()
+
+ def get_element_radius(self, event):
+ self.model.element_radius = self.spin_element_radius.get_value_as_int()
+ self.darea.queue_draw()
+
+ def get_radial_orientate(self, event):
+ self.model.radial_orientate = self.checkbox_radial_orientate.get_active()
+ self.darea.queue_draw()
+
+ def get_base_polygon(self, event):
+ self.model.show_base_polygon = self.checkbox_base_polygon.get_active()
+ self.darea.queue_draw()
+
+ def get_base_polygon_radius(self, event):
+ self.model.base_polygon_radius = self.spin_base_polygon_radius.get_value_as_int()
+ self.darea.queue_draw()
+
+ def get_draw_elements(self, event):
+ self.model.show_elements = self.checkbox_elements.get_active()
+ self.darea.queue_draw()
+
+ def get_draw_stars(self, event):
+ self.model.show_stars = self.checkbox_stars.get_active()
+ self.darea.queue_draw()
+
+ def get_draw_labels(self, event):
+ self.model.show_labels = self.checkbox_labels.get_active()
+ self.darea.queue_draw()
+
+ def get_global_angle(self, event):
+ angle = self.normalize_angle(self.spin_global_angle.get_value_as_int())
+ self.spin_global_angle.set_value(angle)
+ self.model.base_polygon_angle = math.radians(angle)
+ self.darea.queue_draw()
+
+ def get_element_angle(self, event):
+ angle = self.normalize_angle(self.spin_element_angle.get_value_as_int())
+ self.spin_element_angle.set_value(angle)
+ self.model.element_angle_offset = math.radians(angle)
+ self.darea.queue_draw()
+
+ def on_export_diagram(self, event):
+ dialog = Gtk.FileChooserDialog("Export Diagram", self,
+ Gtk.FileChooserAction.SAVE,
+ (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
+ Gtk.STOCK_SAVE, Gtk.ResponseType.OK))
+ dialog.set_property('do-overwrite-confirmation', True)
+
+ filter_svg = Gtk.FileFilter()
+ filter_svg.set_name("SVG files")
+ filter_svg.add_mime_type("image/svg+xml")
+ dialog.add_filter(filter_svg)
+
+ filter_png = Gtk.FileFilter()
+ filter_png.set_name("PNG files")
+ filter_png.add_mime_type("image/png")
+ dialog.add_filter(filter_png)
+
+ response = dialog.run()
+ if response == Gtk.ResponseType.OK:
+ filename = dialog.get_filename()
+ if filename.endswith(".svg"):
+ self.diagram.save_svg(filename[:-4])
+ elif filename.endswith(".png"):
+ self.diagram.save_png(filename[:-4])
+
+ dialog.destroy()
+
+ def draw(self, darea, cr):
+ self.diagram.draw(self.model)
+
+ src_surface = self.diagram.surface
+ cr.set_source_surface(src_surface, 0, 0)
+ cr.paint()
+
+
+class RadialSymmetryApp(Gtk.Application):
+
+ def __init__(self):
+ Gtk.Application.__init__(self,
+ application_id=APPLICATION_ID,
+ flags=Gio.ApplicationFlags.FLAGS_NONE)
+ self.connect("activate", self.activateCb)
+
+ def activateCb(self, app):
+ window = RadialSymmetryWindow()
+ app.add_window(window)
+ window.show_all()
+
+if __name__ == "__main__":
+ app = RadialSymmetryApp()
+ exit_status = app.run(sys.argv)
+ sys.exit(exit_status)