Окна

В этом уроке рассмотрим основные настройки окон, в которых располагаются виджеты. Обычные окна в tkinter порождаются не только от класса Tk, но и Toplevel. От Tk принято создавать главное окно. Если создается многооконное приложение, то остальные окна создаются от Toplevel. Методы обоих классов схожи.

Размер и положение окна

По умолчанию окно приложения появляется в верхнем левом углу экрана. Его размер (ширина и высота) определяется совокупностью размеров расположенных в нем виджетов. В случае если окно пустое, то tkinter устанавливает его размер в 200 на 200 пикселей.

С помощью метода geometry() можно изменить как размер окна, так и его положение. Метод принимает строку определенного формата.

from tkinter import *
 
root = Tk()
 
root.geometry('600x400+200+100')
 
root.mainloop()

Первые два числа в строке-аргументе geometry() задают ширину и высоту окна. Вторая пара чисел обозначает смещение на экране по осям x и y. В примере окно размерностью 600 на 400 будет смещено от верхней левой точки экрана на 200 пикселей вправо и на 100 пикселей вниз.

Если перед обоими смещениями вместо плюса указывается минус, то расчет происходит от нижних правых углов экрана и окна. Так выражение root.geometry('600x400-0-0') заставит окно появиться в нижнем правом углу.

В аргументе метода geometry() можно не указывать либо размер, либо смещение. Например, чтобы сместить окно, но не менять его размер, следует написать root.geometry('+200+100').

Бывает удобно, чтобы окно появлялось в центре экрана. Методы winfo_screenwidth() и winfo_screenheight() возвращают количество пикселей экрана, на котором появляется окно. Рассмотрим, как поместить окно в центр, если размер окна известен:

…
w = root.winfo_screenwidth() # ширина экрана
h = root.winfo_screenheight() # высота экрана
w = w//2 # середина экрана
h = h//2 
w = w - 200 # смещение от середины
h = h - 200
root.geometry('400x400+{}+{}'.format(w, h))

Здесь мы вычитаем половину ширины и высоты окна (по 200 пикселей). Иначе в центре экрана окажется верхний левый угол окна, а не его середина.

Если размер окна неизвестен, то его можно получить с помощью того же метода geometry(), но без аргументов. В этом случае метод возвращает строку, содержащую сведения о размерах и смещении, из которой можно извлечь ширину и высоту окна.

from tkinter import *
root = Tk()
 
Button(text="Button", width=20).pack()
Label(text="Label", width=20, height=3).pack()
Button(text="Button", width=20).pack()
 
root.update_idletasks()
s = root.geometry()
s = s.split('+')
s = s[0].split('x')
width_root = int(s[0])
height_root = int(s[1])
 
w = root.winfo_screenwidth()
h = root.winfo_screenheight()
w = w // 2
h = h // 2 
w = w - width_root // 2
h = h - height_root // 2
root.geometry('+{}+{}'.format(w, h))
 
root.mainloop()

Метод update_idletasks() позволяет перезагрузить данные об окне после размещения на нем виджетов. Иначе geometry() вернет строку, где ширина и высота равняются по одному пикселю. Видимо таковы параметры на момент запуска приложения.

По умолчанию пользователь может разворачивать окно на весь экран, а также изменять его размер, раздвигая границы. Эти возможности можно отключить с помощью метода resizable(). Так root.resizable(False, False) запретит изменение размеров главного окна как по горизонтали, так и вертикали. Развернуть на весь экран его также будет невозможно, при этом соответствующая кнопка разворота исчезает.

resizable.png

Заголовок окна

По умолчанию с стоке заголовка окна находится надпись "tk". Для установки собственного названия используется метод title().

… 
root.title("Главное окно")

root_title.png

Если необходимо, заголовок окна можно вообще убрать. В программе ниже второе окно (Toplevel) открывается при клике на кнопку, оно не имеет заголовка, так как к нему был применен метод overrideredirect() с аргументом True. Через пять секунд данное окно закрывается методом destroy().

from tkinter import *
root = Tk()
root.title("Главное окно")
 
def about():
    a = Toplevel()
    a.geometry('200x150')
    a['bg'] = 'grey'
    a.overrideredirect(True)
    Label(a, text="About this").pack(expand=1)
    a.after(5000, lambda: a.destroy())
 
Button(text="Button", width=20).pack()
Label(text="Label", width=20, height=3).pack()
Button(text="About", width=20, command=about).pack()
 
root.mainloop()

overrideredirect.png

Практическая работа

Напишите программу, в которой на главном окне находятся холст и кнопка "Добавить фигуру". Кнопка открывает второе окно, включающее четыре поля для ввода координат и две радиокнопки для выбора, рисовать ли на холсте прямоугольник или овал. Здесь же находится кнопка "Нарисовать", при клике на которую соответствующая фигура добавляется на холст, а второе окно закрывается. Проверку корректности ввода в поля можно опустить.

prac11.png

Курс с примерами решений практических работ: android-приложение, pdf-версия.

Комментарии

Хотел сделать пропорцию, что бы был квадрат/круг, но что-то глючно вышло. Ещё хотел визуализировать какой где цвет, но для отдельного элемента фон свой сделать нельзя как я понял :(

https://pastebin.com/KfTY41wG

YOU'RE BEEN GNOMED

Практическая работа. Пыталась изобразить из мухи слона и 2 дня провозилась с классами, а потом поняла, что муха летает не хуже.
from tkinter import *
root=Tk()
root.title("Прямовал")
def add_window():
    def paint():        
        xx1=x1.get()
        xx2=x2.get()
        yy1=y1.get()
        yy2=y2.get()        
        if var.get()==2:
            c.create_oval(xx1,yy1,xx2,yy2)
        elif var.get()==1:
            c.create_rectangle(xx1,yy1,xx2,yy2)
        a.destroy()
 
    a=Toplevel()
    a.title("Фигура")
    a.resizable(False, False)  
    Label(a, text="x1").grid(row=0, column=0)
    Label(a, text="y1").grid(row=0, column=2)
    Label(a, text="x2").grid(row=1, column=0)
    Label(a, text="y2").grid(row=1, column=2)
    x1=Entry(a, width=5)
    x1.grid(row=0, column=1, sticky=W)
    y1=Entry(a, width=5)
    y1.grid(row=0, column=3,  sticky=W)
    x2=Entry(a, width=5)
    x2.grid(row=1, column=1,  sticky=W)
    y2=Entry(a, width=5)
    y2.grid(row=1, column=3,  sticky=W)
    var=IntVar()
    Radiobutton(a, text="Прямоугольник", variable=var, value=1).grid(row=2, columnspan=4)
    Radiobutton(a, text="Овал", variable=var, value=2).grid(row=3, columnspan=4, sticky=W)
    Button(a, text="Нарисовать", command=paint).grid(row=4, columnspan=4)
    a.geometry('+{}+{}'.format(w+width_root, h)) # размещение доп.окна справа от основного
 
c=Canvas(root, width=500, height=500, bg="white")
c.grid()
Button(text="Добавить фигуру", width=20, command=add_window).grid()
# размещение окна по центру
root.update_idletasks()
s = root.geometry()
s = s.split('+')
s = s[0].split('x')
width_root = int(s[0])
height_root = int(s[1])
w = root.winfo_screenwidth()
h = root.winfo_screenheight()
w = w // 2
h = h // 2 
w = w - width_root // 2
h = h - height_root // 2
root.geometry('+{}+{}'.format(w, h)) 
root.mainloop()

 

from tkinter import *

root = Tk()
root.title('Главное окно')
newWnd=None #Глобальная. Для проверки окна на 'ужеоткрытость'

def  showNewWnd():
    def drawFigure():
        if     radioButton_var.get()==1:
            holst.create_rectangle(entryX1.get(),entryY1.get(), entryX2.get(),entryY2.get())
        elif radioButton_var.get()==2:
            holst.create_oval(entryX1.get(),entryY1.get(), entryX2.get(),entryY2.get())
        else:
            print('Выбери, что рисовать, да!')
            return
        newWnd.destroy()

    global newWnd
    if type(newWnd) is Toplevel: #Если окно уже открыто, убиваем старый экземпляр
        newWnd.destroy()

    newWnd=Toplevel()
    place=root.geometry() #Берём текущие параметры главного окна
    place=place.split('+')
    rootX=int(place[1])
    rootY=int(place[2])
    place=place[0].split('x')
    rootWidth=int(place[0])
    rootHeight=int(place[0])
    newWndX=rootX+rootWidth+14 #Эмпирический коэффициент 14 для красоты
    newWndY=rootY                                #Другое имя для читабельности
    newWnd.geometry('160x150+{}+{}'.format(newWndX, newWndY))
    newWnd.title('Фигура')

    frame1=Frame(newWnd); frame1.pack(side=TOP, pady=10)   #Некоторые объекты окна
    labelX1=Label(frame1, text='x1').pack(side=LEFT)                   #неправильно отображаются
    entryX1=Entry(frame1, width=5); entryX1.pack(side=LEFT)     #и ведут себя в функциях,
    labelY1=Label(frame1, text='y1').pack(side=LEFT)                   #если запакованы в одной строке
    entryY1=Entry(frame1, width=5); entryY1.pack(side=LEFT)     #var=Entry().pack()

    frame2=Frame(newWnd); frame2.pack(side=TOP)                #Пришлось разбивать на
    labelX2=Label(frame2, text='x2').pack(side=LEFT)                #две логических и записывать
    entryX2=Entry(frame2, width=5); entryX2.pack(side=LEFT)  #в одну для читабельности.
    labelY2=Label(frame2, text='y2').pack(side=LEFT)
    entryY2=Entry(frame2, width=5); entryY2.pack(side=LEFT)

    radioButton_var=IntVar()
    radioButton_var.set(0)
    radioButton1=Radiobutton(newWnd, text='Прямоугольник', value=1, variable=radioButton_var, padx=10); radioButton1.pack(anchor=W)
    radioButton2=Radiobutton(newWnd, text='Овал', value=2, variable=radioButton_var, padx=10); radioButton2.pack(anchor=W)

    buttonNewWnd=Button(newWnd, text='Нарисовать', command=drawFigure).pack()

holst = Canvas(root, width=800, height=600, bg="#eeeeee"); holst.pack()

buttonRoot=Button(text='Добавить фигуру', width=30, command=showNewWnd).pack()

root.mainloop()

 

from tkinter import *
 
 
 
root = Tk()
w = root.winfo_screenwidth()
h = root.winfo_screenheight()
 
w1 = w // 2
h1 = h // 2
 
w2 = w1 - 200
h2 = h1 - 200
 
window = root.geometry("500x500+{}+{}".format(w2, h2))
root.title("Овал/Круг")
c = Canvas(root, width = 500, height = 480)
c.pack()
 
def add():
    a = Toplevel()
    a.title("Фигура")
    a.geometry("200x200+{}+{}".format(w1 + 300, h2))
 
    f_top = Frame(a)
    f_bot = Frame(a)
    l1 = Label(f_top, text = "x1")
    l3 = Label(f_top, text = "y1")
    x1 = Entry(f_top, width = 5)
    y1 = Entry(f_top, width = 5)
 
    l2 = Label(f_bot, text = "x2")
    l4 = Label(f_bot, text = "y2")
    x2 = Entry(f_bot, width = 5)
    y2 = Entry(f_bot, width = 5)
 
    var = IntVar()
    var.set(0)
    r1 = Radiobutton(a, text = "Прямоугольник", value = 1, variable = var)
    r2 = Radiobutton(a, text = "Овал", value = 2, variable = var)
 
    def draw():
        x_1 = x1.get()
        x_2 = x2.get()
        y_1 = y1.get()
        y_2 = y2.get()
        if var.get() == 1:
            c.create_rectangle(x_1, y_1, x_2, y_2, outline="black")
        elif var.get() == 2:
            c.create_oval(x_1, y_1, x_2, y_2, outline="black")
 
 
 
    b2 = Button(a, text = "Нарисовать", command = draw)
 
    f_top.pack()
    f_bot.pack()
    l1.pack(side = LEFT)
    x1.pack(side = LEFT)
 
    l2.pack(side = LEFT)
    x2.pack(side = LEFT)
 
    l3.pack(side = LEFT)
    y1.pack(side = LEFT)
 
    l4.pack(side = LEFT)
    y2.pack(side = LEFT)
 
    r1.pack()
    r2.pack()
 
    b2.pack()
 
 
 
b1 = Button(text = "Добавить фигуру", command = add)
b1.pack(side = BOTTOM)
 
 
root.mainloop()

Я только сегодня понял, что если создать объект внутри функции, то обратиться к нему из другой функции или из основной программы нельзя. Посмотрел, как сделали другие, в основном суют функцию рисования внутрь функции создания второго окна, но мне такой способ вообще в голову не пришел, он мне не кажется логичным... Я опять сделал через глобалки, а везде говорят, не делайте глобалки! А как еще можно вызвать атрибуты объекта из другой функции, подскажите!
from tkinter import *
 
def action1():
    global entry1,entry2,entry3,entry4,r_var
    #global r_var
    root2 = Toplevel()
    root2.minsize(width=300, height=10)
    root2.title('параметры')
    frame1 = Frame(root2);frame1.pack()
    frame2 = Frame(root2);frame2.pack()
    lab1 = Label(frame1, text='x1');lab1.pack(side=LEFT)
    entry1 = Entry(frame1,width=3);entry1.pack(side=LEFT)
    lab2 = Label(frame1, text='y1');lab2.pack(side=LEFT)
    entry2 = Entry(frame1,width=3);entry2.pack(side=LEFT)
    lab3 = Label(frame2, text='x2');lab3.pack(side=LEFT)
    entry3 = Entry(frame2,width=3);entry3.pack(side=LEFT)
    lab4 = Label(frame2, text='y2');lab4.pack(side=LEFT)
    entry4 = Entry(frame2,width=3);entry4.pack(side=LEFT)
    r_var = BooleanVar()
    r_var.set(0)
    rbut1 = Radiobutton(root2, text='прямоугольник',variable=r_var, value=0);rbut1.pack()
    rbut2 = Radiobutton(root2, text='овал',variable=r_var, value=1);rbut2.pack()
    but2 = Button(root2, text='нарисовать', command=action2);but2.pack()
 
def action2():
    x1, y1, x2, y2=entry1.get(),entry2.get(),entry3.get(),entry4.get()
    if r_var.get()==0:
        try:
            c.create_rectangle(x1,y1,x2,y2)
        except:
            return
    else:
        try:
            c.create_oval(x1,y1,x2,y2)
        except:
            return
 
root = Tk()
c = Canvas(root, width=300, height=200, bg="white");c.pack()
but1=Button(root,text='Добавить фигуру',command=action1);but1.pack()
 
root.mainloop()

from tkinter import *
root = Tk()
c = Canvas(root, width = 600, height = 400)
root.geometry('600x400')
root['bg'] = 'white'
 
def painting():
    window_paint = Toplevel()
    window_paint.geometry('200x200')
    window_paint['bg'] = 'white'
 
    def print_figure():
        x1, y1, x2, y2 = entry_x1.get(), entry_y1.get(), entry_x2.get(), entry_y2.get()
        radio_val = val_type.get()
        if radio_val == 1:
            c.create_rectangle(x1, y1, x2, y2)
        else:
            c.create_oval(x1, y1, x2, y2)
 
    frame_1 = Frame(window_paint)
    frame_2 = Frame(window_paint)
    frame_1.pack(side = LEFT)
    frame_2.pack(side = LEFT)
 
    label_x1 = Label(window_paint, text = 'x1:')
    entry_x1 = Entry(window_paint, width = 5)
    label_y1 = Label(window_paint, text = 'y1:')
    entry_y1 = Entry(window_paint, width = 5)
 
    label_x2 = Label(window_paint, text = 'x2:')
    entry_x2 = Entry(window_paint, width = 5)
    label_y2 = Label(window_paint, text = 'y2')
    entry_y2 = Entry(window_paint, width = 5)
 
    label_x1.pack(side = TOP, anchor = NW)
    entry_x1.pack(side = TOP, anchor = NW)
    label_y1.pack(side = TOP, anchor = NW)
    entry_y1.pack(side = TOP, anchor = NW)
 
    label_x2.pack(side = TOP, anchor = NW)
    entry_x2.pack(side = TOP, anchor = NW)
    label_y2.pack(side = TOP, anchor = NW)
    entry_y2.pack(side = TOP, anchor = NW)
 
    val_type = BooleanVar()
    radio_1 = Radiobutton(frame_2, text = 'rectangle', variable = val_type, value = 1)
    radio_2 = Radiobutton(frame_2, text = 'oval', variable = val_type, value = 0)
    button_print = Button(frame_2, text = 'Print', command = print_figure)
 
    radio_1.pack(side = LEFT)
    radio_2.pack(side = LEFT)
    button_print.pack()
    c.pack()
 
Button(text='Add figure', width=20, command = painting).pack(side = BOTTOM)
root.mainloop()
p.s. все рисует, ляпота. Прошу простить за беспорядок во втором окне(уж сложно расстановка дается без grida) p.s.s. посоветуйте как можно улучшить код

import tkinter as tk
 
COLORS = ['snow', 'ghost white', 'white smoke', 'gainsboro', 'floral white', 'old lace',
          'linen', 'antique white', 'papaya whip', 'blanched almond', 'bisque', 'peach puff',
          'navajo white', 'lemon chiffon', 'mint cream', 'azure', 'alice blue', 'lavender',
          'lavender blush', 'misty rose', 'dark slate gray', 'dim gray', 'slate gray',
          'light slate gray', 'gray', 'light grey', 'midnight blue', 'navy', 'cornflower blue', 'dark slate blue',
          'slate blue', 'medium slate blue', 'light slate blue', 'medium blue', 'royal blue', 'blue',
          'dodger blue', 'deep sky blue', 'sky blue', 'light sky blue', 'steel blue', 'light steel blue',
          'light blue', 'powder blue', 'pale turquoise', 'dark turquoise', 'medium turquoise', 'turquoise',
          'cyan', 'light cyan', 'cadet blue', 'medium aquamarine', 'aquamarine', 'dark green', 'dark olive green',
          'dark sea green', 'sea green', 'medium sea green', 'light sea green', 'pale green', 'spring green',
          'lawn green', 'medium spring green', 'green yellow', 'lime green', 'yellow green',
          'forest green', 'olive drab', 'dark khaki', 'khaki', 'pale goldenrod', 'light goldenrod yellow',
          'light yellow', 'yellow', 'gold', 'light goldenrod', 'goldenrod', 'dark goldenrod', 'rosy brown',
          'indian red', 'saddle brown', 'sandy brown',
          'dark salmon', 'salmon', 'light salmon', 'orange', 'dark orange',
          'coral', 'light coral', 'tomato', 'orange red', 'red', 'hot pink', 'deep pink', 'pink', 'light pink',
          'pale violet red', 'maroon', 'medium violet red', 'violet red',
          'medium orchid', 'dark orchid', 'dark violet', 'blue violet', 'purple', 'medium purple',
          'thistle', 'snow2', 'snow3',
          'snow4', 'seashell2', 'seashell3', 'seashell4', 'AntiqueWhite1', 'AntiqueWhite2',
          'AntiqueWhite3', 'AntiqueWhite4', 'bisque2', 'bisque3', 'bisque4', 'PeachPuff2',
          'PeachPuff3', 'PeachPuff4', 'NavajoWhite2', 'NavajoWhite3', 'NavajoWhite4',
          'LemonChiffon2', 'LemonChiffon3', 'LemonChiffon4', 'cornsilk2', 'cornsilk3',
          'cornsilk4', 'ivory2', 'ivory3', 'ivory4', 'honeydew2', 'honeydew3', 'honeydew4',
          'LavenderBlush2', 'LavenderBlush3', 'LavenderBlush4', 'MistyRose2', 'MistyRose3',
          'MistyRose4', 'azure2', 'azure3', 'azure4', 'SlateBlue1', 'SlateBlue2', 'SlateBlue3',
          'SlateBlue4', 'RoyalBlue1', 'RoyalBlue2', 'RoyalBlue3', 'RoyalBlue4', 'blue2', 'blue4',
          'DodgerBlue2', 'DodgerBlue3', 'DodgerBlue4', 'SteelBlue1', 'SteelBlue2',
          'SteelBlue3', 'SteelBlue4', 'DeepSkyBlue2', 'DeepSkyBlue3', 'DeepSkyBlue4',
          'SkyBlue1', 'SkyBlue2', 'SkyBlue3', 'SkyBlue4', 'LightSkyBlue1', 'LightSkyBlue2',
          'LightSkyBlue3', 'LightSkyBlue4', 'SlateGray1', 'SlateGray2', 'SlateGray3',
          'SlateGray4', 'LightSteelBlue1', 'LightSteelBlue2', 'LightSteelBlue3',
          'LightSteelBlue4', 'LightBlue1', 'LightBlue2', 'LightBlue3', 'LightBlue4',
          'LightCyan2', 'LightCyan3', 'LightCyan4', 'PaleTurquoise1', 'PaleTurquoise2',
          'PaleTurquoise3', 'PaleTurquoise4', 'CadetBlue1', 'CadetBlue2', 'CadetBlue3',
          'CadetBlue4', 'turquoise1', 'turquoise2', 'turquoise3', 'turquoise4', 'cyan2', 'cyan3',
          'cyan4', 'DarkSlateGray1', 'DarkSlateGray2', 'DarkSlateGray3', 'DarkSlateGray4',
          'aquamarine2', 'aquamarine4', 'DarkSeaGreen1', 'DarkSeaGreen2', 'DarkSeaGreen3',
          'DarkSeaGreen4', 'SeaGreen1', 'SeaGreen2', 'SeaGreen3', 'PaleGreen1', 'PaleGreen2',
          'PaleGreen3', 'PaleGreen4', 'SpringGreen2', 'SpringGreen3', 'SpringGreen4',
          'green2', 'green3', 'green4', 'chartreuse2', 'chartreuse3', 'chartreuse4',
          'OliveDrab1', 'OliveDrab2', 'OliveDrab4', 'DarkOliveGreen1', 'DarkOliveGreen2',
          'DarkOliveGreen3', 'DarkOliveGreen4', 'khaki1', 'khaki2', 'khaki3', 'khaki4',
          'LightGoldenrod1', 'LightGoldenrod2', 'LightGoldenrod3', 'LightGoldenrod4',
          'LightYellow2', 'LightYellow3', 'LightYellow4', 'yellow2', 'yellow3', 'yellow4',
          'gold2', 'gold3', 'gold4', 'goldenrod1', 'goldenrod2', 'goldenrod3', 'goldenrod4',
          'DarkGoldenrod1', 'DarkGoldenrod2', 'DarkGoldenrod3', 'DarkGoldenrod4',
          'RosyBrown1', 'RosyBrown2', 'RosyBrown3', 'RosyBrown4', 'IndianRed1', 'IndianRed2',
          'IndianRed3', 'IndianRed4', 'sienna1', 'sienna2', 'sienna3', 'sienna4', 'burlywood1',
          'burlywood2', 'burlywood3', 'burlywood4', 'wheat1', 'wheat2', 'wheat3', 'wheat4', 'tan1',
          'tan2', 'tan4', 'chocolate1', 'chocolate2', 'chocolate3', 'firebrick1', 'firebrick2',
          'firebrick3', 'firebrick4', 'brown1', 'brown2', 'brown3', 'brown4', 'salmon1', 'salmon2',
          'salmon3', 'salmon4', 'LightSalmon2', 'LightSalmon3', 'LightSalmon4', 'orange2',
          'orange3', 'orange4', 'DarkOrange1', 'DarkOrange2', 'DarkOrange3', 'DarkOrange4',
          'coral1', 'coral2', 'coral3', 'coral4', 'tomato2', 'tomato3', 'tomato4', 'OrangeRed2',
          'OrangeRed3', 'OrangeRed4', 'red2', 'red3', 'red4', 'DeepPink2', 'DeepPink3', 'DeepPink4',
          'HotPink1', 'HotPink2', 'HotPink3', 'HotPink4', 'pink1', 'pink2', 'pink3', 'pink4',
          'LightPink1', 'LightPink2', 'LightPink3', 'LightPink4', 'PaleVioletRed1',
          'PaleVioletRed2', 'PaleVioletRed3', 'PaleVioletRed4', 'maroon1', 'maroon2',
          'maroon3', 'maroon4', 'VioletRed1', 'VioletRed2', 'VioletRed3', 'VioletRed4',
          'magenta2', 'magenta3', 'magenta4', 'orchid1', 'orchid2', 'orchid3', 'orchid4', 'plum1',
          'plum2', 'plum3', 'plum4', 'MediumOrchid1', 'MediumOrchid2', 'MediumOrchid3',
          'MediumOrchid4', 'DarkOrchid1', 'DarkOrchid2', 'DarkOrchid3', 'DarkOrchid4',
          'purple1', 'purple2', 'purple3', 'purple4', 'MediumPurple1', 'MediumPurple2',
          'MediumPurple3', 'MediumPurple4', 'thistle1', 'thistle2', 'thistle3', 'thistle4',
          'gray1', 'gray2', 'gray3', 'gray4', 'gray5', 'gray6', 'gray7', 'gray8', 'gray9', 'gray10',
          'gray11', 'gray12', 'gray13', 'gray14', 'gray15', 'gray16', 'gray17', 'gray18', 'gray19',
          'gray20', 'gray21', 'gray22', 'gray23', 'gray24', 'gray25', 'gray26', 'gray27', 'gray28',
          'gray29', 'gray30', 'gray31', 'gray32', 'gray33', 'gray34', 'gray35', 'gray36', 'gray37',
          'gray38', 'gray39', 'gray40', 'gray42', 'gray43', 'gray44', 'gray45', 'gray46', 'gray47',
          'gray48', 'gray49', 'gray50', 'gray51', 'gray52', 'gray53', 'gray54', 'gray55', 'gray56',
          'gray57', 'gray58', 'gray59', 'gray60', 'gray61', 'gray62', 'gray63', 'gray64', 'gray65',
          'gray66', 'gray67', 'gray68', 'gray69', 'gray70', 'gray71', 'gray72', 'gray73', 'gray74',
          'gray75', 'gray76', 'gray77', 'gray78', 'gray79', 'gray80', 'gray81', 'gray82', 'gray83',
          'gray84', 'gray85', 'gray86', 'gray87', 'gray88', 'gray89', 'gray90', 'gray91', 'gray92',
          'gray93', 'gray94', 'gray95', 'gray97', 'gray98', 'gray99']
 
class ColorButton:
    def __init__(self, master, color):
        self.master = master
        self.color = color
 
        self.b = tk.Button(self.master.m, bg=color, width=0, height=1, command=self.setColor)
 
    def setColor(self):
        self.master.master.choosenColor['bg'] = self.color
        self.master.master.palleteIsCreated = 0
        self.master.m.destroy()
 
 
class ColorPallete():
    def __init__(self, master):
        self.master = master
 
        self.m = tk.Toplevel(self.master.m)
        self.m.title("Pallete")
        self.m.overrideredirect(1)
 
        self.max_row = 11
 
        row = 0
        col = 0
 
        for i in COLORS:  # c http://www.science.smith.edu/dftwiki/index.php/Color_Charts_for_TKinter
            b = ColorButton(self, i)
            b.b.grid(row=row, column=col, sticky='ew')
            row += 1
            if row > self.max_row:
                row = 0
                col += 1
 
 
class Menu():
    def __init__(self, master, width, height, x, y):
        self.master = master
 
        self.m = tk.Toplevel(master.master)
        self.m.transient(master.master)
        self.m.protocol("WM_DELETE_WINDOW", self.isOff)
 
        self.palleteIsCreated = 0
 
        self.m.geometry(f'+{x}+{y}')
        self.m.title("Menu")
        #
        # x line
        self.xsFrame = tk.Frame(self.m, bg='lightgrey', height=30)
        self.xsFrame.pack(fill="x")
 
        self.x1Frame = tk.Frame(self.xsFrame)
        self.x1Frame.pack(expand=1, side='left')
        tk.Label(self.x1Frame, bg='lightgrey', text='x1').pack(expand=1, side='left')
        self.x1data = tk.Entry(self.x1Frame, width=3)
        self.x1data.pack(expand=1, side='left')
 
        self.x2Frame = tk.Frame(self.xsFrame)
        self.x2Frame.pack(expand=1, side='left')
        tk.Label(self.x2Frame, bg='lightgrey', text='x2').pack(expand=1, side='right')
        self.x2data = tk.Entry(self.x2Frame, width=3)
        self.x2data.pack(expand=1, side='right')
        #
        # y line
        self.ysFrame = tk.Frame(self.m, bg='lightgrey', height=30)
        self.ysFrame.pack(fill="x")
 
        self.y1Frame = tk.Frame(self.ysFrame)
        self.y1Frame.pack(expand=1, side='left')
        tk.Label(self.y1Frame, bg='lightgrey', text='y1').pack(expand=1, side='left')
        self.y1data = tk.Entry(self.y1Frame, width=3)
        self.y1data.pack(expand=1, side='left')
 
        self.y2Frame = tk.Frame(self.ysFrame)
        self.y2Frame.pack(expand=1, side='left')
        tk.Label(self.y2Frame, bg='lightgrey', text='y2').pack(expand=1, side='right')
        self.y2data = tk.Entry(self.y2Frame, width=3)
        self.y2data.pack(expand=1, side='right')
        #
 
        self.figureFrame = tk.Frame(self.m, bg='lightgrey', height=30)
        self.figureFrame.pack(fill="x")
 
 
        self.ChoosenVar = tk.IntVar()
        self.ChoosenVar.set(2)
        self.CuboidRButton = tk.Radiobutton(self.figureFrame, bg='lightgrey', text="Cuboid", variable=self.ChoosenVar, value=0)
        self.CuboidRButton.pack(expand=1, anchor='nw')
 
        self.OvaloidRButton = tk.Radiobutton(self.figureFrame, bg='lightgrey', text="Ovaloid", variable=self.ChoosenVar, value=1)
        self.OvaloidRButton.pack(expand=1, anchor='nw')
 
        self.oneperoneVar = tk.BooleanVar()
        self.oneperoneVar.set(0)
        self.oneperone = tk.Checkbutton(self.figureFrame, text="1x1", bg='lightgrey', variable=self.oneperoneVar)
        self.oneperone.pack(expand=1, anchor='ne')
        tk.Label(self.figureFrame, bg='lightgrey').pack()
 
        self.PointRButton = tk.Radiobutton(self.figureFrame, bg='lightgrey', text="Point", variable=self.ChoosenVar, value=2)
        self.PointRButton.pack(expand=1, anchor='nw')
 
        self.PointLineRButton = tk.Radiobutton(self.figureFrame, bg='lightgrey', text="Point Line", variable=self.ChoosenVar, value=3)
        self.PointLineRButton.pack(expand=1, anchor='nw')
 
 
        self.buttonFrame = tk.Frame(self.m, bg='grey')
        self.buttonFrame.pack(side='bottom', fill="x")
        self.drawButton = tk.Button(self.buttonFrame, text="Draw", command=self.makeFigure).pack(expand=1, side='left')
        self.chColButton = tk.Button(self.buttonFrame, text="Choose Color...", command=self.setPallete).pack(expand=1,
                                                                                                             side='left')
        self.choosenColor = tk.Label(self.m, font="Arial 7", text="Choosen color", bg="yellow")
        self.choosenColor.pack(side='bottom', fill="x")
 
        self.state = tk.Label(self.m, font="Arial 8", text="Hello!", bg="grey")
        self.state.pack(side='bottom', fill="x")
 
 
    def isOff(self):
        self.master.master.unbind('<Button-1>')
        self.master.master.unbind('<ButtonRelease-1>')
        self.master.master.unbind('<B1-Motion>')
        self.master.menuIsCreated = 0
        print("menu deleted")
        self.m.destroy()
 
 
    def insertMouseValue(self, x, y, state):
        if state == 0:
            self.x1data.delete(0, tk.END)
            self.x1data.insert(0, x)
 
            self.y1data.delete(0, tk.END)
            self.y1data.insert(0, y)
 
            self.state['text'] = f"pressed: {x}x{y}"
 
            if self.ChoosenVar.get() == 2:
                filler = self.choosenColor['bg']
                self.master.c.create_oval(
                    x-1, y-1,
                    x+1, y+1,
                    fill=filler, width=0)
 
 
        elif state == 1:
            self.x2data.delete(0, tk.END)
            self.x2data.insert(0, x)
 
            self.y2data.delete(0, tk.END)
            self.y2data.insert(0, y)
            self.state['text'] = f"unpressed: {x}x{y}"
        else:
            self.state['text'] = f"motion: {x}x{y}"
 
            if self.ChoosenVar.get() == 3:
                filler = self.choosenColor['bg']
                self.master.c.create_oval(
                    x-1, y-1,
                    x+1, y+1,
                    fill=filler, width=0)
 
    def setPallete(self):
        if self.palleteIsCreated == 0:
            self.palleteIsCreated = 1
 
            ColorPallete(self)
 
    def makeFigure(self): #0-cube, 1-oval, 2-point, 3-pointline
        try:
            x1, y1, x2, y2 = int(self.x1data.get()), int(self.y1data.get()), int(self.x2data.get()), int(self.y2data.get())
        except:
            x1 = 0; x2 = 0; y1 = 0; y2 = 0
 
        filler = self.choosenColor['bg']
        print(x1, x2, y1, y2)
 
        if self.oneperoneVar.get() == 1:
            y2 = y1 + (x1 - x2)
 
        if self.ChoosenVar.get() == 0:
            self.master.c.create_rectangle(
                x1, y1,
                x2, y2,
                fill=filler, width=0)
        elif self.ChoosenVar.get() == 1:
            self.master.c.create_oval(
                x1, y1,
                x2, y2,
                fill=filler, width=0)
 
 
class HalfPaint():
    def __init__(self, master, width, height, x, y):
        self.master = master
        self.width = width
        self.height = height
        self.x = x
        self.y = y
 
        self.menuIsCreated = 0
 
        self.c = tk.Canvas(master, width=width, height=height - 30, bg='white')
        self.c.pack(side='top')
 
        self.buttonFrame = tk.Frame(master).pack(side='bottom')
        self.createButton = tk.Button(self.buttonFrame, text="Create...",
                                      command=self.setCreatButton).pack(side='top')
 
 
 
    def setCreatButton(self):
        if self.menuIsCreated == 0:
            self.menuIsCreated = 1
 
            self.menu = Menu(self, 200, 200,
                             self.x + self.width + self.width // 77,
                             self.y + self.height // 77)
 
            self.master.bind('<Button-1>', lambda event: self.getInsCoord(event, 0))
            self.master.bind('<ButtonRelease-1>', lambda event: self.getInsCoord(event, 1))
            self.master.bind('<B1-Motion>', lambda event: self.getInsCoord(event, 2))
 
    def getInsCoord(self, event, state):
        self.menu.insertMouseValue(event.x, event.y, state)
 
        #
 
root = tk.Tk()
root.resizable(0, 0)
 
winw, winh, winx, winy = root.winfo_screenwidth() // 2, \
                         root.winfo_screenheight() // 2, \
                         100, 100
 
root.geometry(f'{winw}x{winh}+{winx}+{winy}')
root.title("Half-Paint")
 
HalfPaint(root, winw, winh, winx, winy)
 
root.mainloop()