I'm trying to use a list_box to select from different types of conversions. When I start the program and enter a number it does nothing, but if I click the next item in the list_box it works fine. This makes me think my method is not getting it's value from the list_box. Here's the code:
Shoes.app :title=> 'Temperature Converter', :width => 200, :height => 200 do
def convert(temp, unit='C')
if unit == "C"
(temp.to_i * 9.0 / 5.0) + 32.0
elsif unit == "F"
"Fail"
end
end
list_box :items => ["C", "F"], :choose => "C" do |item|
#unit.text = item.text
end
line1 = edit_line :width => 100
button 'Compute' do
#result.text = convert(line1.text, #unit.text)
end
#unit = para
#result = para
end
I tried setting 'C' as the default variable but that didn't work either. Is there a way to force the list_box to send it's value on startup?
Also, and unrelated, if I remove '#unit = para' from the end it won't print anything, even the #result. Why is that?
Any help would be awesome.
It probably won't print anything because your button and list_box are trying to call #unit.text, so you must continue to define #unit.
I think that is possibly the same reason that it won't choose "C" by default. At the time you choose "C", #unit is not defined.
Try this:
#list_box = list_box :items => ["C", "F"]
line1 = edit_line :width => 100
button 'Compute' do
#result.text = convert(line1.text, #unit.text)
end
#unit = para
#list_box.change{|item|
#unit.text = item.text
}
#list_box.choose("C")
I am unsure whether you need to separate the change method from the list box, but I have done so to be on the safe side.
EDIT: 2012-01-31 13:29
Shoes.app :title=> 'Temperature Converter', :width => 200, :height => 200 do
def convert(temp, unit='C')
#unit.text = unit
if unit == "C"
(temp.to_i * 9.0 / 5.0) + 32.0
elsif unit == "F"
"Fail"
end
end
#list_box = list_box :items => ["C", "F"]
line1 = edit_line :width => 100
button 'Compute' do
#result.text = convert(line1.text, #list_box.text)
end
#unit = para
#list_box.choose("C")
#result = para
end
The reason the op's code does not succeed in retrieving the initial value of the list box is because the block given to list_box() is only executed onchange, i.e. when the user changes the selection in the listbox. The button click in the op's code retrieves the current value of a para, horribly named "list_box"--but the para is only set when the onchcange event of the listbox fires, and the initial value of the para is set to nothing.
The way to get the initial value of the listbox when a button is clicked is by not relying on the onchange event. Instead, when the button is clicked, simply query the listbox for its current value, e.g. #my_listbox.text.
However, querying a listbox for its current value does not work directly inside the app block--apparently the listbox does not exist until after the app block has finished executing. In that case, you need to manually set the initial value of the list box:
#default_choice = "red"
#current_choice = "red"
list_box :items => ["blue", "red", "green"], :choose => #default_choice do |list|
#current_choice = list.text
end
Related
I'm trying to create some multi-line cells with the axlsx gem in Ruby. I found this issue which suggested a fix using to_xml_string - but no matter what I try I can't get those multi-line cells!
Here is my current code:
def saveAsXlsx
Axlsx::Package.new do |p|
p.workbook.add_worksheet(:name => "Basic Worksheet") do |sheet|
sheet.add_row #headers
#headers.each_with_index do |line, i|
#headerValues[i].unshift #noteValues[i]
sheet.add_row #headerValues[i]
sheet.to_xml_string
end
end
p.serialize('simple.xlsx')
end
end
If anyone can help me out here I would be most appreciative...
Facetoe -
I've a feeling that if you take your code out the block against the package, :preserve value for xml_space is properly initialized within the workbook.
You have highlighted a hidden assumption in the library that assumes you will never be building directly off the yielded package.
Obviously I will make efforts to support this use case.
In the mean time, you are going to save your processor a whole lot of work by doing the following:
p = Axlsx::package.new
p.workbook.add_worksheet do |sheet|
# - your code - styles is available via sheet
# !! you do _not_ need to call to_xml_string
end
p.serialize('foo')
To be honest I never envisioned that someone would do all their report processing in a single method, so this has been most enlightening!
Best,
randym
In case anyone else has this problem, I was using the to_xml_string in the wrong place...
Here is the updated code:
#Save to excel spreadsheet found at file
def saveAsXlsx(file)
Axlsx::Package.new do |p|
p.workbook.styles do |s|
#Column header format
black_cell = s.add_style :bg_color => "00", :fg_color => "FF", :sz => 14, :alignment => { :horizontal=> :center }
headerFormat = []
#headers.size.times {headerFormat << black_cell}
p.workbook.add_worksheet(:name => "Basic Worksheet") do |sheet|
sheet.add_row #headers, :style => headerFormat
numEntries = #headerValues.size
#Add the values to the spreadsheet
0.upto(numEntries-1) do |i|
sheet.add_row #headerValues[i], :height => 60, :widths=>[50, :auto, :auto, :auto, :auto]
end
#This is necessary to preserve newlines
sheet.to_xml_string
end
p.serialize(file)
end
end
end
The following code creates 3 buttons and adds a handler to each one. The Quit button works, the Give Up button produces an error NameError unknown option settings_change and suggests that an object has been deleted. Same with the Next button. The code works ok when I put the event handlers outside the class.
It turns out that if a callback is created by first doing something like next_note_proc = proc {next_note}, then in the button creation do command next_note_proc. Why does this work??
Why do the callbacks work differently when inside or outside the class?
require 'tk'
require 'tkextlib/tile'
class App
def next_note
puts "Got next note"
end
def settings_change
puts "Got settings change"
end
def quit
puts "Got exit"
exit(1)
end
def initialize
$number_correct = TkVariable.new;
$mode = TkVariable.new
#root = TkRoot.new {title "Music Training"}
#content = Tk::Tile::Frame.new(#root) {padding "0 0 0 0"}.grid( :sticky => 'nsew')
#a = Tk::Tile::Button.new(#content) {text 'Next'; command {next_note}}.grid( :column => 1, :row => 1, :sticky => 'w')
#b = Tk::Tile::Button.new(#content) {text 'Give up'; command {settings_change}}.grid( :column => 2, :row => 1, :sticky => 'w')
#c = Tk::Tile::Button.new(#content) {text 'Quit'; command {quit}}.grid( :column => 2, :row => 2, :sticky => 'w')
TkWinfo.children(#content).each {|w| TkGrid.configure w, :padx => 0, :pady => 0}
#c.bind("1") {quit}
#a.bind("1") {next_note}
#b.bind("1") {settings_change}
puts "Starting up"
end
def run
Tk.mainloop
end
end
the_app = App.new
the_app.run
Commands executed from buttons run in the global context, but settings_change, quit and next_note are in the context of the class. When you use the proc command it creates a new Proc object which calls the method, and which can be called from other contexts.
The reason the quit command seems to work is probably because there is another quit command at the global scope that is getting called -- it is almost certainly not calling the quit method of the App object. You can verify that by adding a print statement in the quit method.
Hey, all. I'm working on making a GUI for a Ruby project using Shoes.
I've got a class called Manager (as in memory manager) that loads a 'process list' from a file, splits it up and assigns things to different 'pages' in memory when a certain execution call is made. I really don't think this part matters too much, though. It all works as a terminal application just fine.
However, Shoes is just baffling me. Here's what I've got so far:
Shoes.app(:title => "Paging Simulator", :width => 800, :height => 450) do
#manager = Manager.new
stack(:width => 200) do
#exec_list = stack {
title "Execution Queue", :size => 14
#exec_lines = para "click button to load", :size => 9
#file_button = button "Load Process List"
#file_button.click {
filename = ask_open_file
# #manager.set_exec_list filename
# alert "this makes no sense"
#exec_lines.text = #manager.exec_list.join "\n"
# exec_lines.text = File.read filename
}
}
end
end
What happens when I run this:
The program view loads as expected. I get a header, a paragraph that says "click button....", and a button. I click the button and I select the file. But this is where things get weird.
If I run the last commented line exec_lines.text = File.read filename it does as I would like, but my manager doesn't get any of the information it needs.
If I run the #manager.set_exec_list filename line, nothing from that line on in the block gets run, including the alert, or any other code I try to put in there.
if I run as shown above, however, I get the output I expect, but I don't get to set my data from the file that I select.
I've tried to figure this out from the Shoes Rules page, but this doesn't seem to be an issue that they address, and their "it changes/doesn't change self" I think I grasp, but it's confusing and I don't think it's exactly related to this problem.
Does anyone have any idea how to get this to work? I'm kind of down to crunch time on this project and I can't seem to get any other Ruby GUI toolkit to even run, so I think I'm pretty stuck with Shoes.
Thanks.
Update
I've tried running ruby-debug on the code when I make the call to #manager.set_exec_list filename, and stepping through it shows that this call is made, but the code never actually (from what I can tell) jumps into that method, and acts like it's the last line of code in the block. Do I need to include these classes inside the Shoes.app block?
Update Nope. That does nothing different.
update fullsource code follows:
#!/usr/bin/env shoes
require 'rubygems'
require 'ruby-debug'
class MemSegment
attr_accessor :filled, :pid, :seg, :seg_id
def initialize(filled=false, pid=nil, seg=nil, seg_id=0)
#filled = filled
#pid = pid.to_i
#seg = seg.to_s
#seg_id = seg_id.to_i
self
end
def fill(pid, seg, seg_id)
#filled = true; #pid = pid; #seg = seg; #seg_id = seg_id;
self
end
def clear
self.filled = false; self.pid = nil; self.seg = nil;
self
end
def filled?
#filled
end
def to_s
filled? ? "#{seg} #{seg_id} for pid #{pid}" : "Free"
end
end
class SimProc
include Enumerable
attr_accessor :pid, :code, :data
def initialize(pid, code, data)
#pid = pid.to_i
#code = code.to_i
#data = data.to_i
end
def each
yield :code, code
yield :data, data
end
def to_s
"[SimProc :pid => #{pid}, :code => #{code}, :data => #{data}]"
end
def to_a
[#pid, #code, #data]
end
end
class Manager
attr_reader :segments, :processes, :exec_list, :exec_object
def initialize
#exec_list = [[1, 2], [3, 4], [5, 6]]
#processes = {}
#segments = Array.new(8) { MemSegment.new }
end
def print_activity
#segments.each_with_index {|s, index| puts "Seg #{index} => #{s}" }
#processes.each_value {|s| puts s }
end
def load_process(pcb, exec_index)
if pcb.size == 3
p = SimProc.new(*pcb)
bad_load = false
#processes.store p.pid, p
#processes[p.pid].each do |proc_seg, bsize|
(bsize / 512.0).ceil.times do |seg_id|
#segments.each_with_index do |s, index|
if !s.filled
#find the first empty memory segment
s.fill p.pid, proc_seg, seg_id
break
# if all slots are filled and we couldn't place a proc block
elsif index == #segments.size - 1
bad_load = true
puts "Cannot find a place for #{proc_seg} segment of size #{bsize}. Requeueing..."
break;
end
end
break if bad_load
end
end
# recover pages and queue the process for later
if bad_load
#segments.each_with_index do |seg, seg_index|
# clear any segments that didn't get loaded properly
if seg.pid == p.pid
seg.clear
puts "Seg #{seg_index} => segment cleared: #{seg}"
end
end
# reinsert this process after the next in the execution list
# it will attempt to load and run after the next process is performed
#exec_list.insert(exec_index + 2, p.to_a)
end
print_activity
elsif pcb.size == 2 and pcb[1] == -1
# a process is exiting
puts "removing pid #{pcb[0]}"
#segments.each { |s| s.clear if s.pid == pcb[0] }
#processes.delete pcb[0]
print_activity
end
end
def set_exec_list(filename)
file = File.open filename
file.each { |pcb| #exec_list << pcb.split.map(&:to_i) } unless file.nil?
filename
end
def main
exseq = File.open('exseq2.txt')
set_exec_list exseq
# this is the object that will be used to run each process with .next
#exec_object = #exec_list.each_with_index
# #exec_list.each_with_index { |pcb, exec_index| load_process(pcb, exec_index) }
(#exec_list.size + 1).times do
load_process(*#exec_object.next)
end
end
end
=begin
manager = Manager.new
manager.main
=end
#=begin
Shoes.app(:title => "Paging Simulator", :width => 800, :height => 450) do
#manager = Manager.new
stack(:width => 200) do
#exec_list = stack {
title "Execution Queue", :size => 14
#exec_lines = para "click button to load", :size => 9
#file_button = button "Load Process List"
debugger
#file_button.click {
filename = ask_open_file
#manager.set_exec_list filename
# alert "this makes no sense"
# #exec_lines.text = #manager.exec_list
# #exec_lines.text = File.read filename
#exec_lines.text = #manager.exec_list.join "\n"
}
}
end
end
#=end
So, a few things:
#1, I don't have the implementation of Manager, so I can't tell you why it breaks. Did you try checking the Shoes console for any errors? Hit control-/ to bring that up. If 'nothing runs after it hits that line,' that's probably the issue.
#2, this does work for me, as long as you change exec_lines to #exec_lines on the last line. Here's what I tried:
class Manager;end
Shoes.app(:title => "Paging Simulator", :width => 800, :height => 450) do
#manager = Manager.new
stack(:width => 200) do
#exec_list = stack {
title "Execution Queue", :size => 14
#exec_lines = para "click button to load", :size => 9
#file_button = button "Load Process List"
#file_button.click {
filename = ask_open_file
#alert "this makes no sense"
#exec_lines.text = File.read filename
}
}
end
end
Hope that helps!
i have an action called individual user pdf per page, it communicates with two model, the user model which retrives information from the database, but specific information only for all users, it then takes the information and send it to the model report which, then when i inspect the attributes of each user it is displayed in way that i have not called it from the model user, it takes each pair and places it in a different order, so when i write information to a table on a pdf it displays it bad, like initials might be the last record of my table and cellphone might be the first one.. and i have to create a new page for each and every user's information..
my report code look's like:
def self.generate_individual_pdf(people,residents,user,estate = nil, title = nil)
pdf = PDF::Writer.new
title = !title ? "Report" :title
pdf.select_font "Times-Roman"
pdf.text "<em>Compiled By: #{user.presentation_name}</em> <em>Estate: #{estate.name}</em> <em>Created On: #{Time.now.strftime('%d/%m/%Y %H:%M')}</em>", :justification => :center
x = 0
25.times do
pdf.text ""+" "
x+=1
puts x
end
pdf.text "<em>User Report For: #{estate.name}</em> ", :font_size => 18, :bold => true, :justification => :center
if estate.logo
pdf.image "#{RAILS_ROOT}/public"+estate.logo.public_filename,:width => 100, :height => 100, :resize => 1.1, :justification => :center
end
y = 0
#loop people to create table for every person
people.each do |p|
pdf.start_new_page
pdf.text" "+p.Title.to_s+" "+p.Firstname.to_s+" "+p.Lastname.to_s, :font_size => 12, :justification => :center
pdf.text ""+" "
pdf.text ""+" "
pdf.text "Please verify if your details below are correct, and rectify them in the third column of the table.", :font_size => 8
pdf.text ""+" "
table = PDF::SimpleTable.new
table.column_order.push(*["head1","head2","head3"])
headings = ["head1","head2","head3"]
headings.each do |h|
table.columns[h] = PDF::SimpleTable::Column.new(h)
table.columns[h].heading = h.humanize
end
#display tables
table.show_headings = false
table.orientation = :right
table.position = :left
data = []
p.attributes.each_pair do |h|
data << { "head1" => h[0], "head2" => h[1], "head3" => " "}
end
table.data.replace data
table.render_on(pdf)
i = 0
28.times do
pdf.text ""+" "
i+=1
puts i
end
pdf.text "Kind Regards"+ " "+p.Title.to_s+" "+p.Firstname.to_s+" "+p.Lastname.to_s, :font_size => 11
pdf.text ""+" "
pdf.text "Signature "+"......................................."
end
then it produces the output when it loops throught the p.attribute.each_pair do |h|
data << {"head1" => h[0], "head2" => h[1], "head3" => " this should be nil "}
end
please guys i have been trying to use all this kind of sorting but i just cant get the right answer..anyone who can help please feel free.. thanks in advance.
Hashes in ruby are inherently unordered objects. You aren't guaranteed to get data out of them in the same order you put it in.
It sounds like the pairs of attributes are coming out in a random order (or at least not the order you want). One possible way of dealing with this is...
p.attributes.keys.sort do |key|
data << { "head1" => key, "head2" => p.attributes[key], "head3" => nil }
end
This will give you back all your attributes sorted alphabetically by value name
Ruby 1.9 will contain ordered hashes however if you are with 1.8 then take a look at the dictionary class from ruby facets. ( http://facets.rubyforge.org/doc/api/more/classes/Dictionary.html )
This helped me do this in the past.
Question: Does anyone have an example of a "filter as you type" dropdown control using Shoes?
Examples: If you are looking for examples of what i am talking about, see these.
http://docs.jquery.com/Plugins/Autocomplete
Jquery: Filter dropdown list as you type
I've never seen one in the wild. Here's the code for one I started working on a while back before getting distracted. It's extremely rough, but maybe you can take it from here:
class Dropdown < Widget
def initialize (list, opts = {})
#max_items = opts[:max_items] || 5
#min_letters = opts[:min_letters] || 3
#width = opts[:width] || 280
#fill = opts[:background] || white
#highlight = opts[:highlight] || yellow
#match_anywhere = opts[:match_anywhere].nil? ? true : opts[:match_anywhere]
#ignore_case = opts[:ignore_case].nil? ? true : opts[:ignore_case]
#entries = list
#text_box = edit_line :width => #width do |box|
if box.text.length >= #min_letters
update_list(box.text)
else
#list.clear if #list
#list = nil
end
end
end
def update_list(search)
search.downcase! if #ignore_case
choices = []
#entries.collect do |x|
temp = #ignore_case ? x.downcase : x
if #match_anywhere
choices << x if temp.include?(search)
else
choices << x if temp.index(search) == 0
end
break if choices.length == #max_items
end
#list.clear if #list
#list = nil
app.append do
#list = stack :width => #width do
background #fill
choices.each do |choice|
f = flow { para choice }
f.click do
#text_box.text = choice
#list.clear if #list
#list = nil
end
f.hover {|h| h.contents.first.fill = #highlight }
f.leave {|l| l.contents.first.fill = nil }
end
end
end unless choices.nil?
#list.move(0,0)
#list.displace(0, #text_box.height + #text_box.top)
end
end
Shoes.app(:width => 500, :height => 500) do
dropdown ['Breed', 'Green', 'Greetings', 'Phoning', 'Ponies', 'Reed', 'Trees'], {:max_items => 3}
para 'Ponies are awesome!'
para 'Bananas are yellow.'
para 'Sometimes I like to eat bananas, but never ponies.'
end
Try the "jquery options filter", based on real select box and matching in the mid of option texts:
http://plugins.jquery.com/project/jquery_options_filter
for diyism