Change label text from within command proc - ruby

I have already created a TkLabel beforehand and now I want to update its text variable inside a command proc. The only problem is that NameError: unknown option 'codelabel' for #<Tk::Button:0x00000000050797e8 #path=".w00003"> (deleted widget?).
It seems it is unable to reference the label variable from within the command proc.
Is this the right way to assign?
TkButton.new(root){
text "GET"
#command proc { p txthost.value; p txtpath.value;exit }
command proc{
rc.get_method(txthost.value,txtpath.value);
txtcode.value=rc.code;
txthtml.value=rc.html;
codelabel.text=txtcode.value #label text updated here
}
pack('side'=>'bottom', 'padx'=>10, 'pady'=>10)
}
Any advice?
Forgive me, for the question, I am new to Ruby.

The problematic context (where it's unable to reference the label variable) isn't the command proc. Instead, unexpectedly, it's the entire code block passed to TkButton.new.
Generally speaking, the best way around this problem is to avoid passing a code block to TkButton.new in the first placeā€”as follows:
b = TkButton.new(root)
b.text "GET"
#command proc { p txthost.value; p txtpath.value;exit }
b.command proc{
rc.get_method(txthost.value, txtpath.value)
txtcode.value = rc.code
txthtml.value = rc.html
codelabel.text = txtcode.value #label text updated here
}
b.pack('side'=>'bottom', 'padx'=>10, 'pady'=>10)

Related

Ruby String to access an object attribute

I have a text file (objects.txt) which contains Objects and its attributes.
The content of the file is something like:
Object.attribute = "data"
On a different file, I am Loading the objects.txt file and if I type:
puts object.attribute it prints out data
The issue comes when I am trying to access the object and/or the attribute with a string. What I am doing is:
var = "object" + "." + "access"
puts var
It prints out object.access and not the content of it "data".
I have already tried with instance_variable_get and it works, but I have to modify the object.txt and append an # at the beginning to make it an instance variable, but I cannot do this, because I am not the owner of the object.txt file.
As a workaround I can parse the object.txt file and get the data that I need but I don't want to do this, as I want take advantage of what is already there.
Any suggestions?
Yes, puts is correctly spitting out "object.access" because you are creating that string exactly.
In order to evaluate a string as if it were ruby code, you need to use eval()
eg:
var = "object" + "." + "access"
puts eval(var)
=> "data"
Be aware that doing this is quite dangerous if you are evaluating anything that potentially comes from another user.

How to I temporarily override print and puts from a class?

I'm working on a shell for my project. I want the Shell class to override all print functions while the shell is running, so I've done this:
# WARNING: Blocks until the user exits.
def start
# Override Kernel print functions.
master_print = Kernel.method :print
master_puts = Kernel.method :puts
Kernel.module_exec {
define_method(:print) { |text = ""|
self.send(:print_override, master_print, text)
}
define_method(:puts) { |text = ""|
self.send(:puts_override, master_puts, text)
}
define_method(:puts_padded) { |text = ""|
self.send(:puts_override, master_puts, "")
self.send(:puts_override, master_puts, text)
self.send(:puts_override, master_puts, "")
}
}
# Readline loop and command parsing here...
end
This works well as long as only the Shell class is outputting any text, but as soon as a command class tries to puts I get this:
NoMethodError: undefined method `puts_override' for #<AddressKit::CLI::Interactiv
e::Commands::LoadTable:0x000000027735b0>
I thought that the print and puts blocks I wrote above would stay in this scope, not execute in which ever scope they happened to be called from. Is it possible to fix that?
Bonus question: How do I put the original print functions back? I had planned this:
Kernel.module_exec {
define_method(:print, &master_print)
define_method(:puts, &master_puts)
undef_method(:puts_padded)
}
But I haven't tried it yet, and I don't know if that will leave Kernel exactly as I found it.
While blocks do preserve scope, self is special and refers to the instance running the method (I assume because this is more useful).
An alternative strategy would be to use the fact that Kernel#puts just does $stdout.puts, so instead of overriding puts, set $stdout to one of your classes, that can massage the values before passing them to puts on the former $stdout.
When you're done, restore $stdout to its original value.
Well, I think I fixed the problem, but I'm not sure why it works. I'm guessing that the blocks are in both the scope where it was called from, as well as in the scope they were defined in, but with higher priority on the former. That would mean that I can't use self and expect it to point to the shell. Instead I did this:
shell = self
Kernel.module_exec {
define_method(:print) { |text = ""|
shell.send(:print_override, master_print, text)
}
define_method(:puts) { |text = ""|
shell.send(:puts_override, master_puts, text)
}
define_method(:puts_padded) { |text = ""|
shell.send(:puts_override, master_puts, "")
shell.send(:puts_override, master_puts, text)
shell.send(:puts_override, master_puts, "")
}
}
Which works perfectly but (if I'm right) would break as soon as the variable shell exists in the same scope as print or puts is called from. But it doesn't break, I tested it! I really have no idea what's going on and I'd be very grateful if somebody could explain it :-)

Why does ::TableView.model return nilClass when Nokogiri is used?

EDIT
The problem was with something else so the trouble wasn't really Qt, still I don't know why this happened.
The thing was that in the method display_filesize #yt.get_filesize(row_id, format) I used Nokogiri to parse the XML. I don't know if the XML was corrupted (it was loaded from quvi), but it was definitely the culprit. After switching to XMLSimple everything works fine.
The code I used:
def get_filesize(video_id, format)
video = #videos[video_id]
if video.formats[format].empty?
to_parse = `quvi --xml --format #{format} #{video.player_url}`
parsed = Nokogiri.parse(to_parse)
video.formats[format] = { :size => parsed.at('length_bytes').text,
:url => parsed.at('link').at('url').text }
end
video.formats[format][:size]
end
Now I use something like this:
def get_filesize(video_id, format)
video = #videos[video_id]
if video.formats[format].empty?
to_parse = `quvi --xml --format #{format} #{video.player_url}`
parsed = XmlSimple.xml_in(to_parse, {'KeyAttr' => 'name'})
video.formats[format] = { :size => parsed['link'][0]['length_bytes'][0],
:url => URI.decode(parsed['link'][0]['url'][0]) }
end
video.formats[format][:size]
end
It works beautifully. Still, I don't know why it crashed. This is the real question.
OLD QUESTION
I have a Qt::TableView that contains Qt::StandardItemModel. A row in the model consists of text, Qt::PushButton, checkbox and Qt::ComboBox. It works like this:
The user is presented with text values and can explore further if they want to.
The user clicks Qt::PushButton and the next cell is populated with a Qt::ComboBox containing other possible values to choose from.
If the user chooses an option from Qt::ComboBox, magic happens, objects get created, hashes populated and the cell on the right gets populated with appropriate text (through a Qt::StandardItem)
Then the checkbox can be checked.
After selecting the rows the user wants, a Qt::PushButton located outside of the Qt::TableView can be clicked. It then iterates through the model, tests if the checkbox is selected and should it be, tries to access the value in the appropriate ComboBox.
The problem is, when I insert code that tries to access the Qt::ComboBox, I can't insert the Qt::StandardItem, because I can't get the model, because Qt::TableView.model returns NilClass (at some point).
I don't know why and how this happens. It's a random thing, sometimes the value of Qt::ComboBox can be changed a couple times, sometimes the first try ends with an error.
Here is how I create the Qt::StandardItem:
def display_filesize
row_id = row_id_from_object_name(sender.objectName)
format = sender.currentText
filesize = #yt.get_filesize(row_id, format) # get the text
filesize_item = Qt::StandardItem.new("#{(filesize.to_i/1024/1024)} MB ")
# #tc simply stores the indexes of columns so I can access them easily
#ui.tableView.model.setItem(row_id, #tc[:filesize], filesize_item)
end
And here is how I try to access the Qt::ComboBox value:
model = #ui.tableView.model
checked = model.rowCount.times.map do |i|
if model.item(i, #tc[:check]).checkState == Qt::Checked
# if I remove the following two lines it works...
index = model.index(i, #tc[:formats])
format = #ui.tableView.indexWidget(index).currentText
#yt.videos[i][format]
end
end
And this is the error I am trying to get rid of:
searcher.rb:86:in `display_filesize': undefined method `index' for nil:NilClass (NoMethodError)
from /var/lib/gems/1.9.1/gems/qtbindings-4.8.3.0/lib/Qt/qtruby4.rb:469:in `qt_metacall'
from /var/lib/gems/1.9.1/gems/qtbindings-4.8.3.0/lib/Qt/qtruby4.rb:469:in `method_missing'
from /var/lib/gems/1.9.1/gems/qtbindings-4.8.3.0/lib/Qt/qtruby4.rb:469:in `exec'
from qutub-player.rb:17:in `<main>'

How to modify the value of a variable whose name is given

I wish to have a method that takes in a string, and then updates a variable with the name of that string. This is an example of my attempt:
#other_class = OtherClass.new()
def change_variable(variable_string)
self.#other_class.send.variable_string += 1
end
I am getting the error:
syntax error, unexpected tIVAR
with the pointer just before 'send' in the method above. Does anyone have any suggestions to make this work?
You probably want instance_variable_set http://ruby-doc.org/core-2.0/Object.html#method-i-instance_variable_set
The syntax using your variables is I think:
other_var = ('#' + variable_string).to_sym
#other_class.instance_variable_set( other_var, #other_class.instance_variable_get( other_var ) + 1 )
The immediate syntatic error is that you're using self and # wrongly.
Either one is fine but not in conjunction with each other.
So in your case self.other_class.send... would be fine but then you cant declare it as #.
As would # be but then you cant do self.
These are meant to do different things and these are that
# is an instance variable and so is self but the difference is that using # calls the attribute other_class directly as to where self calls the method other_class.
So # is both a getter and setter in one so you can do
#other_class = my_milk_man as to where
self.other_class -> self.other_class (as getter),
self.other_class = my_milk_man -> self.other_class= (as setter).

Is 'buggy_logger' a reference to the 'status' string in this Ruby example?

In this example, do the nukes get launched because any changes that you make to buggy_logger get applied to the 'status' string - just like using a copy of a reference to an object -> when you make a change to the copy of the reference, the change gets applied to the underlying object -> that change is, in turn, reflected in any other references to the object. So, in other words, buggy_logger is an alias to the 'status' object without specifically using the alias keyword? Is that correct? So, in ruby, you just say
b = a
and then any changes you make to b afterwards are also reflected in a. Or is this only true because we're talking about Strings, which are mutable in Ruby?
# example-4.rb
status = "peace"
buggy_logger = status
print "Status: "
print buggy_logger << "\n" # <- This insertion is the bug.
def launch_nukes?(status)
unless status == 'peace'
return true
else
return false
end
end
print "Nukes Launched: #{launch_nukes?(status)}\n"
# => Status: peace
# => Nukes Launched: true
Yes, it is because strings are objects. Try
buggy_logger = status.dup
If you want a distinct object with the same initial value.
As for your question about alias I suspect you aren't correctly understanding how alias is used in ruby; it's used on methods, not objects, and isn't related to mutability.
Note also that the same semantics would have applied with any class; if status had been an array, a file, or anything else (provided it had mutable state suitable for use as a logger), you would have gotten analogous results.
One warning about dup though. If your object refers to other objects, the copy will also refer to the same objects. It's fine once you start thinking about it the right way, but tricky till then.

Resources