Python Forum

Full Version: Class Struggle: Connecting a Method to an Outside Variable
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
First off, be warned: I am a hopeless newbie within the realms of python with just shy of two months of experience thus far.

I am making slow progress, however, and normally I can manage working out the bugs and assorted snags if I just put a little work into researching.

In this case, however, I'm somewhat stuck as I cannot wrap my head around what would otherwise appear to be a simple operation.
During the past week it has grown into some variation of 'code blindness'; the further I've been digging, the worse the code has gotten*, so now I turn to you, the good people of python-forum.

I would like to somehow connect 'myVar' to 'button_1', 'button_2' and 'button_3' in the method shown below.
I've made a v1.0 of the program (a weather station that scrapes the web for a forecast and assorted data) which works witout using classes, but then I thought that it would be a good exercise to implement classes. Oh, Icarus.

I've omitted the rest of the code as I don't think it bears relevance, but will upload the entire script if needed, of course; just let me know, please.

Thank you for taking the time to read through this post :)


from tkinter import *


class lbl(object):
    def __init__(self, pos, txt, row, col, colspan):
        self.position = pos
        self.text = txt
        self.row = row
        self.column = col
        self.columnspan = colspan

        myVar = Label(self.position, self.text)
        myVar.grid(self.row, self.column, self.columnspan)

root = Tk()
root.title("Vejrstation 1.0")
root.geometry("350x350")
#root.resizable(True, True)
root.resizable(False, False)

label_1 = lbl(root, "temp.", 0, 0, 1)
label_2 = lbl(root, "fugt", 0, 1, 1)
label_3 = lbl(root, "vind", 0, 2, 1)


root.mainloop()
* I would supply some of my more horrendous attempts and ideas for the shere comedy value, but let's keep it concise for now, eh? ;)
(Jan-21-2018, 06:40 PM)vulpesVelox Wrote: [ -> ]I would like to somehow connect 'myVar' to 'button_1', 'button_2' and 'button_3' in the method shown below.
You want the bind() method:
myVar.bind('Button-1', myMethod)
(Jan-21-2018, 09:07 PM)Gribouillis Wrote: [ -> ]You want the bind() method:
myVar.bind('Button-1', myMethod)
I've tried to insert your suggestion in a couple of different ways now, but all attempts result in either:

NameError: name 'myVar' is not defined
or:
NameError: name 'label_1' is not defined

I did a bit of reading on the subject, and it turns out that 'Button-1' is a fixed expression within the realm of the bind method, hence it wont bind to a label or a text field or any object which is not a button in tkinter. - Right? Shifty

Thank you all the same; now I know there's a method called bind. Any type of progress is good progress :)

For clarity, here's version 1 (without using classes). This one works just fine.

from tkinter import *
import requests
from bs4 import BeautifulSoup
import re

#-------------------------------#
# - request module - Forecast - #
#-------------------------------#

data1 = requests.get('https://www.dmi.dk/vejr/til-lands/byvejr/by/vis/DK/1000/K%C3%B8benhavn,%20Danmark/')
soup = BeautifulSoup(data1.text, 'html.parser')
#print(soup)
data2 = soup.find('div',{'id':'NJ'})
#print(data2)
data3 = data2.find_all('p')
#print(data3)
data4 = data3[2]

#-------------------------------#
#       - request modules -     #
#-------------------------------#

blabla = requests.get('https://www.dmi.dk/vejr/til-lands/regionaludsigten/kbhnsjaelland/')
toast = BeautifulSoup(blabla.text, 'html.parser')
#print(toast)
bleble = toast.find('div', {'id':'c5886'})
#print(bleble)
blibli = bleble.find_all('td')
#print(blibli)

#temp
bloblo = blibli[2]
#print(bloblo.string[0:6])

#fugt
blublu = blibli[3]
#print(blublu.string[0:5])

#vind
blybly = blibli[5]
#print(blybly.string[0:5])

#solopgang
blaxblax = toast.find('div', {'id':'c3065'})
#print(blaxblax)
blexblex = blaxblax.find_all('p')
#print(blexblex)

blixblix = str(blexblex[2])
#print(blixblix)
#print(type(blixblix))

bloxblox = re.search('solnedgang: ', blixblix)
#print(str(bloxblox))
start = bloxblox.end()
end = start + 10
#print(blixblix[start:end])

#-------------------------------#
#       - tkinter window -      #
#-------------------------------#

root = Tk()
root.title("Vejrstation 1.0")
root.geometry("350x360")
root.resizable(False, False)
#root.resizable(False, False)

#-------------------------------#
#        - button funcs -       #
#-------------------------------#

def butt1_func():
    print("it's alive.")

def butt2_func():
    print("This one is also alive.")

def butt3_func():
    quit()

#-------------------------------#
# - labels, buttons and text -  #
#-------------------------------#

"""smølfe om til classes, måske?"""

label_1 = Label(root, text="Temp.")
label_1.grid(row=0, column=0)
label_1 = Label(root, text="Fugt.")
label_1.grid(row=0, column=1)
label_1 = Label(root, text="Vind")
label_1.grid(row=0, column=2)

textbox_1 = Text(root, height=1,width=10)
textbox_1.grid(row=1, column=0)
textbox_1.insert(END, bloblo.string[0:6])

textbox_1 = Text(root, height=1,width=10)
textbox_1.grid(row=1, column=1)
textbox_1.insert(END, blublu.string[0:5])

textbox_1 = Text(root, height=1,width=10)
textbox_1.grid(row=1, column=2)
textbox_1.insert(END, blybly.string[0:6])

#spacer
spacer = Label(root)
spacer.grid(row=2)

label_3 = Label(root, text="Solopgang/solnedgang")
label_3.grid(row=3, column=0, columnspan=1)

textbox_3 = Text(root, height=1, width=10)
textbox_3.grid(row=4, column=0, columnspan=1)
textbox_3.insert(END, blixblix[start:end])

#spacer
spacer = Label(root)
spacer.grid(row=5)

label_2 = Label(root, text="DMI Vejrudsigt")
label_2.grid(row=6, column=0, columnspan=3)

Scroller = Scrollbar(root)
textbox_2 = Text(root, height=10, width=49, wrap=WORD)
Scroller.grid(row=7, column=0)
textbox_2.grid(row=7, column=0, columnspan=3)
Scroller.config(command=textbox_2.yview)
textbox_2.config(yscrollcommand=Scroller.set)
textbox_2.insert(END, data4.string)

#spacer
spacer = Label(root)
spacer.grid(row=8)

label_2 = Label(root, text="Nogle forskellige knapper")
label_2.grid(row=9, column=0, columnspan=3)

button_1 = Button(root, text="SEND!", command=butt1_func)
button_1.grid(row=10, column=0, sticky="NWNESWSE")
button_2 = Button(root, text="ASSIGN!", command=butt2_func)
button_2.grid(row=10, column=1, sticky="NWNESWSE")
button_3 = Button(root, text="ABORT!", command=butt3_func)
button_3.grid(row=10, column=2, sticky="NWNESWSE")

#-------------------------------#
#       - run that sucka! -     #
#-------------------------------#

root.mainloop()
And here's version 2 in which I would like to create classes for the elements involved (doesn't work; computer says nooooo!):

from tkinter import *
import requests
from bs4 import BeautifulSoup
import re

#-------------------------------#
# - request module - Forecast - #
#-------------------------------#

data1 = requests.get('https://www.dmi.dk/vejr/til-lands/byvejr/by/vis/DK/1000/K%C3%B8benhavn,%20Danmark/')
soup = BeautifulSoup(data1.text, 'html.parser')
#print(soup)
data2 = soup.find('div',{'id':'NJ'})
#print(data2)
data3 = data2.find_all('p')
#print(data3)
data4 = data3[2]

#-------------------------------#
#   - other request modules -   #
#-------------------------------#

blabla = requests.get('https://www.dmi.dk/vejr/til-lands/regionaludsigten/kbhnsjaelland/')
toast = BeautifulSoup(blabla.text, 'html.parser')
#print(toast)
bleble = toast.find('div', {'id':'c5886'})
#print(bleble)
blibli = bleble.find_all('td')
#print(blibli)

#temp
bloblo = blibli[2]
#print(bloblo.string[0:6])

#fugt
blublu = blibli[3]
#print(blublu.string[0:5])

#vind
blybly = blibli[5]
#print(blybly.string[0:5])

#solopgang
blaxblax = toast.find('div', {'id':'c3065'})
#print(blaxblax)
blexblex = blaxblax.find_all('p')
#print(blexblex)

blixblix = str(blexblex[2])
#print(blixblix)
#print(type(blixblix))

bloxblox = re.search('solnedgang: ', blixblix)
#print(str(bloxblox))
start = bloxblox.end()
end = start + 10
#print(blixblix[start:end])

#-------------------------------#
#       - tkinter window -      #
#-------------------------------#

root = Tk()
root.title("Vejrstation 1.0")
root.geometry("350x350")
#root.resizable(True, True)
root.resizable(False, False)

#-------------------------------#
#        - button funcs -       #
#-------------------------------#

def butt1_func():
    print("it's alive.")

def butt2_func():
    print("This one is also alive.")

def butt3_func():
    quit()

#-------------------------------#
#         - classes -           #
#-------------------------------#

class lbl(object):
    def __init__(self, pos, txt, row, col, colspan):
        self.position = pos
        self.text = txt
        self.row = row
        self.column = col
        self.columnspan = colspan

        myVar = Label(self.position, self.text)
        myVar.grid(self.row, self.column, self.columnspan)

class spcr(object):
    def __init__(self, pos, row, col, colspan):
        self.position = pos
        self.row = row
        self.column = col
        self.columnspan = colspan

        myVar = Label(self.position)
        myVar.grid(self.row, self.column, self.columnspan)

class txtbox(object):
    def __init(self, pos, height, width, wrap, txt, row, col, colspan):
        self.position = pos
        self.height = height
        self.width = width
        self.text = txt
        self.row = row
        self.column = col
        self.columnspan = colspan
        self.wrap = wrap

        myVar = Text(self.position, self.height, self.width, self.wrap)
        myVar.grid(self.row, self.column, self.columnspan)
        myVar.insert(END, self.text)

class btn(object):
    def __init__(self, pos, txt, cmd, row, col, colspan, sticky):
        self.position = pos
        self.text = txt
        self.command = cmd
        self.row = row
        self.column = col
        self.columnspan = colspan
        self.sticky = sticky

        myVar = Button(self.position, self.text, self.cmd)
        myVar.grid(self.row, self.column, self.columnspan, self.sticky)

label_1 = lbl(root, "temp.", 0, 0, 1)
label_2 = lbl(root, "fugt", 0, 1, 1)
label_3 = lbl(root, "vind", 0, 2, 1)

textbox_1 = txtbox(root, 1, 10, None, 1, 0, 1)
textbox_2 = txtbox(root, 1, 10, None, 1, 1, 1)
textbox_3 = txtbox(root, 1, 10, None, 1, 2, 1)

spacer_1 = spcr(root, 2, 0, 3)
label_4 = lbl(root, "solopgang/solnedgang", 3, 0, 1)
textbox_4 = txtbox(root, 1, 10, None, 4, 0, 1)

spacer_2 = spcr(root, 5, 0, 3)
label_5 = lbl(root, "DMI vejrudsigt", 6, 0, 3)
textbox_5 = txtbox(root, 10, 49, None, 7, 0, 3)

spacer_3 = spcr(root, 8, 0, 3)
label_6 = lbl(root, "Nogle forskellige knapper", 9, 0, 3)

button_1 = btn(root, "TRANSMIT!", butt1_func, 10, 0, 1, "NWNESWSE")
button_2 = btn(root, "UPDATE!", butt2_func, 10, 1, 1, "NWNESWSE")
button_3 = btn(root, "ABORT!", butt3_func, 10, 2, 1, "NWNESWSE")


#-------------------------------#
#       - run that sucka! -     #
#-------------------------------#

root.mainloop()
I'm not sure it's a good idea to remove the explicit keywords when calling tkinter's methods, because the order of the arguments may be incorrect. Also you may need to keep a reference to the object you created in the init method, for example
class btn(object):
    def __init__(self, pos, txt, cmd, row, col, colspan, sticky):
        self.position = pos
        self.text = txt
        self.command = cmd
        self.row = row
        self.column = col
        self.columnspan = colspan
        self.sticky = sticky
 
        self.myVar = Button(self.position, text=self.text, command=self.cmd)
        self.myVar.grid(row=self.row, column=self.column, columnspan=self.columnspan, sticky=self.sticky)
Concerning myVar.bind('Button-1', myMethod), this call binds the event 'Button-1', which is the left mouse button event to the method myMethod for the tkinter widget myVar. Of course you can use this call only if there are a widget and a method named myVar and myMethod in the scope of the call.
using myVar as a name for a widget is misleading.
the textvariable attribute should point to a tk.StringVar which you can preset using
that attribute and a tk.StringVar.
The StringVar can be set externally with string_var_name.set() (replace string_var_name with actual name)
It can be initally be set within the button definition
string_var_name = StringVar()
self.myVar = Button(self.position, text=self.text, textvariable=string_var_name, command=self.cmd)
Again, rename myVar to a meaningful widget name.
Also to be in conformance with PEP8, don't use camel case, use underscore instead
see: http://effbot.org/tkinterbook/variable.htm

Sorry for the edits, just got up
(Jan-23-2018, 06:52 AM)Gribouillis Wrote: [ -> ]I'm not sure it's a good idea to remove the explicit keywords when calling tkinter's methods, because the order of the arguments may be incorrect. Also you may need to keep a reference to the object you created in the init method, for example
class btn(object):
    def __init__(self, pos, txt, cmd, row, col, colspan, sticky):
        self.position = pos
        self.text = txt
        self.command = cmd
        self.row = row
        self.column = col
        self.columnspan = colspan
        self.sticky = sticky
 
        self.myVar = Button(self.position, text=self.text, command=self.cmd)
        self.myVar.grid(row=self.row, column=self.column, columnspan=self.columnspan, sticky=self.sticky)
Concerning myVar.bind('Button-1', myMethod), this call binds the event 'Button-1', which is the left mouse button event to the method myMethod for the tkinter widget myVar. Of course you can use this call only if there are a widget and a method named myVar and myMethod in the scope of the call.

The explicit order of the args is addressed via the grid args, isn't it? Am I to understand that version 1 is more correct than version 2?

(Jan-23-2018, 10:53 AM)Larz60+ Wrote: [ -> ]using myVar as a name for a widget is misleading.
the textvariable attribute should point to a tk.StringVar which you can preset using
that attribute and a tk.StringVar.
The StringVar can be set externally with string_var_name.set() (replace string_var_name with actual name)
It can be initally be set within the button definition
string_var_name = StringVar()
self.myVar = Button(self.position, text=self.text, textvariable=string_var_name, command=self.cmd)
Again, rename myVar to a meaningful widget name.
Also to be in conformance with PEP8, don't use camel case, use underscore instead
see: http://effbot.org/tkinterbook/variable.htm

Sorry for the edits, just got up

Going through PEP8 documentation and getting all those things right is a 2nd priority for the time being. It wouldn't make much sense sifting through that information if you basically don't know how to write code; sort of like learning grammar theory without knowing how to read/spell.
It is in the post, however, and I do understand what you're trying to convey. And I'm trying to adhere whilst unlearning/modifying what I know from ActionScript.

"myVar" doesn't belong. I think. It's there for lack of an alternative method (what I'm looking for), but we could call it whatever you would prefer as far as I'm concerned. "myString", "myTempWord", "Knudpovlsen" or even "Giraffe".
(Jan-23-2018, 03:18 PM)vulpesVelox Wrote: [ -> ]The explicit order of the args is addressed via the grid args, isn't it?
The signature of the grid method is
>>> inspect.signature(tkinter.Button.grid)
<Signature (self, cnf={}, **kw)>
The documentation doesn't suggest a particular order for the arguments, so yes the keywords are needed.

(Jan-23-2018, 03:18 PM)vulpesVelox Wrote: [ -> ]"myVar" doesn't belong. I think. It's there for lack of an alternative method
I think myVar is indeed misleading in a tkinter program if the value is not a tkinter variable.
I had my mate go over it with me, and the conclusion is, that it would probably be more effective if I let it go for now and focus on learning different things.

Thank you all the same, gentlemen, very kind of you :)