Ruby + Tk Command Binding - Scope Issue

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:

First

Second

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



Leave a reply



Submit