3 # An app to interactively change RadialSymmetryDiagram properies
5 # Copyright (C) 2015 Antonio Ospite <ao2@ao2.it>
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 from gi.repository import Gtk, Gio
24 import RadialSymmetryDiagram
26 APPLICATION_ID = "it.ao2.RadialSymmetryApp"
27 WINDOW_TITLE = "Radial Symmetry"
32 class RadialSymmetryWindow(Gtk.ApplicationWindow):
35 Gtk.Window.__init__(self, title=WINDOW_TITLE)
36 self.set_border_width(10)
37 self.set_property('resizable', False)
39 self.model = model = RadialSymmetryDiagram.RadialSymmetryModel()
40 self.diagram = RadialSymmetryDiagram.RadialSymmetryDiagram(CANVAS_WIDTH,
42 model.base_polygon_sides = 6
43 ad = Gtk.Adjustment(model.base_polygon_sides, 1, 32, 1, 0, 0)
44 self.spin_base_polygon_sides = Gtk.SpinButton(adjustment=ad, climb_rate=1, digits=0)
45 self.spin_base_polygon_sides.connect("value-changed", self.get_base_polygon_sides)
47 model.element_sides = 4
48 ad = Gtk.Adjustment(model.element_sides, 2, 16, 1, 0, 0)
49 self.spin_elem_sides = Gtk.SpinButton(adjustment=ad, climb_rate=1, digits=0)
50 self.spin_elem_sides.connect("value-changed", self.get_elem_sides)
52 max_element_radius = min(CANVAS_WIDTH, CANVAS_HEIGHT)
53 model.element_radius = max_element_radius / 4.0
54 ad = Gtk.Adjustment(model.element_radius, 0, max_element_radius, 1, 0, 0)
55 self.spin_element_radius = Gtk.SpinButton(adjustment=ad, climb_rate=1, digits=0)
56 self.spin_element_radius.connect("value-changed", self.get_element_radius)
58 model.radial_orientate = True
59 self.checkbox_radial_orientate = Gtk.CheckButton(label="Radial orientation")
60 self.checkbox_radial_orientate.set_active(model.radial_orientate)
61 self.checkbox_radial_orientate.connect("toggled", self.get_radial_orientate)
63 model.show_base_polygon = False
64 self.checkbox_base_polygon = Gtk.CheckButton(label="Draw base polygon")
65 self.checkbox_base_polygon.set_active(model.show_base_polygon)
66 self.checkbox_base_polygon.connect("toggled", self.get_base_polygon)
68 max_base_polygon_radius = min(CANVAS_WIDTH, CANVAS_HEIGHT)
69 model.base_polygon_radius = max_base_polygon_radius / 4.0
70 ad = Gtk.Adjustment(model.base_polygon_radius, 0, max_base_polygon_radius, 1, 0, 0)
71 self.spin_base_polygon_radius = Gtk.SpinButton(adjustment=ad, climb_rate=1, digits=0)
72 self.spin_base_polygon_radius.connect("value-changed", self.get_base_polygon_radius)
74 model.show_stars = False
75 self.checkbox_stars = Gtk.CheckButton(label="Draw stars")
76 self.checkbox_stars.set_active(model.show_stars)
77 self.checkbox_stars.connect("toggled", self.get_draw_stars)
79 model.show_elements = True
80 self.checkbox_elements = Gtk.CheckButton(label="Draw elements")
81 self.checkbox_elements.set_active(model.show_elements)
82 self.checkbox_elements.connect("toggled", self.get_draw_elements)
84 model.show_labels = False
85 self.checkbox_labels = Gtk.CheckButton(label="Draw labels")
86 self.checkbox_labels.set_active(model.show_labels)
87 self.checkbox_labels.connect("toggled", self.get_draw_labels)
89 model.element_angle_offset = 0
90 ad2 = Gtk.Adjustment(math.degrees(model.element_angle_offset), -1, 360, 1, 0, 0)
91 self.spin_element_angle = Gtk.SpinButton(adjustment=ad2, climb_rate=1, digits=0)
92 self.spin_element_angle.connect("value-changed", self.get_element_angle)
94 model.base_polygon_angle = 0
95 ad = Gtk.Adjustment(math.degrees(model.base_polygon_angle), -1, 360, 1, 0, 0)
96 self.spin_global_angle = Gtk.SpinButton(adjustment=ad, climb_rate=1, digits=0)
97 self.spin_global_angle.connect("value-changed", self.get_global_angle)
99 self.darea = Gtk.DrawingArea()
100 self.darea.connect("draw", self.draw)
101 self.darea.set_size_request(CANVAS_WIDTH, CANVAS_HEIGHT)
103 self.export_button = Gtk.Button(label="Export diagram")
104 self.export_button.connect("clicked", self.on_export_diagram)
106 controls = Gtk.VBox(spacing=10)
109 vbox.pack_start(Gtk.Label("Elements"), False, False, 0)
110 vbox.pack_start(self.spin_base_polygon_sides, False, False, 0)
111 controls.pack_start(vbox, False, False, 0)
114 vbox.pack_start(Gtk.Label("Element sides"), False, False, 0)
115 vbox.pack_start(self.spin_elem_sides, False, False, 0)
116 controls.pack_start(vbox, False, False, 0)
119 vbox.pack_start(Gtk.Label("Element radius"), False, False, 0)
120 vbox.pack_start(self.spin_element_radius, False, False, 0)
121 controls.pack_start(vbox, False, False, 0)
123 controls.pack_start(self.checkbox_radial_orientate, False, False, 0)
126 vbox.pack_start(Gtk.Label("Element angle"), False, False, 0)
127 vbox.pack_start(self.spin_element_angle, False, False, 0)
128 controls.pack_start(vbox, False, False, 0)
131 vbox.pack_start(Gtk.Label("Global angle"), False, False, 0)
132 vbox.pack_start(self.spin_global_angle, False, False, 0)
133 controls.pack_start(vbox, False, False, 0)
135 controls.pack_start(self.checkbox_base_polygon, False, False, 0)
138 vbox.pack_start(Gtk.Label("Base polygon radius"), False, False, 0)
139 vbox.pack_start(self.spin_base_polygon_radius, False, False, 0)
140 controls.pack_start(vbox, False, False, 0)
142 controls.pack_start(self.checkbox_elements, False, False, 0)
143 controls.pack_start(self.checkbox_stars, False, False, 0)
144 controls.pack_start(self.checkbox_labels, False, False, 0)
145 controls.pack_end(self.export_button, False, False, 0)
147 main_container = Gtk.HBox(spacing=10)
148 main_container.add(controls)
149 main_container.add(self.darea)
151 self.add(main_container)
153 def normalize_angle(self, angle):
161 def get_base_polygon_sides(self, event):
162 self.model.base_polygon_sides = self.spin_base_polygon_sides.get_value_as_int()
163 self.darea.queue_draw()
165 def get_elem_sides(self, event):
166 self.model.element_sides = self.spin_elem_sides.get_value_as_int()
167 self.darea.queue_draw()
169 def get_element_radius(self, event):
170 self.model.element_radius = self.spin_element_radius.get_value_as_int()
171 self.darea.queue_draw()
173 def get_radial_orientate(self, event):
174 self.model.radial_orientate = self.checkbox_radial_orientate.get_active()
175 self.darea.queue_draw()
177 def get_base_polygon(self, event):
178 self.model.show_base_polygon = self.checkbox_base_polygon.get_active()
179 self.darea.queue_draw()
181 def get_base_polygon_radius(self, event):
182 self.model.base_polygon_radius = self.spin_base_polygon_radius.get_value_as_int()
183 self.darea.queue_draw()
185 def get_draw_elements(self, event):
186 self.model.show_elements = self.checkbox_elements.get_active()
187 self.darea.queue_draw()
189 def get_draw_stars(self, event):
190 self.model.show_stars = self.checkbox_stars.get_active()
191 self.darea.queue_draw()
193 def get_draw_labels(self, event):
194 self.model.show_labels = self.checkbox_labels.get_active()
195 self.darea.queue_draw()
197 def get_global_angle(self, event):
198 angle = self.normalize_angle(self.spin_global_angle.get_value_as_int())
199 self.spin_global_angle.set_value(angle)
200 self.model.base_polygon_angle = math.radians(angle)
201 self.darea.queue_draw()
203 def get_element_angle(self, event):
204 angle = self.normalize_angle(self.spin_element_angle.get_value_as_int())
205 self.spin_element_angle.set_value(angle)
206 self.model.element_angle_offset = math.radians(angle)
207 self.darea.queue_draw()
209 def on_export_diagram(self, event):
210 dialog = Gtk.FileChooserDialog("Export Diagram", self,
211 Gtk.FileChooserAction.SAVE,
212 (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
213 Gtk.STOCK_SAVE, Gtk.ResponseType.OK))
214 dialog.set_property('do-overwrite-confirmation', True)
216 filter_svg = Gtk.FileFilter()
217 filter_svg.set_name("SVG files")
218 filter_svg.add_mime_type("image/svg+xml")
219 dialog.add_filter(filter_svg)
221 filter_png = Gtk.FileFilter()
222 filter_png.set_name("PNG files")
223 filter_png.add_mime_type("image/png")
224 dialog.add_filter(filter_png)
226 response = dialog.run()
227 if response == Gtk.ResponseType.OK:
228 filename = dialog.get_filename()
229 if filename.endswith(".svg"):
230 self.diagram.save_svg(filename[:-4])
231 elif filename.endswith(".png"):
232 self.diagram.save_png(filename[:-4])
236 def draw(self, darea, cr):
237 self.diagram.draw(self.model)
239 src_surface = self.diagram.surface
240 cr.set_source_surface(src_surface, 0, 0)
244 class RadialSymmetryApp(Gtk.Application):
247 Gtk.Application.__init__(self,
248 application_id=APPLICATION_ID,
249 flags=Gio.ApplicationFlags.FLAGS_NONE)
250 self.connect("activate", self.activateCb)
252 def activateCb(self, app):
253 window = RadialSymmetryWindow()
254 app.add_window(window)
257 if __name__ == "__main__":
258 app = RadialSymmetryApp()
259 exit_status = app.run(sys.argv)
260 sys.exit(exit_status)