[Tutor] Fwd: Re: Dynamic variables changed via Tkinter button
Alan Gauld
alan.gauld at yahoo.co.uk
Sun Apr 19 10:11:34 EDT 2020
Forwarding to list
-------- Forwarded Message --------
Subject: Re: [Tutor] Dynamic variables changed via Tkinter button
Date: Sun, 19 Apr 2020 11:57:47 +0000
From: Jon Davies <jon_davies17 at hotmail.co.uk>
To: Alan Gauld <alan.gauld at yahoo.co.uk>, Peter Otten
Hi Peter, Alan,
Thanks for your both of your responses. Having reviewed your responses,
I have now made the respective changes and I have a working solution!
* I've now removed the duplicate fuelprice counter, and based the
label on a calculation using fuelcount * currency * fuel_price
* As you both mentioned, it seemed that I'd forgot to include
controller as as part of self.controller.currency, that seemed to do
the trick
* Also simplified the SelectCurrencyPage by introducing a single
"makeCurrencyButtons" based on the list of CURRENCIES outlined in
the constants (which I've now included at the beginning of my code).
This definitely makes sense, as it makes expansion to provide more
currencies so much simpler
* The set currency function now seems to work, and through testing the
fuel_lever, the fuel litres versus the price (based on the
calculations) are correct, which is great!
CURRENCIES = [("GBP", "£", 1.00), ("EUR", "€", 1.14), ("USD", "$", 1.24)]
class SelectCurrencyPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.currency_lbl = tk.Label(self, text = _("PLEASE SELECT CURRENCY"), font = LARGE_FONT)
self.currency_lbl.pack(pady = 10, padx = 10)
self.currency_buttons = [
self.makeCurrencyButton(*currency)
for currency in CURRENCIES
]
self.restart_btn = tk.Button(self, text = _("RESTART"),
command = lambda: self.controller.setState('restart'))
self.restart_btn.pack()
def makeCurrencyButton(self, code, symbol, conversion):
currency_button = tk.Button(
self,
text=f"{symbol} {code}",
command=lambda: self.setCurrency(conversion)
)
currency_button.pack()
return currency_button
def setCurrency(self, conversion):
self.controller.setCurrency(conversion)
self.controller.setState("fuel")
I suppose my only question now, to seal the deal, is how can I display
the selected currency symbol as part of the price label in the Fuelling
page? While I can verify that the currency selection/conversion is
working through looking at the actual numbers that are presented,
showing the actual currency symbol would make it a lot more evident.
I'd assumed it would be something like the below (having taken
inspiration from the def MakeCurrencyButton above)
self.litre_lbl = tk.Label(self, text = _("Fuel = 0"))
self.litre_lbl.pack(pady = 10, padx = 10)
self.price_lbl = tk.Label(self, text = _(f"{symbol}" + "Price = 0"))
self.price_lbl.pack(pady = 10, padx = 10)
def update(self, symbol):
self.fuelcount += 0.1
total_price = self.fuelcount * self.controller.currency * PETROL_PRICE
self.litre_lbl['text'] = _("Litres = %f") % self.fuelcount
self.price_lbl['text'] = _(f"{symbol}" + "Price = %f") % total_price
self.update_idletasks()
if self.state == 'working':
self.lever_btn.after(100, self.update)
But the main thing is, the functionality that I required from the start
has now finally been implemented (translation and currency conversion),
so this is a huge milestone for me and I can now look at tweaking the
design parts. I could not have done it without your help so much
appreciated as always.
Kind regards,
------------------------------------------------------------------------
*From:* Tutor <tutor-bounces+jon_davies17=hotmail.co.uk at python.org> on
behalf of Alan Gauld via Tutor <tutor at python.org>
*Sent:* 19 April 2020 12:08
*To:* tutor at python.org <tutor at python.org>
*Subject:* Re: [Tutor] Dynamic variables changed via Tkinter button
On 18/04/2020 15:22, Jon Davies wrote:
> Given I need two labels (one for Fuel Price, one for Litres), I've implemented two counts
> (fuelcount and pricecount) and separated these within the "update" function. For the pricecount,
> I am looking to enable an increase based on "fuelcount * (currency * petrol_price)".
The currency*price value only needs to be calculated one when the
currency is selected. So in your selectCurrency event handler you should
set the currency symbol and a fuel_rate attribute in your controller
class. Then in the fuelling update method use fetch the rate from the
controller and do the multiplication using that rate.
See below for what I've tried to implement.
>
> def update(self):
> self.fuelcount += 0.1
> self.pricecount += 0.1 * (self.currency * PETROL_PRICE)
The price should not increment it should be calculated.
self.price = self.controller.fuel_rate * self.fuelcount
> self.litre_lbl['text'] = "Litres = %f" % self.fuelcount
> self.price_lbl['text'] = "Price = %f" % self.price
> self.update_idletasks()
> if self.state == 'working':
> self.lever_btn.after(100, self.update)
> The code doesn't seem to recognise self.currency (starting that
> AttributeError: 'BeginFuellingPage' object has no attribute 'currency').
Where did you set the currency? Was it in the fuelling object or in
another class instance? Any data that you want to share between your
objects will need to go into the controller object since it is the
only thing that all the other objects can see. (This is one reason I
don't like this style of GUI design, the controller becomes the owner
of lots of data that should really be in the other objects.)
> I am trying to reference the currency variable that was set earlier on in the GUI via the SelectCurrencyPage.
> class SelectCurrencyPage(tk.Frame):
> def __init__(self, parent, controller):
> tk.Frame.__init__(self, parent)
> self.controller = controller
>
> self.currency_lbl = tk.Label(self, text = _("PLEASE SELECT CURRENCY"), font = LARGE_FONT)
> self.currency_lbl.pack(pady = 10, padx = 10)
>
> self.GBP_btn = tk.Button(self, text = "£ GBP",
> command = lambda c = "£": self.setGBP(c))
> self.GBP_btn.pack()
>
> self.USD_btn = tk.Button(self, text = "$ USD",
> command = lambda c = "$": self.setUSD(c))
> self.USD_btn.pack()
>
> self.EUR_btn = tk.Button(self, text = "€ EUR",
> command = lambda c = "€": self.setEUR(c))
> self.EUR_btn.pack()
As Peter has pointed out you don't need or want lots of separate methods
to set currency. You just want one that gets called with the appropriate
currency. That method is the one that should notify the controller of
the selected currency and the exchange_rate. The controller can then
calculate the fuel_price based on its
base rate * exchange_rate.
In an ideal world the knowledge about currency and rates would stay in
the Currency object and the controller methods would query it from
there. So you would have a get_exchange_details() method in the
controller, called by the Fuelling object. And that method would fetch
the currency symbol and exchange rate from the currency object.
But that frankly complicates matters, so for now just put the data
in the controller...
--
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos
_______________________________________________
Tutor maillist - Tutor at python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
------------------------------------------------------------------------
*From:* Tutor <tutor-bounces+jon_davies17=hotmail.co.uk at python.org> on
behalf of Peter Otten <__peter__ at web.de>
*Sent:* 18 April 2020 16:52
*To:* tutor at python.org <tutor at python.org>
*Subject:* Re: [Tutor] Dynamic variables changed via Tkinter button
Jon Davies wrote:
> Hi Alan,
>
> As always, thanks for your insight - that gives me exactly the sort of
> base I was hoping to build on. I had researched on the web on whether it
> was possible, but given your note that typically this wouldn't considered
> as standard GUI behavior, explains why I maybe didn't find much (or
> perhaps I didn't look hard enough!).
>
> I've now adapted the below and implemented something similar as part of my
> wider Petrol Pump program code and I've managed to get it functioning.
>
> However, now I am looking to enhance this further (using the selected
> currency as input to a calculation, which you are already aware of).
>
> Given I need two labels (one for Fuel Price, one for Litres), I've
> implemented two counts (fuelcount and pricecount) and separated these
> within the "update" function. For the pricecount, I am looking to enable
> an increase based on "fuelcount * (currency * petrol_price)". See below
> for what I've tried to implement.
>
> def update(self):
> self.fuelcount += 0.1
> self.pricecount += 0.1 * (self.currency * PETROL_PRICE)
> self.litre_lbl['text'] = "Litres = %f" % self.fuelcount
> self.price_lbl['text'] = "Price = %f" % self.pricecount
Don't use two independent counters; count the litres of fuel only, and
calculate the price from that:
price = self.fuelcount * self.currency * PETROL_PRICE
self.price_lbl["text"] = "Price = %f" % price
(I'm not sure, but self.currency might actually have to be
self.controller.currency)
> self.update_idletasks()
> if self.state == 'working':
> self.lever_btn.after(100, self.update)
>
> I tried to configure the calculation to use the fuelcount value directly
> i.e. (0.1), but I found that this started to increase the pricecount
> exponentially, so for now I have just aligned the values at 0.1. The code
> doesn't seem to recognise self.currency (starting that AttributeError:
> 'BeginFuellingPage' object has no attribute 'currency'). If I replaced
> self.currency with a number, then it works.
>
> I am trying to reference the currency variable that was set earlier on in
> the GUI via the SelectCurrencyPage. class SelectCurrencyPage(tk.Frame):
> def __init__(self, parent, controller):
> tk.Frame.__init__(self, parent)
> self.controller = controller
>
> self.currency_lbl = tk.Label(self, text = _("PLEASE SELECT
> CURRENCY"), font = LARGE_FONT) self.currency_lbl.pack(pady = 10,
> padx = 10)
>
> self.GBP_btn = tk.Button(self, text = "£ GBP",
> command = lambda c = "£": self.setGBP(c))
> self.GBP_btn.pack()
>
> self.USD_btn = tk.Button(self, text = "$ USD",
> command = lambda c = "$": self.setUSD(c))
> self.USD_btn.pack()
>
> self.EUR_btn = tk.Button(self, text = "€ EUR",
> command = lambda c = "€": self.setEUR(c))
> self.EUR_btn.pack()
>
> self.restart_btn = tk.Button(self, text = _("RESTART"),
> command = lambda:
> self.controller.setState('restart'))
> self.restart_btn.pack()
There's a lot of redundancy here. You can simplify that with a helper
method:
def makeCurrencyButton(self, code, symbol, conversion):
button = tk.Button(
self,
text=f"{symbol} {code}",
command=lambda: self.setCurrency(conversion)
)
button.pack()
return button
>
> def setGBP(self, currency):
> self.controller.setCurrency(GBP)
> self.controller.setState('fuel')
>
> def setUSD(self, currency):
> self.controller.setCurrency(USD)
> self.controller.setState('fuel')
>
> def setEUR(self, currency):
> self.controller.setCurrency(EUR)
> self.controller.setState('fuel')
Replace these with
def setCurrency(self, conversion):
self.controller.setCurrency(conversion)
self.controller.setState("fuel")
Now
> self.GBP_btn = tk.Button(self, text = "£ GBP",
> command = lambda c = "£": self.setGBP(c))
> self.GBP_btn.pack()
>
> self.USD_btn = tk.Button(self, text = "$ USD",
> command = lambda c = "$": self.setUSD(c))
> self.USD_btn.pack()
>
> self.EUR_btn = tk.Button(self, text = "€ EUR",
> command = lambda c = "€": self.setEUR(c))
> self.EUR_btn.pack()
becomes
self.GBP_btn = self.makeCurrencyButton("GBP", "£", GBP)
self.USD_btn = self.makeCurrencyButton("USD", "$", USD)
self.EUR_btn = self.makeCurrencyButton("EUR", "€", EUR)
If you go one step further and use a list to store the (code, symbol,
conversion-rate) triples
CURRENCIES = [("EUR", "€", 1.14), ...]
and the buttons
self.currency_buttons = [
self.makeCurrencyButton(*currency)
for currency in CURRENCIES
]
it will become very easy to add or remove currencies.
Regarding your actual question, I guess that you are making a mess by using
the currency attribute for two different purposes -- but my idea of your
program is not concrete enough to tell you how to fix that, or if that even
is the correct diagnosis. You have to wait for Alan or someone else to
chime
in -- or provide the complete runnable source code somewhere.
_______________________________________________
Tutor maillist - Tutor at python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
More information about the Tutor
mailing list