Раздел «Язык Си».GameCow006:

Игра Корова 006

Правила

Очки

Ранги (очки) коровы:

Скопировать файлы, которые дал преподаватель

Уберите NOSPAM из примеров!

Из учебного класса или общежития:

scp -r student@s41700.vdi.miptNOSPAM.ru:~/shared/Cow006_draft .

Если не получается (или с ноутбука через mipt-ng)

scp -r -P 51940 student@remote.vdi.miptNOSPAM.ru:~/shared/Cow006_draft .

Набросок кода для текстовой игры

Работать с хранилищем данных (репозиторием) через git

Для пользователя stm201701: Замените логин в примере stm201701 на свой логин

Git

Git - система контроля версий. Зачем использовать, если я работаю один над проектом?

Репозиторий (хранилище) - место, где хранятся данные (файлы), история их изменения и другая служебная информация.

IDEA! Все команды выполняются из командной строки. git-команды одинаковые для Linux и для Windows. В статье примеры приведены для Linux.

Книги по tkinter

API reference (описание всех функций и классов; на английском языке)

scp student@s41700.vdi.miptNOSPAM.ru:/book/python/tkinter.pdf .

Книга, 13 глава (русский)

scp student@s41700.vdi.miptNOSPAM.ru:/book/python/Sammerfild_RU.pdf .

Книга 7-11 глава (очень подробно, русский)

scp student@s41700.vdi.miptNOSPAM.ru:/book/python/"Программирование\ на\ Python,\ 4-е\ издание,\ I\ том.pdf" .

Схема JSON

{
    'project': 'cow006',
    'version': '1.0',
    'game' : {
        'players': [
            {
                'computer': True,
                'name': 'Alice',
                'score': 0,
                'hand': [8, 91, 51]
            }
        ]
        'table': [
           [13],
           [35, 37, 39],
           [90, 103] 
        ]
    }
}

tkinter event mainloop

# https://riptutorial.com/tkinter/example/22870/-after--

#import tkinter
try:
    import tkinter as tk 
except ImportError:
    import Tkinter as tk

class Timer:
    def __init__(self, parent):
        # variable storing time
        self.seconds = 0
        # label displaying time
        self.label = tk.Label(parent, text="0 s", font="Arial 30", width=10)
        self.label.pack()
        # start the timer
        self.label.after(1000, self.refresh_label)

    def refresh_label(self):
        """ refresh the content of the label every second """
        # increment the time
        self.seconds += 1
        # display the new time
        self.label.configure(text="%i s" % self.seconds)
        # request tkinter to call self.refresh after 1s (the delay is given in ms)
        self.label.after(1000, self.refresh_label)

if __name__ == "__main__":
    root = tk.Tk()
    timer = Timer(root)
    root.mainloop()

from tkinter import *
import sys
import datetime

def current_time():
   now = datetime.datetime.now()
   print (now.strftime("%H:%M:%S"))

root = Tk()
root.withdraw()

current_time()
print('start')
root.after_idle(lambda: current_time() or print("after idle 1"))

def task(n=0):
    if n > 0:
        current_time()
        print("hello, n =", n)
        root.after(2000, lambda n=n-1: task(n))  # reschedule event in 2 seconds
    else:
        root.after_idle(lambda: current_time() or print("after idle 2") or root.quit())


def wait_exit():
    sys.exit(0)

root.after(2000, lambda: task(3))

root.mainloop()

Еще один пример вызовов функций через after и after_idle. Обратите внимание, как передаются аргументы в вызываемую функцию foo и hello.

import tkinter as tk
import datetime

def current_time():
   now = datetime.datetime.now()
   print (now.strftime("%H:%M:%S"))

def foo(msg='aaa'):
    current_time()
    print(msg)
    

current_time()
root = tk.Tk()
root.withdraw() # не показывать окна
current_time()
print('-------')

def hello(counter):
    if counter == 0:
        root.after_idle(end)    
    else:
        foo('Hello')    
        root.after(1000, hello, counter-1)
        
    
def end():
    foo('The end!')
    root.after_idle(root.quit)

a = 'begin'
root.after_idle(foo, a)     # begin
a = 'continue'
root.after_idle(foo, a)     # continue

root.after_idle(hello, 3)     # +2 sec, Hello


root.mainloop()
print('======')
current_time()

Скопировать файлы, которые дал преподаватель

Уберите NOSPAM из примеров!

Из учебного класса или общежития:

scp -r student@s41700.vdi.miptNOSPAM.ru:~/shared/Cow006_tty_draft .

Если не получается (или с ноутбука через mipt-ng)

scp -r -P 51940 student@remote.vdi.miptNOSPAM.ru:~/shared/Cow006_tty_draft .

Здесь же можно взять картинки с картами.

Label большими буквами

import tkinter as tk
import tkinter.font as tkfont

root = tk.Tk()
print(tkfont.families())

myfont = "Helvetica 16 bold italic" # не работает, потому что в классе не установлена Helvetica
#myfont = tkfont.Font(family='liberation sans narrow     ', size=36, weight='bold') # работает, но только в классе
myfont = tkfont.Font(family='*Serif', size=36, weight='bold')  # работает и в классе, и дома

tk.Label(root, 
         text="Red Text in Times Font",
         fg = "red",
         font = myfont).pack()
tk.Label(root, 
         text="Green Text in Helvetica Font",
         fg = "light green",
         bg = "dark green",
         font = "Helvetica 16 bold italic").pack()
tk.Label(root, 
         text="Blue Text in Verdana bold",
         fg = "blue",
         bg = "yellow",
         font = "Verdana 10 bold").pack()

root.mainloop()

* width у Label - это ширина не в пикселах (как у других элементов), а в символах; * height у Label - это высота не в пикселах (как у других элементов), а в строках;

Возможно, вам будет проще рисовать карты в Canvas (а не в Label)

tkinter.Canvas

Специальный элемент для рисования на нем.

* https://www.python-course.eu/tkinter_canvas.php * http://effbot.org/tkinterbook/canvas.htm

from tkinter import *

canvas_width = 300
canvas_height =300

master = Tk()

canvas = Canvas(master, 
           width=canvas_width, 
           height=canvas_height)
canvas.pack()

img = PhotoImage(file="rocks.ppm")
canvas.create_image(20,20, anchor=NW, image=img)

mainloop()

create_image - добавить изображение на виджет.

Поговорим о параметре anchor. По умолчанию он CENTER. Т.е. если вы не указываете явно anchor=NW, то в координатах виджета (20, 20) будет находиться центр изображения. Что делать, если хочется задавать координаты, например, верхнего левого угла? Указать, где именно будет точка привязки (якорь, anchor). Якорь задается по сторонам света, по первым буквам: N, S, W, E (North, South, West, East), можно задать в стиле NW (North-West, левый верхний угол).

Как нарисовать рамочку на виджете?

Это относится к любому виджету - Canvas, Label, Button и далее, на что у вас хватит фантазии.

В некоторых библиотеках есть явные методы позволяющие ставить/снимать рамку вокруг виджета. Что делать, если такого метода в библиотеке нет, а хочется рисовать рамку вокруг изображения?

Общие методы виджетов в tkinter

Как узнать или изменить значение какого-нибудь свойства в виджете, например, bg?

Задачи по GUI:

Логирование (logging)

Совсем просто:

import logging
logging.warning('Watch out!')  # will print a message to the console
logging.info('I told you so')  # will not print anything

По умолчанию настроен уровень логирования WARNING, поэтому info сообщение не печатается.

Логер по умолчанию root

2 файла, логирование по умолчанию.

В одном месте, СНАЧАЛА конфигурируем логер, потом везде его используем

Файл log_simple.py:

from other_file import foo

import logging

logging.basicConfig(
    filename='app.log',
    filemode='a',         # добавляем в файл, 'w' - записываем файл заново
    level=logging.DEBUG,
    #format='%(levelname)15s:%(asctime)s:%(message)s'
    format='%(filename)s:%(lineno)d - %(funcName)s - %(message)s'
)
logging.debug('Some debug info')
logging.warning('Watch out!')  # will print a message to the console
logging.info('I told you so')  # will not print anything
logging.critical('FIRE')  # MUST print

foo('foo calling')

Файл other_file.py

import logging

def foo(msg):
   logging.error(msg)

Получившийся лог-файл app.log

          DEBUG:2019-04-30 12:50:57,967:Some debug info
        WARNING:2019-04-30 12:50:57,967:Watch out!
           INFO:2019-04-30 12:50:57,967:I told you so
       CRITICAL:2019-04-30 12:50:57,967:FIRE
          ERROR:2019-04-30 12:50:57,967:foo calling
log_simple.py:12 - <module> - Some debug info
log_simple.py:13 - <module> - Watch out!
log_simple.py:14 - <module> - I told you so
log_simple.py:15 - <module> - FIRE
other_file.py:4 - foo - foo calling

Свой логер

Логирование на консоль и в файл

from other_file2 import foo

import logging

# create logger
logger = logging.getLogger('cow006')
logger.setLevel(logging.DEBUG)

# create file handler which logs even debug messages
fh = logging.FileHandler('cow006.log', mode='w')   # добавлять в файл, а не записывать заново mode='a'
fh.setLevel(logging.DEBUG)

# create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)

# create formatter
formatter = logging.Formatter(
   '%(levelname)7s %(filename)s:%(lineno)d - %(funcName)s - %(message)s'
)
formatter_file = logging.Formatter(
   '%(asctime)s %(filename)s:%(lineno)d - %(funcName)s - %(message)s',
   datefmt='%H:%M:%S'
)


# add formatter to ch
ch.setFormatter(formatter)
fh.setFormatter(formatter_file)

# add ch to logger
logger.addHandler(ch)
logger.addHandler(fh)

# format='%(asctime)s %(message)s'
# datefmt='%m/%d/%Y %I:%M:%S %p'

logger.debug('Some debug info')
logger.warning('Watch out!')  # will print a message to the console
logger.info('I told you so')  # will not print anything
logger.critical('FIRE')  # MUST print

foo('foo calling')

Файл other_file2.py:

import logging

logger = logging.getLogger('cow006')

def foo(msg):
   logger.error(msg)

Получили файл cow006.log

13:15:47 log_named.py:38 - <module> - Some debug info
13:15:47 log_named.py:39 - <module> - Watch out!
13:15:47 log_named.py:40 - <module> - I told you so
13:15:47 log_named.py:41 - <module> - FIRE
13:15:47 other_file2.py:6 - foo - foo calling

На консоль печатается:

python log_named.py 
WARNING log_named.py:39 - <module> - Watch out!
   INFO log_named.py:40 - <module> - I told you so
CRITICAL log_named.py:41 - <module> - FIRE
  ERROR other_file2.py:6 - foo - foo calling

-- TatyanaDerbysheva - 14 Nov 2018