I posted an answer on StackOverflow about periodic table sometime back and ever since then I've been thinking about improving/optimizing it and worked up a completely different version of it that opens the wiki link once you click on any element. I have created 4 files:
load.py
that loads the data from the csv filewidgets.py
that has the classes with the custom widgets on itmain.py
that takes all these files and put into a working formdata.csv
with all the data in a csv format
I am not really sure on how I am supposed to paste these files here as I am not frequent in this community. I have uploaded this into a repo too. Anyway, I will include the code for the py files here and a link to the csv file from the repo.
widgets.py
:
import tkinter as tk
import webbrowser
from load import read, color_dict
class Element(tk.Frame):
def __init__(self,master,element,*args,**kwargs):
tk.Frame.__init__(self,master,*args,**kwargs)
if not element.startswith('*'):
data = read(element).values.tolist()
symbol = data[0]
at_no = data[1]
at_ms = data[2]
e_type = data[3]
wiki = data[4]
color = self.find_color(e_type)
tk.Label(self,text=at_no,font=(0,10),bg=color,takefocus=0).pack(anchor='w')
tk.Label(self,text=symbol,font=(0,18,'bold'),bg=color,fg='white',takefocus=0).pack()
tk.Label(self,text=element,font=(0,9),bg=color,takefocus=0).pack()
tk.Label(self,text=at_ms,font=(0,7),bg=color,takefocus=0).pack()
self.bind('<Double-1>',lambda e: webbrowser.open(wiki))
for wid in self.children:
self.children.get(wid).bind('<Double-1>',lambda e: webbrowser.open(wiki))
else:
if element == '*':
text = 'Lanthanide'
color = self.find_color(text)
else:
text = 'Actinide'
color = self.find_color(text)
tk.Label(self,text=text,bg=color,font=(0,9)).pack(fill="both",expand=1)
tk.Label(self,text='89-103',font=(0,18,'bold'),bg=color,fg='white').pack(fill="both",expand=1)
tk.Label(self,text='(Check below)',bg=color,font=(0,8)).pack(fill="both",expand=1)
self['bg'] = color
self.config(width=100)
self.config(height=105)
self.pack_propagate(0)
def find_color(self,e_type):
return color_dict[e_type]
class Key(Element):
def __init__(self,master,e_type,*args,**kwargs):
tk.Frame.__init__(self,master,*args,**kwargs)
color = self.find_color(e_type)
psuedo_img = tk.PhotoImage(height=1,width=1)
tk.Label(self,image=psuedo_img,bg=color,height=15,width=15).pack(side='left')
tk.Label(self,text=e_type).pack()
class Demo(tk.Frame):
def __init__(self,master,e_type,*args,**kwargs):
tk.Frame.__init__(self,master,*args,**kwargs)
self['width'] = 500
self['height'] = 150
cnv = tk.Canvas(self)
cnv.pack()
wid = Element(self,element=e_type)
cnv.create_window(250,100,window=wid,tags='win')
x,y,x1,y1 = 150,60,200,60
cnv.create_line(x ,y ,x1 ,y1 ,arrow='last',tags='arrow')
cnv.create_line(x+150,y+40,x+200 ,y1+40,arrow='first',tags='arrow')
cnv.create_line(x ,y+65,x1 ,y1+65,arrow='last',tags='arrow')
cnv.create_line(x+150,y+85,x+200 ,y1+85,arrow='first',tags='arrow')
cnv.create_text(40,47,text='Atomic Number',anchor='nw')
cnv.create_text(355,90,text='Symbol',anchor='nw')
cnv.create_text(93,115,text='Element',anchor='nw')
cnv.create_text(355,132,text='Atomic Mass',anchor='nw')
self.pack_propagate(0)
if __name__ == '__main__':
root = tk.Tk()
Element(root,'Hydrogen').pack(padx=5,pady=20)
Key(root,e_type='Halogen').pack()
Demo(root,'Hydrogen').pack()
root.mainloop()
load.py
:
import pandas as pd
df = pd.read_csv('data.csv',index_col=0)
df1 = pd.read_csv('data.csv')
elements = df1['Element'].values.tolist()
types = sorted(list(set(df1['Type'].values.tolist())))
def read(element: str):
data = df.loc[element]
return data
main_layout = [['x' ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'x'],
['x' ,'x' ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x'],
['x' ,'x' ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x'],
['x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x'],
['x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x'],
['x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x'],
['x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x']]
sec_layout = [['x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x'],
['x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x']]
color_dict = {'Nonmetal':'#feaa91',
'Noble Gas':'#ffe36b',
'Halogen':'#dadf23',
'Alkali Metal':'#fee0b3',
'Alkaline Earth Metal':'#fdc721',
'Metalloid':'#e79cf4',
'Transition Metal':'#77c3f6',
'Metal':'#ff9fc2',
'Lanthanide':'#39d8ab',
'Actinide':'#abdf4b',
'Unknown Properties':'#b2b3b2'}
main.py
:
import tkinter as tk
from tkinter import ttk
from widgets import Element, Key, Demo
from load import main_layout, sec_layout, elements, types
root = tk.Tk()
root.title('Periodic Table')
# Frame for entire table
table = tk.Frame(root)
table.pack()
# Frame for main elements of table
main_tab = tk.Frame(table)
main_tab.pack()
# Frame for the key of the table
key_tab = ttk.LabelFrame(main_tab,text='Legend')
key_tab.grid(row=0,column=0,columnspan=10,rowspan=3)
# Frame for the demo widget
repr_tab = ttk.Frame(main_tab)
repr_tab.grid(row=0,column=5,columnspan=10,rowspan=3)
# Frame for secondary elements
sec_tab = tk.Frame(table)
sec_tab.pack()
# Heading
tk.Label(main_tab,text='Periodic Table',font=(0,21,'bold')).grid(row=0,column=3,columnspan=10)
# Loop through layout and place elements in the reserved locations
count = 0
for p,i in enumerate(main_layout):
for q,j in enumerate(i):
text = elements[count]
if j:
# For lanthides in between main table
if 56 <= count <= 70:
if count == 56:
text = '*'
else:
count = 71
text = elements[count]
# For actinoids in between main table
elif 88 <= count <= 103:
if count == 88:
text = '**'
else:
count = 103
text = elements[count]
Element(main_tab,element=text).grid(row=p,column=q,padx=2,pady=2)
count += 1
# Loop through secondary layout and place elements in the reserved locations
count = 56 # Start position of lantinides
for p,i in enumerate(sec_layout):
for q,j in enumerate(i):
text = elements[count]
# For Actinoids
if count >= 71:
text = elements[count+17]
Element(sec_tab,element=text).grid(row=p,column=q,padx=2,pady=2)
count += 1
# Placing the legend in 3x4 grid
for i in range(3):
for j in range(4):
if 4*i+j < len(types):
Key(key_tab,e_type=types[4*i+j]).grid(row=i+1,column=j,sticky='w',padx=5,pady=5)
# Placing the representation in the frame
Demo(repr_tab, elements[0]).grid(row=i,column=j+1,rowspan=5)
root.mainloop()
data.csv
:
Element,Symbol,AtomicNumber,AtomicMass,Type,WikiLink
Hydrogen,H,1,1.007,Nonmetal,https://en.wikipedia.org/wiki/Hydrogen
Helium,He,2,4.002,Noble Gas,https://en.wikipedia.org/wiki/Helium
Lithium,Li,3,6.941,Alkali Metal,https://en.wikipedia.org/wiki/Lithium
Beryllium,Be,4,9.012,Alkaline Earth Metal,https://en.wikipedia.org/wiki/Beryllium
Boron,B,5,10.811,Metalloid,https://en.wikipedia.org/wiki/Boron
Carbon,C,6,12.011,Nonmetal,https://en.wikipedia.org/wiki/Carbon
Nitrogen,N,7,14.007,Nonmetal,https://en.wikipedia.org/wiki/Nitrogen
Oxygen,O,8,15.999,Nonmetal,https://en.wikipedia.org/wiki/Oxygen
Fluorine,F,9,18.998,Halogen,https://en.wikipedia.org/wiki/Fluorine
Neon,Ne,10,20.18,Noble Gas,https://en.wikipedia.org/wiki/Neon
Sodium,Na,11,22.99,Alkali Metal,https://en.wikipedia.org/wiki/Sodium
Magnesium,Mg,12,24.305,Alkaline Earth Metal,https://en.wikipedia.org/wiki/Magnesium
Aluminum,Al,13,26.982,Metal,https://en.wikipedia.org/wiki/Aluminum
Silicon,Si,14,28.086,Metalloid,https://en.wikipedia.org/wiki/Silicon
Phosphorus,P,15,30.974,Nonmetal,https://en.wikipedia.org/wiki/Phosphorus
Sulfur,S,16,32.065,Nonmetal,https://en.wikipedia.org/wiki/Sulfur
Chlorine,Cl,17,35.453,Halogen,https://en.wikipedia.org/wiki/Chlorine
Argon,Ar,18,39.948,Noble Gas,https://en.wikipedia.org/wiki/Argon
Potassium,K,19,39.098,Alkali Metal,https://en.wikipedia.org/wiki/Potassium
Calcium,Ca,20,40.078,Alkaline Earth Metal,https://en.wikipedia.org/wiki/Calcium
Scandium,Sc,21,44.956,Transition Metal,https://en.wikipedia.org/wiki/Scandium
Titanium,Ti,22,47.867,Transition Metal,https://en.wikipedia.org/wiki/Titanium
Vanadium,V,23,50.942,Transition Metal,https://en.wikipedia.org/wiki/Vanadium
Chromium,Cr,24,51.996,Transition Metal,https://en.wikipedia.org/wiki/Chromium
Manganese,Mn,25,54.938,Transition Metal,https://en.wikipedia.org/wiki/Manganese
Iron,Fe,26,55.845,Transition Metal,https://en.wikipedia.org/wiki/Iron
Cobalt,Co,27,58.933,Transition Metal,https://en.wikipedia.org/wiki/Cobalt
Nickel,Ni,28,58.693,Transition Metal,https://en.wikipedia.org/wiki/Nickel
Copper,Cu,29,63.546,Transition Metal,https://en.wikipedia.org/wiki/Copper
Zinc,Zn,30,65.38,Transition Metal,https://en.wikipedia.org/wiki/Zinc
Gallium,Ga,31,69.723,Metal,https://en.wikipedia.org/wiki/Gallium
Germanium,Ge,32,72.64,Metalloid,https://en.wikipedia.org/wiki/Germanium
Arsenic,As,33,74.922,Metalloid,https://en.wikipedia.org/wiki/Arsenic
Selenium,Se,34,78.96,Nonmetal,https://en.wikipedia.org/wiki/Selenium
Bromine,Br,35,79.904,Halogen,https://en.wikipedia.org/wiki/Bromine
Krypton,Kr,36,83.798,Noble Gas,https://en.wikipedia.org/wiki/Krypton
Rubidium,Rb,37,85.468,Alkali Metal,https://en.wikipedia.org/wiki/Rubidium
Strontium,Sr,38,87.62,Alkaline Earth Metal,https://en.wikipedia.org/wiki/Strontium
Yttrium,Y,39,88.906,Transition Metal,https://en.wikipedia.org/wiki/Yttrium
Zirconium,Zr,40,91.224,Transition Metal,https://en.wikipedia.org/wiki/Zirconium
Niobium,Nb,41,92.906,Transition Metal,https://en.wikipedia.org/wiki/Niobium
Molybdenum,Mo,42,95.96,Transition Metal,https://en.wikipedia.org/wiki/Molybdenum
Technetium,Tc,43,98.0,Transition Metal,https://en.wikipedia.org/wiki/Technetium
Ruthenium,Ru,44,101.07,Transition Metal,https://en.wikipedia.org/wiki/Ruthenium
Rhodium,Rh,45,102.906,Transition Metal,https://en.wikipedia.org/wiki/Rhodium
Palladium,Pd,46,106.42,Transition Metal,https://en.wikipedia.org/wiki/Palladium
Silver,Ag,47,107.868,Transition Metal,https://en.wikipedia.org/wiki/Silver
Cadmium,Cd,48,112.411,Transition Metal,https://en.wikipedia.org/wiki/Cadmium
Indium,In,49,114.818,Metal,https://en.wikipedia.org/wiki/Indium
Tin,Sn,50,118.71,Metal,https://en.wikipedia.org/wiki/Tin
Antimony,Sb,51,121.76,Metalloid,https://en.wikipedia.org/wiki/Antimony
Tellurium,Te,52,127.6,Metalloid,https://en.wikipedia.org/wiki/Tellurium
Iodine,I,53,126.904,Halogen,https://en.wikipedia.org/wiki/Iodine
Xenon,Xe,54,131.293,Noble Gas,https://en.wikipedia.org/wiki/Xenon
Cesium,Cs,55,132.905,Alkali Metal,https://en.wikipedia.org/wiki/Cesium
Barium,Ba,56,137.327,Alkaline Earth Metal,https://en.wikipedia.org/wiki/Barium
Lanthanum,La,57,138.905,Lanthanide,https://en.wikipedia.org/wiki/Lanthanum
Cerium,Ce,58,140.116,Lanthanide,https://en.wikipedia.org/wiki/Cerium
Praseodymium,Pr,59,140.908,Lanthanide,https://en.wikipedia.org/wiki/Praseodymium
Neodymium,Nd,60,144.242,Lanthanide,https://en.wikipedia.org/wiki/Neodymium
Promethium,Pm,61,145.0,Lanthanide,https://en.wikipedia.org/wiki/Promethium
Samarium,Sm,62,150.36,Lanthanide,https://en.wikipedia.org/wiki/Samarium
Europium,Eu,63,151.964,Lanthanide,https://en.wikipedia.org/wiki/Europium
Gadolinium,Gd,64,157.25,Lanthanide,https://en.wikipedia.org/wiki/Gadolinium
Terbium,Tb,65,158.925,Lanthanide,https://en.wikipedia.org/wiki/Terbium
Dysprosium,Dy,66,162.5,Lanthanide,https://en.wikipedia.org/wiki/Dysprosium
Holmium,Ho,67,164.93,Lanthanide,https://en.wikipedia.org/wiki/Holmium
Erbium,Er,68,167.259,Lanthanide,https://en.wikipedia.org/wiki/Erbium
Thulium,Tm,69,168.934,Lanthanide,https://en.wikipedia.org/wiki/Thulium
Ytterbium,Yb,70,173.054,Lanthanide,https://en.wikipedia.org/wiki/Ytterbium
Lutetium,Lu,71,174.967,Lanthanide,https://en.wikipedia.org/wiki/Lutetium
Hafnium,Hf,72,178.49,Transition Metal,https://en.wikipedia.org/wiki/Hafnium
Tantalum,Ta,73,180.948,Transition Metal,https://en.wikipedia.org/wiki/Tantalum
Wolfram,W,74,183.84,Transition Metal,https://en.wikipedia.org/wiki/Wolfram
Rhenium,Re,75,186.207,Transition Metal,https://en.wikipedia.org/wiki/Rhenium
Osmium,Os,76,190.23,Transition Metal,https://en.wikipedia.org/wiki/Osmium
Iridium,Ir,77,192.217,Transition Metal,https://en.wikipedia.org/wiki/Iridium
Platinum,Pt,78,195.084,Transition Metal,https://en.wikipedia.org/wiki/Platinum
Gold,Au,79,196.967,Transition Metal,https://en.wikipedia.org/wiki/Gold
Mercury,Hg,80,200.59,Transition Metal,https://en.wikipedia.org/wiki/Mercury
Thallium,Tl,81,204.383,Metal,https://en.wikipedia.org/wiki/Thallium
Lead,Pb,82,207.2,Metal,https://en.wikipedia.org/wiki/Lead
Bismuth,Bi,83,208.98,Metal,https://en.wikipedia.org/wiki/Bismuth
Polonium,Po,84,210.0,Metal,https://en.wikipedia.org/wiki/Polonium
Astatine,At,85,210.0,Halogen,https://en.wikipedia.org/wiki/Astatine
Radon,Rn,86,222.0,Noble Gas,https://en.wikipedia.org/wiki/Radon
Francium,Fr,87,223.0,Alkali Metal,https://en.wikipedia.org/wiki/Francium
Radium,Ra,88,226.0,Alkaline Earth Metal,https://en.wikipedia.org/wiki/Radium
Actinium,Ac,89,227.0,Actinide,https://en.wikipedia.org/wiki/Actinium
Thorium,Th,90,232.038,Actinide,https://en.wikipedia.org/wiki/Thorium
Protactinium,Pa,91,231.036,Actinide,https://en.wikipedia.org/wiki/Protactinium
Uranium,U,92,238.029,Actinide,https://en.wikipedia.org/wiki/Uranium
Neptunium,Np,93,237.0,Actinide,https://en.wikipedia.org/wiki/Neptunium
Plutonium,Pu,94,244.0,Actinide,https://en.wikipedia.org/wiki/Plutonium
Americium,Am,95,243.0,Actinide,https://en.wikipedia.org/wiki/Americium
Curium,Cm,96,247.0,Actinide,https://en.wikipedia.org/wiki/Curium
Berkelium,Bk,97,247.0,Actinide,https://en.wikipedia.org/wiki/Berkelium
Californium,Cf,98,251.0,Actinide,https://en.wikipedia.org/wiki/Californium
Einsteinium,Es,99,252.0,Actinide,https://en.wikipedia.org/wiki/Einsteinium
Fermium,Fm,100,257.0,Actinide,https://en.wikipedia.org/wiki/Fermium
Mendelevium,Md,101,258.0,Actinide,https://en.wikipedia.org/wiki/Mendelevium
Nobelium,No,102,259.0,Actinide,https://en.wikipedia.org/wiki/Nobelium
Lawrencium,Lr,103,262.0,Actinide,https://en.wikipedia.org/wiki/Lawrencium
Rutherfordium,Rf,104,261.0,Transition Metal,https://en.wikipedia.org/wiki/Rutherfordium
Dubnium,Db,105,262.0,Transition Metal,https://en.wikipedia.org/wiki/Dubnium
Seaborgium,Sg,106,266.0,Transition Metal,https://en.wikipedia.org/wiki/Seaborgium
Bohrium,Bh,107,264.0,Transition Metal,https://en.wikipedia.org/wiki/Bohrium
Hassium,Hs,108,267.0,Transition Metal,https://en.wikipedia.org/wiki/Hassium
Meitnerium,Mt,109,268.0,Transition Metal,https://en.wikipedia.org/wiki/Meitnerium
Darmstadtium,Ds,110,271.0,Transition Metal,https://en.wikipedia.org/wiki/Darmstadtium
Roentgenium,Rg,111,272.0,Transition Metal,https://en.wikipedia.org/wiki/Roentgenium
Copernicium,Cn,112,285.0,Transition Metal,https://en.wikipedia.org/wiki/Copernicium
Nihonium,Nh,113,284.0,Metal,https://en.wikipedia.org/wiki/Nihonium
Flerovium,Fl,114,289.0,Metal,https://en.wikipedia.org/wiki/Flerovium
Moscovium,Mc,115,288.0,Metal,https://en.wikipedia.org/wiki/Moscovium
Livermorium,Lv,116,292.0,Metal,https://en.wikipedia.org/wiki/Livermorium
Tennessine,Ts,117,295.0,Halogen,https://en.wikipedia.org/wiki/Tennessine
Oganesson,Og,118,294.0,Noble Gas,https://en.wikipedia.org/wiki/Oganesson
Output: ss
I am still learning, so thanks for all the suggestions :)
-
1\$\begingroup\$ Your lanthanide indices are incorrect? \$\endgroup\$Reinderien– Reinderien2022年01月06日 18:16:56 +00:00Commented Jan 6, 2022 at 18:16
-
\$\begingroup\$ @Reinderien Oops, yea you are right. A silly mistake it was \$\endgroup\$Delrius Euphoria– Delrius Euphoria2022年01月06日 18:21:42 +00:00Commented Jan 6, 2022 at 18:21
-
\$\begingroup\$ Heya @CoolCloud, what is it that you'd like feedback on here? Anything and everything? \$\endgroup\$ades– ades2022年01月10日 18:23:16 +00:00Commented Jan 10, 2022 at 18:23
-
\$\begingroup\$ @ades Yea sure, anything :) \$\endgroup\$Delrius Euphoria– Delrius Euphoria2022年01月10日 18:56:55 +00:00Commented Jan 10, 2022 at 18:56
1 Answer 1
Let's start with what I think will be the easiest bit: the data format of your save-files.
CSV
Csv is quite old and not very common nowadays. I'd say most Python developers use json most of the time, but also yaml, toml, and xml are common. That would convert your "database" from
Element,Symbol,AtomicNumber,AtomicMass,Type,WikiLink
Hydrogen,H,1,1.007,Nonmetal,https://en.wikipedia.org/wiki/Hydrogen
...
to
[
{
"Element": "Hydrogen",
"Symbol": "H",
"Atomic Number": 1,
"Atomic Mass": 1.007,
"Type": "Nonmetal",
"Wiki Link": "https://en.wikipedia.org/wiki/Hydrogen"
},
...
]
which is way more readable, and will be very easy to deal with in Python. For example:
>>> import json
>>> with open("elements.json", "r") as f:
... data = json.load(f)
...
>>> data
[{'Element': 'Hydrogen', 'Symbol': 'H', 'Atomic Number': 1, 'Atomic Mass': 1.007, 'Type': 'Nonmetal', 'Wiki Link': 'https://en.wikipedia.org/wiki/Hydrogen'}, {'Element': 'Carbon'}]
Now, as you can see this is essentially the same data structure in Python as it is in json. So I'm gonna make my own life a little easier and just keep this as a variable in a Python-file for now, but you don't have to do that.
Structure and requirements
I think that your application should be packaged. How you'd typically structure a Python package is like this:
.
├── README.rst
├── periodic_table
│ ├── __init__.py
│ ├── elements.py
│ ├── loader.py
│ └── widgets.py
└── requirements.txt
1 directory, 6 files
One thing is that it displays purpose - you can see that all of these files are part of the package periodic_table
. It also creates a namespace. The file __init__.py
will also mean that you can refer to the modules in this package by changing from
from widgets import Element, Key, Demo
to
from periodic_table.widgets import Element, Demo, Key
and now the reader immediately knows where these functions come from. Personally I think that an even better suggestion is:
from periodic_table import widgets
# and then use widgets.Element, etc.
But I concede that I don't abide by this strongly.
But that's not packaged!
I cheated a little just now - I showed a directory with just a requirements.txt
file. Better than this would be to configure the package. I'll be modern and will skip setup.py
, and I will also be very minimal with the metadata. We now have:
.
├── README.rst
├── periodic_table
│ ├── __init__.py
│ ├── elements.py
│ ├── loader.py
│ └── widgets.py
├── pyproject.toml
└── setup.cfg
1 directory, 7 files
pyproject.toml
You can consider this file "boilerplate code" for packages and just copy the content without deeper understanding (for now).
[build-system]
requires = [
"setuptools>=42",
"wheel"
]
build-backend = "setuptools.build_meta"
setup.cfg
This is where you define all your metadata for the package. It doesn't all matter if you just want to make it installable, but if you wanted to publish something on PyPI you should make some effort.
[metadata]
name = periodic-table
version = 1.0.0
[options]
packages = find:
python_requires = >=3.6
setup_requires =
wheel
install_requires =
tkinter
pandas
[options.entry_points]
console_scripts =
show-periodic-table = periodic_table:main
With all of this done, your package would be installable with pip
! You can then from the directory just type pip install .
and the package periodic_table
would be installed in that environment. And this also allows us to define commands to call on certain functions!
And this sort of segways us into the next point - your current entry-point is confusing.
if __name__ == "__main__":
and other tings
When I look at your filenames my interpretation is that main.py
is the entry-point, yet at the bottom of widgets.py
you have:
if __name__ == '__main__':
root = tk.Tk()
Element(root,'Hydrogen').pack(padx=5,pady=20)
Key(root,e_type='Halogen').pack()
Demo(root,'Hydrogen').pack()
root.mainloop()
Here I can clearly see that widgets.py
is "executable" (interpretable?) and that this file is intended to start a tkinter loop. I'm guessing that this just an artifact from when you first learnt to use tkinter, but this is basically what we should have done in your main.py
file, so let's do that.
main.py
The point behind having if __name__ == "__main__":
is that it means that the code wrapped in that condition only will be called upon if the current file is executed directly, like this:
$ python3 main.py # We want this to run our program
But not be loaded if the module is imported, like this:
from periodic_table import main # We do not want this to run our program
Entry-points
What I did in setup.cfg
(go back and have a look at the end of the file) is to also set up an additional way to call on your program. The lines
console_scripts =
show-periodic-table = periodic_table:main
declare that there should be an alias set that you can use from your shell (on Linux and macOS, not sure how this works from PowerShell and others) called show-periodic-table
, and that this should call on the function main
in periodic_table/__init__.py
. You could also call the function show_periodic_table
and place it in widgets.py
if you wanted to - in that case the line would be show-periodic-table = periodic_table.widgets:show_periodic_table
.
To fit your code with this, we only really have to rename main.py
into __init__.py
, add like two lines, and indent most of the existing lines. I will, however, make one other tiny change just to try and keep the code almost working. We can keep widgets.py
exactly as it is today, but remember how I mentioned I'd just store the otherwise-json data in a variable? That allows me to get rid of the loader while also simplifying packaging (I'm not gonna explain what I mean - this review has to end some time today...). So we have:
.
├── README.rst
├── periodic_table
│ ├── __init__.py
│ ├── constants.py
│ └── widgets.py
├── pyproject.toml
└── setup.cfg
1 directory, 6 files
constants.py
ELEMENTS = [
{
"Element": "Hydrogen",
"Symbol": "H",
"Atomic Number": 1,
"Atomic Mass": 1.007,
"Type": "Nonmetal",
"Wiki Link": "https://en.wikipedia.org/wiki/Hydrogen"
},
...
]
SEC_LAYOUT = [['x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x' ,'x'],
...
]
...
__init__.py
This is now where we have our entry point. I've left it called just main
, but an even more fitting name would probably be show_periodic_table
.
import tkinter as tk
from tkinter import ttk
from periodic_table.widgets import Element, Key, Demo
from periodic_table.constants import MAIN_LAYOUT, SEC_LAYOUT, ELEMENTS, TYPES
def main():
root = tk.Tk()
root.title('Periodic Table')
# Frame for entire table
table = tk.Frame(root)
table.pack()
...
# Placing the representation in the frame
Demo(repr_tab, elements[0]).grid(row=i,column=j+1,rowspan=5)
root.mainloop()
And I think I'll let that wrap this session up. The code would need some other modifications to work entirely (since we now already have a list of element dictionaries instead of having to load from csv), but I'll leave that for you to figure out.
-
\$\begingroup\$ Great explanation, I think this is the professional way of releasing packages because I have seen such formats on many big GitHub repos. I will look into these and implement these. And also, about the
if __name__ == '__main__':
part, I said those not for it to be the entry point, but just as for testing, just running that file will give a way on how each of those widgets would look. And got a bit confused, should I make a json file or put the data insideconstants.py
? Thanks for the answer :) \$\endgroup\$Delrius Euphoria– Delrius Euphoria2022年01月11日 02:44:30 +00:00Commented Jan 11, 2022 at 2:44 -
1\$\begingroup\$ Just start with the
constants.py
for now - the reason I didn't say to make ajson
-file for that data is because it would make the packaging a little bit more tricky; you'd have to define that the json-file also should be packaged, and then you need to know how to refer to its installed location. \$\endgroup\$ades– ades2022年01月11日 08:49:53 +00:00Commented Jan 11, 2022 at 8:49