Riešené úlohy k 9. cvičeniu


  1. Program počas ťahania myši zabezpečí kreslenie žltých krúžkov, prvý s polomerom 1, každý ďalší je o 0.1 väčší. Tlačidlom 'Zmaž' sa obrazovka zmaže a nastaví sa kreslenie od najmenšieho krúžku (s polomerom 1). V programe zabezpeč zviazanie ovládača a tlačidla:

    canvas.bind('<B1-Motion>', kresli)
    tkinter.Button(...
    

    Po spustení a ťahaní môžeš dostať, napríklad:

    ../../_images/z09_c02.png

    Teraz pridaj ďalšie tlačidlo (napríklad s textom 'Zmeň farbu'), ktorým sa zmení farba krúžkov na nejakú náhodnú - od tohto momentu budú všetky nasledovné krúžky zafarbené touto novou farbou.

    • Riešenie:

      import tkinter
      import random
      
      r = 1
      farba = 'yellow'
      
      def kresli(event):
          global r
          x, y = event.x, event.y
          canvas.create_oval(x - r, y - r, x + r, y + r, fill=farba)
          r += .1
      
      def zmaz(event):
          global r
          r = 1
          canvas.delete('all')
      
      def zmen_farbu():
          global farba
          farba = f'#{random.randrange(256**3):06x}'
      
      canvas = tkinter.Canvas()
      canvas.pack()
      
      canvas.bind('<B1-Motion>', kresli)
      canvas.bind('<ButtonPress-3>', zmaz)
      tkinter.Button(text='Zmeň farbu', command=zmen_farbu).pack()
      
      tkinter.mainloop()
      

  1. Program zabezpečí klikanie myšou: prvé kliknutie si zapamätá súradnice, druhé kliknutie nakreslí obdĺžnik (create_rectangle), v ktorom jeden vrchol je zapamätaný a druhý vrchol je práve kliknutý. Obdĺžnik je zafarbený náhodnou farbou. Toto sa opakuje pri ďalších klikaniach.

    canvas.bind('<ButtonPress-1>', klik)
    

    Napríklad:

    ../../_images/z09_c03.png
    • Riešenie:

      import tkinter
      import random
      
      zoznam = []
      
      def klik(event):
          zoznam.append((event.x, event.y))
          if len(zoznam) == 2:
              fill = f'#{random.randrange(256**3):06x}'
              canvas.create_rectangle(zoznam, fill=fill)
              zoznam.clear()
      
      canvas = tkinter.Canvas()
      canvas.pack()
      
      canvas.bind('<ButtonPress-1>', klik)
      
      tkinter.mainloop()
      

  1. Podobne ako v (3) úlohe: pri každom kliknutí sa na danej pozícii nakreslí '+' (create_text), pritom pri prvých dvoch kliknutiach sa tieto body zapamätajú. Tretie z týchto troch kliknutí nakreslí náhodne zafarbený trojuholník (create_polygon). Zoznam zapamätaných bodov má momentálne 3 vrcholy. Teraz sa z tohto zapamätaného zoznamu vyhodí prvý prvok a pokračuje sa ďalej s dvomi vrcholmi: ďalšie kliknutie pridá do zoznamu 3. vrchol, nakreslí trojuholník a prvý vrchol sa opäť vyhodí. Môžeš dostať, napríklad:

    ../../_images/z09_c04.png
    • Riešenie:

      import tkinter
      import random
      
      zoznam = []
      
      def klik(event):
          xy = event.x, event.y
          zoznam.append(xy)
          canvas.create_text(xy, text='+')
          if len(zoznam) == 3:
              fill = f'#{random.randrange(256**3):06x}'
              canvas.create_polygon(zoznam, fill=fill)
              zoznam.pop(0)
      
      canvas = tkinter.Canvas()
      canvas.pack()
      
      canvas.bind('<ButtonPress-1>', klik)
      
      tkinter.mainloop()
      

  1. Podobne ako v (3) úlohe: klikanie do plochy kreslí '+' a zapamätáva tieto body. Ak mám aspoň 3 zapamätané body a pritom prvý a posledný majú vzdialenosť menšiu ako 5, ukončí sa zapamätávanie bodov, nakreslí sa z nich náhodne zafarbený polygón a všetko sa začne od začiatku. Môžeš dostať, napríklad:

    ../../_images/z09_c05.png
    • Riešenie:

      import tkinter
      import random
      
      zoznam = []
      
      def klik(event):
          x, y = event.x, event.y
          zoznam.append((x, y))
          canvas.create_text(x, y, text='+')
          x0, y0 = zoznam[0]
          if len(zoznam) > 2 and (x - x0) ** 2 + (y - y0) ** 2 < 25:
              fill = f'#{random.randrange(256**3):06x}'
              canvas.create_polygon(zoznam, fill=fill)
              zoznam.clear()
      
      canvas = tkinter.Canvas()
      canvas.pack()
      
      canvas.bind('<ButtonPress-1>', klik)
      
      tkinter.mainloop()
      

  1. Vylepši riešenie (4) úlohy: pri kreslení polygónu sa automaticky všetky '+' z plochy vymažú (canvas.delete(...)). Každému nakreslenému '+' môžeš pridať rovnaký tag a potom ich naraz zrušíš. Pozri, napríklad 3. prednášku.

    Okrem vymazávania značiek '+' pridaj aj tri tlačidlá:

    • s textom 'Zmaž' zmaže obrazovku, pritom zruší aj zapamätané body doteraz vytváraného polygónu

    • s textom 'Zmeň farbu' zmení farbu naposledy nakresleného polygónu na nejakú inú náhodnú

    • s textom 'Späť' zmaže naposledy nakreslený polygón

    • Riešenie:

      import tkinter
      import random
      
      zoznam = []       # momentálne vytvarané vrcholy
      polygony = []     # všetky nakreslené polygóny
      
      def klik(event):
          x, y = event.x, event.y
          zoznam.append((x, y))
          canvas.create_text(x, y, text='+', tag='+')
          x0, y0 = zoznam[0]
          if len(zoznam) > 2 and (x - x0) ** 2 + (y - y0) ** 2 < 25:
              fill = f'#{random.randrange(256**3):06x}'
              polygony.append(canvas.create_polygon(zoznam, fill=fill))
              zoznam.clear()
              canvas.delete('+')
      
      def zmaz():
          canvas.delete('all')
          zoznam.clear()
          polygony.clear()
      
      def zmen_farbu():
          if polygony:
              fill = f'#{random.randrange(256**3):06x}'
              canvas.itemconfig(polygony[-1], fill=fill)
      
      def spat():
          if polygony:
              canvas.delete(polygony.pop(0))
      
      canvas = tkinter.Canvas()
      canvas.pack(side='left')
      
      canvas.bind('<ButtonPress-1>', klik)
      tkinter.Button(text='Zmaž', command=zmaz).pack()
      tkinter.Button(text='Zmeň farbu', command=zmen_farbu).pack()
      tkinter.Button(text='Späť', command=spat).pack()
      
      tkinter.mainloop()
      

  1. Najprv zadefinuj štyri premenné (napríklad x1, y1, x2, y2 = 100, 50, 200, 100) a pomocou nich nakresli farebný obdĺžnik. Potom program pri každom kliknutí, ale len do vnútra obdĺžnika, mu zmení farbu výplne. Každé kliknutie do vnútra obdĺžnika cyklicky strieda jednu zo 4 farieb, napríklad farby = ['blue', 'red', 'green', 'yellow']. Nepoužívaj global.

    • Riešenie:

      import tkinter
      
      def klik(event):
          x, y = event.x, event.y
          if x1 <= x <= x2 and y1 <= y <= y2:
              farba = farby.pop(0)
              farby.append(farba)
              canvas.itemconfig(obd, fill=farba)
      
      canvas = tkinter.Canvas()
      canvas.pack()
      
      x1, y1, x2, y2 = 100, 50, 200, 100
      farby = ['blue', 'red', 'green', 'yellow']
      obd = canvas.create_rectangle(x1, y1, x2, y2, fill=farby[-1])
      
      canvas.bind('<ButtonPress>', klik)
      
      tkinter.mainloop()
      

      alebo pomocou canvas.tag_bind, ktorý zviaže udalosť len s daným objektom:

      import tkinter
      
      def klik(event):
          farba = farby.pop(0)
          farby.append(farba)
          canvas.itemconfig(obd, fill=farba)
      
      canvas = tkinter.Canvas()
      canvas.pack()
      
      x1, y1, x2, y2 = 100, 50, 200, 100
      farby = ['blue', 'red', 'green', 'yellow']
      obd = canvas.create_rectangle(x1, y1, x2, y2, fill=farby[-1])
      
      canvas.tag_bind(obd, '<ButtonPress>', klik) # klikanie len do vnútra útvaru
      
      tkinter.mainloop()
      

  1. Predstav si, že celá grafická plocha je štvorcová sieť, ktorej štvorčeky majú veľkosť 50x50. Napíš program, ktorý pri každom kliknutí do grafickej plochy nakreslí náhodne zafarbený príslušný štvorček tejto siete (pomocou create_rectangle()). Nepoužívaj global. Pravdepodobne na zistenie štvorčeka siete využiješ niečo ako x // 50 a y // 50.

    • Riešenie:

      import tkinter
      import random
      
      def klik(event):
          x = event.x // 50 * 50
          y = event.y // 50 * 50
          farba = f'#{random.randrange(256**3):06x}'
          canvas.create_rectangle(x, y, x + 50, y + 50, fill=farba)
      
      canvas = tkinter.Canvas()
      canvas.pack()
      
      canvas.bind('<ButtonPress>', klik)
      
      tkinter.mainloop()
      

  1. Napíš program, ktorý bude robiť efekt spreja: ťahanie myšou so zatlačeným ľavým tlačidlom nakreslí 20 farebných bodiek (farba podľa globálnej premennej, napríklad farba = 'blue') na náhodných pozíciách. Tieto náhodné bodky budú mať od kliknutého miesta takúto vzdialenosť: x-ová súradnica bude z intervalu <x-30, x+30> a y-ová z <y-30, y+30>. Najlepšie je ich kresliť ako kruhy s polomerom 2 bez obrysu (width=0).

    Do programu pridaj aj spracovanie tlačidla 'Zmeň farbu': vtedy sa nastaví premenná farba na náhodnú farbu. Vďaka tomuto každé ďalšie ťahanie myšou bude sprejovať už touto novou farbou. Okrem tlačidla pridaj aj widget Label, ktorý bude zobrazovať aktuálne nastavenú farbu spreja, napríklad v tvare: 'farba: #456789'. Môžeš použiť napríklad:

    vypis = tkinter.Label(text='farba: blue')
    vypis.pack()
    

    a pre zmenu textu v tomto widgete:

    vypis['text'] = 'novy text'
    
    • Riešenie:

      import tkinter
      import random
      
      farba = 'blue'
      
      def tahaj(event):
          for i in range(20):
              x = event.x + random.randint(-30, 30)
              y = event.y + random.randint(-30, 30)
              canvas.create_oval(x - 2, y - 2, x + 2, y + 2,
                                 fill=farba, width=0)
      
      def zmen_farbu(event=None):
          global farba
          farba = f'#{random.randrange(256**3):06x}'
          vypis['text'] = f'farba: {farba}'
      
      canvas = tkinter.Canvas()
      canvas.pack()
      
      canvas.bind('<B1-Motion>', tahaj)
      tkinter.Button(text='Zmeň farbu', command=zmen_farbu).pack()
      vypis = tkinter.Label(text='farba: blue')
      vypis.pack()
      
      tkinter.mainloop()
      

  1. Stláčaním malých a veľkých písmen abecedy (bez diakritiky) sa tieto vypisujú nejakým väčším fontom vedľa seba (Napríklad 'arial 30'). Využi jeden grafický objekt pre text (create_text) a tomuto budeš pri stláčaní písmen pridávať vypisovaný text (pomocou canvas.itemconfig()). Program by mal akceptovať aj stláčanie medzery a Enter (do textu vloží '\n' alebo '\r'). Použi metódu bind_all('<KeyPress>', ...) pričom vo viazanej funkcii pracuj s hodnotou event.char.

    • Riešenie:

      import tkinter
      
      text = ''
      
      def klaves(event):    # event.char: '\r' return, '\b' backspace
          global text
          if 'a' <= event.char.lower() <= 'z' or event.char in ' \r':
              text += event.char
              canvas.itemconfig(riadky, text=text)
          elif event.char == '\b':
              text = text[:-1]
              canvas.itemconfig(riadky, text=text)
      
      canvas = tkinter.Canvas()
      canvas.pack()
      
      riadky = canvas.create_text(190, 130, fill='blue', font='arial 30')
      canvas.bind_all('<KeyPress>', klaves)
      
      tkinter.mainloop()
      

      alebo pomocou canvas.itemcget(...), ktorá vráti momentálnu hodnotu atribútu 'text' v texte:

      import tkinter
      
      def klaves(event):    # event.char: '\r' return, '\b' backspace
          if 'a' <= event.char.lower() <= 'z' or event.char in ' \r':
              text = canvas.itemcget(riadky, 'text') + event.char
              canvas.itemconfig(riadky, text=text)
          elif event.char == '\b':
              text = canvas.itemcget(riadky, 'text')[:-1]
              canvas.itemconfig(riadky, text=text)
      
      canvas = tkinter.Canvas()
      canvas.pack()
      
      riadky = canvas.create_text(190, 130, fill='blue', font='arial 30')
      canvas.bind_all('<KeyPress>', klaves)
      
      tkinter.mainloop()