this can be a very simple question but I have been searching for it a long time and haven't found any valid answer yet...
I am try to do a Shoes app with ruby to get a list of names and save it in a file. I would like to show the list that have been introduced already and as a new name enters the list the list which is shown would be updated. Also, I would like to associate a delete button to each element of the list so the user can remove that name if needed.
After all this, the only thing that I can get is the part in which you add a name and it gets saved into a list but adding this second stack with a list of all names with a delete button... not so lucky. This is how my code looks like, the functions addName and saveFile are not here but they only do some work with the list. Also, some global variables and constat definitions are not shown:
Shoes.app(title: "My Higgs field!",
width: APPW, height: APPH, resizable: false) {
stack{
flow{
#edit = edit_line
#enter = button "Add"
#enter.click do
info "Enter to add #{#edit.text}"
addName(#edit.text)
end
}
#save_file = button "Guardar ficheiro"
#save_file.click do
saveFile
end
}
stack do
# Show a list with all the names inserted
$names.each do |name|
flow{
#line = para name
info "Putting line #{#line}"
#delete = button "Apagar"
#delete.click do
deleteName name
end
}
end
end
}
Any inputs on this is very wellcome!
Thanks a lot!
A shoes question so long not answered ! That can't be.
Here a fully working version, i took the liberty to reorganise everything and use my favorite shoes color: green
require 'green_shoes'
Shoes.app(title: "My Higgs field!", width: 200, height: 200, resizable: false) do
#names, #list = [], nil
def update
#list.clear()
#names.each do |name|
#list.append do
flow do
para link('del'){del(name); update}, ' ', name
end
end
end
end
def add name
#names << name
#names = #names.uniq.sort
update
end
def del name
#names.delete(name)
update
end
stack do
flow do
#edit = edit_line(width: 110, height: 23)
keypress do|k|
if k == "\n"
add(#edit.text)
end
end
button "Add" do
add(#edit.text)
end
button "Save file" do
File.open("names.txt", "w+") { |file| file.write(#names.join("\n")) }
alert("names.txt is saved")
end
end
#list = flow do
#names.each do |p|
para p, :size => 10
end
end
end
end
Related
Using Shoes 3.3.7
How do I go about grabbing the text typed in an Edit_box and saving it to a file on a button click?
This is what i used. It created the file but it just stays empty...
Shoes.app do
Stack do
flow do
new_box = edit_box "placeholdertext"
end
flow do
button "Save" do
note_save = ask_save_file
File.open("#{note_save}", "a") do |copy|
copy.para "#{new_box.text}"
end
end
end
end
end
Edit : setting code to,
copy.write(new_box.text)
Still creates a file with empty content
I'm pretty new to all this. Any help is appreciated 😊
you have to use instance variables in such cases:
Shoes.app do
stack do
flow do
#new_box = edit_box "placeholdertext"
end
flow do
button "Save" do
note_save = ask_save_file
File.open("#{note_save}", "w") do |file|
file.write #new_box.text
end
end
end
end
end
Best, seba
If I don't use a thread, I can normally use #which_port.text.to_i in port declaration. If I use a thread, it looks like this statement #which_port.text.to_i don't work. I can only write port manually, e.g. 6000 and then my program works good. I have to use thread because my program freezes otherwise. Is any way able to use #which_port.text.to_i despite the use thread?
require 'socket'
require 'thread'
Shoes.app do
def write
#t = TCPSocket.new("xx.xx.xx.xx", #which_port.text.to_i)
loop do
msg = #t.recv(4096)
#pa1.text = #pa1.text + msg
end
end
#btn = button("button", width: 80, height: 50) do
window(left: 300, top: 300) do
#pa1 = para ""
#th1 = Thread.new { write }
end
end
#e_ln = edit_line(width: 320, height: 25, margin_top: 5)
#which_port = list_box :items => ["5000", "6000", "7000"],
:width => 120,
:choose => "5000" do |list|
end
end
end
Every time you launch a window you are launching a totally new app so variables in that new app are unknown to the other app, and those in the first app, the one that fired the window method are unknown to the new app ! Fortunately there is a owner method available in every Shoes app, in your case owner called inside the new app gives you a reference to the first app, the one that owns the new app !
One way to do what you want (Threading or not):
Shoes.app title: "Main Shoes app" do
def write(paragr)
msg = "#{self.inspect} === #{#which_port.text}"
paragr.text = paragr.text + msg
end
#btn = button("button", width: 80, height: 50) do
window(left: 300, top: 300, title: "Another Shoes app") do
#pa1 = para ""
Thread.new { owner.write(#pa1) }
end
end
#which_port = list_box :items => ["5000", "6000", "7000"],
:width => 120,
:choose => "5000" do |list|
end
end
You can also investigate the Shoes.APPS method which gives you back an array containing all opened app.
If this is Shoes 4 then it is very likely that the thread doesn't have access to the UI (only the main thread has access to the UI). You could try extracting the value beforehand and then passing it to write as an argument:
def write(port)
#...
end
port = #which_port.text.to_i
#th1 = Thread.new { write(port) }
I'm pretty new to ruby and am trying to implement a Tk application that will display a window prompting for input at a certain interval. In between the interval I want the window to not display in any taskbars, etc. and so I've implemented the following code that seems to work perfectly the first time through, but after the window displays the second time and I enter text in the TkEntry and click the TkButton the window is dismissed and never returns. I've tried putting in some "puts" calls at key locations to see what is happening and it seems that it never even makes it past the call to "displayUi".
*EDIT:
I'm running ruby 1.9.3p385 (2013-02-06) [i386-mingw32] on a Windows 7 system (in case that makes any difference)
Any help (even if it's providing a different mechanism to accomplish the same goal) would be appreciated, but please keep in mind that I'm a ruby noobie. Thanks!
require "tk"
class Sample
attr_accessor :root, :active
#active = false
def initialize
# init
end
def entry (task)
# do some work here
#active = false
end
def displayUi ()
#active = true
if (#root.nil?)
#root = TkRoot.new { title "Sample App" }
else
# already running just restart
Tk.restart
end
TkLabel.new(#root) {
text 'Sample Text'
pack { padx 15; pady 15; side 'left' }
}
statusInput = TkEntry.new(#root) {
pack('side'=>'left', 'padx'=>10, 'pady'=>10)
}
statusInput.focus
response = TkVariable.new
statusInput.textvariable = response
TkButton.new(#root, :text => "Ok", :command => proc { entry(response.value); #root.destroy }) {
pack('side'=>'left', 'padx'=>10, 'pady'=>10)
}
Tk.mainloop
end
end
i=0
st = Sample.new
while (true)
if (!st.active)
st.displayUi()
end
sleep(1)
end
In Shoes, I'd like to create a button that is initially hidden. I've tried passing :hidden => true as part of the button style, as well as calling #button.hide() after creating it, but it remains obstinately visible.
I've been able to work around this by not creating the button until I want it shown, but that requires checking to see if it already exists, rather than just using it.
Not at present. Buttons are still fairly unreliable in Shoes, especially on Windows. You can work around the issue by putting the button in a slot and hiding or showing the slot, but you may discover that the button won't hide again once it has been clicked once:
Shoes.app do
para 'This is some text.'
#flow = flow :width => 50, :hidden => true do
button 'a' do |btn|
alert 'Hello, World!'
end
end
button 'toggle' do
#flow.toggle
end
para 'Blah blah blah'
end
Luckily, there is a way out: slots. Slots can be given a click event, which makes them behave much as a button does. In fact, you can make fairly decent buttons out of slots.
Here's something I cobbled together. It lets you use the pesto_button method to generate buttons built on flows. You can modify it to fit your needs, including such things as using an image as the background, modifiable text (with auto-expanding width?), ability to change styles on the fly, etc:
class PestoButton < Widget
def initialize (text, opts = {})
#border_color = opts[:border_color] || gray
#border_width = opts[:border_width] || 3
#color = opts[:up_color] || gainsboro
#click_color = opts[:down_color] || #border_color
#width = opts[:width] || 80
#click = block_given? ? Proc.new { yield } : nil
#text = text
#visible = true
#flow = flow :width => #width do
background #color
border #border_color, :strokewidth => #border_width
para #text, :align => 'center'
end
#flow.click do
#flow.clear
#flow.append do
background #click_color
border #border_color, :strokewidth => #border_width
para #text, :align => 'center'
end
end
#flow.release do
#flow.clear
#flow.append do
background #color
border #border_color, :strokewidth => #border_width
para #text, :align => 'center'
#click.call if #click
end
end
end
def click
#click = block_given? ? Proc.new { yield } : nil
end
def show
#flow.show
end
def toggle
#flow.toggle
end
def hide
#flow.hide
end
end
Shoes.app do
para 'This is some text.'
#btn = pesto_button 'Click me!' do
alert 'Hello, World!'
end
button 'toggle' do
#btn.toggle
end
button 'new block' do
#btn.click do
alert 'Goodbye, World!'
end
end
button 'no block' do
#btn.click #Clears the click method
end
para 'Blah blah blah'
end
I want to use shoes to be able to download the body of a website that a user enters in an edit_line. How can I make this work? Can anyone help explain why the below code does not work? It just pops up a new window, and does not download the site from the text entered....
Here's my code thus far:
Shoes.app do
stack (:left => 175, :top => 200) do
para "Enter a url:"
flow do
#url = edit_line
button "OK" do
window do
stack do
title "Searching site", :size => 16
#status = para "One moment..."
# Search site for query and print body
download #url.text do |site|
#status.text = "Body: " + site.response.body.inspect
end
end
end
end
end
end
end
Nevermind - I figured it out. I just didn't pop to a new window and it downloads and prints the body fine. : )