Now that we have had a glimpse of working with JSON files, this should be easy. Let's take a look at the first few lines of the chords.json file from the json directory:
{
"Major" : [0, 4, 7],
"Minor" : [0, 3, 7],
"Sus4" : [0, 5, 7],
"5" : [0, 4, 6],
"Diminished" : [0, 3, 6],
...
}
This is very similar to the scales structure. Let's say we want to figure out what the C# major chord would look like. So we start with the C# key, which is 0. Then we look at the list of major chords, which read: [0, 4, 7]. So starting at C# the next key to highlight is 4 semitones above and the next is 7 semitones above C#. So the final chord structure for the C# major chord would be:
C#, (C# + 4 semitones) , (C# + 7 semitones)
The GUI is also very similar to the scales section:
We begin by adding a constant for the path to the chords.json file in 7.05 constants.py:
CHORDS_JSON_FILE = '../json/chords.json'
Next, we read the contents of this file in a new class attribute, self.chords:
with open(CHORDS_JSON_FILE, 'r') as f:
self.chords = json.load(f, object_pairs_hook=OrderedDict)
We then modify the chords frame GUI to add two combobox (see the complete GUI in 7.05 view.py build_chords_frame):
self.chords_selector = ttk.Combobox(self.chords_frame, values=[k for k
in self.chords.keys()])
self.chords_selector.current(0)
self.chords_selector.bind("<<ComboboxSelected>>", self.on_chord_changed)
self.chords_selector.grid(row=1, column=1, sticky='e', padx=10,
pady=10)
self.chords_key_selector = ttk.Combobox(self.chords_frame, values=[k
for k in KEYS])
self.chords_key_selector.current(0)
self.chords_key_selector.bind("<<ComboboxSelected>>", self.on_chords_key_changed)
Next, we added the two event callbacks defined previously:
def on_chord_changed(self, event):
self.remove_all_key_highlights()
self.find_chord(event)
def on_chords_key_changed(self, event):
self.remove_all_key_highlights()
self.find_chord(event)
The find_chord method queries the self.chords dict for the keys to be highlighted, adds the key offsets from the root note, and calls it to be highlighted and played:
def find_chord(self, event=None):
self.selected_chord = self.chords_selector.get()
self.chords_selected_key = self.chords_key_selector.get()
index_of_selected_key = KEYS.index(self.chords_selected_key)
self.keys_to_highlight = [ ALL_KEYS[i+index_of_selected_key] for
i in self.chords[self.selected_chord]]
self.highlight_list_of_keys(self.keys_to_highlight)
play_chord_in_new_thread(self.keys_to_highlight)
The final code in this iteration modifies the on_mode_changed method to highlight the chord as soon as the chord mode is selected:
def on_mode_changed(self, event):
self.remove_all_key_highlights()
selected_mode = self.mode_selector.get()
if selected_mode == 'Scales':
self.show_scales_frame()
self.find_scale()
elif selected_mode == 'Chords':
self.show_chords_frame()
self.find_chord()
elif selected_mode == 'Chord Progressions':
self.show_progressions_frame()
That concludes the iteration. If you now run 7.05 view.py, you will find a functional chords section that lets us find chords of different varieties in different scales.