How do I restart my program without restarting the variable declarations - python-3.x

I'm still making my weather app and I need a function to change the City or Country, The problem is that I have to restart the program to display the changes but when I do restart- The default city gets loaded instead of the new one, I have tried many ways to fix this but they all didn't work, Thanks in advance!
# !/usr/bin/python3
#Please don't use my API-KEY for bad purposes, I have only included it to help run the code
import requests, json
from tkinter import *
import os
CITY = "Glasgow"
BASE_URL = "https://api.openweathermap.org/data/2.5/weather?"
URL = "https://api.openweathermap.org/data/2.5/weather?q=+" + CITY + "&units=metric&APPID=confedential"
response = requests.get(URL)
def func() :
def change():
y = Toplevel()
y.geometry("200x100")
en = Entry(y, width=10)
en.place(x=25, y=25)
en.focus()
def getr(e):
def restart():
x.destroy()
func()
CITY = en.get()
restart()
en.bind("<Return>", getr)
if response.status_code == 200:
data = response.json()
main = data['main']
temperature = main['temp']
humidity = main['humidity']
pressure = main['pressure']
report = data['weather']
print(f"{CITY:-^30}")
print(f"Temperature: {temperature}")
print(f"Humidity: {humidity}")
print(f"Pressure: {pressure}")
print(f"Weather Report: {report[0]['description']}")
rep = report[0]['main'].lower()
if "clear" in rep:
image = 'images/sunny.png'
if "cloud" in rep:
image = 'images/cloud.png'
if "rain" in rep:
image = 'images/rain.png'
if "thunder" in rep:
image = 'images/thunder.png'
if "mist" in rep:
image = 'images/mist.png'
if "snow" in rep:
image = 'images/snow.png'
x = Tk()
# Creating Menubar
menubar = Menu(x)
menubar.config(bg="#484848", fg="white", font=("Stencil Std", 10))
# Adding Help Menu
help_ = Menu(menubar, tearoff=0, bg="#484848", fg="white", font=("Stencil Std", 10))
menubar.add_cascade(label='Countries', menu=help_)
help_.add_command(label='Change Current Country', command=change)
help_.add_command(label='Show Current Country', command=None)
help_.add_separator()
help_.add_command(label='Change Timezone', command=None)
help_.add_command(label='Show Current Timezone', command=None)
help_.add_separator()
help_.add_command(label="Exit", command=x.destroy)
# display Menu
x.config(menu=menubar)
x.resizable(False, False)
gif = PhotoImage(file=image)
cvwid = gif.width()
cvhei = gif.height()
canvas = Canvas(x, width=cvwid, height=cvhei, bg='lightblue')
canvas.pack(fill=BOTH)
img = canvas.create_image(0, 0, image=gif, anchor=NW)
temp = canvas.create_text(cvwid / 2, 350, fill="White", font="Helvetica 30", text=str(int(temperature)) + "°C")
reportr = canvas.create_text(cvwid / 2, 400, fill="White", font="Helvetica 20", text=report[0]["main"])
x.title(f"{CITY:-^30}")
x.mainloop()
func()

The problem is that you assign response = requests.get(URL) only once. No matter how often you change the city, the response that was established with the parameter "Glasgow" will always stay the same.
The fastest fix without refactoring the whole codebase would be to make CITY and response global so you can modify them from within a function. For example:
CITY = "Glasgow"
URL = "https://api.openweathermap.org/data/2.5/weather?q=+{}&units=metric&APPID=confidential"
def get_response(city=CITY):
return requests.get(URL.format(city))
response = get_response()
Then you could global your variables in your getr(e) function like this.
def getr(e):
global CITY, response
CITY = en.get()
response = get_response(CITY)
restart()
This is a solution to the problem you currently have. However I would suggest, to go back to your codebase. With a good refactor you would not need this: You could think about seperating logic and ui, make your functions accept parameters and seperate them from each other, research how to update/change display values within a running Tk() mainloop instead of destroying and recalling it. Maybe it would help, to post your code on stackexchange for codereview.

Related

Can't pass path name created from QFileDialog to QLineEdit in PyQt5

I have created a QDialog window with a QLineEdit field and Browse button to pass the path of a folder into a program I am working on, I have problem with it primarily at point of running program, the path has not been generated.
Nonetheless, please see my codes below:
def RPA_OCR_module(self):
# Define the dialog window
d_width = 500
d_height = 200
module_form = QDialog()
module_form.setGeometry(int((width/2)-(d_width/2)), self.y(), d_width, d_height)
module_form.setWindowTitle('RPA OCR Module')
# Define text for text field
l_col = 20
element_width = 350
element_height = 25
path_field_text = QLabel('Key in the path to working folder', module_form)
path_field_text.setGeometry(l_col,25, element_width,element_height)
path_field_text.setFont(QtGui.QFont('Helvetica', 14))
path_field_text.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
# Define the text field
working_folder_path_field = QLineEdit('Default', module_form)
working_folder_path_field.move(l_col,50)
working_folder_path_field.resize(element_width,element_height)
# Define browse button for directory name
browse_btn = QPushButton('Browse', module_form)
browse_btn.setGeometry(l_col+element_width+1,48, 119,element_height)
browse_btn.clicked.connect(self.openDirNameDialog)
try:
if QFileDialog.Accepted:
working_folder_path_field.setText(fileName)
else:
pass
finally:
pass
x = module_form.exec_()
def openDirNameDialog(self):
# options = QFileDialog.Options()
# options |= QFileDialog.DontUseNativeDialog
global fileName
fileName = str(QFileDialog.getExistingDirectory(self, "Select Directory")) # , options=options
if fileName:
print(fileName)
#self.working_folder_path_field.setText(fileName) # This doesn't work, so I commented it out
else:
pass
If anybody has any idea how to get this done, please let me know. Thanks.
You have to change working_folder_path_field to self.working_folder_path_field so you can access it from another function. Then you just have to call setText(). Here is a generic template to follow.
class Template(QWidget):
def __init__(self):
super().__init__()
btn = QPushButton('Browse')
btn.clicked.connect(self.open)
self.box = QLineEdit()
vbox = QVBoxLayout(self)
vbox.addWidget(btn)
vbox.addWidget(self.box)
def open(self):
name = QFileDialog.getExistingDirectory(self, "Select Directory")
if name:
self.box.setText(name)

How do I make sure all GUI input variables can be accessed by certain classes and function calls?

When I provide an address /and or location to the entry bar and I press the "Get forecast" button the script fails at line 22. I think the error is raised because the str(address.get()) cant find the address variable, probably because it doesn't technically exist during that point of run time (I'm not able to log the error due to the structure of that function).
My question is; How do I make sure that my "get_hourly_forecast" function is able to access the address entry variable?
I have tried instantiating the address variable in various locations, e.g in the MainWeatherHub class, as well as in the MyWeatherApp class and then passing it as an argument to the MainWeatherHub in line 79, neither variation has worked. The current code shows the former variation.
import urllib, json, requests
from tkinter import *
from tkinter import ttk
def get_hourly_forecast(*args):
## params *args:
#A location argument
#Returns:
# A list of temps in Farenheit for the next 156 hours
API_KEY = 'removing my API key for security purposes'
try:
print('here') # The code makes it to here
curr_address = str(address.get()) # Code seems to fail here (not sure how to have the error print)
print('here')
geocode_url = "https://maps.googleapis.com/maps/api/geocode/json?address={}&key={}".format(cur_address, API_KEY)
response = requests.get(geocode_url)
response_dict = response.json()['results']
location = response_dict[0]['geometry']['location']
lat = location['lat']
lng = location['lng']
local_url_request = 'https://api.weather.gov/points/lat={}lng={}'.format(lat, lng)
response_one = requests.get(local_url_request)
json_dict_one = response_one.json()
local_props = json_dict_one['properties']
local_forecast_request = local_props['forecastHourly']
resposne_two = requests.get(local_forecast_request)
json_dict_two = resposne_two.json()
local_forecast_properites = json_dict_two['properties']
hourly_updates = local_forecast_properites['periods']
out = []
for i in hourly_updates:
for key, value in i.items():
if key == "temperature":
out.append(value)
current_weather.set(out[0])
except:
print("Not working.")
#############################################################
class MyWeatherApp:
"""
MyWeatherApp is the primary Frame for this GUI application
"""
def __init__(self, master):
super(MyWeatherApp, self).__init__()
self.master = master
# Create the main window Frame
master_style = ttk.Style()
master_style.configure('Master.TFrame')
self.master.title("My Weather")
self.master.geometry("500x500")
MWA = ttk.Frame(self.master, style='Master.TFrame')
MWA.place(relheight=1.0, relwidth=1.0)
# Run other widgets within this class
MainWeatherHub(MWA)
#############################################################
class MainWeatherHub(MyWeatherApp):
"""
The MainWeatherHub (MWH) is the top panel of the app
"""
def __init__(self, mainwindow):
super(MyWeatherApp, self).__init__()
self.mainwindow = mainwindow
# Create a Frame for the MainWeatherHub
MWH_style = ttk.Style()
MWH_style.configure('MWH.TFrame')
MWH = ttk.Frame(self.mainwindow, style='MWH.TFrame', relief='sunken')
MWH.place(relheight=0.33, relwidth=0.95, relx=0.025, rely=0.025)
# Create an entry widget to take a location
# and store that as a loction variable.
address = StringVar()
loc_entry = ttk.Entry(MWH, textvariable=address)
loc_entry.place(relheight=0.30, relwidth=.95, relx=0.025, rely=0.05)
# Get weather button finds weather for the users location
current_weather = StringVar()
get_weather_button = ttk.Button(loc_entry, text="Get Forecast", command=get_hourly_forecast)
get_weather_button.place(relheight=0.85,relwidth=0.2, relx=0.79, rely=0.075)
#Display weather in the Message widget
weath_display = Message(MWH, textvariable=current_weather)
weath_display.place(relwidth=0.95, relheight=0.55, relx=0.025, rely=0.375)
root = Tk()
my_gui = MyWeatherApp(root)
root.mainloop()
If this script works properly, it should return the current temperature in degrees Fahrenheit of the location that was provided in the entry bar.
You should send it as parameter
def get_hourly_forecast(cur_address):
geocode_url = "...".format(cur_address, API_KEY)
And then assing to button function which runs get_hourly_forecast with string
class MainWeatherHub(MyWeatherApp):
def __init__(self, mainwindow):
self.address = StringVar() # use self.
ttk.Button(loc_entry, text="Get Forecast", command=run_it)
def run_it(self):
get_hourly_forecast(self.address.get())
or using lambda
class MainWeatherHub(MyWeatherApp):
def __init__(self, mainwindow):
ttk.Button(loc_entry, text="Get Forecast", command=lambda:get_hourly_forecast(address.get()))
EDIT:
I see you use current_weather (StringVar from MainWeatherHub) in get_hourly_forecast to set value current_weather.set(out[0]).
You could send current_weather to get_hourly_forecast as parameter
def get_hourly_forecast(cur_address, current_weather):
geocode_url = "...".format(cur_address, API_KEY)
current_weather.set(out[0])
and
class MainWeatherHub(MyWeatherApp):
def __init__(self, mainwindow):
self.address = StringVar() # use self.
self.current_weather = StringVar() # use self.
ttk.Button(loc_entry, text="Get Forecast", command=run_it)
def run_it(self):
get_hourly_forecast(self.address.get(), self.current_weather)
but it could be better to return value from get_hourly_forecast
def get_hourly_forecast(cur_address):
geocode_url = "...".format(cur_address, API_KEY)
return out[0]
and get it in run_it
def run_it(self):
result = get_hourly_forecast(self.address.get())
if result is not None:
self.current_weather.set(result)
This way get_hourly_forecast doesn't work with StringVar and you can use it in other program which doesn't use StringVar.

tkinter Radiobutton string selector

Some time ago I wrote a Python3 program to allow me to connect to one of a number of computers.
#! /usr/bin/env python3
from tkinter import *
import os
Computers = [
'RaspberryPi3',
'PiUbuntu',
'Thylacoleo']
def sel():
cmd = "open afp://" + Computers[var.get()] + ".local"
os.system( cmd )
root = Tk() # create tkinter object
root.title("Connect to Computer") # give the window a title...
root.minsize(250, 100)
var = IntVar()
button=0
for cc in Computers:
R1 = Radiobutton(root, text=cc, variable=var, value=button, command=sel)
R1.pack( anchor = W )
button += 1
root.mainloop()
This works, and I though it would be simple to adapt this, using a Dictionary to mount nfs shares.
Unfortunately I can't seem to get it to work.
My previous effort returned an integer from each button. but this would not work with a Dictionary, and I wanted to get a string value returned.
Computers = {
'RaspberryPi3': 'Pi3',
'PiUbuntu': 'PiUbuntu',
'Ubuntu-T': 'Thylaco'
}
def sel():
print("selection", tvar)
selection = "You selected the option " + tvar
root = Tk() # create tkinter object
root.title("Connect to Computer") # give the window a title...
root.minsize(250, 100)
# var = IntVar()
# tvar = StringVar()
tvar = str()
button=0
for cc in list(Computers.keys()):
# R1 = Radiobutton(root, text=cc, variable=var, value=button, command=sel)
R1 = Radiobutton(root, text=cc, variable=tvar, value=cc, command=sel)
# R1 = Radiobutton(root, text=cc, textvariable=tvar, command=sel)
R1.pack( anchor = W )
root.mainloop()
I know I could make a list, and use an integer, but I am trying to understand how to get a string returned. I have read the documentation, and looked at lots of links, but not discovered any relevant examples.
tvar = StringVar() results in an error:-
TypeError: must be str, not StringVar
I susspect you have problem in line (you didn't show full Traceback)
selection = "You selected the option " + tvar
You have to use tvar.get() to get value/string from tvar
selection = "You selected the option " + tvar.get()

Trying to build Risk style game in Tkinter

I have been trying to build my skills in Python and am trying to create a risk style game.
I am not far in to it at the moment as I am trying to get to grips with classes and Tkinter.
My first trial is to create a series of buttons to take the place of the different countries. I then want these buttons to update the amount of armies on the country when they are clicked.
So far I have been able to get the map to generate from the class I have created and the buttons are clickable. When a button is clicked it updates the amount of armies but always for the last button.
How do I get it so that the button I click updates and not the last one?
Have I gone about this in entirely the wrong way?
from tkinter import *
import random
class territory:
def __init__ (self, country, player = "1", current_armies = 0, x=0, y=0):
self.country = country
self.current_armies = current_armies
self.player = player
self.y = y
self.x = x
def get_armies(self):
print(self.country + " has " + str( self.current_armies)+ " armies.")
def add_armies (self, armies):
self.current_armies += armies
def roll_dice (self, dice=1):
rolls = []
for i in range(0, dice):
rolls.append(random.randint(1,6))
rolls.sort()
rolls.reverse()
print (self.country + " has rolled " + str(rolls))
return rolls
def owner(self):
print (self.country + " is owned by " + self.player)
def get_country(self):
print(country)
def button (self):
Button(window, text = territories[0].current_armies, width = 10, command = click1(territories, 0)).grid(row=y,column=x)
window = Tk()
def create_territories():
countries = ["UK", "GER", "SPA", "RUS"]
terr_pos = [[1,0],[2,0],[1,5],[4,1]]
sta_arm = [1,1,1,1]
terr = []
player = "1"
for i in range(len(countries)):
terr.append(territory(countries[i],player, sta_arm [i] , terr_pos[i][0],terr_pos[i][1]))
if player == "1":
player = "2"
else:
player = "1"
return terr
def click1(territory, i):
territory[i].current_armies += 1
build_board(territory)
def build_board(territories):
for i in range(0,4):
Button(window, text = territories[i].country+"\n"+str(territories[i].current_armies), width = 10, command = lambda: click1(territories, i)).grid(row=territories[i].y,column=territories[i].x)
territories = create_territories()
window.title ("Domination")
create_territories()
build_board(territories)
window.mainloop()
In your def button(self):... you are always referencing territories[0]:
Button(window, text=territories[0].current_armies,... command=click1(territories, 0)...
As such, you are always using the first territory as your reference, so you ought to initialize each territory with its index in territories[] so you can pass that into your Button constructor.
On your question of "entirely the wrong way," I'd personally send that question over to CodeReview, since that's more of their domain (we fix broken code, they address smelly code), though there is significant overlap. We do prefer one question per question, however, and "is this whole thing wrong?" is a little broad for StackOverflow.

Creating tkinter gui.How to display information when ID number provided

I am working on creating a gui and could use some help. There are three programs that build off of each other. MY imput will be two CSV files, Examples below:
items4.csv
12345,Italy,100
13579,China,50
ETC.
transactions4.csv
12345,15
13579,10
12345,20
13579,-10
What I want it to do is upon running the gui will open and the window will ask for the "Item ID" Upon entering an id, say 12345, it will open up a new window that displays the following: getID, getName, getAvailableStart, and getAvailableEnd, all of these definitions are were created by the travelclass program (found below) when the program travelToolbox reads the two CSV files.
Note: Both the travelClass and travelToolbox programs are working fine. IT is only travelSystem that needs changing at this point
What I've done:
At this point I have managed to get the travelSystems presents the initial GUI however it doesn't matter what I enter in the entry box because when I hit the open button the next gui opens up but it only shows the information found in the first line of items4.csv. I think my main issue is line 34 or may be line 16. Let me know if any other information is needed.
I could really appreciate some help. Thank you in advance.
travelsystem
from travelToolbox import readItems, readTransactions
from tkinter import *
from tkinter import font
import tkinter as tk
class myApp :
def __init__(self, top, itemRecords) :
self.root = top
# Create a container Frame at the bottom
self.bframe = Frame(self.root)
self.bframe.pack(side=BOTTOM) # Create Label
self.xlabel = Label(self.root, text="Item ID")
self.xlabel.pack(side=LEFT)
self.xentry = Entry(self.root, bd=5) # Create Entry box
self.xentry.pack(side=LEFT)
self.xentry.focus_set() # Set focus in Entry box
self.xopen = Button(self.root, text="Open", command=self.showStockItem) # Create open Button
self.xopen.pack(side=LEFT)
self.xquit = Button(self.bframe, text="Quit", command=self.quitit ) # Create quit Button
#self.root instead of bframe
self.xquit.pack(side=BOTTOM)
def showStockItem(self):
tbl = Toplevel() # Create Toplevel window
hdr = font.Font(underline=1, weight="bold") # Create header font
tid = Label(tbl, text="ID", font=hdr) # Create 4 column headers in grid
tid.grid(row=0, column=0)
tlast = Label(tbl, text="Name", font=hdr)
tlast.grid(row=1, column=0)
tfirst = Label(tbl, text="Start", font=hdr)
tfirst.grid(row=2, column=0)
tlabel = Label(tbl, text="End", font=hdr)
tlabel.grid(row=3, column=0)
for rec in itemRecords.values() :
tid = Label(tbl, text= rec.getID()) # Create 4 column headers in grid
tid.grid(row=0, column=1)
tlast = Label(tbl, text= rec.getName())
tlast.grid(row=1, column=1)
tfirst = Label(tbl, text= rec.getAvailableStart())
tfirst.grid(row=2, column=1)
tlabel = Label(tbl, text= rec.getAvailableEnd())
tlabel.grid(row=3, column=1)
#self.xquit.pack(side=BOTTOM) #May not need this.
return
def quitit(self):
self.root.destroy()
return
itemsFileName = "items4.csv"
transactionsFileName = "transactions4.csv"
# itemRecords is a dictionary of stockItem records indexed by item ID
itemRecords = {}
# Read the items from itemsFileName into itemRecords
readItems(itemsFileName, itemRecords)
# Read the transactions from transactionsFileName into itemRecords
readTransactions(transactionsFileName, itemRecords)
top = tk.Tk()
#top = Travel()
app = myApp(top, itemRecords)
top.mainloop()
Other files:
travelToolbox
import csv
from travelClass import travelItem
def readItems(itemsFileName, itemRecords) :
# readItems reads items from itemsFileName into the itemRecords dictionary
# Open itemsFileName and create a CSV file reader
itemsFile = open(itemsFileName, 'r')
itemsReader = csv.reader(itemsFile)
# Process each row of the items file
for row in itemsReader :
# Get the values for the record
(iid, iname, icount) = row
iid = str(iid)
iname = str(iname)
icount = int(icount)
# Check if this ID is not yet in itemRecords
if (not(iid in itemRecords)) :
# Create a travelItem object and add it to itemRecords
itemRecord = travelItem(iid, iname, icount)
itemRecords[iid] = itemRecord
def readTransactions(transactionsFileName, itemRecords) :
# readTransactions reads transactions from transactionsFileName into the itemRecords dictionary
transactionFile = open(transactionsFileName, 'r')
transactionReader = csv.reader(transactionFile)
for row in transactionReader :
# Get the values for the record
(iid, itransaction) = row
iid = str(iid)
itransaction = int(itransaction)
itemRecords[iid].appendTransaction(itransaction)
#for key, value in itemRecords.items() :
transactionFile.close()
travelClass
class travelItem :
Class travelItem :
def __init__(self, itemID, itemName, itemCount) :
# Constructor to create inventoryItem CORRECT
self.id = itemID
self.name = itemName
self.AvailableStart = itemCount
self.transactions = []
def getID(self) :
# getID returns the tour ID CORRECT
return(self.id)
def getName(self) :
# getName returns the tour name CORRECT
return (self.name)
def setName(self, newName) :
# setName sets the tour name
self.name = newName
def getAvailableStart(self) :
# returns the starting availability
return (self.AvailableStart)
def appendTransaction(self, num) :
# appendTransaction appends a transaction to the transactions list
self.transactions.append(num)
def getTransactions(self) :
# getTransactions returns the list of transactions
return (self.transactions)
def getReservations(self) :
# returns the total of reservation transactions
total = 0
for num in self.transactions :
if ((num) > 0):
total += (num)
self.Reservations = total
return(self.Reservations)
def getCancellations(self) :
# returns the total of cancellation transactions
total = 0
for num in self.transactions :
if (num <= 0):
total += (num)
return(total)
def getAvailableEnd(self) :
# returns the ending availability, which is availableStart minus transactions
self.AvailableEnd = ((self.AvailableStart) - (self.Reservations) - (self.getCancellations()))
return(self.AvailableEnd)

Resources