#!/usr/bin/env python # # An app to interactively change RadialSymmetryDiagram properies # # Copyright (C) 2015 Antonio Ospite # # 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 . 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)