Осталось только привязать к канвасу обработку только что созданного метода. Добавьте следую строку после прикрепления канваса (self.canvas.grid...)
self.canv.bind("", self.draw)
означает "при движении зажатой левой кнопки мыши" вызывать метод draw. Теперь мы можем рисовать!

Уже выглядит неплохо. Добавим возможность изменять цвет кисти, заставим кнопки верхнего ряда работать. Для этого сначала создадим метод изменения цвета кисти:
| def set_color(self, new_color):
| self.color = new_color
После этого в каждой кнопке верхнего ряда следует добавить код обработки нажатия этой кнопки по следующему шаблону:
| red_btn = Button(self, text="Red", width=10, command=lambda: self.set_color("red"))
| red_btn.grid(row=0, column=1)
Код который мы добавили - command = lambda: self.set_color("red"), привязывает функцию с нужным нам аргументом к кнопке. Мы используем lambda-функцию потому-что, без lambda функция вызовется сразу при создании кнопки, а не только при ее нажатии. (Можете попробовать такую строку command = self.set_color("red") и цвет сразу установится на красный). Добавив этот код с нужными аргументами (а это "green", "blue", "black", "white") ко всем кнопкам получим возможность изменять цвет кисти:

Теперь добавим метод изменения размера кисти:
| def set_brush_size(self, new_size):
| self.brush_size = new_size
И модернизируем код каждой кнопки нижнего ряда по следующему шаблону:
one_btn = Button(self, text="Two", width=10, command=lambda: self.set_brush_size(2))
Логика работы этих кнопок та же, что и у кнопок изменения цвета кисти. Получаем следующий результат:
На этом практически все, осталось добавить функционал очистки холста. Мы оставили место в верхнем ряду кнопок, его и заполним. Добавьте следующий код в наш метод setUI:
| clear_btn = Button(self, text="Clear all", width=10, command=lambda: self.canv.delete("all"))
| clear_btn.grid(row=0, column=6, sticky=W)
Для очистки холста мы используем метод delete класса Canvas, чтобы удалить все с холста в качестве аргумента методу delete мы передаем "all". Вот и все, у нас есть примитивный Paint.
P.S.: Запустив данное приложение, вы заметите, что если провести мышкой быстро, то линии у нас получаются прерывчастыми. Это вызвано тем, что движение происходит быстрее, чем завершается очередной цикл root.mainloop, соответственно все, что попадает "между" итерациями цикла пропадает. Решение этой проблемы само по себе достаточно интересное задание, однако оно требует намного более сложного кода.
P.P.S: Исходный код проекта на GitHub (https://github.com/g0t0wasd/python/blob/master/programs/gui/PyPaint.py)
Всем привет! Меня зовут Дмитрий Красота, я ведущий программист, сертифицированный Python-cпециалист.
О себе.
Изначально программирование было просто хобби. Еще в школе я баловался с JavaScript, потом прочел пару книг по С++. Как и многие хотел написать собственную игру. Конечно, до тех пор, пока я не узнал о языке программирования Pythonэто были только фантазии. Постепенно совершенствуясь и открывая новые источники информации, я все больше и больше стал склоняться к выбору профессии программиста, разработчика на Python.
О блоге. (http://pythonicway.com)
Основная проблема, с которой я сталкивался когда был еще школьником это отсутствие качественного обучающего материала по программированию. Обидно, ведь тема благодатная и прибыльная, многие известные люди называют программирование - грамотностью 21 века. Так же как умение читать в 18 веке давало значительное преимущество перед остальными людьми, так и умение программировать сегодня это в значительной мере залог успешной и благополучной жизни (ни для кого не секрет сколько сегодня получают программисты). И тем не менее информация в книгах достаточно ограниченая, скудная, лишенная наглядных примеров.
В своем блоге я стараюсь дать максимально широкий спектр информации по теме программирования на Python. И что самое важное - дополнить его наглядными примерами программ на языке Python, включительно с примерами игр на этом ЯП.