1
by David Planella
Initial project creation with Quickly! |
1 |
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
|
2 |
### BEGIN LICENSE
|
|
20
by David Planella
Added license |
3 |
# Copyright (C) 2012 David Planella <david.planella@ubuntu.com>
|
72
by David Planella
Updated version to follow that of the package |
4 |
# This program is free software: you can redistribute it and/or modify it
|
5 |
# under the terms of the GNU General Public License version 3, as published
|
|
20
by David Planella
Added license |
6 |
# by the Free Software Foundation.
|
72
by David Planella
Updated version to follow that of the package |
7 |
#
|
8 |
# This program is distributed in the hope that it will be useful, but
|
|
9 |
# WITHOUT ANY WARRANTY; without even the implied warranties of
|
|
10 |
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
|
|
20
by David Planella
Added license |
11 |
# PURPOSE. See the GNU General Public License for more details.
|
72
by David Planella
Updated version to follow that of the package |
12 |
#
|
13 |
# You should have received a copy of the GNU General Public License along
|
|
20
by David Planella
Added license |
14 |
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
1
by David Planella
Initial project creation with Quickly! |
15 |
### END LICENSE
|
16 |
||
21
by David Planella
Started adding more bling to the QR code drawing area with Cairo |
17 |
import cairo |
18 |
import math |
|
75
by David Planella
Decoupled QR code types from the QR window and from a particular GUI toolkit (right now only GTK is supported) |
19 |
from gi.repository import Gtk, Gdk, GdkPixbuf # pylint: disable=E0611 |
1
by David Planella
Initial project creation with Quickly! |
20 |
import logging |
21 |
logger = logging.getLogger('qreator') |
|
22 |
||
61.1.16
by David Planella
Simplified internationalization code and made it work with /opt without having to depend on quickly packaging changes |
23 |
from qreator_lib.i18n import _ |
1
by David Planella
Initial project creation with Quickly! |
24 |
from qreator_lib import Window |
75
by David Planella
Decoupled QR code types from the QR window and from a particular GUI toolkit (right now only GTK is supported) |
25 |
from qreator_lib.helpers import get_media_file |
26 |
||
27 |
from QRCode import QRCode |
|
28 |
from QRCode import QRCodeOutput |
|
3
by David Planella
First working version |
29 |
|
17
by David Planella
First working version ready for release |
30 |
COL_DESC = 0 |
3
by David Planella
First working version |
31 |
COL_PIXBUF = 1 |
75
by David Planella
Decoupled QR code types from the QR window and from a particular GUI toolkit (right now only GTK is supported) |
32 |
COL_ID = 2 |
3
by David Planella
First working version |
33 |
|
17
by David Planella
First working version ready for release |
34 |
PAGE_NEW = 0 |
35 |
#PAGE_HISTORY = 1
|
|
36 |
PAGE_ABOUT = 1 |
|
37 |
PAGE_QR = 2 |
|
3
by David Planella
First working version |
38 |
|
39 |
||
1
by David Planella
Initial project creation with Quickly! |
40 |
# See qreator_lib.Window.py for more details about how this class works
|
41 |
class QreatorWindow(Window): |
|
42 |
__gtype_name__ = "QreatorWindow" |
|
3
by David Planella
First working version |
43 |
|
17
by David Planella
First working version ready for release |
44 |
def finish_initializing(self, builder): # pylint: disable=E1002 |
1
by David Planella
Initial project creation with Quickly! |
45 |
"""Set up the main window"""
|
46 |
super(QreatorWindow, self).finish_initializing(builder) |
|
47 |
||
48 |
# Code for other initialization actions should be added here.
|
|
16
by David Planella
Added some documentation, cleaned up the QRCode's object methods a bit |
49 |
|
50 |
# Initialize the clipboard
|
|
3
by David Planella
First working version |
51 |
self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) |
16
by David Planella
Added some documentation, cleaned up the QRCode's object methods a bit |
52 |
|
53 |
# Initialize the style for the main toolbar
|
|
12
by David Planella
Moved the QR generation code to a separate module, independent of the UI |
54 |
context = self.ui.toolbar1.get_style_context() |
55 |
context.add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR) |
|
16
by David Planella
Added some documentation, cleaned up the QRCode's object methods a bit |
56 |
|
57 |
# Initialize the Cairo surface that will contain the QR code
|
|
3
by David Planella
First working version |
58 |
self.surface = None |
16
by David Planella
Added some documentation, cleaned up the QRCode's object methods a bit |
59 |
|
60 |
# Hide the notebook tabs
|
|
3
by David Planella
First working version |
61 |
self.ui.notebook1.set_show_tabs(False) |
14
by David Planella
Added support to display maps with libchamplain. python-osmgpsmap, as an alternative without Clutter, does not seem to work because it's not introspectable -gtk3 branch is still WIP. Commented out GSettings code, at it seems to crash Winpdb (settings schema not found) |
62 |
|
32
by David Planella
Added About dialog - thanks Damian for the help http://is.gd/9ouXsz |
63 |
# Initialize about dialog
|
75
by David Planella
Decoupled QR code types from the QR window and from a particular GUI toolkit (right now only GTK is supported) |
64 |
self.init_about_dialog() |
65 |
||
66 |
# Initialize the QR types icon view
|
|
67 |
self.init_qr_types() |
|
68 |
||
69 |
# Load the background texture in the QR code page
|
|
61.1.14
by David Planella
Moved the USC database initialization code to be a task and a callback, in a first attempt to run it asynchronously. Started trying that with python-defer |
70 |
self.texture = cairo.ImageSurface.create_from_png( |
71 |
get_media_file("pattern.png")) |
|
72 |
||
75
by David Planella
Decoupled QR code types from the QR window and from a particular GUI toolkit (right now only GTK is supported) |
73 |
# Add an initial text, so that there is an initial QR code
|
74 |
self.qr_code_placeholder = 'http://launchpad.net/qreator' |
|
75 |
||
76 |
def init_qr_types(self): |
|
61.1.15
by David Planella
Fixed a bug whereby the number of icon columns in the main iconview stayed always fixed at 1 |
77 |
# Set up the QR types shown in the main icon view
|
78 |
self.ui.qr_types_view.set_text_column(COL_DESC) |
|
79 |
self.ui.qr_types_view.set_pixbuf_column(COL_PIXBUF) |
|
80 |
||
75
by David Planella
Decoupled QR code types from the QR window and from a particular GUI toolkit (right now only GTK is supported) |
81 |
from qrcodes.QRCodeText import QRCodeText |
82 |
from qrcodes.QRCodeURL import QRCodeURL |
|
83 |
from qrcodes.QRCodeLocation import QRCodeLocation |
|
84 |
from qrcodes.QRCodeWifi import QRCodeWifi |
|
85 |
from qrcodes.QRCodeSoftwareCenterApp import QRCodeSoftwareCenterApp |
|
86 |
||
61.1.15
by David Planella
Fixed a bug whereby the number of icon columns in the main iconview stayed always fixed at 1 |
87 |
self.qr_types = [ |
75
by David Planella
Decoupled QR code types from the QR window and from a particular GUI toolkit (right now only GTK is supported) |
88 |
QRCodeURL(self.update_qr_code, 'url.png', _('URL'), 0), |
89 |
QRCodeText(self.update_qr_code, 'text.png', _('Text'), 1), |
|
90 |
QRCodeLocation(self.update_qr_code, 'location.png', |
|
91 |
_('Geolocation'), 2), |
|
92 |
QRCodeWifi(self.update_qr_code, 'wifi.png', _('Wifi network'), 3), |
|
93 |
QRCodeSoftwareCenterApp(self.update_qr_code, 'softwarecentre.png', |
|
94 |
_('Ubuntu Software Center app'), 4), |
|
61.1.15
by David Planella
Fixed a bug whereby the number of icon columns in the main iconview stayed always fixed at 1 |
95 |
]
|
96 |
||
75
by David Planella
Decoupled QR code types from the QR window and from a particular GUI toolkit (right now only GTK is supported) |
97 |
self.qr_types_store = Gtk.ListStore(str, GdkPixbuf.Pixbuf, int) |
98 |
# ^ desc, icon, id ^
|
|
61.1.15
by David Planella
Fixed a bug whereby the number of icon columns in the main iconview stayed always fixed at 1 |
99 |
self.qr_types_store.set_sort_column_id(COL_DESC, |
100 |
Gtk.SortType.ASCENDING) |
|
101 |
self.ui.qr_types_view.set_model(self.qr_types_store) |
|
102 |
self.fill_qr_types_store() |
|
103 |
||
104 |
self.curr_height = 0 |
|
105 |
self.curr_width = 0 |
|
106 |
||
75
by David Planella
Decoupled QR code types from the QR window and from a particular GUI toolkit (right now only GTK is supported) |
107 |
for qr_type in self.qr_types: |
82
by Stefan Schwarzburg
replaced qr_input_notebook with qr_input_box, because boxes are much easier to automatically resize. |
108 |
self.ui.qr_input_box.add(qr_type.widget.grid) |
79
by David Planella
An attempt to get notebook pages to resize. Thanks to xubuntix for contributing to the code |
109 |
|
61.1.15
by David Planella
Fixed a bug whereby the number of icon columns in the main iconview stayed always fixed at 1 |
110 |
def fill_qr_types_store(self): |
111 |
self.qr_types_store.clear() |
|
112 |
||
113 |
for qr_type in self.qr_types: |
|
75
by David Planella
Decoupled QR code types from the QR window and from a particular GUI toolkit (right now only GTK is supported) |
114 |
icon = GdkPixbuf.Pixbuf.new_from_file(get_media_file( |
115 |
qr_type.icon_path)) |
|
116 |
self.qr_types_store.append([qr_type.description, |
|
117 |
icon, |
|
118 |
qr_type.id]) |
|
61.1.15
by David Planella
Fixed a bug whereby the number of icon columns in the main iconview stayed always fixed at 1 |
119 |
|
120 |
def on_qreator_window_check_resize(self, widget): |
|
121 |
'''We need this function to fix the issue described at
|
|
122 |
http://bit.ly/LW94BO whereby the number of columns of the icon view
|
|
123 |
widget stays fixed at the number set for the initial width of the
|
|
124 |
window'''
|
|
125 |
# Get the new size
|
|
126 |
new_width = widget.get_size()[0] |
|
127 |
new_height = widget.get_size()[1] |
|
128 |
# If the size has changed...
|
|
129 |
if(new_width != self.curr_width or new_height != self.curr_height): |
|
130 |
# Remember new size
|
|
131 |
self.curr_width = new_width |
|
132 |
self.curr_height = new_height |
|
133 |
# and refill iconviews with icons to adjust columns number
|
|
134 |
self.fill_qr_types_store() |
|
135 |
||
75
by David Planella
Decoupled QR code types from the QR window and from a particular GUI toolkit (right now only GTK is supported) |
136 |
def init_about_dialog(self): |
137 |
# Initialize about dialog
|
|
138 |
about = Gtk.AboutDialog() |
|
139 |
about.set_program_name("Qreator") |
|
140 |
about.set_copyright( |
|
141 |
"Copyright (c) 2012 David Planella" + |
|
142 |
"\nhttp://about.me/david.planella") |
|
143 |
about.set_website("https://launchpad.net/qreator") |
|
144 |
||
145 |
about.set_version('12.05.6') |
|
146 |
about.set_authors([ |
|
147 |
'David Planella <david.planella@ubuntu.com>', |
|
148 |
'Michael Hall <mhall119@ubuntu.com>', |
|
149 |
'Andrew Starr-Bochicchio <andrewsomething@ubuntu.com >', |
|
150 |
])
|
|
151 |
about.set_license(_('Distributed under the GPL v3 license.') + |
|
152 |
'\nhttp://www.opensource.org/licenses/gpl-3.0.html') |
|
153 |
||
154 |
about.set_translator_credits(_("translator-credits")) |
|
155 |
||
156 |
box = self.ui.about_box |
|
157 |
about.vbox.reparent(box) |
|
158 |
||
159 |
# Get rid of the 'Close' button
|
|
160 |
for button in about.action_area.get_children(): |
|
161 |
if button.get_property('label') == 'gtk-close': |
|
162 |
button.destroy() |
|
163 |
||
164 |
##########################################
|
|
14
by David Planella
Added support to display maps with libchamplain. python-osmgpsmap, as an alternative without Clutter, does not seem to work because it's not introspectable -gtk3 branch is still WIP. Commented out GSettings code, at it seems to crash Winpdb (settings schema not found) |
165 |
|
17
by David Planella
First working version ready for release |
166 |
def on_toolbuttonNew_clicked(self, widget, data=None): |
16
by David Planella
Added some documentation, cleaned up the QRCode's object methods a bit |
167 |
'''Shows the home page'''
|
17
by David Planella
First working version ready for release |
168 |
self.ui.notebook1.set_current_page(PAGE_NEW) |
3
by David Planella
First working version |
169 |
|
170 |
def on_toolbuttonHistory_clicked(self, widget, data=None): |
|
16
by David Planella
Added some documentation, cleaned up the QRCode's object methods a bit |
171 |
'''Shows the history page'''
|
17
by David Planella
First working version ready for release |
172 |
pass # self.ui.notebook1.set_current_page(PAGE_HISTORY) |
3
by David Planella
First working version |
173 |
|
174 |
def on_toolbuttonAbout_clicked(self, widget, data=None): |
|
16
by David Planella
Added some documentation, cleaned up the QRCode's object methods a bit |
175 |
'''Shows the about page'''
|
3
by David Planella
First working version |
176 |
self.ui.notebook1.set_current_page(PAGE_ABOUT) |
177 |
||
75
by David Planella
Decoupled QR code types from the QR window and from a particular GUI toolkit (right now only GTK is supported) |
178 |
##########################################
|
17
by David Planella
First working version ready for release |
179 |
|
61.1.15
by David Planella
Fixed a bug whereby the number of icon columns in the main iconview stayed always fixed at 1 |
180 |
def on_qr_types_view_item_activated(self, widget, item): |
16
by David Planella
Added some documentation, cleaned up the QRCode's object methods a bit |
181 |
'''Loads the UI for the appropriate QR type'''
|
14
by David Planella
Added support to display maps with libchamplain. python-osmgpsmap, as an alternative without Clutter, does not seem to work because it's not introspectable -gtk3 branch is still WIP. Commented out GSettings code, at it seems to crash Winpdb (settings schema not found) |
182 |
|
183 |
model = widget.get_model() |
|
75
by David Planella
Decoupled QR code types from the QR window and from a particular GUI toolkit (right now only GTK is supported) |
184 |
qr_id = model[item][COL_ID] |
185 |
self.qr_types[qr_id].widget.on_activated() |
|
17
by David Planella
First working version ready for release |
186 |
|
187 |
self.ui.notebook1.set_current_page(PAGE_QR) |
|
82
by Stefan Schwarzburg
replaced qr_input_notebook with qr_input_box, because boxes are much easier to automatically resize. |
188 |
|
189 |
for child in self.ui.qr_input_box.get_children(): |
|
190 |
child.hide() |
|
191 |
self.ui.qr_input_box.get_children()[qr_id].show() |
|
192 |
||
75
by David Planella
Decoupled QR code types from the QR window and from a particular GUI toolkit (right now only GTK is supported) |
193 |
|
194 |
##########################################
|
|
3
by David Planella
First working version |
195 |
|
196 |
def get_pixbuf_from_drawing_area(self): |
|
17
by David Planella
First working version ready for release |
197 |
window = self.ui.qr_drawingarea.get_window() |
5
by David Planella
Removed whitespace |
198 |
|
17
by David Planella
First working version ready for release |
199 |
src_x, src_y = self.get_centered_coordinates(self.ui.qr_drawingarea, |
3
by David Planella
First working version |
200 |
self.surface) |
11
by David Planella
Set up automatic resizing of images through PIL, and 3 standard output image sizes. Fixed RGBA > BGRA conversion going from PIL to Cairo |
201 |
image_height = self.surface.get_height() |
202 |
image_width = self.surface.get_width() |
|
5
by David Planella
Removed whitespace |
203 |
|
3
by David Planella
First working version |
204 |
return Gdk.pixbuf_get_from_window(window, src_x, src_y, |
205 |
image_width, image_height) |
|
206 |
||
75
by David Planella
Decoupled QR code types from the QR window and from a particular GUI toolkit (right now only GTK is supported) |
207 |
##########################################
|
208 |
||
3
by David Planella
First working version |
209 |
def on_toolbuttonSave_clicked(self, widget, data=None): |
210 |
if not self.surface: |
|
211 |
return
|
|
5
by David Planella
Removed whitespace |
212 |
|
7
by David Planella
Added a file chooser to save the resulting image. Started playing with fade ins. |
213 |
dialog = Gtk.FileChooserDialog(_("Please choose a file"), self, |
214 |
Gtk.FileChooserAction.SAVE, |
|
215 |
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, |
|
216 |
Gtk.STOCK_SAVE, Gtk.ResponseType.OK)) |
|
217 |
||
218 |
filter_png = Gtk.FileFilter() |
|
219 |
filter_png.set_name(_("PNG images")) |
|
220 |
filter_png.add_mime_type("image/png") |
|
221 |
dialog.add_filter(filter_png) |
|
222 |
||
223 |
response = dialog.run() |
|
224 |
||
225 |
if response == Gtk.ResponseType.OK: |
|
226 |
# We cannot write directly from the surface, as the
|
|
227 |
# Surface.write_to_png() method writes the image in the original
|
|
228 |
# size returned by qrencode (i.e. non-scaled), and the
|
|
229 |
# SurfacePattern does not have any methods to write to disk.
|
|
230 |
# So we read the contents from the Gtk.DrawingArea, put them into
|
|
231 |
# a Gdk.Pixbuf and use its 'savev' method to write to disk.
|
|
232 |
||
233 |
pixbuf = self.get_pixbuf_from_drawing_area() |
|
234 |
||
235 |
pixbuf.savev(dialog.get_filename(), 'png', [], []) |
|
236 |
||
237 |
dialog.destroy() |
|
3
by David Planella
First working version |
238 |
|
239 |
def on_toolbuttonCopy_clicked(self, widget, data=None): |
|
240 |
if not self.surface: |
|
241 |
return
|
|
5
by David Planella
Removed whitespace |
242 |
|
3
by David Planella
First working version |
243 |
pixbuf = self.get_pixbuf_from_drawing_area() |
244 |
self.clipboard.set_image(pixbuf) |
|
5
by David Planella
Removed whitespace |
245 |
|
75
by David Planella
Decoupled QR code types from the QR window and from a particular GUI toolkit (right now only GTK is supported) |
246 |
##########################################
|
247 |
||
17
by David Planella
First working version ready for release |
248 |
def update_qr_code(self, text): |
249 |
self.qr_code_placeholder = text |
|
250 |
self.ui.qr_drawingarea.queue_draw() |
|
251 |
||
3
by David Planella
First working version |
252 |
def get_centered_coordinates(self, drawing_area, surface): |
5
by David Planella
Removed whitespace |
253 |
|
3
by David Planella
First working version |
254 |
drawing_area_height = drawing_area.get_allocated_height() |
255 |
drawing_area_width = drawing_area.get_allocated_width() |
|
9
by David Planella
Scaling is now done at the source |
256 |
image_height = surface.get_height() |
257 |
image_width = surface.get_width() |
|
3
by David Planella
First working version |
258 |
|
5
by David Planella
Removed whitespace |
259 |
return (drawing_area_width / 2 - image_width / 2, |
260 |
drawing_area_height / 2 - image_height / 2) |
|
3
by David Planella
First working version |
261 |
|
17
by David Planella
First working version ready for release |
262 |
def on_qr_drawingarea_draw(self, widget, ctx, data=None): |
14
by David Planella
Added support to display maps with libchamplain. python-osmgpsmap, as an alternative without Clutter, does not seem to work because it's not introspectable -gtk3 branch is still WIP. Commented out GSettings code, at it seems to crash Winpdb (settings schema not found) |
263 |
text = self.qr_code_placeholder |
3
by David Planella
First working version |
264 |
|
22
by David Planella
Some cleanup in the Cairo code |
265 |
## Fill the background
|
266 |
||
267 |
# Create a rounded rectanble
|
|
21
by David Planella
Started adding more bling to the QR code drawing area with Cairo |
268 |
x = 0.0 |
22
by David Planella
Some cleanup in the Cairo code |
269 |
y = 0.0 |
21
by David Planella
Started adding more bling to the QR code drawing area with Cairo |
270 |
width = widget.get_allocated_width() |
271 |
height = widget.get_allocated_height() |
|
272 |
aspect = 1.0 |
|
273 |
corner_radius = height / 50.0 |
|
274 |
||
275 |
radius = corner_radius / aspect |
|
276 |
degrees = math.pi / 180.0 |
|
277 |
||
22
by David Planella
Some cleanup in the Cairo code |
278 |
ctx.new_sub_path() |
279 |
ctx.arc(x + width - radius, y + radius, radius, -90 * degrees, |
|
280 |
0 * degrees) |
|
281 |
ctx.arc(x + width - radius, y + height - radius, radius, 0 * degrees, |
|
282 |
90 * degrees) |
|
283 |
ctx.arc(x + radius, y + height - radius, radius, 90 * degrees, |
|
284 |
180 * degrees) |
|
285 |
ctx.arc(x + radius, y + radius, radius, 180 * degrees, 270 * degrees) |
|
286 |
ctx.close_path() |
|
287 |
||
288 |
# Fill the rounded rectangle with a linear gradient
|
|
21
by David Planella
Started adding more bling to the QR code drawing area with Cairo |
289 |
lg = cairo.LinearGradient(0.0, 0.0, 500.0, 500.0) |
61.1.14
by David Planella
Moved the USC database initialization code to be a task and a callback, in a first attempt to run it asynchronously. Started trying that with python-defer |
290 |
lg.add_color_stop_rgba(0, 0.27, 0.27, 0.27, 1) # 1,1,1 is white |
61.1.13
by David Planella
Added proper icon for the Software Centre app QR codes. Added texture to the background of the QR code image |
291 |
lg.add_color_stop_rgba(1, 0.22, 0.22, 0.22, 1) |
22
by David Planella
Some cleanup in the Cairo code |
292 |
|
21
by David Planella
Started adding more bling to the QR code drawing area with Cairo |
293 |
ctx.set_source(lg) |
294 |
ctx.fill() |
|
61.1.14
by David Planella
Moved the USC database initialization code to be a task and a callback, in a first attempt to run it asynchronously. Started trying that with python-defer |
295 |
|
61.1.15
by David Planella
Fixed a bug whereby the number of icon columns in the main iconview stayed always fixed at 1 |
296 |
# Load a texture and overlay it to the gradient, tiled
|
61.1.13
by David Planella
Added proper icon for the Software Centre app QR codes. Added texture to the background of the QR code image |
297 |
pattern = cairo.SurfacePattern(self.texture) |
298 |
pattern.set_extend(cairo.EXTEND_REPEAT) |
|
299 |
ctx.set_source(pattern) |
|
300 |
ctx.paint() |
|
61.1.14
by David Planella
Moved the USC database initialization code to be a task and a callback, in a first attempt to run it asynchronously. Started trying that with python-defer |
301 |
|
74
by David Planella
Added icons for the 'clear' action to all text entries, used symbolic icons in the QR page, delayed loading Software Centre apps until the apps tab is loaded for the first time |
302 |
if not text == '': |
303 |
## Create the QR code
|
|
304 |
qr_code = QRCode() |
|
305 |
self.surface = qr_code.encode(text, QRCodeOutput.CAIRO_SURFACE) |
|
306 |
||
307 |
# Center the image in the drawing area
|
|
308 |
centered_width, centered_height = \ |
|
309 |
self.get_centered_coordinates(widget, self.surface) |
|
310 |
ctx.translate(centered_width, centered_height) |
|
311 |
||
312 |
# Set the surface as the context's source
|
|
313 |
ctx.set_source_surface(self.surface) |
|
314 |
||
315 |
# Render the image
|
|
316 |
ctx.paint() |