Permalink
Browse files

annotated example 4. Working clock with limited effort

- 4 examples OK, 38 workings specs. CLOCKY!!!
- annotated example 3
- rpprpp on track. But too complicated to finish, too little gain
- major refactoring
- splitted color of from brush
- timer_spec works (1 example)
- moved basefiles for plugins in proper plugin dir using _ prefix
- it tries to include widget.rb from two different places.
  Should always use 'realpath'
- ESafety -> E_SAFETY. 3 values. NOT boolean anymore
- added VERYUNSAFE which can only be used if the conversion is
  guaranteed.     As in	    array.assign(rb_ary_new(), VERYUNSAFE).
  NO CHECKS at all are done, which is what I want here anyway.
  • Loading branch information...
1 parent 2a8fdf1 commit 03b49145c3602e67cda45a233b6cfedc8c179895 @EugeneBrazwick committed Feb 21, 2013
Showing with 4,083 additions and 2,765 deletions.
  1. +2 −0 .gitignore
  2. +82 −50 Rakefile
  3. +20 −3 examples/003_hsv_model.rb
  4. +21 −0 examples/004_clocky.rb
  5. +46 −41 lib/reform/app.rb
  6. +48 −51 lib/reform/context.rb
  7. +56 −12 lib/reform/control.rb
  8. +20 −2 lib/reform/graphics/line.rb
  9. +5 −0 lib/reform/graphics/pathitem.rb
  10. +65 −0 lib/reform/graphicsitem.rb
  11. +47 −25 lib/reform/model.rb
  12. +9 −1 lib/reform/models/rubydata.rb
  13. +100 −0 lib/reform/models/time_model.rb
  14. +1 −0 lib/reform/models/timer.rb
  15. +164 −18 lib/reform/object.rb
  16. +34 −0 lib/reform/r.rb
  17. +1 −1 lib/reform/{abstractbutton.rb → widgets/_abstractbutton.rb}
  18. +2 −2 lib/reform/{layout.rb → widgets/_layout.rb}
  19. 0 lib/reform/{layoutable.rb → widgets/_layoutable.rb}
  20. +13 −0 lib/reform/widgets/frame.rb
  21. +34 −0 lib/reform/widgets/graphicsscene.rb
  22. +7 −3 lib/reform/widgets/graphicsview.rb
  23. +20 −2 lib/reform/widgets/grid.rb
  24. +1 −1 lib/reform/widgets/hboxlayout.rb
  25. +27 −0 lib/reform/widgets/lcdnumber.rb
  26. +1 −1 lib/reform/widgets/pushbutton.rb
  27. +18 −4 lib/reform/widgets/slider.rb
  28. +1 −1 lib/reform/widgets/vboxlayout.rb
  29. +11 −2 lib/reform/widgets/widget.rb
  30. +4 −0 lib/rpp/rpp.rb
  31. +70 −17 lib/ruby++/array.h
  32. +359 −0 lib/ruby++/basicobject.h
  33. +53 −0 lib/ruby++/class.h
  34. +24 −4 lib/ruby++/dataobject.h
  35. +22 −14 lib/ruby++/hash.h
  36. +94 −0 lib/ruby++/module.h
  37. +41 −5 lib/ruby++/numeric.h
  38. +37 −0 lib/ruby++/object.h
  39. +17 −4 lib/ruby++/proc.h
  40. +11 −2 lib/ruby++/rppstring.h
  41. +1 −0 lib/ruby++/ruby++.cpp
  42. +2 −357 lib/ruby++/ruby++.h
  43. +106 −0 lib/ruby++/scan.h
  44. +1 −0 lib/ruby++/symbol.h
  45. +8 −0 lib/ruby++ruby++/Rakefile
  46. +79 −0 lib/ruby++ruby++/basicobject.cpp
  47. +45 −0 lib/ruby++ruby++/basicobject.h
  48. +44 −0 lib/ruby++ruby++/class.cpp
  49. +13 −0 lib/ruby++ruby++/class.h
  50. +56 −0 lib/ruby++ruby++/module.cpp
  51. +13 −0 lib/ruby++ruby++/module.h
  52. +15 −0 lib/ruby++ruby++/object.cpp
  53. +13 −0 lib/ruby++ruby++/object.h
  54. +24 −77 lib/urqt/application.cpp
  55. +8 −6 lib/urqt/application.h
  56. +4 −470 lib/urqt/brush.cpp
  57. +21 −29 lib/urqt/brush.h
  58. +8 −7 lib/urqt/button.cpp
  59. +407 −0 lib/urqt/color.cpp
  60. +67 −0 lib/urqt/color.h
  61. +21 −0 lib/urqt/frame.cpp
  62. +12 −0 lib/urqt/frame.h
  63. +75 −144 lib/urqt/graphicsitem.cpp
  64. +22 −4 lib/urqt/graphicsitem.h
  65. +42 −100 lib/urqt/graphicsscene.cpp
  66. +32 −42 lib/urqt/graphicsview.cpp
  67. +17 −18 lib/urqt/label.cpp
  68. +67 −68 lib/urqt/layout.cpp
  69. +2 −2 lib/urqt/layout.h
  70. +102 −0 lib/urqt/lcdnumber.cpp
  71. +18 −18 lib/urqt/lineedit.cpp
  72. +16 −24 lib/urqt/lineitem.cpp
  73. +3 −5 lib/urqt/mainwindow.cpp
  74. +31 −32 lib/urqt/pathitem.cpp
  75. +124 −143 lib/urqt/pen.cpp
  76. +25 −3 lib/urqt/pen.h
  77. +19 −21 lib/urqt/polygon.cpp
  78. +9 −8 lib/urqt/qtellipse.cpp
  79. +39 −44 lib/urqt/rectangle.cpp
  80. +36 −21 lib/urqt/slider.cpp
  81. +105 −157 lib/urqt/widget.cpp
  82. +2 −2 lib/urqt/widget.h
  83. +4 −9 lib/urqtCore/api_utils.cpp
  84. +6 −50 lib/urqtCore/api_utils.h
  85. +178 −602 lib/urqtCore/object.cpp
  86. +49 −11 lib/urqtCore/object.h
  87. +1 −1 lib/urqtCore/rake_ruby_so.rb
  88. +1 −0 lib/urqtCore/rvalue.h
  89. +38 −20 lib/urqtCore/signalproxy.moc.cpp
  90. +5 −1 lib/urqtCore/signalproxy.moc.h
  91. +231 −0 lib/urqtCore/time_model.cpp
  92. +9 −0 lib/urqtCore/time_model.h
  93. +26 −0 lib/urqtCore/timeouthandler.moc.cpp
  94. +27 −0 lib/urqtCore/timeouthandler.moc.h
  95. +3 −1 spec/reform/app_spec.rb
  96. +1 −1 spec/reform/model_spec.rb
  97. +1 −1 spec/reform/object_spec.rb
  98. +26 −0 spec/reform/slider_spec.rb
  99. +20 −0 spec/reform/timer_spec.rb
  100. +72 −0 spec/rpp/module_spec.rb
  101. +22 −0 test/extern_C+namespace.cpp
  102. +21 −0 test/rpprpp.rb
View
2 .gitignore
@@ -5,4 +5,6 @@ Makefile
*.moc
*.kch
.*.swp
+html
+core
mkmf.log
View
132 Rakefile
@@ -5,8 +5,11 @@ require 'rspec/core/rake_task' # as in Upgrade.markdown
#require '/var/lib/gems/1.9.1/gems/rspec-core-2.0.1/lib/rspec/core/rake_task'
RSPEC = `gem list | grep rspec`.strip != ''
RCOV = `gem list | grep rcov`.strip != ''
+RDOC = true
+RGEM = false
+
+require 'rdoc/task' if RDOC
-require 'rake/rdoctask'
DARKFISH = false
# apart from that, it still says: 'Generating Darkfish...'
# maybe rdoc already uses it if installed. That may explain why it does not work if 'required' by hand
@@ -19,15 +22,16 @@ RUBY = 'ruby -w -I lib'
CLEAN.include('*.o', '*.so')
CLOBBER.include('*.log')
-# automatically create an rdoc task, + rerdoc [+ clobber_rdoc]
-Rake::RDocTask.new do |rd|
- rd.rdoc_files.include('LICENSE', 'README', '**/*.rb', '**/*.cpp')
- rd.options << %q[--exclude="bin/|,v|Makefile|\.yaml|\.css|\.html|\.dot|\.rid|\.log"] <<
- '--main=lib/rrts/rrts.rb' <<
- '--title=Midibox'
- DARKFISH and
- rd.options << '--format=darkfish'
-end
+if RDOC
+ # automatically create an rdoc task, + rerdoc [+ clobber_rdoc]
+ Rake::RDocTask.new do |rd|
+ rd.rdoc_files.include('LICENSE', 'README', '**/*.rb', '**/*.cpp')
+ rd.options << %q[--exclude="bin/|,v|Makefile|\.yaml|\.css|\.html|\.dot|\.rid|\.log"] <<
+ '--main=lib/rrts/rrts.rb' <<
+ '--title=Midibox'
+ DARKFISH and rd.options << '--format=darkfish'
+ end
+end # RDOC
if RSPEC
desc "Run all rspec_test"
@@ -50,17 +54,19 @@ if RSPEC
# require 'rake/runtest'
# Rake.run_tests 'test/**/*_spec.rb'
end
-end
+end # RSPEC
file ALSALIB => FileList['lib/rrts/driver/*.cpp'] do
Dir.chdir 'lib/rrts/driver' do
- sh "#{ENV['RUBY'] || 'ruby'} ./extconf.rb && make && rm -f *.o mkmf.log"
+ ruby './extconf.rb'
+ sh "make && rm -f *.o mkmf.log"
end
end
file PERLINLIB => FileList['ext/ruby-perlin/*.cpp'] do
Dir.chdir 'ext/ruby-perlin' do
- sh "#{ENV['RUBY'] || 'ruby'} ./extconf.rb && make && rm -f *.o mkmf.log"
+ ruby './extconf.rb'
+ sh "make && rm -f *.o mkmf.log"
end
end
@@ -76,8 +82,31 @@ desc 'build the perlin shared library'
task :build_perlin => [PERLINLIB] do
end
-desc 'build the required library and the documentation'
-task :default => [:build_alsamidi, :build_perlin, :rdoc] do
+desc 'build the ruby++ shared library'
+task :build_rpp do
+ Dir.chdir 'lib/ruby++' do
+ ruby '-S', 'rake'
+ end
+end
+
+desc 'build the urqtCore shared library'
+task :build_urqtCore do
+ Dir.chdir 'lib/urqtCore' do
+ ruby '-S', 'rake'
+ end
+end
+
+desc 'build the urqt shared library'
+task :build_urqt do
+ Dir.chdir 'lib/urqt' do
+ ruby '-S', 'rake'
+ end
+end
+
+# rdoc takes far longer than anything else
+desc 'build the required library bot not the documentation (use rdoc task)'
+task :default => [:build_rpp, :build_urqtCore, :build_urqt,
+ :build_alsamidi, :build_perlin] do
end
desc 'play a track using rplaymidi++'
@@ -125,38 +154,41 @@ task :panic do
sh "#{RUBY} bin/panic #{MIDI_OUT_PORT}"
end
-require 'rake/gempackagetask'
-reform_spec = Gem::Specification.new do |spec|
- spec.name = 'reform'
- spec.summary = 'RAD gui builder, state: toy'
- spec.description = <<-EOF
- 'reform' is a declarative gui builder tool, similar to 'shoes'.
- It can currenty only be used to toy around with, as the API
- is still changing heavily.
- Do not use for production stuff.
- EOF
- spec.requirements = ['qtbindings gem v4.6.3.1 or higher']
- spec.version = '0.0.0.0'
- spec.author = 'Eugene Brazwick'
- spec.email = 'eugene.brazwick@rattink.com'
- spec.homepage = 'https://github.com/EugeneBrazwick/Midibox'
- spec.platform = Gem::Platform::CURRENT # ie, my current platform, ie Linux
- spec.required_ruby_version = '>=1.9.2'
- #
- spec.files = Dir['bin/reform*', 'lib/reform/**', 'ext/ruby-perlin/**', 'Rakefile']
- # it tries to run it as ruby, but it is a bash script. There is always bin/reform
- # the handiest method is to use 'alias' like:
- # alias reform=/var/lib/gems/1.9.1/bin/reform-0.0.0.0-x86_64-linux/bin/reform.bash
- # spec.executable = 'reform.bash'
- spec.executables = []
- spec.add_dependency 'qtbindings', '>= 4.6.3.1'
- spec.add_development_dependency 'rspec', '>= 2.0'
- spec.test_files = Dir['test/reform_spec.rb', 'test/structure_spec.rb']
- # this is completely optional. Needed in some example only.
- spec.extensions << 'ext/ruby-perlin/extconf.rb'
- spec.require_paths = ['lib', 'ext']
- spec.license = 'GPL-3'
- spec.has_rdoc = true
-end
-
-Rake::GemPackageTask.new(reform_spec).define
+if RGEM
+ require 'rubygems/package_task'
+ reform_spec = Gem::Specification.new do |spec|
+ spec.name = 'reform'
+ spec.summary = 'RAD gui builder, state: toy'
+ spec.description = <<-EOF
+ 'reform' is a declarative gui builder tool, similar to 'shoes'.
+ It can currenty only be used to toy around with, as the API
+ is still changing heavily.
+ Do not use for production stuff.
+ EOF
+ spec.requirements = ['qtbindings gem v4.6.3.1 or higher']
+ spec.version = '0.0.0.0'
+ spec.author = 'Eugene Brazwick'
+ spec.email = 'eugene.brazwick@rattink.com'
+ spec.homepage = 'https://github.com/EugeneBrazwick/Midibox'
+ spec.platform = Gem::Platform::CURRENT # ie, my current platform, ie Linux
+ spec.required_ruby_version = '>=1.9.2'
+ #
+ spec.files = Dir['bin/reform*', 'lib/reform/**', 'ext/ruby-perlin/**', 'Rakefile']
+ # it tries to run it as ruby, but it is a bash script. There is always bin/reform
+ # the handiest method is to use 'alias' like:
+ # alias reform=/var/lib/gems/1.9.1/bin/reform-0.0.0.0-x86_64-linux/bin/reform.bash
+ # spec.executable = 'reform.bash'
+ spec.executables = []
+ spec.add_dependency 'qtbindings', '>= 4.6.3.1'
+ spec.add_development_dependency 'rspec', '>= 2.0'
+ spec.test_files = Dir['test/reform_spec.rb', 'test/structure_spec.rb']
+ # this is completely optional. Needed in some example only.
+ spec.extensions << 'ext/ruby-perlin/extconf.rb'
+ spec.require_paths = ['lib', 'ext']
+ spec.license = 'GPL-3'
+ spec.has_rdoc = true
+ end
+
+ Rake::GemPackageTask.new(reform_spec).define # too old code... BROKEN
+end # RGEM
+
View
23 examples/003_hsv_model.rb
@@ -1,18 +1,32 @@
# Copyright (c) 2013 Eugene Brazwick
+
+# you can run this program from anywhere using
+# RUBYLIB=<installpath>/Midibox/lib ruby <installpath>/Midibox/examples/003_hsv_model.rb
+
require 'reform/app'
Reform::app {
widget {
+ # 'model', an integer representing 'hue'.
data 0
+ # horizontal layout:
hbox {
+ # canvas = graphicsview. These are aliases btw.
canvas {
+ # 'virtual' mapping so coords are from 0..100 no matter the window size
+ # it seems that this tweaks the sizeHint but that's OK
area 100
+ # apply a matrix
scale 2
+ # it will create an internal scene, if not otherwise specfied.
+ # qtellipse is kind of ugly since it use tlrb and not radius + center.
qtellipse {
rect 5, 5, 90, 90
+ # 'fill' is an alias for 'brush'
fill {
color {
+ # connect the hue to the 'data'. And the only thing there is is data.self
hue connector: :self
saturation 255
value 255
@@ -21,12 +35,15 @@
} # qtellipse
} # canvas
slider {
- #trace_propagation true
- connector :self
+ # orientation :vertical # this is the default.
# setting an integer range makes it an integer-slider
# the default is a 'float' slider with range 0.0 .. 1.0
range 0..360
- } # vslider
+ # trace_propagation true # use this to see data pushing in action
+ # same as qtellipse.fill.color.hue. So now moving the slider changes 'data'
+ # which in turn will change the qtellipse brush-color
+ connector :self
+ } # slider
} # hbox
} # widget
} # app
View
21 examples/004_clocky.rb
@@ -0,0 +1,21 @@
+
+# Copyright (c) 2013 Eugene Brazwick
+#
+# you can run this program from anywhere using
+# RUBYLIB=<installpath>/Midibox/lib ruby <installpath>/Midibox/examples/004_clocky.rb
+
+require 'reform/app'
+
+Reform::app {
+ # the default interval is 100ms which only increases the load.
+ # Now with this coarse interval is technically possible that the colon is a little out of sync.
+ time_model interval_ms: 1000
+ lcdnumber {
+ size 200, 80
+ title 'Digital Clocky'
+ # the more generic solution:
+ #connector -> time { time.strftime(time.sec % 2 == 0 ? '%H %M' : '%H:%M') }
+ # But secretly Qt::LCDNumber has support for Qt::Timer as well as ruby ::Time.
+ connector :now
+ } # lcdnumber
+} # app
View
87 lib/reform/app.rb
@@ -1,64 +1,53 @@
# Copyright (c) 2013 Eugene Brazwick
+require_relative 'r'
require_relative 'control' # for attr_dynamic
require_relative 'context'
-module R
-
- public # methods of R
- ## use this to wrap a rescue clause around any block.
- # transforms the exception (RuntimeError+IOError+StandardError) to a warning on stderr.
- # Reform::Error is also a RuntimeError.
- #
- # UNLESS: 'fail_on_errors true' is specified in Application.
- def self.escue
- begin
- return yield
- rescue IOError => exception
- raise if $app.fail_on_errors?
- msg = "#{exception.message}\n"
- rescue StandardError, RuntimeError => exception
- raise if $app.fail_on_errors?
- #tag "got exception"
- #tag "exception class: #{exception.class}"
- #tag "exception msg: #{exception}"
- #tag "exception backtrace: #{exception.backtrace.join "\n"}"
- msg = "#{exception.class}: #{exception}\n" + exception.backtrace.join("\n")
- end
- $stderr << msg
- nil
- end # escue
-
-end # module R
-
module Reform
private # methods of EForm
- # scan given dir for fixed set of subdirectories. Each maps to a context by hash
+ # scan given dir for fixed set of subdirectories. Each maps to a context by hash.
+ # each file not starting with '_' is supposed to be a pluging and it
+ # added as a method to the proper context.
+ # symlinks will cause 'alias' to be used.
+ #
+ # known problem:
+ # the plugin-method (like 'widget') will first do a require_relative.
+ #
def self.internalize dirprefix, hash
# tag "internalize"
- dirprefix = File.dirname(__FILE__) + '/' + dirprefix unless dirprefix[0] == '/'
# note that dirs need not exist. But at least one should!
- located = false
+ located_any_plugins = false
for dir, klass in hash
- fulldir = dirprefix + '/' + dir.to_s
+ fulldir = if dirprefix.empty? then dir.to_s else "#{dirprefix}/#{dir}" end
+ unless fulldir[0] == '/'
+ fulldir = File.dirname(__FILE__) + '/' + fulldir
+ end
symlinks = {}
-# tag "GLOBBING #{dirprefix}/#{dir}/*.rb"
+ #tag "GLOBBING #{fulldir}/*.rb"
for file in Dir["#{fulldir}/*.rb"]
+ #tag "file = '#{file}'"
basename = File.basename(file, '.rb')
+ next if basename[0] == '_'
# tag "INTERNALIZE #{basename} from #{file}"
if File.symlink?(file)
- symlinks[basename.to_sym] = File.basename(File.readlink(file), '.rb').to_sym
+ link_basename = File.basename(File.readlink(file), '.rb')
+ next if link_basename[0] == '_'
+ symlinks[basename.to_sym] = link_basename.to_sym
else
+ # tag "registerClassProxy(#{klass}, #{basename})"
registerClassProxy klass, basename, "#{dirprefix}/#{dir}/#{basename}"
end
- located = true
+ located_any_plugins = true
end # for
symlinks.each { |key, value| registerClassProxy klass, key, value }
end # for
- raise Reform::Error, tr("incorrect plugin directory '#{dirprefix}'") unless located
+ unless located_any_plugins
+ Reform::Error.raise "incorrect plugin directory '#{dirprefix}'"
+ end
end
# scan given dirs for fixed set of subdirectories. Each maps to a context
@@ -79,6 +68,7 @@ def self.app quickyhash = nil, &block
R::Qt::Application.new.scope do |app|
begin
# note that app is identical to $app
+ #tag "__FILE__ = #{__FILE__}"
internalize_dir '.', 'contrib'
#tag "calling #{app}.setup"
app.setup quickyhash, &block
@@ -93,12 +83,14 @@ def self.app quickyhash = nil, &block
# delegator. see Instantiator::registerControlClassProxy
# we add the X classProxy to those contexts in which we want the plugins
# to become available.
- def self.registerClassProxy klass, id, path = nil
+ #
+ # Context: internalize
+ def self.registerClassProxy klass, id, path
contexts = Contexts[klass] and
contexts.each { |ctxt| ctxt::registerControlClassProxy id, path }
end # self.registerClassProxy
-end # module R::EForm
+end # module Reform
module R::Qt
@@ -108,6 +100,13 @@ class Application < Control
private # methods of Application
+ def initialize
+ super
+ @toplevel_widgets = []
+ @quit = false
+ $app = self
+ end
+
# run (show) first widget defined.
# if a model is set, propagate it
# It is bad to do nothing, if there is no widget available (shown)
@@ -126,10 +125,16 @@ def fail_on_errors value
signal :created
- protected # methods of Application
-
public # methods of Application
+ # override
+ def enqueue_children queue = nil
+ super
+ @toplevel_widgets.each { |wdgt| queue and queue.push wdgt or yield wdgt }
+ end
+
+ def quit?; @quit; end
+
# you cannot say: attr :fail_on_errors?
def fail_on_errors?; @fail_on_errors; end
@@ -151,7 +156,7 @@ def execute
## delete the toplevel widgets and the global $app variable
def cleanup
# tag "Application::cleanup, #toplevel_widgets = #{@toplevel_widgets.length}"
- @toplevel_widgets.each &:delete
+ @toplevel_widgets.each(&:delete)
$app = nil
end
end # class Application
View
99 lib/reform/context.rb
@@ -22,40 +22,60 @@ module Reform # aka R::EForm
module Instantiator
# hash Symbol=>class
- @@instantiator = {}
+ @instantiator = {}
+
+ class << self
+
+ # returns class to use
+ def [] name; @instantiator[name]; end
+
+ def []= name, klass
+ @instantiator[name.to_sym] = klass
+ end
+
+ end # eigenclass Instantiator
private # methods of Instantiator
- # It may return nil on exceptions... This is by design
- def define_proxy_method name, thePath
- #tag "define_proxy_method(#{name}, #{thePath})"
+ # It may return nil on exceptions... This is by design
+ # Context: registerControlClassProxy(_i) <- Reform#internalize
+ # The caller must test whether the method already exists.
+ def define_proxy_method name, path
+ #tag "self=#{self}, name=#{name}, path=#{path}"
define_method name do |quicky = nil, &block|
R::escue do
# are we registered at this point?
# this is done by the require which executes createInstantiator.
- unless @@instantiator[name]
+ unless Instantiator[name]
#tag "arrived in #{self}##{name}, first time, loading file, just in time style"
- require_relative thePath
+ #tag "REQUIRE(#{path})"
+ if path[0] == '/'
+ require path
+ else
+ require_relative path
+ end
# the loaded module should call createInstantiator (and so registerControlClass) which alters
# @@instantiator
- raise "'#{name}' did not register an instantiator!!!" unless @@instantiator[name]
+ raise "'#{name}' did not register an instantiator!!!" unless Instantiator[name]
end
#tag "HERE, name = #{name}"
- klass = @@instantiator[name]
+ klass = Instantiator[name]
unless quicky == nil || Hash === quicky
- raise ArgumentError, "Bad param #{quicky.inspect} passed to instantiator '#{name}'"
+ ArgumentError.raise "Bad param #{quicky.inspect} passed to instantiator '#{name}'"
end
# tag "quicky hash = #{quicky.inspect}"
child = instantiate_child klass, self
child.setup quicky, &block
end # R::escue
end # define_method
+ private name
end # define_proxy_method
public # methods of Instantiator
- # add a record to @@instantiator
- def createInstantiator name, klass
- @@instantiator[name.to_sym] = klass
+ # add a record to Instantiator.instantiator
+ # context: Reform::createInstantiator
+ def createInstantiator path, klass
+ Instantiator[path] = klass
end # createInstantiator
# Example:
@@ -70,36 +90,31 @@ def createInstantiator name, klass
# When called it performs 'require' and
# the unit loaded should call registerControlClass and so createInstantiator above.
#
- # if 'thePath' is a symbol we create an alias for name. This is done when
+ # if 'path' is a symbol we create an alias for name. This is done when
# a softlink is read by internalize_dir
- def registerControlClassProxy name, thePath
+ #
+ # Context: Reform::registerClassProxy
+ def registerControlClassProxy name, path
name = name.to_sym
-# tag "#{self}::registerControlClassProxy_i(#{name}, #{thePath})"
- case thePath
- when Symbol
-# tag "Create alias :#{name} :#{thePath}"
- return module_eval("alias :#{name} :#{thePath}")
- end
+# tag "#{self}::registerControlClassProxy_i(#{name}, #{realpath})"
# to avoid endless loops we must consider that by loading some classes it is possible
# that we already loaded the file:
- return if private_method_defined?(name)
+ return if private_method_defined? name
+ if method_defined? name
+ NameError.raise "the plugin #{self}.#{name} is already defined as " +
+ "#{instance_method(name).owner}.#{name}"
+ end
+ if Symbol === path
+ alias_method name, path
+ private name
+ else
+ define_proxy_method name, path
+ end
# failing components do not stop the setup process.
#tag "Defining proxy_method #{self}.#{name}"
- define_proxy_method name, thePath
# make it private to complete it:
- private name
end # registerControlClassProxy_i
- # return hash
- def self.instantiator
- # tag "instantiator -> #{@@instantiator}"
- @@instantiator
- end
-
- # returns class to use
- def self.[] name
- @@instantiator[name]
- end
end # module Instantiator
# including this context makes all 'widgets' available as 'constructor' shortcuts.
@@ -121,24 +136,6 @@ module AnimationContext
extend Instantiator
end # module AnimationContext
-=begin ????
- # include this to make it possible canning commands into named 'macros'.
- module MacroContext
- extend Instantiator
-
- private # methods of MacroContext
-
- #override
- def self.define_proxy_method name, thePath
- define_method name do |quicky = nil, &block|
- #tag "#{self.class}::#{name} called, creating Macro!"
- Macro.new self, name, quicky, block
- end # define_method
- end # define_proxy_method
-
- end # module MacroContext
-=end
-
module StateContext
extend Instantiator
end # module StateContext
View
68 lib/reform/control.rb
@@ -91,31 +91,37 @@ def want_data path = []
end # want_data
# note that propagation can be dumped by specifying 'trace_propagation true' in the sender cq widget
- def push_data value, sender = nil, path = []
+ def push_data value, path = []
#tag "#{self}::push_data(#{value})"
- sender ||= self
path.unshift self
if @model
#tag "delegate #{value} to @model #@model"
- @model.model_push_data value, sender, path
+ @model.model_push_data value, path
elsif par = parent
#tag "delegate to parent"
- par.push_data value, sender, path
+ par.push_data value, path
end
end # push_data
# context: Control#objectName=
def registerName name, child
#tag "registerName(#{name})"
- m = method(name) rescue nil
- raise NameError, "the name '#{name}' is already in use" if m
+ if respond_to? name
+ raise NameError, "the name '#{name}' is already in use"
+ end
define_singleton_method name do
child
end
end # registerName
public #methods of Control
+ # override
+ def objectName= newname
+ super newname
+ newname && (c = collector) and c.registerName newname, self
+ end
+
def addDynamicAttribute attr
addObject attr
end # addDynamicAttribute
@@ -135,29 +141,36 @@ def connector value = nil, &block
end # connector
## :call-seq: attr_dynamic type, :meth1 [,:meth2...] [, optionhash]
+ # 'type' refers to the attribute, so String if it is a string.
+ # This typing is required for Qt and also to create a proper default value.
def self.attr_dynamic klass, *methods
#tag "#{self}.attr_dynamic #{klass} #{methods.inspect}" # INTERESTING
options = Hash === methods[-1] ? methods.pop : nil
with_acceptors = options && options[:with_acceptors]
- methods.each do |method| # NOT 'for' GODDAMNED!
+ methods.each do |meth| # NOT 'for' GODDAMNED!
#tag "creating method :#{method}"
- define_method method do |*args, &block|
- handle_dynamics klass, method, options, *args, &block
+ if method_defined? meth
+ NameError.raise "the dynamic attribute #{self}.#{meth} is already defined as " +
+ "#{instance_method(meth).owner}.#{meth}"
+ end
+ define_method meth do |*args, &block|
+ handle_dynamics klass, meth, options, *args, &block
end
if with_acceptors
- assigner = (method.to_s + '=').to_sym
+ assigner = (meth.to_s + '=').to_sym
#tag "creating acceptor :#{assigner}"
define_method assigner do |value|
#tag "executing acceptor :#{assigner}(#{value})"
- apply_dynamic_setter method, value
+ apply_dynamic_setter meth, value
end
end # with_acceptors
end # each
#tag "OK"
end # attr_dynamic
- attr_dynamic String, :objectName
+ # attr_dynamic String, :objectName
+ alias collectnames collect_names
end # class Control
@@ -167,6 +180,26 @@ class NoQtControl < Control
public # methods of NoQtControl
+ # overrides:
+ def enqueue_children *args; end
+ def qtobject?; end
+ def widget?; end
+ def qtparent_get; end
+ def mark_ownership; end
+ def qtchildren_get; []; end
+
+ alias qtchildren qtchildren_get
+
+ # override
+ def qtparent= parent
+ ArgError.raise "cannot assign a qtparent to a #{self.class}"
+ end
+
+ # override
+ def qtchildren= arg
+ ArgError.raise "cannot assign qtchildren to a #{self.class}"
+ end
+
# override. Because they are not QObjects in the first place
def addObject child
raise TypeError, "cannot add indiscrimate object (#{child}) to a #{self.class}"
@@ -191,7 +224,18 @@ def objectName_get
# overrides
attr_writer :objectName, :parent
+ # override
+ def takeOwnership
+ NotImplementedError.raise 'takeOwnership on non-QObjects'
+ end
+
end # class NoQtControl
+
+ class SynthObject < R::Qt::Object
+ # override
+ def synthesized?; true; end
+ end
+
end # module R::Qt
if __FILE__ == $0
View
22 lib/reform/graphics/line.rb
@@ -1,7 +1,25 @@
-require_relative '../graphicsitem' # required to get the GraphicsItem class complete!
+require_relative '../graphicsitem'
-Reform.createInstantiator __FILE__, R::Qt::GraphicsLineItem
+module R::Qt
+ class GraphicsLineItem < GraphicsItem
+
+ public # methods of GraphicsLineItem
+
+ # override
+ def enqueue_children queue = nil
+ super
+ if @pen
+ queue and queue.push @pen or yield @pen
+ end
+ end
+
+ def pen_get; @pen; end
+
+ attr_dynamic PointF, :from, :to
+ end
+ Reform.createInstantiator __FILE__, GraphicsLineItem
+end # module R::Qt
if __FILE__ == $0
require 'reform/app'
View
5 lib/reform/graphics/pathitem.rb
@@ -409,6 +409,11 @@ def setup hash = nil, &initblock
end # class GraphicsPathItem
+ class BezierCurve < GraphicsPathItem
+ public # methods of BezierCurve
+ attr_dynamic PointF, :from, :to, :c1, :c2
+ end
+
Reform.createInstantiator __FILE__, GraphicsPathItem
end # module R::Qt
View
65 lib/reform/graphicsitem.rb
@@ -61,8 +61,73 @@ def pen *args, &block
alias :fillcolor :brush
alias :stroke :pen
+ # override
+ def connect signal, proc
+ TypeError.raise 'GraphicsItems only support ruby signals' unless Symbol === signal
+ super
+ end
+
+ # override
+ def emit signal, *args
+ TypeError.raise 'GraphicsItems only support ruby signals' unless Symbol === signal
+ super
+ end
+ end # class GraphicsItem
+
+ class Brush < NoQtControl
+ public # methods of Bruhs
+ # override
+ def parent= parent
+ old_parent = @parent and old_parent.brush = nil
+ @parent = parent and parent.brush = self
+ end
+
+ # override
+ def apply_model data
+ apply_dynamic_setter :color, data
+ end
end # class GraphicsItem
+ class AbstractGraphicsShapeItem < GraphicsItem
+
+ public # methods of AbstractGraphicsShapeItem
+
+ # override
+ def enqueue_children queue = nil
+ super
+ if @brush
+ queue and queue.push @brush or yield @brush
+ end
+ if @pen
+ queue and queue.push @pen or yield @pen
+ end
+ end
+
+ def brush_get; @brush; end
+ def pen_get; @pen; end
+ end # class AbstractGraphicsShapeItem
+
+ class SynthItem < GraphicsItem
+ public # methods of SynthItem
+ #override
+ def synthesized?; true; end
+ end # class SynthItem
+
+ class Pen < NoQtControl
+ public # methods of Pen
+ attr_dynamic Color, :color, klass: DynamicColor, require: 'dynamic_color'
+ attr_dynamic Float, :widthF
+ attr_dynamic Symbol, :capStyle, :joinStyle
+
+ alias width widthF
+ alias size widthF
+ alias weight widthF
+ alias cap capStyle
+ alias join joinStyle
+
+ @@capstyles = @@joinstyles = nil
+ end
+
Reform.createInstantiator __FILE__, GraphicsItem
end
View
72 lib/reform/model.rb
@@ -17,25 +17,31 @@ def initialize *args
@model_listeners = []
end # initialize
- # every node in the controlpath can have a connector set
+ # controlpath is the list of controls, starting with the control that has the model, and ending
+ # with the original sender.
+ # Every node in the controlpath can have a connector set.
def model_init_path controlpath
- $stderr.puts "model_init_path(#{controlpath.inspect})" if controlpath[-1].trace_propagation
+ sender = controlpath[-1]
+ $stderr.puts "model_init_path(#{controlpath.inspect})" if sender.trace_propagation
v = self
- lastcontrol = nil
controlpath.each do |control|
v &&= Model::model_apply_connector v, control
- lastcontrol = control
end
- #tag "lastcontrol=#{lastcontrol}, v =#{v}"
- lastcontrol.apply_model(v) if v && lastcontrol
+ #tag "sender=#{sender}, v =#{v}"
+ v and sender.apply_model v
end # model_init_path
- def model_propagate cidpath, value, sender
- Model::model_propagate_i cidpath, value, sender, self, @model_listeners
+ # context: model_push_data, delegates to model_propagate_i
+ # cidpath can be an array, but normally it's just the initiating method.
+ def model_propagate cidpath, sender
+ cidpath = [cidpath] unless Array === cidpath
+ Model::model_propagate_i cidpath, sender, self, @model_listeners
end
+ # context: model_init_path and model_push_data.
+ # Delegates to model_apply_cid
def self.model_apply_connector v, control, cidpath = nil
- model_apply_cid( v, control && control.connector, cidpath)
+ model_apply_cid v, control && control.connector, cidpath
end # model_apply_connector
def self.model_apply_cid v, cid, cidpath
@@ -45,11 +51,12 @@ def self.model_apply_cid v, cid, cidpath
#tag "nil"
v
when Proc
+ #tag "Passing #{v.class} #{v} to proc #{cid}"
cid[v]
when Array
#tag "Array, apply them in order"
cid.inject(v) do |w, nm|
- w && model_apply_cid(w, nm, cidpath)
+ w and model_apply_cid w, nm, cidpath
end
else
#tag "should be symbol, delegate to model_apply_getter"
@@ -65,7 +72,16 @@ def self
self
end
- def self.model_propagate_cid cidpath, control, cid, value, sender, v, subs
+ # context: model_propagate_i <- model_propagate
+ # delegates to control.apply_model unless control == sender
+ # cidpath: an array of cids that indicates the altered value within the model. It is how
+ # sender connects to us.
+ # control: subject of the propagation algo
+ # cid: normally control.connector
+ # sender: original sender (some control) of datachange
+ # v: starts out as the model, but we keep applying the cid on it as we go deeper
+ # subs: model_listeners
+ def self.model_propagate_cid cidpath, control, cid, sender, v, subs
if sender.trace_propagation
$stderr.puts "PROPAGATE_CID(#{cidpath.inspect}, #{cid.inspect}, on #{v}, subs = #{subs})"
end
@@ -76,7 +92,7 @@ def self.model_propagate_cid cidpath, control, cid, value, sender, v, subs
v = cid[v]
when Array
cid.each do |el|
- v &&= model_propagate_cid cidpath, control, el, value, sender, v, subs
+ v &&= model_propagate_cid cidpath, control, el, sender, v, subs
end
return v
else
@@ -92,11 +108,11 @@ def self.model_propagate_cid cidpath, control, cid, value, sender, v, subs
end
if subs
#tag "propagate to subs #{subs.inspect}"
- model_propagate_i cidpath, value, sender, v, subs
+ model_propagate_i cidpath, sender, v, subs
else
unless control.equal? sender
#tag "ARRIVAL at accepting endpoint #{control}, v = #{v}"
- control.apply_model(v) if v
+ v and control.apply_model v
end
end
v
@@ -117,9 +133,12 @@ def self.model_propagate_cid cidpath, control, cid, value, sender, v, subs
# Note we must not change the model, that's already been done.
# We must inform all controls that connect to cidpath and reattach
# by calling control.apply_model
- def self.model_propagate_i cidpath, value, sender, v, listeners
+ #
+ # Context: model_propagate
+ # Delegates to model_propagate_cid
+ def self.model_propagate_i cidpath, sender, v, listeners
if sender.trace_propagation
- $stderr.puts "PROPAGATE(#{cidpath.inspect}, #{value}, on #{v}, listeners = #{listeners})"
+ $stderr.puts "PROPAGATE(#{cidpath.inspect}, on #{v}, listeners = #{listeners})"
end
listeners.each do |listener|
if Array === listener
@@ -128,7 +147,7 @@ def self.model_propagate_i cidpath, value, sender, v, listeners
control, subs = listener, nil
end
#tag "control = #{control}"
- model_propagate_cid cidpath, control, control.connector, value, sender, v, subs
+ model_propagate_cid cidpath, control, control.connector, sender, v, subs
end # each
end # model_propagate_i
@@ -177,7 +196,7 @@ def self.model_apply_setter v, cid, value, sender
nil
when Array
# apply them in order
- if sub = model_apply_cid(v, cid[0...-1])
+ if sub = model_apply_cid(v, cid[0...-1])
model_apply_setter sub, cid[-1], value, sender
else
nil
@@ -218,26 +237,29 @@ def model_add_listener path
model_init_path path
end # model_add_listener
- def model_push_data value, sender, controlpath
+ # Context: Control.push_data
+ # controlpath is the list of controls starting with the one owning the model, and ending with
+ # the original sender.
+ def model_push_data value, controlpath
+ sender = controlpath[-1]
$stderr.puts "model_push_data(#{value}, #{sender}, #{controlpath})" if sender.trace_propagation
v = self
cidpath = []
- lastcontrol = controlpath[-1]
controlpath[0...-1].each do |control|
v &&= Model::model_apply_connector v, control, cidpath
end
- #tag "lastcontrol=#{lastcontrol}, cid=#{(lastcontrol && lastcontrol.connector).inspect}"
+ #tag "sender=#{sender}, cid=#{(sender && sender.connector).inspect}"
#tag "v = #{v.inspect}"
- if v && lastcontrol && cid = lastcontrol.connector
+ if v && sender && cid = sender.connector
cidpath << cid
#tag "cidpath=#{cidpath.inspect}"
- #tag "lastcontrol=#{lastcontrol}, cid=#{cid}"
+ #tag "sender=#{sender}, cid=#{cid}"
if Model::model_apply_setter v, cid, value, sender
#tag "setter was applied, now propagate change"
- model_propagate cidpath, value, sender
+ model_propagate cidpath, sender
end
end
- end
+ end # model_push_data
def model_apply_setter methodname, value, sender
if sender.trace_propagation
View
10 lib/reform/models/rubydata.rb
@@ -87,6 +87,7 @@ def initialize *args
end
def overwrite_with init_value
+ #tag "overwrite_with(#{init_value.inspect})"
@rubydata_node = Node::rubydata_inter init_value, self
end
@@ -97,7 +98,12 @@ def data value
protected # methods of RubyData
public # methods of RubyData
- alias :self= :overwrite_with
+
+ alias self= overwrite_with
+
+ def inspect
+ super + "[#{@rubydata_node.inspect}]"
+ end
# override
def model_apply_getter methodname
@@ -112,6 +118,8 @@ def model_apply_getter methodname
end
end
+ attr :rubydata_node
+
end # class RubyData
Reform.createInstantiator __FILE__, RubyData
View
100 lib/reform/models/time_model.rb
@@ -0,0 +1,100 @@
+
+# Copyright (c) 2010-2013 Eugene Brazwick
+
+require_relative '../model'
+require_relative '../context'
+
+module R::Qt
+ # A class representing the current time.
+ # Available as
+ # connector :now
+ # Similar you can do this:
+ # connector :now, :sec
+ # connector :now, :min
+ # connector :now, :hour
+ # connector :now, :to_s
+ class Timer < Model
+ private # methods of TimeModel
+ def initialize *args
+ super
+ @autostart = true
+ # cycle_frequency is the number of 360 degree cycles per second.
+ # Used by angle.
+ @cycle_frequency = 1.0
+ @frame_nr = 0
+ #tag "tracing prop"; trace_propagation true
+ timeout do
+ @frame_nr += 1
+ #tag "And now we propagate the lot"
+ [:frame_nr, :now, :angle, :hour12_f, :hour_f, :min_f].each do |comp|
+ model_propagate comp, self
+ end
+ end
+ interval 100
+ end # initialize
+
+ public # methods of TimeModel
+
+ alias singleShot_get singleShot?
+
+ attr_dynamic TrueClass, :autostart, attr_accessor: true
+ attr_dynamic FalseClass, :singleShot, attr_accessor: true
+ attr_dynamic Float, :cycle_frequency, attr_accessor: true
+ # interval is QTimer.interval
+ attr_dynamic Fixnum, :interval
+ attr_dynamic Symbol, :timeType
+
+ alias single_shot singleShot
+ alias one_shot singleShot
+ alias oneShot singleShot
+ alias interval_ms interval
+
+ class << self
+ alias single_shot singleShot
+ alias one_shot singleShot
+ alias oneShot singleShot
+ end
+
+ attr :frame_nr
+
+ def now
+ Time.now
+ end
+
+ def setup *args
+ super
+ start if @autostart
+ end
+
+ def angle
+ tag "angle, now=#{now.inspect}, now.to_f=#{now.to_f}"
+ (now.to_f * 360.0 * @cycle_frequency) % 360.0
+ end
+
+ def hour_f
+ n = now
+ n.hour + n.min / 60.0 + n.sec / 3600.0
+ end
+
+ def hour12_f
+ n = now
+ n.hour % 12 + n.min / 60.0 + n.sec / 3600.0
+ end
+
+ def min_f
+ n = now
+ n.min + n.sec / 60.0
+ end
+
+ def method_missing sym, *args
+ #tag "Qt::Timer.method_missing(:#{sym}, #{args.inspect})"
+ now.send sym, *args
+ end
+
+ signal 'timeout()'
+
+ end # class Timer
+
+ Reform.createInstantiator __FILE__, Timer
+end # module R::Qt
+
View
1 lib/reform/models/timer.rb
View
182 lib/reform/object.rb
@@ -2,6 +2,7 @@
# Copyright (c) 2013 Eugene Brazwick
require_relative 'liburqtCore'
+require_relative 'r'
module Kernel
private # methods of Kernel
@@ -12,18 +13,13 @@ def tag msg
end # module Kernel
-module R
- module EForm
- Size = Array
- Point = Array
-
- class Error < RuntimeError
- end
- end
+class Exception
+ public # methods of Exception
+ def self.raise msg, caller = nil
+ Kernel::raise self, msg, caller
+ end
end
-Reform = R::EForm
-
module R::Qt
##
@@ -39,6 +35,27 @@ module R::Qt
#
class Object
private # methods of Object
+
+ # call-seq: new *args, &block
+ #
+ # If an Object is passed it becomes the parent, and self is added to parent.children
+ # If a String is passed it is assigned using objectName=.
+ # If a Hash is passed it is passed to setupQuickyhash. :parent and :objectName are valid keys.
+ # If block is given it is evaluated in the context of self.
+ #
+ def initialize a0 = nil, a1 = nil, a2 = nil, &block
+ #tag "initialize(#{a0}, #{a1}, #{a2}, #{block})"
+ mark_ownership
+ if a0
+ initialize_arg a0
+ if a1
+ initialize_arg a1
+ a2 and initialize_arg a2
+ end
+ end
+ block and instance_eval(&block)
+ end
+
def self.create_qt_signal s
'2' + s
end
@@ -98,6 +115,39 @@ def data arg
rubydata data: arg
end
+ # context: initialize
+ def initialize_arg arg
+ #tag "initialize_arg(#{arg.inspect})"
+ case arg
+ when String, Symbol
+ self.objectName = arg
+ when Hash
+ setupQuickyhash arg
+ when R::Qt::Object
+ self.parent = arg
+ else
+ TypeError.raise "BAD argtype #{arg.class} for Object.new"
+ end
+ end
+
+ # context: Object::signal
+ # with a block it delegates to 'connect', otherwise it delegates to 'emit'
+ def signal_implementation methodname, signal, args, block
+ #tag "signal_implementation(#{methodname}, #{signal}, #{args.inspect}, #{block}"
+ if args.length == 1 && !block && Proc === args[0]
+ block, args = args[0], nil
+ end
+ if block
+ TypeError.raise "cannot use args with block" if args && !args.empty?
+ connect signal, block
+ else
+ args.unshift methodname
+ #tag "emit *#{args.inspect}"
+ emit *args
+ end
+ self
+ end
+
protected # methods of Object
# the default assigns the QT(!) parent
@@ -108,13 +158,76 @@ def addObject child
# any object, except graphicsitem can have a model
# this includes models. Delegating is more flexible
def addModel child
- model and raise Reform::Error, "object already has a model"
+ model and Reform::Error.raise "object already has a model"
@model = child
addObject child
end # addModel
public # methods of Object
+ def objectName name = nil
+ return objectName_get if name.nil?
+ self.objectName = name
+ end
+
+ # override
+ def to_s
+ return 'zombie' if zombified?
+ name = objectName and "#{self.class}:'#{name}'" or super
+ end
+
+ def each_child &block
+ return to_enum :each_child unless block
+ #tag "calling #{self}.enqueue_children()"
+ enqueue_children &block
+ end
+
+ def each_child_with_root &block
+ return to_enum :each_child_with_root unless block
+ yield self
+ each_child &block
+ end
+
+ def each_sub
+ return to_enum :each_sub unless block_given?
+ queue = []
+ enqueue_children queue
+ while !queue.empty?
+ node = queue.shift
+ yield node unless node.synthesized?
+ node.enqueue_children queue
+ end
+ end
+
+ def each_sub_with_root &block
+ return to_enum :each_sub_with_root unless block
+ yield self
+ each_sub &block
+ end
+
+ # signal can be :symbol or 'qt_signal_str' Like 'validated(int)'
+ def emit signal, *args
+ #tag "#{self}.emit #{signal.inspect} #{args.inspect}"
+ # block_given? is always true if called from EventSignalBroker::eventFilter()
+ # Even more the block looks like: (#<Proc:0x00000000000000>)
+ # BROKEN TypeError.raise "blocks (#{block.inspect}) cannot be passed to emit" if block_given?
+ if Symbol === signal
+ if @connections
+ proxylist = @connections[signal]
+ proxylist and proxylist.each do |proxy|
+ #tag "#{proxy}.call(*#{args.inspect})"
+ proxy[*args]
+ end
+ end
+ self
+ else
+ NotImplementedError.raise 'emitting qt-signals'
+ end
+ end
+
+ def qtobject?; true; end
+ def synthesized?; end
+
# it might be a better idea using 'each_child.to_a' here as well.
# the idea is that each_child.to_a is relatively slow.
def children
@@ -127,11 +240,6 @@ def parent_get
@parent || qtparent_get
end
- alias :children_get :children
-
- # you can 'mount' a model into any object.
- attr :model
-
## callback, called when a DynamicAttribute is given a 'connector'
# Semantics: a RW-widget can decide to add a push-channel for data.
# Erm... See widgets/lineedit.rb to get the idea.
@@ -140,6 +248,7 @@ def connect_attribute attrname, dynattr; end
# callback, called after the instance is parented
def setup hash = nil, &initblock
+ #tag "__FILE__ = #{__FILE__}"
instance_eval(&initblock) if initblock
setupQuickyhash hash if hash
end # setup
@@ -149,10 +258,10 @@ def parent= parent
parent.addObject self
end
- # either parent_set or parent_get
+ # either parent= or parent_get
def parent parent = nil
if parent
- #tag "#{self}.parent_set #{parent}"
+ #tag "#{self}.parent= #{parent}"
self.parent = parent
else
#tag "#{self}.parent_get"
@@ -224,16 +333,53 @@ def find_all *args
r
end
+ # for each signal it creates a method with the name of the signal.
+ # You can pass the method arguments and it calls 'emit'
+ # Or you can pass it a block and it calls 'connect'.
+ #
+ # signal 'valueChanged(int)'
+ #
+ # valueChanged { |val| puts "value became #{val}" }
+ # valueChanged { |val| $app.quit }
+ # valueChanged(44)
+ #
+ # More than one block can be connected and they will all be executed.
+ # Signal can be a Qt signal, or it can be any ruby symbol.
+ #
def self.signal *signals
for signal in signals
m = signal.to_s.sub(/\(.*/, '').to_sym # !
+ #tag "define method :#{m} for signal '#{signal}'"
+ if method_defined? m
+ NameError.raise "the signal #{self}.#{m} is already defined as " +
+ "#{instance_method(m).owner}.#{m}"
+ end
define_method m do |*args, &block|
+ #tag "signal received. m=#{m.inspect}, signal=#{signal}, args=#{args.inspect}"
+ #tag "block=#{block}"
signal_implementation m, signal, args, block
end
end
end # signal
+ #override, this disables dup and clone on QObject.
+ # Which will cause disasters, since a ruby ref has a 1 on 1 relationship with a QObject
+ # We should make a deep copy, but I don't believe Qt supports that.
+ def initialize_copy other
+ Reform::Error.raise "Qt objects cannot be duplicated or cloned"
+ end
+
signal 'destroyed(QObject *)'
+
+
+ # you can 'mount' a model into any object.
+ attr :model
+
+ alias children_get children
+ alias qtchildren qtchildren_get
+ alias name objectName
+ alias each each_child
+
end #class Object
public # methods of R::Qt
View
34 lib/reform/r.rb
@@ -0,0 +1,34 @@
+module R
+
+ public # methods of R
+ ## use this to wrap a rescue clause around any block.
+ # transforms the exception (RuntimeError+IOError+StandardError) to a warning on stderr.
+ # Reform::Error is also a RuntimeError.
+ #
+ # UNLESS: 'fail_on_errors true' is specified in Application.
+ def self.escue
+ begin
+ return yield
+ rescue IOError => exception
+ raise if $app.fail_on_errors?
+ msg = "#{exception.message}\n"
+ rescue StandardError, RuntimeError => exception
+ raise if $app.fail_on_errors?
+ #tag "got exception"
+ #tag "exception class: #{exception.class}"
+ #tag "exception msg: #{exception}"
+ #tag "exception backtrace: #{exception.backtrace.join "\n"}"
+ msg = "#{exception.class}: #{exception}\n" + exception.backtrace.join("\n")
+ end
+ $stderr << msg
+ nil
+ end # escue
+
+ module EForm
+ class Error < RuntimeError; end
+ end
+end # module R
+
+Reform = R::EForm
+
+
View
2 lib/reform/abstractbutton.rb → lib/reform/widgets/_abstractbutton.rb
@@ -1,7 +1,7 @@
# Copyright (c) 2013 Eugene Brazwick
-require_relative 'widgets/widget'
+require_relative 'widget'
module R::Qt
class AbstractButton < Widget
View
4 lib/reform/layout.rb → lib/reform/widgets/_layout.rb
@@ -1,6 +1,6 @@
-require_relative 'context'
-require_relative 'layoutable'
+require_relative '../context'
+require_relative '_layoutable'
module R::Qt
View
0 lib/reform/layoutable.rb → lib/reform/widgets/_layoutable.rb
File renamed without changes.
View
13 lib/reform/widgets/frame.rb
@@ -0,0 +1,13 @@
+
+# Copyright (c) 2013 Eugene Brazwick
+
+require_relative 'widget'
+
+module R::Qt
+
+# class Frame < Widget
+# public # methods of Widget
+# end # class Frame
+
+ Reform.createInstantiator __FILE__, Frame
+end
View
34 lib/reform/widgets/graphicsscene.rb
@@ -5,10 +5,44 @@
module R::Qt
## Note that our scene diverts from the Qt classtree!
class GraphicsScene < Control
+
+ #tag "Scanning class GraphicsScene, caller = #{caller.join("\n")}"
+
include Reform::GraphicContext
public # methods of GraphicsScene
+ # override
+ def parent= par
+ par.addScene self
+ end
+
+ # override
+ def addObject child
+ TypeError.raise 'can only add GraphicsItems to a GraphicsScene'
+ end
+
+ # override
+ def children
+ each_child.to_a
+ end
+
+ def backgroundBrush_get; @brush; end
+
+ def backgroundBrush *args, &block
+ return backgroundBrush_get if args.empty?
+ Brush.new self, *args, &block
+ end
+
+ # tag "setting up 'title' attr_dynamic"
+ attr_dynamic RectF, :sceneRect
+
+ alias background backgroundBrush
+ alias brush= backgroundBrush=
+ alias addGraphicsItem addItem
+ alias area sceneRect
+
+ #tag "Done scanning class GraphicsScene"
end # class GraphicsScene
Reform.createInstantiator __FILE__, GraphicsScene
View
10 lib/reform/widgets/graphicsview.rb
@@ -3,7 +3,7 @@
require 'forwardable'
module R::Qt
- class GraphicsView < Widget
+ class GraphicsView < AbstractScrollArea
include Reform::GraphicContext
extend Forwardable
@@ -15,20 +15,24 @@ class GraphicsView < Widget
def infused_scene!
unless @infused_scene
- require_relative 'scene'
+ require_relative 'graphicsscene' # do NOT use the link!!!
scene
raise Reform::Error, "broken scenery" unless @infused_scene
end
@infused_scene
end
public #methods of GraphicsView
-
+
+ def scale_get; @scale; end
+
def addScene scene
@infused_scene = scene
scene.qtparent = self
self.scene = scene
end
+
+ attr_dynamic SizeF, :scale
end
# req. for a plugin:
View
22 lib/reform/widgets/grid.rb
@@ -1,7 +1,25 @@
# Copyright (c) 2013 Eugene Brazwick
-require_relative '../layout'
+require_relative '_layout'
-Reform.createInstantiator __FILE__, R::Qt::GridLayout
+module R::Qt
+ class GridLayout < Layout
+ private # methods of GridLayout
+ def initialize *args
+ super
+ @currow = @curcol = 0
+ end
+ public # methods of GridLayout
+
+ attr_dynamic Fixnum, :columnCount
+
+ alias columncount columnCount
+ alias colcount columnCount
+ alias columns columnCount
+
+ end
+
+ Reform.createInstantiator __FILE__, GridLayout
+end # module R::Qt
View
2 lib/reform/widgets/hboxlayout.rb
@@ -1,7 +1,7 @@
# Copyright (c) 2013 Eugene Brazwick
-require_relative '../layout'
+require_relative '_layout'
# req. for a plugin:
Reform.createInstantiator __FILE__, R::Qt::HBoxLayout
View
27 lib/reform/widgets/lcdnumber.rb
@@ -0,0 +1,27 @@
+
+# Copyright (c) 2013 Eugene Brazwick
+
+require_relative 'frame'
+
+module R::Qt
+ class LCDNumber < Frame
+ public # methods of LCDNumber
+
+ # override
+ def apply_model data
+ case data
+ when Timer, ::Time then display data.strftime(data.sec % 2 == 0 ? '%H %M' : '%H:%M')
+ else display data
+ end
+ end
+
+ attr_dynamic Fixnum, :digitCount
+ attr_dynamic Symbol, :segmentStyle
+
+ alias segments segmentStyle
+ alias display= display
+
+ end # class LCDNumber
+
+ Reform.createInstantiator __FILE__, LCDNumber
+end
View
2 lib/reform/widgets/pushbutton.rb
@@ -1,7 +1,7 @@
# Copyright (c) 2013 Eugene Brazwick
-require_relative '../abstractbutton'
+require_relative '_abstractbutton'
module R::Qt
# a PushButton can be connected to data in several ways.
View
22 lib/reform/widgets/slider.rb
@@ -23,7 +23,7 @@ class AbstractSlider < Widget
# override
def apply_model data
#tag "#{self}.apply_model(#{data.inspect})"
- apply_dynamic_setter :value, data
+ apply_dynamic_setter(if @floatmode then :valueF else :value end, data)
end # apply_model
# override
@@ -32,14 +32,28 @@ def setup hash = nil, &initblock
@mem_val = value
if connector
valueChanged do |val|
- #tag "Aha, valueChanged(#{val})"
- push_data(@mem_val = val) unless @mem_val == val
+ #tag "Aha, valueChanged(#{val.inspect})"
+ unless @mem_val == val
+ @mem_val = val # still an int
+ val /= FloatModeFactor if @floatmode
+ push_data val
+ end
end
end # connector
end # setup
+
+ attr_dynamic Fixnum, :value, :minimum, :maximum
+ attr_dynamic Symbol, :orientation
+ attr_dynamic Float, :minimumF, :maximumF, :valueF
- end
+ # shouldn't there be a valueF ??
+ end #class AbstractSlider
+ class Slider < AbstractSlider
+ public # methods of Slider
+ attr_dynamic Symbol, :tickPosition
+ attr_dynamic Fixnum, :tickInterval
+ end # class Slider
Reform.createInstantiator __FILE__, Slider
end # module R::Qt
View
2 lib/reform/widgets/vboxlayout.rb
@@ -1,7 +1,7 @@
# Copyright (c) 2013 Eugene Brazwick
-require_relative '../layout'
+require_relative '_layout'
# req. for a plugin:
Reform.createInstantiator __FILE__, R::Qt::VBoxLayout
View
13 lib/reform/widgets/widget.rb
@@ -2,10 +2,13 @@
# Copyright (c) 2013 Eugene Brazwick
require_relative '../context'
-require_relative '../layoutable'
+require_relative '_layoutable'
module R::Qt
class Widget < Control
+
+ # tag "Scanning class Widget, caller = #{caller.inspect}"
+
# you can include any widget inside any other:
include Reform::WidgetContext, Reform::ModelContext
include Layout::Able
@@ -35,14 +38,20 @@ def parent= parent
parent.addWidget self
end # parent=
+ #tag "setting up 'title' attr_dynamic"
attr_dynamic String, :title
- attr_dynamic Reform::Size, :size
+ attr_dynamic Size, :size, :minimumSize, :maximumSize
alias caption title
alias windowTitle title
+ #tag "Scanned class Widget OK"
end # class Widget
+ class SynthWidget < Widget
+ def synthesized?; true; end
+ end
+
# req. for a plugin:
Reform.createInstantiator __FILE__, Widget
View
4 lib/rpp/rpp.rb
@@ -0,0 +1,4 @@
+
+require_relative 'librpprpp'
+
+
View
87 lib/ruby++/array.h
@@ -37,30 +37,46 @@ class Array: public Object
void operator=(Fixnum arg) const { assign(arg); }
void operator=(double arg) const { assign(DBL2NUM(arg)); }
void operator=(const char *arg) const { assign(rb_str_new_cstr(arg)); }
+ operator VALUE() const
+ {
+ if (HasLength)
+ {
+ VALUE v[2] = { Index, Length };
+ return rb_ary_aref(2, v, Ary);
+ }
+ return rb_funcall(Ary, rb_intern("at"), 1, Index);
+ }
};
public:
- enum ECreateSingleton { CreateSingleton };
+ enum ECreateSingleton { CreateSingleton};
public:
Array(): inherited(rb_ary_new()) {}
+ Array(E_SAFETY /*UNSAFE*/) {}
// VERY BAD IDEA Array(VALUE v_1): inherited(rb_ary_new3(1, v_1)) {}
Array(VALUE v_1, ECreateSingleton): inherited(rb_ary_new3(1, v_1)) {}
- Array(bool b): inherited(rb_ary_new3(1, b ? Qtrue : Qfalse)) {}
- Array(int i): inherited(rb_ary_new3(1, INT2NUM(i))) {}
- Array(Fixnum i): inherited(rb_ary_new3(1, i)) {}
- Array(double f): inherited(rb_ary_new3(1, DBL2NUM(f))) {}
- Array(const char *s): inherited(rb_ary_new3(1, rb_str_new_cstr(s))) {}
+ Array(bool b, ECreateSingleton): inherited(rb_ary_new3(1, b ? Qtrue : Qfalse)) {}
+ Array(int i, ECreateSingleton): inherited(rb_ary_new3(1, INT2NUM(i))) {}
+ Array(Fixnum i, ECreateSingleton): inherited(rb_ary_new3(1, VALUE(i))) {}
+ Array(double f, ECreateSingleton): inherited(rb_ary_new3(1, DBL2NUM(f))) {}
+ Array(const char *s, ECreateSingleton): inherited(rb_ary_new3(1, rb_str_new_cstr(s))) {}
Array(VALUE v_1, VALUE v_2): inherited(rb_ary_new3(2, v_1, v_2)) {}
Array(VALUE v_1, VALUE v_2, VALUE v_3): inherited(rb_ary_new3(3, v_1, v_2, v_3)) {}
Array(VALUE v_1, VALUE v_2, VALUE v_3, VALUE v_4): inherited(rb_ary_new3(4, v_1, v_2, v_3, v_4)) {}
- Array(VALUE v, ESafety safe = Safe): inherited(v)
+ Array(VALUE v, E_SAFETY safe = SAFE)
{
- if (safe)
+ assign(v, safe);
+ }
+ override void assign(VALUE v, E_SAFETY safe)
+ {
+ inherited::assign(v, safe);
+ if (safe == SAFE || safe == UNSAFE && !isNil())
{
- V = rb_check_array_type(v);
+ V = rb_check_array_type(V);
if (NIL_P(V))
rb_raise(rb_eTypeError, "Could not convert %s to an array", TO_CSTR(v));
}
}
+ Array(int count): inherited(rb_ary_new2(count)) {}
// The following is unsafe, but required for some internal argument hocus pocus
Array(int argc, VALUE *argv): inherited(rb_ary_new4(argc, argv)) {}
@@ -76,24 +92,61 @@ class Array: public Object
}
// arg1 can be a Range or a Fixnum
VALUE slice(VALUE arg1) const { return rb_ary_aref(1, &arg1, V); }
+ VALUE push(VALUE arg) const { return rb_ary_push(V, arg); }
+ // etc.... ?
+ VALUE unshift(VALUE arg) const { return rb_ary_unshift(V, arg); }
+ // etc.... ?
VALUE at(VALUE pos) const { return call("at", pos); }
VALUE at(int pos) const { return call("at", Fixnum(pos)); }
VALUE at(Fixnum pos) const { return call("at", pos); }
- VALUE operator[](VALUE i) const { return at(i); }
- VALUE operator[](int i) const { return at(i); }
- VALUE operator[](Fixnum i) const { return at(i); }
+ // entry is a lot faster than 'at'
+ VALUE entry(int pos) const { return rb_ary_entry(V, pos); }
+ VALUE operator[](VALUE i) const { return entry(NUM2INT(i)); }
+ VALUE operator[](int i) const { return entry(i); }
+ VALUE operator[](Fixnum i) const { return entry(NUM2INT(i)); }
+ VALUE shift() const { return rb_ary_shift(V); }
const Closure operator[](VALUE i) { return Closure(*this, i); }
- const Closure operator[](int i) { return (*this)[Fixnum(i)]; }
- const Closure operator[](Fixnum i) { return (*this)[i]; }
- // For fast and very C-like access use length() and ptr().
- int length() const { return RARRAY_LEN(V); }
+ const Closure operator[](int i) { return Closure(*this, INT2NUM(i)); }
+ const Closure operator[](Fixnum i) { return Closure(*this, i); }
+ // For fast and very C-like access use len() and ptr(). UNSAFE!!
+ long len() const { return RARRAY_LEN(V); }
VALUE *ptr() const { return RARRAY_PTR(V); }
};
inline VALUE
Class::new_instance(const Array v_1) const
{
- return rb_class_new_instance(v_1.length(), v_1.ptr(), V);
+ return rb_class_new_instance(v_1.len(), v_1.ptr(), V);
+}
+
+inline VALUE
+BasicObject::call(const char *method, Array args)
+{
+ return rb_funcall2(V, rb_intern(method), args.len(), args.ptr());
+}
+
+inline VALUE
+BasicObject::call_public(const char *method, Array args)
+{
+ return rb_funcall3(V, rb_intern(method), args.len(), args.ptr());
+}
+
+inline Array
+BasicObject::check_array_type() const
+{
+ return Array(rb_check_array_type(V), UNSAFE);
+}
+
+inline Scan &
+Scan::splat(Array &v)
+{
+ if (GotSplat) rb_raise(rb_eFatal, "double splat() given");
+ GotOpt = GotSplat = true;
+ if (ArgC <= 0) v.assign(rb_ary_new(), VERYUNSAFE);
+ v.assign(rb_ary_new4(ArgC, ArgV), VERYUNSAFE);
+ ArgV += ArgC;
+ ArgC = 0;
+ return *this;
}
} // namespace RPP
View
359 lib/ruby++/basicobject.h
@@ -0,0 +1,359 @@
+#if !defined(_RPP_BO_H_)
+#define _RPP_BO_H_
+
+namespace RPP {
+
+#include <ruby/ruby.h>
+
+#define override virtual
+
+// forwards
+class Array;
+class Class;
+class Fixnum;
+class Proc;
+class String;
+
+enum E_SAFETY
+{
+ // Passing VERYUNSAFE will disable all checking.
+ VERYUNSAFE,
+ // Passing UNSAFE will disable typecheck for Qnil.
+ UNSAFE,
+ // Passing SAFE will cause a typecheck for any value
+ SAFE
+}; // for use with constructors.
+
+typedef VALUE (*ArgVMethod)(int argc, VALUE *argv, VALUE v_self);
+typedef VALUE (*Arg0Method)(VALUE v_self);
+typedef VALUE (*Arg1Method)(VALUE v_self, VALUE v_1);
+typedef VALUE (*Arg2Method)(VALUE v_self, VALUE v_1, VALUE v_2);
+typedef VALUE (*Arg3Method)(VALUE v_self, VALUE v_1, VALUE v_2, VALUE v_3);
+typedef VALUE (*Arg4Method)(VALUE v_self, VALUE v_1, VALUE v_2, VALUE v_3, VALUE v_4);
+
+// prefix is 'iv' (instancevar) or 'cv' (classvar) or 'gv' (globalvar)
+// note that Fixnum has ambiguous casts of ->VALUE and ->int (since VALUE == int)...
+#define RPP_SETTERS(klass, prefix) \
+ const klass &prefix##_set(const char *name, VALUE v) const \
+ { \
+ rb_##prefix##_set(V, name, v); \
+ return *this; \
+ } \
+ const klass &prefix##_set(const char *name, Fixnum v) const; /*FORWARDED*/ \
+ const klass &prefix##_set(const char *name, bool v) const \
+ { \
+ rb_##prefix##_set(V, name, v ? Qtrue : Qfalse); \
+ return *this; \
+ } \
+ const klass &prefix##_set(const char *name, int v) const \
+ { \
+ rb_##prefix##_set(V, name, INT2NUM(v)); \
+ return *this; \
+ } \
+ const klass &prefix##_set(const char *name, double v) const \
+ { \
+ rb_##prefix##_set(V, name, DBL2NUM(v)); \
+ return *this; \
+ } \
+ const klass &prefix##_set(const char *name, const char *v) const \
+ { \
+ rb_##prefix##_set(V, name, rb_str_new_cstr(v)); \
+ return *this; \