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) }
Related
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
I have a ruby program that uses the TK gui package and I am having trouble with TkButton, specifically the command part. I am trying to run a method that's in the same class when the button is clicked. My code is below. I'm very new to Ruby but not programming in general. When the button is clicked in the gui I get an Application error that says "Error: NameError: unknown option..." where it identifies the method call in the button as the error cause. Can someone explain what I'm doing wrong? I'm using RubyMine to develop.
Code:
require 'tk'
require 'test/unit'
require_relative 'calc'
require_relative 'calcTest'
class CalcUIK
def test_add
calc = Calc.new
expected = Calc.add tk6.get().to_i,tk6.get().to_i
tk8['textvariable'] = 'Result: ' + expected
end
hello = TkRoot.new do
title "Hello World"
# the min size of window
minsize(400,400)
end
tk1 = TkLabel.new(hello) do
text 'Super Calculator'
foreground 'red'
pack { padx 15; pady 15; side 'left'}
end
tk5 = TkLabel.new(hello) do
text 'Enter two numbers to math'
foreground 'blue'
pack { padx 15; pady 15; side 'left'}
end
tk6 = TkEntry.new(hello) do
foreground 'blue'
pack { padx 15; pady 15; side 'left'}
end
tk7 = TkEntry.new(hello) do
foreground 'blue'
pack { padx 15; pady 15; side 'left'}
end
tk8 = TkLabel.new(hello) do
textvariable
foreground 'blue'
pack { padx 15; pady 15; side 'left'}
end
tk2 = TkButton.new(hello){
text 'Add'
command (proc {self.test_add})
pack('padx'=>'20')
pack('side'=>'left')
}
end
Tk.mainloop
PS I know that this code is kind of dumb but it is just a dummy program to set some more important things up. The issue I need to resolve is why the button click is not executing the test_add method. Thanks.
You've defined an instance method on your class called test_add, but in the context in which your "Add" button is defined, self refers to the class CalcUIK. Edit - now that I think about it, I think since self occurs within a block given to TkButton.new, self refers to the new instance of TkButton, which is trying to receive the test_add method, but doesn't recognize it as a valid option. Define the proc as proc { CalcUIK.new.test_add } to generate a new instance that will run the method.
I would also recommend moving all of the TkRoot, TkButton, etc. calls either outside the class entirely, or move them into an initialize method on CalcUIK. Right now, they are run as the class is evaluated, but it's very unusual to have code that is unrelated to the class executed within the context of the class definition.
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
I've got a problem when using Shoes. I'm basically trying to open an excel document and pass the names of the worksheets to a list_box. The following method is called on a button press after selecting a file. (This all works and the file opens)
exc = WIN32OLE::new('excel.Application')
excWB = exc.Workbooks.Open(xlsFile)
#excWS = Array::new
exc.visible = true
excWB.Worksheets.each { |ws| #excWS.push(ws.name) }
para #excWS
list_box :items=> #excWS
Not only do the names not show up in the list_box, the app crashes shortly after loading the box with no error. para #excWS shows the names of the worksheets with no problem.
What am I doing wrong?
it is the encoding that was the problem
This works
Shoes.app :width => 400, :height => 340, :size => 8 do
require 'win32ole'
exc = WIN32OLE::new('excel.Application')
excWB = exc.Workbooks.Open('C:/Shoes/0.r1514/test/book1.xls')
#excWS = Array::new
exc.visible = false
excWB.Worksheets.each { |ws| #excWS.push(ws.name.force_encoding("UTF-8")) }
list_box :items=> #excWS
exc.ActiveWorkbook.Close(0);
exc.Quit();
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