qt_connect 1.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,277 @@
1
+ =begin
2
+
3
+ Author: Cees Zeelenberg (c.zeelenberg@computer.org)
4
+ Copyright: (c) 2010-2012 by Cees Zeelenberg
5
+ License: This program is free software; you can redistribute it and/or modify
6
+ it under the terms of the GNU Lesser General Public License as published
7
+ by the Free Software Foundation; either version 2 of the License, or
8
+ (at your option) any later version. *
9
+
10
+ This gem can be used to extend the current signals/slots API in QtRuby.
11
+ With this interface there is no need to use C++ signatures or slots on the application level..
12
+ The syntax is similar to the one used in QtJambi (the Java version of the Qt framework):
13
+ Each Signal-emitting Qt class is extended with a method for each Signal it may emit.
14
+ The default name of the method is the same as the signature name. In case of ambiguity
15
+ when the signal has arguments or overloads, the default name for a given signature can be
16
+ overruled through an entry in the Signature2Signal table. The initial entries in the table
17
+ are the same as used in the QtJambi interface.
18
+
19
+ Example:
20
+
21
+ button=Qt::PushButton.new
22
+ button.clicked.connect{ puts "Hello World"} #connecting signal to a Ruby Block
23
+ button.clicked.connect(self,:methodname) #connecting to receiver/method
24
+ button.clicked.disconnect #disconnect all connections originating from this button
25
+ button.clicked.emit #can be used to emit the 'clicked' signal (normally
26
+ #generated internally
27
+ label=Qt::Label.new #example passing String as argument
28
+ label.linkActivated.connect{ |s| Qt::DesktopServices.openUrl(Qt::Url.new(s))}
29
+
30
+ Custom components can define their own signals and emit them like this:
31
+
32
+ signal=Qt::Signal.new
33
+ signal.connect{ |a1,a2| puts "someone send me #{a1} and #{a2}"}
34
+ .
35
+ .
36
+ signal.emit(100,200)
37
+
38
+ =end
39
+
40
+ require 'Qt'
41
+ $VERB=1 if Qt.debug_level==Qt::DebugLevel::Minimal #minimal debugging
42
+ $VERB=2 if Qt.debug_level>Qt::DebugLevel::Minimal #list Class/Signals info
43
+
44
+
45
+
46
+
47
+ module Qt
48
+ #signal=Qt::Signal.new(pushbutton,'clicked()') signalemitter can perform (internal) emit; this constructor
49
+ # normally used internally
50
+ #signal=Qt::Signal.new only emit through signal.emit
51
+ class Signal
52
+ def initialize(signalemitter=nil,signature=nil)
53
+ @signature=signature
54
+ @signalemitter=signalemitter
55
+ @actions=[]
56
+ end
57
+
58
+ # signal1.connect{ |arg1| puts arg1} connect this signal emitter to a Ruby Block
59
+ # signal1.connect(self,:my_method) connect this signal emitter to a receiver/method
60
+ #signal1.connect(Qt::Signal signal2) connect this signal to another (Qt::Signal instance)
61
+ def connect(receiver=nil,method=nil,&block)
62
+ if @signalemitter
63
+ if receiver && (receiver.kind_of?(Qt::Signal))
64
+ @signalemitter.connect(SIGNAL(@signature)){|*args| receiver.emit(*args)}
65
+ return
66
+ end
67
+ @signalemitter.connect(SIGNAL(@signature),receiver,method) if receiver && method
68
+ @signalemitter.connect(SIGNAL(@signature),&block) if block_given?
69
+ else
70
+ @actions << [receiver,method,block]
71
+ end
72
+ end
73
+
74
+ def emit(*args)
75
+ if @signalemitter
76
+ sig=@signature.gsub(/[(].*[)]/,"").to_sym
77
+ #n.b. arguments passed in a block are passed to the superclass version(e.g. original) of the signal method
78
+ @signalemitter.send(sig){args}
79
+ else
80
+ @actions.each{ |action| receiver,method,block=action
81
+ block.call(*args) if block
82
+ receiver.send(method,*args) if receiver && method
83
+ }
84
+ end
85
+ end
86
+
87
+ #disconnect() disconnects all connections originating in this signal emitter
88
+ def disconnect
89
+ @signalemitter.disconnect(SIGNAL(@signature)) if @signalemitter
90
+ @actions=[]
91
+ end
92
+
93
+ end
94
+ # support named colors as Class methods: (compatibility QtJambi)
95
+ #e.g. Color.black => Color.new(Qt::black)
96
+ Color.class_eval do
97
+ class << self
98
+ def method_missing(m,*a,&b)
99
+ #puts "Colors missing method #{m}"
100
+ eval "Qt::Color.new(Qt::#{m})"
101
+ end
102
+ end
103
+ end
104
+ Timer.class_eval do
105
+ class << self
106
+ alias :orig_singleShot :singleShot
107
+ def singleShot(timeout,receiver,method)
108
+ if method.kind_of? Symbol
109
+ receiver.class.class_eval{ slots(method.to_s+'()')}
110
+ m=SLOT(method)
111
+ else
112
+ m=method
113
+ end
114
+ orig_singleShot(timeout,receiver,m)
115
+ end
116
+ end
117
+ end
118
+
119
+
120
+ module Internal
121
+
122
+ EmbeddedClasses={} #{ 'Qt::WebPage' => ['Qt::WebFrame']}
123
+
124
+ # this table overrides the default signature to method name mapping
125
+ #signals mapped to an empty string are ignored
126
+
127
+ Signature2Signal={
128
+
129
+ 'Qt::AbstractButton' => {
130
+ 'clicked()' => '',
131
+ 'activated(int)' => ''},
132
+
133
+ 'Qt::AbstractItemDelegate' => {
134
+ 'closeEditor(QWidget*)' => ''},
135
+
136
+ 'Qt::Action' => {
137
+ 'triggered()' => '',
138
+ 'activated()' => '',
139
+ 'activated(int)' => ''},
140
+
141
+ 'Qt::ButtonGroup' => {
142
+ 'buttonClicked(int)' => 'buttonIdClicked',
143
+ 'buttonPressed(int)' => 'buttonIdPressed',
144
+ 'buttonReleased(int)' => 'buttonIdReleased'},
145
+
146
+ 'Qt::ComboBox' => {
147
+ 'activated(int)' => 'activatedIndex',
148
+ 'highlighted(int)' => 'highlightedIndex',
149
+ 'currentIndexChanged(QString)' => 'currentStringChanged'},
150
+
151
+ 'Qt::Completer' => {
152
+ 'activated(QModelIndex)' => 'activatedIndex',
153
+ 'highlighted(QModelIndex)' => 'highlightedIndex'},
154
+
155
+ 'Qt::DoubleSpinBox' => {
156
+ 'valueChanged(QString)' => 'valueStringChanged'},
157
+
158
+ 'Qt::GroupBox' => {
159
+ 'clicked()' => ''},
160
+
161
+ 'Qt::Process' => {
162
+ 'finished(int,QProcess::ExitStatus)' => 'finishedWithStatusCode'},
163
+
164
+ 'Qt::SignalMapper' => {
165
+ 'mapped(int)' => 'mappedInteger',
166
+ 'mapped(QString)' => 'mappedString',
167
+ 'mapped(QObject*)' => 'mappedQObject'},
168
+
169
+ 'Qt::SpinBox' => {
170
+ 'valueChanged(QString)' => 'valueStringChanged'},
171
+
172
+ 'Qt::TabWidget' => {
173
+ 'currentChanged(QWidget*)' => ''},
174
+
175
+ 'Qt::TextBrowser' => {
176
+ 'highlighted(QString)' => 'highlightedString'}}
177
+
178
+ end
179
+ end
180
+ =begin
181
+ extend the 'new' class method for each Qt Class
182
+ the first time any Qt class is created, it creates new methods for each associated signal
183
+ the signal names are obtained from the metaobject for each class and its superclasses
184
+ e.g the class 'Qt::AbstractListModel' generates a signal: 'dataChanged(QModelIndex,QModelIndex)'
185
+ the first time Qt::AbstractListModel.new is called, the class Qt::AbstractListModel
186
+ is extended with the method:
187
+
188
+ def dataChanged(&block)
189
+ return super(*yield) if block_given?
190
+ return (@_dataChanged ||= Qt::Signal.new(self,'dataChanged(QModelIndex,QModelIndex)')
191
+ end
192
+ alias data_changed dataChanged
193
+
194
+ the 'super' method is only called when an internally defined signal is emitted externally e.g.
195
+ dataChanged.emit(index1,index2)
196
+
197
+ this first calls the 'dataChanged' method to obtain the underlying instance of the Signal class and
198
+ then the 'emit' method of Signal calls the same 'dataChanged' method, but with a block argument
199
+ to trigger the same method in the inherited superclass.
200
+
201
+ (N.B. 'alias', 'respond_to?' and ,'method_defined?' do not find the original signalemitters as methods)
202
+
203
+ the approach of catching the first instantiation of a Qt class does not work for (the small number)
204
+ of embedded signal emitting classes for which the instances are generated internally by Qt and thus
205
+ the 'new' Ruby class method never is called. To avoid this problem, all classes 'associated' with a class
206
+ being 'activated' are (recursively) also activated. Associated classes are classes of which instances
207
+ are returned by method calls, properties or arguments of signals
208
+ *TODO* a potential problem with this approach is that the MetaObject class does not return all methods of
209
+ a class, but only externally 'invokable' methods. Possible solution is to get this information directly
210
+ from the smoke API if possible.
211
+
212
+
213
+ =end
214
+ Qt::Base.class_eval do
215
+ class << self
216
+ alias new_before_signals new
217
+ def new(*args,&block)
218
+ setup_qt_signals
219
+ new_before_signals(*args,&block)
220
+ end
221
+
222
+ def setup_qt_signals
223
+ return if @not_first_instance
224
+ @not_first_instance=true
225
+ associates=[]
226
+ printf("\n%-30s (first instance at %s)\n" ,"#{self.name}","#{caller[0].split('/')[-1]}") if $VERB && ($VERB>1)
227
+ signals={}
228
+ begin
229
+ ancestors.each{ |klass|
230
+ next unless klass.name.start_with?('Qt::')
231
+ begin ; metaobject=klass.staticMetaObject ; rescue ; next ; end
232
+ metaobject.signalNames.each{ |signalname|
233
+ next unless md=signalname.match(/([^ ]+)\ ([^(]+)\(([^)]*)/) #<returntype><space<methodname>(<args>)
234
+ signature=md[2]+'('+md[3]+')'
235
+ methodname=if (h=Qt::Internal::Signature2Signal[klass.name]) && (mr=h[signature]) ; mr ; else ; md[2] end
236
+ next if methodname.size==0
237
+ next if self.method_defined?(methodname.to_sym)
238
+ printf(" %-30s => %s\n",methodname,signature) if $VERB && ($VERB > 1)
239
+ uscore_name = if (methodname =~ /\A[A-Z]+\z/)
240
+ methodname.downcase
241
+ else
242
+ methodname.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z\d])([A-Z])/, '\1_\2').tr("-","_").downcase
243
+ end
244
+ #puts "defining #{methodname} for #{self.name} #{self.method_defined? :new} (inherited from #{klass.name})"
245
+ self.class_eval(%Q[
246
+ def #{methodname}(&block)
247
+ return super(*yield) if block_given?
248
+ return @_#{methodname} ||=Qt::Signal.new(self,'#{signature}')
249
+ end
250
+ #{(uscore_name==methodname) ? '' : "alias :#{uscore_name} :#{methodname}"}
251
+ ])
252
+ }
253
+ (0...metaobject.propertyCount).each{ |i| associates |= [metaobject.property(i).typeName]}
254
+ (0...metaobject.methodCount).each{ |m| method=metaobject.method(m)
255
+ case method.methodType
256
+ when Qt::MetaMethod::Signal
257
+ if md=method.signature.match(/([^(]+)\(([^)]*)/)
258
+ associates |= md[2].split(',').map{ |s| s.delete('*&<>')}
259
+ end
260
+ else
261
+ associates |= [method.typeName] if method.typeName.size>0
262
+ end
263
+ }
264
+ }
265
+ rescue => e
266
+ puts e if $VERB
267
+ end
268
+ x=associates.map{|c| Qt::Internal::normalize_classname(c)}. #normalized names e.g. QWebFrame => Qt::WebFrame
269
+ select{ |c| c.start_with?('Qt::')}. #remove non-Qt classes
270
+ each{ |klassname| eval("#{klassname}.setup_qt_signals") rescue next } #recursive call to setup associates signals
271
+ #(Internal::EmbeddedClasses["#{self.name}"] || []).each{ |klassname| eval("#{klassname}.setup_qt_signals")}
272
+ end
273
+
274
+
275
+ end
276
+
277
+ end
@@ -0,0 +1,3 @@
1
+ module QtJambi
2
+ Version='1.5.1'
3
+ end
@@ -0,0 +1,8 @@
1
+
2
+ if RUBY_PLATFORM =~ /java/
3
+ require 'java'
4
+ require 'signalactions.jar'
5
+ require 'qt_connect/qt_jbindings'
6
+ else
7
+ raise 'qt_jambi API requires Java platform'
8
+ end
Binary file
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: qt_connect
3
+ version: !ruby/object:Gem::Version
4
+ hash: 1
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 5
9
+ - 1
10
+ version: 1.5.1
11
+ platform: ruby
12
+ authors:
13
+ - Cees Zeelenberg
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-05-16 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: "Qt bindings for JRuby with (optional) extension to provide backward\n compatibility with Qt Ruby programs. The API is implemented using Qt Jambi, the\n Java version of the Qt framework. For Qt Ruby programs it provides a QtJambi\n inspired interface to the Signals/Slots system without the need to use\n C++ signatures.\n "
23
+ email: c.zeelenberg@computer.org
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - lib/Qt.rb
32
+ - lib/qt_connect/qt_compat.rb
33
+ - lib/qt_connect/qt_jbindings.rb
34
+ - lib/qt_connect/qt_sugar.rb
35
+ - lib/qt_connect/version.rb
36
+ - lib/qt_connect.rb
37
+ - lib/qt_jambi.rb
38
+ - java/SignalActions.java
39
+ - lib/signalactions.jar
40
+ - examples/classexplorer/classexplorer.rb
41
+ - examples/classexplorer/menus/helpmenu.rb
42
+ - examples/classexplorer/menus.rb
43
+ - examples/classexplorer/packagetree.rb
44
+ - examples/classexplorer/qt_extensions/htmldelegate.rb
45
+ - examples/classexplorer/qt_extensions/line_edit.rb
46
+ - examples/classexplorer/qt_extensions.rb
47
+ - examples/deform/arthurframe.rb
48
+ - examples/deform/deform-demo.png
49
+ - examples/deform/Deform.html
50
+ - examples/deform/deform.rb
51
+ - examples/deform/qt-logo.png
52
+ - examples/deform/Thumbs.db
53
+ - examples/qtmapzoom/qtmapzoom.rb
54
+ - COPYING.LIB.txt
55
+ - Rakefile
56
+ - README.md
57
+ has_rdoc: true
58
+ homepage: https://github.com/CeesZ/qt_connect
59
+ licenses:
60
+ - LGPLv2.1
61
+ post_install_message:
62
+ rdoc_options: []
63
+
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ hash: 3
72
+ segments:
73
+ - 0
74
+ version: "0"
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ hash: 3
81
+ segments:
82
+ - 0
83
+ version: "0"
84
+ requirements: []
85
+
86
+ rubyforge_project:
87
+ rubygems_version: 1.3.7
88
+ signing_key:
89
+ specification_version: 3
90
+ summary: Uniform Qt bindings for JRuby and Ruby
91
+ test_files: []
92
+