1 # Copyright (c) 2009 Paolo Capriotti <p.capriotti@gmail.com>
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
8 require 'observer_utils'
10 require 'toolkits/qt_gui_builder'
12 ParseException = Class.new(Exception)
16 # Convert any marshallable ruby object into a QVariant.
23 # Extract the ruby object contained in a QVariant.
27 Marshal.load(str) if str
32 def self.from_hex(str)
33 new([str.gsub(/\W+/, '')].pack('H*'))
39 # Ensure this painter is closed after the block is executed.
48 # Execute a block, then restore the painter state to what it
49 # was before execution.
61 # Convert this image to a pixmap.
64 Qt::Pixmap.from_image self
68 # Paint on an image using the given block. The block is passed
69 # a painter to use for drawing.
71 def self.painted(size, &blk)
72 Qt::Image.new(size.x, size.y, Qt::Image::Format_ARGB32_Premultiplied).tap do |img|
74 Qt::Painter.new(img).paint(&blk)
79 # Render an svg object onto a new image of the specified size. If id is not
80 # specified, the whole svg file is rendered.
82 def self.from_renderer(size, renderer, id = nil)
83 img = Qt::Image.painted(size) do |p|
85 renderer.render(p, id)
96 self.x == other.x and self.y == other.y
100 "(#{self.x}, #{self.y})"
106 "[#{self.x}, #{self.y} - #{self.width}, #{self.height}]"
111 include PrintablePoint
114 Qt::PointF.new(self.x, self.y)
119 include PrintablePoint
122 Qt::Point.new(self.x.to_i, self.y.to_i)
127 include PrintablePoint
139 include PrintablePoint
151 include PrintableRect
154 Qt::RectF.new(self.x, self.y, self.width, self.height)
159 include PrintableRect
164 # Render a pixmap from an svg file. See also Qt::Image#renderer.
166 def self.from_svg(size, file, id = nil)
167 from_renderer(size, Qt::SvgRenderer.new(file), id)
171 # Render a pixmap using an svg renderer. See also Qt::Image#renderer.
173 def self.from_renderer(size, renderer, id = nil)
174 Qt::Image.from_renderer(size, renderer, id).to_pix
179 def create_signal_map
181 (0...methodCount).map do |i|
183 if m.methodType == Qt::MetaMethod::Signal
185 sign =~ /^(.*)\(.*\)$/
186 sig = $1.underscore.to_sym
187 val = [sign, m.parameterTypes]
199 class SignalDisconnecter
200 def initialize(obj, sig)
206 @obj.disconnect(@sig)
210 class ObserverDisconnecter
211 def initialize(obj, observer)
217 @obj.delete_observer(@observer)
224 def initialize(signal, types)
225 raise "Only symbols are supported as signals" unless signal.is_a?(Symbol)
230 def self.create(signal, types)
231 if signal.is_a?(self)
243 def on(sig, types = nil, &blk)
244 sig = Signal.create(sig, types)
245 candidates = if is_a? Qt::Object
246 signal_map[sig.symbol]
250 # find candidate with the correct argument types
251 candidates = candidates.find_all{|s| s[1] == types }
253 if candidates.size > 1
254 # find candidate with the correct arity
258 candidates = [candidates.first]
260 candidates = candidates.find_all{|s| s[1].size == arity }
263 if candidates.size > 1
264 raise "Ambiguous overload for #{sig} with arity #{arity}"
265 elsif candidates.empty?
267 "with types #{types.join(' ')}"
269 "with arity #{blk.arity}"
271 raise "No overload for #{sig} #{msg}"
273 sign = SIGNAL(candidates.first[0])
275 SignalDisconnecter.new(self, sign)
277 observer = observe(sig.symbol, &blk)
278 ObserverDisconnecter.new(self, observer)
282 def in(interval, &blk)
283 Qt::Timer.in(interval, self, &blk)
291 self.class.signal_map(self)
294 def self.signal_map(obj)
295 @signal_map ||= self.create_signal_map(obj)
298 def self.create_signal_map(obj)
299 obj.meta_object.create_signal_map
305 # Execute the given block every interval milliseconds and return a timer
306 # object. Note that if the timer is garbage collected, the block will not
307 # be executed anymore, so the caller should keep a reference to it for as
309 # To prevent further invocations of the block, use QTimer#stop.
311 def self.every(interval, &blk)
316 timer.connect(SIGNAL('timeout()')) { blk[time.elapsed] }
317 timer.start(interval)
318 # return the timer, so that the caller
319 # has a chance to keep it referenced, so
320 # that it is not garbage collected
325 # Execute the given block after interval milliseconds. If target is
326 # specified, the block is invoked in the context of target.
328 def self.in(interval, target = nil, &blk)
329 single_shot(interval,
330 Qt::BlockInvocation.new(target, blk, 'invoke()'),
338 # Create a list from an array of pairs (text, data)
339 # The data for each item can be retrieved using the
341 # Note that if an array element is not a pair, its
342 # value will be used both for the text and for the
345 # For example: <tt>list.current_item.get</tt>
347 def from_a(parent, array)
348 new(parent).tap do |list|
349 list.reset_from_a(array)
355 # Select the item for which the given block
358 def select_item(&blk)
359 (0...count).each do |i|
361 self.current_index = i
369 # Populate the list with values from an array.
372 def reset_from_a(array)
374 array.each do |values|
375 text, data = if values.is_a?(String)
380 create_item(text, data)
384 def self.included(base)
385 base.extend ClassMethods
390 FROM_A_DATA_ROLE = Qt::UserRole
393 class Item < Qt::ListWidgetItem
394 def initialize(text, list, data)
396 set_data(FROM_A_DATA_ROLE, Qt::Variant.from_ruby(data))
400 data(FROM_A_DATA_ROLE).to_ruby
404 def current_index=(i)
408 def create_item(text, data)
409 Item.new(text, self, data)
414 def self.get_open_url(dir, filter, parent, caption)
415 filename = get_open_file_name(parent, caption, dir.to_local_file, filter)
416 Qt::Url.from_local_file(filename)
419 def self.get_save_url(dir, filter, parent, caption)
420 filename = get_save_file_name(parent, caption, dir.to_local_file, filter)
421 Qt::Url.from_local_file(filename)
433 # Helper method to delete model rows from within a block. This method
434 # ensures that the appropriate begin/end functions are called.
436 def removing_rows(parent, first, last)
441 begin_remove_rows(parent || Qt::ModelIndex.new, first, last)
450 # Helper method to insert model rows from within a block. This method
451 # ensures that the appropriate begin/end functions are called.
453 def inserting_rows(parent, first, last)
458 begin_insert_rows(parent || Qt::ModelIndex.new, first, last)
469 attr_accessor :main_layout
471 def add_layout(layout)
473 owner.main_layout = layout
476 def add_accessor(name, result)
477 owner.metaclass_eval do
478 define_method(name) { result }
495 Qt::GuiBuilder.build(self, gui)
496 buddies.each do |label, buddy|
497 label.buddy = owner.__send__(buddy)
505 Item = Struct.new(:get)
507 def create_item(text, data)
508 add_item(text, Qt::Variant.from_ruby(data))
516 Item.new(item_data(i).to_ruby)
520 def KDE.download_tempfile(url, parent)