Skip to content
This repository was archived by the owner on Aug 24, 2024. It is now read-only.

Commit a5ed633

Browse files
authored
Re-designed from scratch
1 parent cbb25d5 commit a5ed633

File tree

9 files changed

+496
-5
lines changed

9 files changed

+496
-5
lines changed

README.md

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
# Pie-Chart-Creator
22

3-
<img src="included_files/main.jpg">
3+
<img src="included_files/main.gif">
44

55
A Python program which creates a pie chart using MatPlotLib and has a GUI created using Tkinter. Feel free to copy and use my code anywhere, just don't forget to credit me!
66

77
I would like to thank Ghanteyyy (http://github.com/ghanteyyy) for contributing to this project. This program wouldn't have been so nice without his efforts.
8-
9-
**Important Note**
10-
11-
This program will not work unless `pie_chart_creator.py` and the `included_files` folder are in the same folder on your device.

commands.py

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
'''
2+
Different commands when user clicks different button
3+
'''
4+
5+
6+
from tkinter import messagebox
7+
import matplotlib.pyplot as plt
8+
9+
10+
def default_state(entry_widgets, styles):
11+
'''Set the entries box and radio-button the default state after adding the value'''
12+
13+
for widget, text in {entry_widgets[0]: 'Items', entry_widgets[1]: 'Values'}.items():
14+
widget.delete(0, 'end')
15+
widget.insert('end', text)
16+
17+
for style, name in styles.items(): # Change the foreground color of entries box
18+
style.configure(name, foreground='grey')
19+
20+
entry_widgets[2].set(0)
21+
22+
23+
def add_command(entry_widgets, _vars, styles):
24+
'''Store item_name, values and explode in respective variables'''
25+
26+
entry_get = entry_widgets[0].get().strip()
27+
values_get = entry_widgets[1].get().strip()
28+
split_value_get = values_get.split('.')
29+
explode_get = entry_widgets[2].get()
30+
31+
pie_items_var, values_var, explode_var = _vars
32+
33+
if entry_get in ['', 'Items']:
34+
messagebox.showerror('Invalid Name', 'Provide valid item name')
35+
36+
elif entry_get in _vars[0]:
37+
messagebox.showerror('Exists', f'{entry_get} already exists in the register')
38+
39+
elif not split_value_get[0].isdigit() and not split_value_get[1].isdigit():
40+
messagebox.showerror('Invalid Values', 'Values was expected in number')
41+
42+
elif explode_get not in [1, 2]:
43+
messagebox.showerror('Invalid Explode', 'You must select Enable or Disable in Explode option')
44+
45+
else:
46+
pie_items_var.append(entry_get)
47+
values_var.append(float(values_get))
48+
49+
if explode_get == 1:
50+
explode_var.append(0.1)
51+
52+
else:
53+
explode_var.append(0)
54+
55+
messagebox.showinfo('Added', 'Values are added')
56+
default_state(entry_widgets, styles)
57+
58+
59+
def insert_to_text_widget(text_widgets, _vars):
60+
'''Inserting data from self.explode, self.pie_items and self.pie_items_values in their own text_widget'''
61+
62+
for widget in text_widgets:
63+
widget.config(state='normal')
64+
widget.delete('1.0', 'end')
65+
66+
pie_items, pie_values, pie_explode = _vars
67+
pie_explode = ['Enabled' if explode else 'Disabled' for explode in pie_explode]
68+
69+
for index, values in enumerate(zip(pie_items, pie_values, pie_explode)):
70+
item, values, explode = values
71+
72+
text_widgets[0].insert('1.0', f'{item}\n')
73+
text_widgets[1].insert('1.0', f'{values}\n')
74+
text_widgets[2].insert('1.0', f'{explode}\n')
75+
76+
for widget in text_widgets:
77+
widget.config(state='disabled', cursor='arrow')
78+
79+
80+
def make_chart(_vars):
81+
'''Make pie-chart as per the user's data'''
82+
83+
items, values, explode = _vars
84+
85+
if items:
86+
figure, pie_chart = plt.subplots()
87+
88+
if messagebox.askyesno('Shadow', 'Do you want to show in the pie-char?'):
89+
pie_chart.pie(values, explode=explode, labels=items, autopct='%1.2f%%', shadow=True, startangle=90)
90+
91+
else:
92+
pie_chart.pie(values, explode=explode, labels=items, autopct='%1.2f%%', shadow=False, startangle=90)
93+
94+
pie_chart.axis('equal')
95+
plt.show()
96+
97+
else:
98+
messagebox.showerror('No data', 'No data were inputed to make pie-charts')
99+
100+
101+
def clear(_vars, text_widgets=None, styles=None):
102+
'''Clear data from the register'''
103+
104+
if _vars[0]:
105+
if messagebox.askyesno('Clear Register?', 'Do you really want to clear REGISTER?'):
106+
for _var in _vars:
107+
_var.clear()
108+
109+
messagebox.showinfo('Cleared', 'All values are cleared from the REGISTER')
110+
default_state(text_widgets, styles)
111+
112+
else:
113+
messagebox.showinfo('Clear Register', "No items added yet. How about adding some items?")

include.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
'''
2+
The global function used by both main window and view window
3+
'''
4+
5+
6+
import os
7+
import sys
8+
9+
10+
def resource_path(relative_path):
11+
'''Get absolute path to resource from temporary directory
12+
13+
In development:
14+
Gets path of files that are used in this script like icons, images or file of any extension from current directory
15+
16+
After compiling to .exe with pyinstaller and using --add-data flag:
17+
Gets path of files that are used in this script like icons, images or file of any extension from temporary directory'''
18+
19+
try:
20+
base_path = sys._MEIPASS # PyInstaller creates a temporary directory and stores path of that directory in _MEIPASS
21+
22+
except AttributeError:
23+
base_path = os.path.abspath(".")
24+
25+
return os.path.join(base_path, relative_path)
26+
27+
28+
def initial_position(window, title):
29+
'''Set window to the center of the screen at the startup'''
30+
31+
window.withdraw()
32+
window.update()
33+
34+
width, height = window.winfo_width(), window.winfo_height()
35+
screen_width, screen_height = window.winfo_screenwidth() // 2, window.winfo_screenheight() // 2
36+
37+
window.geometry(f'{width}x{height}+{screen_width - width // 2}+{screen_height - height // 2}')
38+
39+
window.resizable(0, 0)
40+
window.iconbitmap(resource_path('included_files\\icon.ico'))
41+
window.title(title)
42+
43+
window.deiconify()

included_files/main.gif

763 KB
Loading

link.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
'''
2+
Open the github repository to view the codes of this program
3+
'''
4+
5+
import requests
6+
import webbrowser
7+
from tkinter import messagebox
8+
9+
10+
def is_internet():
11+
'''Check if you are connected to internet'''
12+
13+
try:
14+
requests.get('http://google.com')
15+
return True
16+
17+
except requests.ConnectionError:
18+
return False
19+
20+
21+
def open_link(master, event=None):
22+
'''Open the github page of the author(NMrocks) in the default browser'''
23+
24+
if is_internet():
25+
master.after(0, lambda: webbrowser.open('http://github.com/NMrocks/Pie-Chart-Creator'))
26+
27+
else:
28+
messagebox.showerror('ERROR', 'Unable to load page because you are not connected to internet')

run.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
'''
2+
Run the entire program
3+
'''
4+
5+
6+
import ui_main
7+
8+
9+
ui_main.UI()

tips.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
'''
2+
Get a pop-up window when hovered to any widget with a short
3+
message for what that widgets is designed for.
4+
'''
5+
6+
7+
import tkinter.tix as tix
8+
9+
10+
class Tips:
11+
def __init__(self, window):
12+
self.balloon = tix.Balloon(window)
13+
14+
def set_tips(self, widget, message):
15+
'''Generating pop-up window'''
16+
17+
self.balloon.bind_widget(widget, balloonmsg=message)

ui_main.py

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
'''
2+
The main GUI window.
3+
'''
4+
5+
import tkinter as tk
6+
import tkinter.ttk as ttk
7+
import tkinter.tix as tix
8+
from tkinter import messagebox
9+
import tips
10+
import link
11+
import ui_view
12+
import include
13+
import commands
14+
15+
16+
class UI:
17+
def __init__(self):
18+
self.explode, self.pie_items, self.pie_items_percentage = [], [], []
19+
20+
self.master = tix.Tk()
21+
self.container_frame = tix.Frame(self.master, bg='white')
22+
self.tips = tips.Tips(self.master)
23+
24+
self.image_frame = tix.Frame(self.container_frame)
25+
self.image_obj = tix.PhotoImage(file=include.resource_path('included_files\\PCC_Logo.png'))
26+
self.image_label = tix.Label(self.image_frame, image=self.image_obj, cursor='hand2')
27+
self.image_label.bind('<Button-1>', lambda e: link.open_link(self.master))
28+
self.image_label.pack()
29+
self.image_frame.pack()
30+
31+
self.entries_frame = tix.Frame(self.container_frame, bg='white')
32+
self.items_style, self.value_style = ttk.Style(), ttk.Style()
33+
self.items_style.configure('I.TEntry', foreground='grey')
34+
self.value_style.configure('V.TEntry', foreground='grey')
35+
self.item_entry = ttk.Entry(self.entries_frame, justify=tix.CENTER, width=35, style='I.TEntry')
36+
self.value_entry = ttk.Entry(self.entries_frame, justify=tix.CENTER, width=35, style='V.TEntry')
37+
38+
self.radio_button_style = ttk.Style()
39+
self.radio_button_style.configure('R.TRadiobutton', background='white')
40+
self.explode_frame = tk.LabelFrame(self.container_frame, text='Explode', bg='white')
41+
self.radio_button_var = tix.IntVar()
42+
self.explode_radio_button_enable = ttk.Radiobutton(self.explode_frame, text='Enable', variable=self.radio_button_var, value=1, cursor='hand2', style='R.TRadiobutton')
43+
self.explode_radio_button_disable = ttk.Radiobutton(self.explode_frame, text='Disable', variable=self.radio_button_var, value=2, cursor='hand2', style='R.TRadiobutton')
44+
self.explode_radio_button_enable.pack(side=tix.LEFT, ipadx=15, ipady=5)
45+
self.explode_radio_button_disable.pack(side=tix.LEFT)
46+
47+
self.item_entry.pack(ipady=2, pady=5)
48+
self.value_entry.pack(ipady=2, pady=5)
49+
self.entries_frame.pack(pady=5)
50+
self.explode_frame.pack(ipady=2, pady=5)
51+
52+
for widget, text in {self.item_entry: 'Items', self.value_entry: 'Values'}.items():
53+
widget.insert(tix.END, text)
54+
55+
self.styles_list = {self.items_style: 'I.TEntry', self.value_style: 'V.TEntry'}
56+
self.pie_vars = (self.pie_items, self.pie_items_percentage, self.explode)
57+
self.pie_entries = (self.item_entry, self.value_entry, self.radio_button_var)
58+
59+
self.buttons_frame = tix.Frame(self.container_frame, bg='white')
60+
self.add_value_button = tix.Button(self.buttons_frame, text='Add Values', bg='white', activebackground='white', cursor='hand2', relief=tix.GROOVE, command=lambda: commands.add_command(self.pie_entries, self.pie_vars, self.styles_list))
61+
self.view_button = tix.Button(self.buttons_frame, text='View Register', bg='white', activebackground='white', cursor='hand2', relief=tix.GROOVE, command=lambda: ui_view.UI(self.master, self.pie_vars))
62+
self.clear_button = tix.Button(self.buttons_frame, text='Clear Register', bg='white', activebackground='white', cursor='hand2', relief=tix.GROOVE, command=lambda: commands.clear(self.pie_vars, self.pie_entries, self.styles_list))
63+
self.make_pie_chart_button = tix.Button(self.buttons_frame, text='Make Pie-Chart', bg='white', activebackground='white', cursor='hand2', relief=tix.GROOVE, command=lambda: commands.make_chart(self.pie_vars))
64+
65+
self.add_value_button.pack(ipadx=71, ipady=2, pady=3)
66+
self.view_button.pack(ipadx=65, ipady=2, pady=3)
67+
self.clear_button.pack(ipadx=64, ipady=2, pady=3)
68+
self.make_pie_chart_button.pack(ipadx=59, ipady=2, pady=3)
69+
70+
self.buttons_frame.pack()
71+
72+
self.tips.set_tips(self.item_entry, 'Input desire name for your item.')
73+
self.tips.set_tips(self.clear_button, 'Clear ALL VALUES from the register.')
74+
self.tips.set_tips(self.view_button, 'View and Edit items stored in register.')
75+
self.tips.set_tips(self.image_label, 'http://github.com/NMrocks/Pie-Chart-Creator')
76+
self.tips.set_tips(self.value_entry, 'Input values for the name of your item.')
77+
self.tips.set_tips(self.explode_frame, 'Explode is spliting some part of pie-chart if Enabled')
78+
self.tips.set_tips(self.add_value_button, 'Add values you have entered in the input fields to the register.')
79+
self.tips.set_tips(self.make_pie_chart_button, 'Generate a pie-chart according to the data provided by you.')
80+
81+
self.item_entry.bind('<FocusIn>', self.bindings)
82+
self.value_entry.bind('<FocusIn>', self.bindings)
83+
self.value_entry.bind('<FocusOut>', lambda event, focus_out=True: self.bindings(event, focus_out))
84+
85+
self.master.after(0, lambda: include.initial_position(self.master, 'Pie Chart Creator'))
86+
self.master.protocol('WM_DELETE_WINDOW', self.exit)
87+
self.master.bind('<Button-1>', self.bindings)
88+
self.container_frame.pack()
89+
self.master.mainloop()
90+
91+
def config(self, widget=None, mode=None, text=None, color=None, styles=None):
92+
'''Behaviour for the entry boxes when user gets in and out of them '''
93+
94+
if widget:
95+
widget.delete(0, tix.END)
96+
97+
if mode == 'insert':
98+
widget.insert(tix.END, text)
99+
100+
if widget == self.value_entry and self.item_entry.get() == 'Items':
101+
self.items_style.configure('I.TEntry', foreground='grey')
102+
103+
styles[0].configure(styles[1], foreground=color)
104+
105+
def bindings(self, event, focus_out=False):
106+
'''When user clicks in and out of the entries widgets'''
107+
108+
widgets = {self.item_entry: 'Items', self.value_entry: 'Values'}
109+
110+
if event.widget == self.item_entry:
111+
styles = (self.items_style, 'I.TEntry')
112+
113+
else:
114+
styles = (self.value_style, 'V.TEntry')
115+
116+
for widget, text in widgets.items():
117+
if widget == event.widget:
118+
if widget.get() == text:
119+
self.config(widget, 'delete', text, 'black', styles)
120+
121+
else:
122+
if not widget.get():
123+
self.config(widget, 'insert', text, 'grey', styles)
124+
125+
if focus_out:
126+
if not widget.get():
127+
self.config(widget, 'insert', text, 'grey', styles)
128+
129+
if event.widget not in widgets:
130+
_styles = [(self.items_style, 'I.TEntry'), (self.value_style, 'V.TEntry')]
131+
132+
for index, widget in enumerate(widgets.items()):
133+
wid, text = widget
134+
135+
if wid.get().strip() == text:
136+
self.config(color='grey', styles=_styles[index])
137+
138+
self.master.focus()
139+
140+
def exit(self):
141+
'''When user clicks to X button in the title bar'''
142+
143+
if messagebox.askyesno('Exit', 'Do you really want to exit?', parent=self.master):
144+
self.master.destroy()

0 commit comments

Comments
 (0)