qt_connect 1.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING.LIB.txt +510 -0
- data/README.md +257 -0
- data/Rakefile +72 -0
- data/examples/classexplorer/classexplorer.rb +828 -0
- data/examples/classexplorer/menus.rb +1 -0
- data/examples/classexplorer/menus/helpmenu.rb +73 -0
- data/examples/classexplorer/packagetree.rb +91 -0
- data/examples/classexplorer/qt_extensions.rb +2 -0
- data/examples/classexplorer/qt_extensions/htmldelegate.rb +40 -0
- data/examples/classexplorer/qt_extensions/line_edit.rb +65 -0
- data/examples/deform/Deform.html +23 -0
- data/examples/deform/Thumbs.db +0 -0
- data/examples/deform/arthurframe.rb +273 -0
- data/examples/deform/deform-demo.png +0 -0
- data/examples/deform/deform.rb +502 -0
- data/examples/deform/qt-logo.png +0 -0
- data/examples/qtmapzoom/qtmapzoom.rb +340 -0
- data/java/SignalActions.java +13 -0
- data/lib/Qt.rb +10 -0
- data/lib/qt_connect.rb +12 -0
- data/lib/qt_connect/qt_compat.rb +700 -0
- data/lib/qt_connect/qt_jbindings.rb +657 -0
- data/lib/qt_connect/qt_sugar.rb +277 -0
- data/lib/qt_connect/version.rb +3 -0
- data/lib/qt_jambi.rb +8 -0
- data/lib/signalactions.jar +0 -0
- metadata +92 -0
@@ -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
|
data/lib/qt_jambi.rb
ADDED
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
|
+
|