Ruby + Tk command binding - scope issue?
If you put a
p self
into the do ... end
block of your code, then you'll probably find out that the current scope is different than your Foo
object.
Ruby / TK button event handler code not firing as expected
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.
Access outside scope when using Class.new
Even though @MarekLipka answer is correct - it is always risky to change variable scope. This works because each block carries the context it has been created in and hence your local variable a
suddenly is not that local - it became a 'hidden' global:
a = 5
object = Class.new { define_method('b') { a } }.new
object.b #=> 5
a = 4
object.b #=> 4
This is naturally useful if you want to pass non-duplicable variable by reference (this actually works very alike javascript closures), however in most of the cases it will introduce extremely hard to debug bugs:
a = 5
object = Class.new { define_method('b') { a = a + 1 } }.new
object.b
a #=> 6
It is much cleaner to use instance variable instead:
a = 5
Class.new do
def initialize(a)
@a = a
end
def b
@a
end
end.new(a).b #=> 5
How to create a Calendar widget in Ruby TK
This answer will install the latest release of Ruby (at the time of writing), along with the latest compatible version of Tcl.
Note: after installing Ruby 2.7.0, $ gem install tk
says "Tcl/Tk8.6 is not supported[;] it will not work correctly." So, instead we must limit our use of Tcl to version 8.5. We will do this by installing ActiveTcl version 8.5.
These steps are for Debian Stretch—so, for Fedora 31, YMMV. :)
Create some directories:
$ mkdir ~/install
$ mkdir ~/install/temp
$ mkdir ~/progra
Using a web browser, download ActiveTcl 8.5 from ActiveState. Then, install it:
$ pushd ~/install/temp
$ tar zxf ~/Downloads/ActiveTcl-8.5*.tar.gz
$ cd ActiveTcl-8.5*
$ ./install.sh
Answer its installation questions:
Please specify the installation directory.
Path [/opt/ActiveTcl-8.5]: ~/progra/ActiveTcl-8.5
Please specify the directory for the demos.
Path [~/progra/ActiveTcl-8.5/demos]:
Please specify the runtime installation directory.
Path [~/progra/ActiveTcl-8.5]:
$ echo 'export PATH="$HOME/progra/ActiveTcl-8.5/bin:$PATH"' >> ~/.bashrc
$ echo 'export MANPATH="$HOME/progra/ActiveTcl-8.5/bin/man:$MANPATH"' >> ~/.bashrc
Install some system packages which are required by rvm
:
$ sudo apt-get install curl dirmngr gnupg
Install rvm (the Ruby enVironment Manager) by following these steps, according to its instructions:
$ \curl -sSL https://get.rvm.io | bash -s -- --ignore-dotfiles
$ echo '# Add RVM to PATH for scripting. Make sure this is the last PATH variable change.' >> ~/.bashrc
$ echo 'export PATH="$PATH:$HOME/.rvm/bin"' >> ~/.bashrc
$ echo '[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm" # Load RVM into a shell session *as a function*' >> ~/.bashrc
$ exit
Check and use rvm
:
$ type rvm | head -n 1 # It should say, 'rvm is a function'.
$ rvm list known
$ rvm install 2.7.0 --enable-shared --enable-pthread --with-tk --with-tcl
Install a system package required by the Tk gem:
$ sudo apt-get install libx11-dev
Install the Tk gem and check the Tk installation:
$ gem install tk
$ ruby -W0 -e "require 'tk'; p Tk::TK_PATCHLEVEL"
$ ruby -W0 -e "require 'tk'; require 'tkextlib/iwidgets'; p 'ok'"
Now, when I run your program, I see a calendar widget.
Tkinter global modules
It's hard to define what is the "best", it depends many things. I have written a possible solution for you. It's an OOP approach and it doesn't contain global variables/function (Of course, except the my_app
variable).
The code contains many comment for the better understanding.
Code:
"""
Example application for TkInter.
"""
from tkinter import Tk, ttk, Entry
class MyApp(Tk):
"""
The MyApp class is inherited from Tk from tkinter package.
"""
# These are class variables. You can use them as "global variables" inside the class.
# Perhaps you can put them to "__init__" method as well then they are instance variables.
# The proper location of these variables depend on the usage of them.
inp_f = None
bt1 = None
def __init__(self):
"""
This is the constructor of the "MyApp" class. It's called when you create an instance from
the class.
Eg.:
my_app = MyApp()
"""
# You have to call the "__init__" method of the super class (Tk). There is not input
# of the __init__ method so you shouldn't pass anything.
super().__init__()
# Set the required configurations. You can use the "self" to access the attributes of Tk.
self.grid()
self.geometry("200x100")
self.bind("<Return>", self.func1)
self.create_widgets()
def func(self):
"""
Your function (Otherwise it's method).
Returns: None
"""
# You can access to class variables with name of your class (MyApp)
MyApp.bt1.destroy()
MyApp.inp_f = Entry(self)
MyApp.inp_f.grid(padx=38, pady=20)
@staticmethod
def func1(_): # I use "_" as input parameter because it's not used in the method.
"""
Your function
Args:
_: This is the event input.
Returns: None
"""
try:
print(str(MyApp.inp_f.get()))
except Exception: # It's a too broad exception.
print("Button not already attributed")
def create_widgets(self):
"""
Creating the widgets on the window.
Returns: None
"""
MyApp.bt1 = ttk.Button(self, command=self.func, text="Login")
MyApp.bt1.grid(padx=60, pady=20)
# Create an instance from your class.
my_app = MyApp()
# Call the "mainloop" method of our class.
my_app.mainloop()
Console:
>>> python3 test.py
Button not already attributed
Button not already attributed
foo
foo
GUI:
Why does my tkinter app have an extraneous 'tk' window?
Too much code, too many questions. This should be three separate questions with a small bit of code for each. Read and act on this SO help page.
Lets make this question about the extraneous little window labelled 'tk'. You are assuming that it has something to do with introframe
, but you don't know that. If you had developed your code incrementally, testing as you go, you would have noticed which addition made it appear. Given what you have, delete until the problem disappears.
Here is what I did. I commented out everything between setup_root
and root.mainloop
. Still got two windows. Removing more, it turns out that style() creates a blank tk
window if there is not one already. If you had run you code with that one line in the main clause, you would have seen the problem immediately. Create root first and the problem disappears.
I used 3.5. Use something newer than 3.2 if you can with RPy.
How to programmatically iterate datagrid rows?
foreach(var row in DataGrid1.Rows)
{
DoStuff(row);
}
//Or ---------------------------------------------
foreach(DataGridRow row in DataGrid1.Rows)
{
DoStuff(row);
}
//Or ---------------------------------------------
for(int i = 0; i< DataGrid1.Rows.Count - 1; i++)
{
DoStuff(DataGrid1.Rows[i]);
}
Related Topics
Upsert Multiple Records with Mongodb
Rails - Multi Tenant Application with Customization Framework
Having Difficulty Accessing Validation Errors in Sinatra
Overriding Model in Gem, Adding Callback and Methods
How to Build Ruby 2.1.3 on Osx 10.10 Gm 3.0 with Rbenv
State MAChine, Model Validations and Rspec
Grit's Clone Method Is Undefined
Typeerror: Object Doesn't Support This Property or Method
Why Is the Splat Used Inside an Array Definition Here
Problem Running Thinking Sphinx with Rails 2.3.5
How to Access Current_User Object in Model
How to Execute 2 or More Commands in the Same Ssh Session
Include Erb Delimiters Inside of a String in an Erb Block