Python Tkinter货币转换器JSONDecodeError问题求助及代码优化建议
Hey there! Let's break down your problem step by step. That JSON decode error happens because the API response you're trying to parse isn't valid JSON—most likely because your request is failing (returning an empty string or an error page) when run inside the convert function. Here's why that's happening and how to fix it:
First, Fix the Immediate Error
Extract the actual currency code from dropdown selections
Your dropdown has options like'ALL: Albania [Lek]', but currency APIs only accept the 3-letter currency code (likeALL). Right now, you're passing the entire string to the API, which causes a malformed request. Add this code inside yourconvertfunction to extract the code:# Get selected values and extract currency codes selected_from = currencyBase_ToConvert_Box.get() currencyBase_ToConvert = selected_from.split(':')[0].strip() selected_to = currencyBase_Converted_Box.get() # Assumes this is your target currency dropdown currencyBase_Converted = selected_to.split(':')[0].strip()Debug the API response
Before parsing JSON, verify if your request was successful. Add these lines right after making the API call to see what's being returned:r = requests.get(your_api_url_here) # Replace with your actual API call print(f"Status Code: {r.status_code}") # Should return 200 for successful requests print(f"Response Content: {r.text}") # View raw response to spot errorsIf you see status codes like 400/403/404, your request is invalid (wrong parameters, missing API key, etc.). Fix the API URL or parameters to match the service's documentation.
Fix variable scoping issues
If you're relying on global variables inside theconvertfunction, you need to declare them with theglobalkeyword (or better yet, avoid globals entirely):def convert(): global value_Converted # Declare if you need to update a global variable # Rest of your code
Next, Pythonic Code Optimizations
Let's clean up your code to make it more maintainable, readable, and idiomatic:
Replace the countries list with a dictionary
This makes mapping display names to currency codes much simpler:CURRENCY_MAP = { 'ALL': 'Albania [Lek]', 'AMD': 'Armenia [Armenian Dram]', 'AUD': 'Australia [Australian Dollar]', # Add all your remaining currencies here } # Generate dropdown options dynamically DROPDOWN_OPTIONS = [f"{code}: {name}" for code, name in CURRENCY_MAP.items()]Use a class to organize your UI logic
Classes eliminate messy global variables and keep related code grouped together:import requests import tkinter as tk from tkinter import ttk, messagebox class CurrencyConverter: def __init__(self, root): self.root = root self.root.title("Simple Currency Converter") # Currency data self.currency_map = { 'USD': 'United States [US Dollar]', 'EUR': 'Eurozone [Euro]', 'GBP': 'United Kingdom [Pound Sterling]', 'CNY': 'China [Yuan Renminbi]', # Add all your currencies here } self.dropdown_options = [f"{code}: {name}" for code, name in self.currency_map.items()] # Initialize UI self.create_widgets() def create_widgets(self): # Amount input ttk.Label(self.root, text="Amount:").grid(row=0, column=0, padx=5, pady=5) self.amount_var = tk.StringVar() self.amount_entry = ttk.Entry(self.root, textvariable=self.amount_var) self.amount_entry.grid(row=0, column=1, padx=5, pady=5) # From currency dropdown ttk.Label(self.root, text="From:").grid(row=1, column=0, padx=5, pady=5) self.from_currency_var = tk.StringVar() self.from_dropdown = ttk.Combobox(self.root, textvariable=self.from_currency_var, values=self.dropdown_options) self.from_dropdown.grid(row=1, column=1, padx=5, pady=5) self.from_dropdown.current(0) # Set default selection # To currency dropdown ttk.Label(self.root, text="To:").grid(row=2, column=0, padx=5, pady=5) self.to_currency_var = tk.StringVar() self.to_dropdown = ttk.Combobox(self.root, textvariable=self.to_currency_var, values=self.dropdown_options) self.to_dropdown.grid(row=2, column=1, padx=5, pady=5) self.to_dropdown.current(1) # Set default selection # Convert button self.convert_btn = ttk.Button(self.root, text="Convert", command=self.convert) self.convert_btn.grid(row=3, column=0, columnspan=2, padx=5, pady=10) # Result display self.result_var = tk.StringVar(value="Result will appear here") ttk.Label(self.root, textvariable=self.result_var).grid(row=4, column=0, columnspan=2, padx=5, pady=5) def fetch_exchange_rate(self, from_code, to_code): """Fetch exchange rate from a reliable API""" try: # Example using exchangerate-api (free tier available) response = requests.get(f"https://v6.exchangerate-api.com/v6/YOUR_API_KEY/pair/{from_code}/{to_code}") response.raise_for_status() # Raise error for HTTP issues data = response.json() return data["conversion_rate"] except requests.exceptions.RequestException as e: messagebox.showerror("Error", f"Failed to fetch rate: {str(e)}") return None except KeyError: messagebox.showerror("Error", "Unexpected API response format") return None def convert(self): """Handle conversion logic with validation""" # Validate amount input try: amount = float(self.amount_var.get()) except ValueError: messagebox.showerror("Invalid Input", "Please enter a valid number") return # Extract currency codes from_code = self.from_currency_var.get().split(':')[0].strip() to_code = self.to_currency_var.get().split(':')[0].strip() # Handle same currency conversion if from_code == to_code: self.result_var.set(f"{amount:.2f} {from_code} = {amount:.2f} {to_code}") return # Fetch rate and calculate result rate = self.fetch_exchange_rate(from_code, to_code) if rate: converted_amount = amount * rate self.result_var.set(f"{amount:.2f} {from_code} = {converted_amount:.2f} {to_code}") if __name__ == "__main__": root = tk.Tk() app = CurrencyConverter(root) root.mainloop()Add user-friendly error handling
The code above usesmessageboxto display clear errors for invalid input, network failures, or API issues instead of crashing silently.Follow
requestsbest practices- Use
response.raise_for_status()to automatically catch HTTP errors. - Avoid hardcoding API keys—store them in environment variables for security.
- Use
paramsinstead of string concatenation for API parameters (safer and cleaner).
- Use
Final Notes
- Ensure you're using a working currency API—many free services require an API key (like exchangerate-api's free tier).
- Test your API call outside the function first to confirm it returns valid JSON before integrating it into the UI.
内容的提问来源于stack exchange,提问作者Tessalarius

