Acquiring a reference to the emitting widget with QtRuby - ruby

I'm currently connecting the signal to a function like this:
dashboard.ui.start_button_r1.connect(:clicked, dashboard.ui.start_button_r1, :handler)
where start_button_r1 is a QPushButton
Now what I want is a reference to the sending widget within handler, since I will connect this signal to several widgets. Ideally I would like my handler function to receive a emitter argument I can play around with. I could put handlerwithin a class inheriting from Qt::Object (say HandlerContainer) and call sender(), but then how do I connect the signal to HandlerContainer's inner method? I tried instance.method(:handler), but connect doesn't receive slots in that way
I usually use this approach in PyQt, but can't figure out how to do it with Ruby. I feel like I'm doing something terribly wrong since there isn't much discussion on how to get the sender from within a slot using QtRuby. How is it usually done? I have read about QSignalMapper, but that seems awfully overkill for my use case.

You can do it with QObject::sender() function. As you've said inside handler slot, typecast sender() to the type, you expect, QPushButton in your case, and you,ve got a reference to sender object.
In c++ it could be done like this:
// inside handler method
QPushButton *tmpBtn= dynamic_cast<QPushButton*>(sender());
Edit:
A minimal example on how to do this in Ruby:
class SlotContainer < Qt::Object
slots "handler()"
def handler
puts "called by: " + sender().to_s
end
end
if $0 == __FILE__
app = Qt::Application.new(ARGV)
ui = Ui_MainWindow.new
container = SlotContainer.new
window = Qt::MainWindow.new
ui.setupUi(window)
Qt::Object.connect(ui.pushButton, SIGNAL("clicked()"), container, SLOT("handler()"))
window.show
app.exec
end

Related

Use the result of checked method inside conditional statement

I'm trying to implement and Authorization module, because currently the authorization logic for a certain resource is separated on two or three different places and even though I'm not sure this is the best approach, at least I think it will provide some encapsulation.
Since I'm checking for several different things
Does the user have the right role
Is the resources in the right state to process the required action
Do the user has the right to perform the required action on this particular resource
So as you can see, there are several checks, I'm not pretending to be completely correct here, but this is pretty close to the real case, so I've decided to use something like a Result Object even though it's actually not an object but a struct and I'm not using Gem but pretty simple custom implementation.
So part of my Authorization module is this:
module Authorization
Result = Struct.new(:successfull?, :error)
extend self
def read(user, resource, message: 'Permission denied')
can_read =
[
condition1,
condition2,
condition3
]
return Result.new(can_read.any?, can_read.any? ? nil : message))
end
However within this Authorization module I have a lot of methods and some of them check read internally like so:
def assign(user, resource, message: 'Permission denied')
return read(user, resource) unless read(user, resource).successfull?
Result.new(true, nil)
end
So my main question is how to avoid this double call to read(user, resource). I guess one option would be to just call it before the check like:
result = read(user, resource)
return result unless result.successfull?
However I'm pretty new to Ruby and I suspect that maybe there is more ruby-like way to do this. Just to inline it somehow by assigning the result from read inside the condition check...However this is just wild guess.
And one more question, that came up while I was writing this. Currently if I want to send nil for message when the authorization passes I'm doing this:
return Result.new(can_read.any?, can_read.any? ? nil : message))
Because message unless can_read.any? is throwing and error even though I thought it would default to nil. So again, is there some more ruby-like way to do this?
First part can be written with Object#yield_self:
def assign(user, resource, message: 'Permission denied')
read(user, resource).yield_self do |res|
res.successful? ? Result.new(true, nil) : res
end
end
successfull? -> successful? for English reasons. I am not convinced this is more readable than using a local variable though. Alternatively:
(res = read(user, resource)).successful? ? Result.new(true, nil) : res
As for your second question, you'll need more parentheses
Result.new(can_read.any?, (message if can_read.none?))
the return is not needed.
I would also advise you to slow down with all the unlesses, try to swap your conditions to if whenever possible -- I find it quite useful to make Result a class and define a failed? method for it. Actually, I'd consider this:
class Result
def initialize(error)
#error = error
end
def successful?
#error.nil?
end
def failed?
!successful?
end
end
That depends on how complicated your Result gets, but for the use case shown, it would be a little cleaner imho.

Custom Signal with GTK3 in Ruby

I would like to emit a custom signal from a Widget in gtk3.
In GTK2, there was a function called signal_new to create a new signal.
You can see an example here: https://github.com/ruby-gnome2/ruby-gnome2/blob/ec373f87e672dbeeaa157f9148d18b34713bb90e/glib2/sample/type-register.rb
In GTK3 it seems this function isn't available anymore. So what is the new way to create custom signals in ruby's GTK3?
GTK3 changed to using a define_signal method instead of signal_new. This method is called on class-level (much like something like attr_accessor).
Here's an example class, using a custom signal called example. I've adapted this from a sample on the glib2 GitHub repo.
class ExampleClass < GLib::Object
# To use define_signal, the class must first be registered with GLib's
# type hierarchy.
type_register
# This declares the custom signal.
define_signal(
'example', # signal name
nil, # flags (combination of GLib::Signal's constants)
nil, # accumulator; apparently not supported
nil # signal return type, where nil is void
)
end
By default, a signal's handler block accepts one parameter, which is the object which emitted the signal. If you'd like the signal to use additional parameters, you can pass extra arguments to define_signal with their types.
Once you've configured a signal, you can emit it using signal_emit:
example = ExampleClass.new
example.signal_emit('example')
If there were additional arguments to pass, their values can be passed as extra arguments to signal_emit.
This signal can be connected to just like any other GTK3 signal, using signal_connect:
e = ExampleClass.new
e.signal_connect('example') do |sender|
puts "Handler 1, sent by #{sender}"
end
e.signal_connect('example') do |sender|
puts "Handler 2, sent by #{sender}"
end
e.signal_emit('example')
This would output something like:
Handler 1, sent by #<ExampleClass:0x0000564d436331e8>
Handler 2, sent by #<ExampleClass:0x0000564d436331e8>
Again, if your signal uses additional parameters, they would be extra arguments passed to signal_connect's block.

Using Ruby to solve a quiz

So I found this quiz on a website that I was excited to solve with my newly acquired Ruby skills (CodeAcademy, not quite finished yet).
What I want to do is make an array with 100 entries, all set to "open". Then, I planned to create a method containing a for loop that iterates through every nth entry of the array and changes it to either "open" or "closed", based on what it was before. In the for loop, n should be increased from 1 to 100.
What I have so far is this:
change_state = Proc.new { |element| element == "open" ? element = "closed" : element = "open" }
def janitor(array,n)
for i in 1..n
array.each { |element| if array.index(element) % i == 0 then element.change_state end }
end
end
lockers = [*1..100]
lockers = lockers.map{ |element| element = "closed" }
result = janitor(lockers,100)
When trying to execute I receive an error saying:
undefined method `change_state' for "closed":String (NoMethodError)
Anybody an idea what is wrong here? I kinda think I'm calling the "change_state" proc incorrectly on the current array element.
If you know the quiz, no spoilers please!
As you have implemented change_state, it is not a method of any class, and definitely not one attached to any of the individual elements of the array, despite you using the same variable name element. So you cannot call it as element.change_state.
Instead, it is a variable pointing to a Proc object.
To call the code in a Proc object, you would use the call method, and syntax like proc_obj.call( params ) - in your case change_state.call( element )
If you just drop in that change, your error message will change to:
NameError: undefined local variable or method `change_state' for main:Object
That's because the change_state variable is not in scope inside the method, in order to be called. There are lots of ways to make it available. One option would be to pass it in as a parameter, so your definition for janitor becomes
def janitor(array,n,state_proc)
(use the variable name state_proc inside your routine instead of change_state - I am suggesting you change the name to avoid confusing yourself)
You could then call it like this:
result = janitor(lockers,100,change_state)
Although your example does not really need this structure, this is one way in which Ruby code can provide a generic "outer" function - working through the elements of an array, say - and have the user of that code provide a small internal custom part of it. A more common way to achieve the same result as your example is to use a Ruby block and the yield method, but Procs also have their uses, because you can treat them like data as well as code - so you can pass them around, put them into hashes or arrays to decide which one to call etc.
There may be other issues to address in your code, but this is the cause of the error message in the question.

Bindings in Ruby. What is a good way to think of them. What do they contain

I am confused about bindings.
def repl(input_stream, output_stream)
loop do
output_stream.print "> "
input = input_stream.gets()
result = binding.eval(input)
output_stream.puts(result)
end
end
repl($stdin, $stdout)
I am going to call repl with just $stdin and $stdout. I need a dumbed down version of what the line:
binding.eval(input) is doing.
Bindings are just where we currently are in the call stack right? They hold the current local variables? Anything else? What's a good way to think of them differently from the current scope?
Objects of class Binding encapsulate the execution context at some particular place in the code and retain this context for future use. The variables, methods, value of self, and possibly an iterator block that can be accessed in this context are all retained. Binding objects can be created using Kernel#binding, and are made available to the callback of Kernel#set_trace_func.
static VALUE
bind_local_variable_defined_p(VALUE bindval, VALUE sym)
{
ID lid = check_local_id(bindval, &sym);
const rb_binding_t *bind;
if (!lid) return Qfalse;
GetBindingPtr(bindval, bind);
return get_local_variable_ptr(bind->env, lid) ? Qtrue : Qfalse;
}
Here is a example of Public Instance Methods
def get_binding(param)
return binding
end
b = get_binding("hello")
b.eval("param") #=> "hello"

How to allow for a local event listener method in Corona

Say I have an eventlistener in a class as follows:
Vehicle = {}
...
function Vehicle:touch(event)
-- do something with self
return
end
displayObject:addEventListener("touch", self)
How do I make the "touch" method local so that nobody gets the urge to call it from outside this class? Without making the compiler complain.
Thanks Mike. I didn't realize you could do as UI.lua does:
local function newButtonHandler( self, event )
...
end
function newButton( params )
...
button.touch = newButtonHandler
button:addEventListener( "touch", button )
...
end
However, the parameters (self, event) in
local function newButtonHandler( self, event )
is something I haven't seen before - ususally only (event). Does self, as well as event get sent along to the event listener method automagically as an implication of addEventListener?
Anyway, what I originally wanted to do is to have a different object (call it "buttonManager") than the display object be sent along to the class eventListener method, because I need access to buttonManager there. So I thought I could write:
button:addEventListener( "touch", buttonManager )
But that results in the eventListener not being called at all. How do I get buttonManager to the eventListener?
It sounds to me like you could create a separate module for the class you're designing, and make the touch handler a local function to that module. See Corona's ui.lua file (included in many of their sample projects) to see how they do it.
module(..., package.seeall)
local function newButtonHandler( self, event )
. . .
end
function newButton( params )
. . .
button.touch = newButtonHandler
button:addEventListener( "touch", button )
. . .
return button
end
This is boiled down from their code. As you can see, newButtonHandler is local, so it cannot be called by the outside world.
Hope that helps!

Resources