Форум сайта python.su
Ужас. Поля не квадратные и разной ширины, их ширина зависит от числа букв в надписях сверху, эту зависимость убрать с помощью фрейма, одного на “всю бухгалтерию”, а не трёх как вы сделали. Вверху один фрейм содержит все надписи и кнопки, а под ним девять клеток.
Клетки выделять с помощью рельефа и бордюра (borderwidth=3, relief='raised'), padx и pady лучше убрать.
Должно быть примерно так:
Офлайн
ramiПолностью согласен, это уже не программирование а какая-то кросс-браузерная верстка. Короче, у меня все нормально отображается и сейчас и в первый раз. Файл прикрепил, как оно у меня выглядит. Предлагаю, если вы имеете опыт такой разработки, сделайте и разместите здесь код, если у всех он будет нормально отображаться, я просто перейму ваш стиль организации разметки. А так это вряд ли закончится.
Ужас.
Отредактировано anickone (Июль 19, 2018 21:31:15)
Прикреплённый файлы:
img.png (20,3 KБ)
Офлайн
anickoneнормуль
Переписал под grid. У кого в интерфейсе были сползания, посмотрите что сейчас?
Офлайн
VigiСпасибо за ответ, для меня это важно было услышать. Также рад, что вам по силам обойти зашитую в игру стратегию, в отличии от меня. PEHDOM это поправил) Поделился выигрышными стратегиями со мной и теперь я тоже могу выигрывать)))
нормуль
Офлайн
anickoneПоделюсь, но если честно, после ваших слов:
rami
Вы поделитесь своим кодом? Я по вашему скриншоту вижу, что вы его написали. Это не мой дизайн. Привязывать вашу разметку к коду не обязательно. Просто хочется увидеть ваш подход к разметке.
anickoneрасхотелось обсуждать эту тему. Если вы любитель и программируете только для себя, то получилось довольно хорошо, я рад, что у вас всё отображается как вы хотите, но если вы хотите быть профессиональным программистом, то так говорить нельзя. Проблема не в том, что у меня комп кривой, а в том, что вы программируете “для себя”, а не “для людей”. Корень проблемы в разных настройках по умолчанию: раньше более крупный шрифт не вмещался в строго ограниченную рамку, теперь чтобы вместить текст одна из рамок увеличивается сама и растягивает игровые ячейки в своём столбце, т.е. проблема как была, так и осталась, только видоизменилась. Решить эту проблему можно и нужно с помощью одного единственного фрейма, который будет включать в себя либо все рамки и кнопку (так проще), а девять игровых ячеек останутся в главном окне, либо наоборот (так правильнее, но чуть сложнее) игровые ячейки будут в отдельном фрейме, а всё остальное в главном окне. Теперь, даже если мы увеличим в несколько раз ширину какой-либо рамки это никак не отобразится на игровых ячейках, так как у них будут независимые системы столбцов.
Полностью согласен, это уже не программирование а какая-то кросс-браузерная верстка. Короче, у меня все нормально отображается и сейчас и в первый раз.
import tkinter as tk import tkinter.ttk as ttk from random import choice,randint,sample,shuffle OX = ['O', 'X'] VINS = ((0, 1, 2), (3, 4, 5), (6, 7, 8), (0, 3, 6), (1, 4, 7), (2, 5, 8), (0, 4, 8), (2, 4, 6)) PS = [[0, 2, 6, 8], [1, 3, 5, 7]] S='Сыграно игр: {}.\nИз них:\n побед - {};\n поражений - {};\n ничьих - {}.' class Square(tk.Label): '''Создаёт ячейки для игры "Крестики - Нолики"''' def __init__(self, fr, app, c, r, num, **kwargs): self.app = app self.square_num = num kwargs.update({'font': 'Menlo 100', 'bd': 3, 'anchor': 'center', 'relief': 'raise', 'width': 2}) tk.Label.__init__(self, fr, **kwargs) self.grid(column=c, row=r+2, sticky='nsew') def bind_(self): '''Назначает ячейке событие "Нажатие мышкой" с передачей номера ячейки в вызываемую функцию''' self.bind("<Button-1>", lambda x: App.turn_user(self.app, self.square_num)) class App(tk.Frame): def __init__(self, master): super(App, self).__init__(master) # статистика игры self.drawn_game = 0 self.user_win = 0 self.pc_win = 0 self.create_widgets() def create_widgets(self): # статистика statistics = ttk.LabelFrame(self, text='Статистика') statistics.grid(column=0, row=0, rowspan=2, sticky='nsew') self.stat_msg = tk.StringVar(value=S.format(0,0,0,0)) ttk.Label(statistics, textvariable=self.stat_msg, justify='left').grid() # Выбор хода step_choice = ttk.LabelFrame(self, text='Выбрать ход') step_choice.grid(column=1, row=0, rowspan=2, sticky='nsew') self.step = tk.IntVar() step0 = ttk.Radiobutton(step_choice, text='Случайным', variable=self.step, value=0) step1 = ttk.Radiobutton(step_choice, text='Первым', variable=self.step, value=1) step2 = ttk.Radiobutton(step_choice, text='Вторым', variable=self.step, value=2) step0.grid(column=0, row=0, sticky='we') step1.grid(column=0, row=1, sticky='we') step2.grid(column=0, row=2, sticky='we') # Выбор знака sign_choice = ttk.LabelFrame(self, text='Выбор знака') sign_choice.grid(column=2, row=0, sticky='nsew') self.sign = tk.IntVar() sign0 = ttk.Radiobutton(sign_choice, text='Ваш знак "0"', variable=self.sign, value=0) sign1 = ttk.Radiobutton(sign_choice, text='Ваш знак "X"', variable=self.sign, value=1) sign0.grid(column=0, row=0, sticky='we') sign1.grid(column=0, row=1, sticky='we') # кнопка старт btn = ttk.Button(self, text="Старт", command=self.start) btn.grid(column=2, row=1, sticky='nsew') # Статус игры status = ttk.LabelFrame(self, text='Статус игры') status.grid(column=0, row=2, columnspan=3, sticky='nsew') self.status_msg = tk.StringVar(value='Добро пожаловать в игру "Крестики - Нолики"!') ttk.Label(status, textvariable=self.status_msg).grid() self.widgets = [sign0, sign1, step0, step1, step2, btn] #все радиокнопки и кнопка для активации и деактивации fr=tk.Frame(self) fr.grid(column=0, row=3, columnspan=3) self.squares = [Square(fr, self, i % 3, i // 3, i) for i in range(9)] #список полей def start(self): self.continue_ = True self.board = [''] * 9 self.free_squares = [x for x in range(9)] self.status_msg.set('Осторожно! Игра активирована! Удачи!') for square in self.squares: square.bind_() square.config(background='white', text='') if self.step.get() == 2: self.turn_pc() elif self.step.get() == 0: if choice([1, 2]) == 2: self.turn_pc() self.widget_state() def widget_state(self, wst='disabled'): for widget in self.widgets: widget.config(state=wst) def find_best_turn(self, sign): for iturn in self.free_squares: iboard = self.board[:] iboard[iturn] = sign for a, b, c in VINS: if iboard[a] == iboard[b] == iboard[c] == sign: return iturn def default_choice(self): choise = [4] for i in PS: shuffle(i) choise.extend(i) for iturn in choise: if iturn in self.free_squares: return iturn def turn_pc(self): ''' ход пк ''' square_num = self.find_best_turn(not self.sign.get()) if square_num == None: square_num = self.find_best_turn(self.sign.get()) if square_num == None: square_num = self.default_choice() self.turn(square_num, not self.sign.get()) def turn(self, square_num, sign): square = self.squares[square_num] square.unbind("<Button-1>") #отключает ячейку square.config(text=OX[sign]) #пишет символ в ячейку self.board[square_num] = sign self.free_squares.remove(square_num) self.test_win(sign) def turn_user(self, square_num): ''' ход игрока ''' self.turn(square_num, self.sign.get()) if self.continue_: self.turn_pc() #передача хода компьютеру def test_win(self, sign): ''' определение исхода игры ''' for a, b, c in VINS: if self.board[a] == self.board[b] == self.board[c] == sign: if sign == self.sign.get(): self.status_msg.set('Поздравляю с победой!') self.user_win += 1 color = 'green' else: self.status_msg.set('Победа за компьютером! Ну как так?!!!') self.pc_win += 1 color = 'red' for i in a, b, c: self.squares[i].config(background=color) #окраска выигравших или проигравших ячеек for i in self.free_squares: self.squares[i].unbind("<Button-1>") #в случае выигрыша делает оставшиеся ячейки не активными self.game_over() return if not self.free_squares: self.status_msg.set('Ничья. Вы точно можете лучше!') self.drawn_game += 1 [square.config(background='yellow') for square in self.squares] self.game_over() def game_over(self): ''' окончание игры ''' self.continue_ = False self.stat_msg.set(S.format(self.user_win + self.pc_win + self.drawn_game, self.user_win, self.pc_win, self.drawn_game)) self.widget_state('normal') def main(): root = tk.Tk() root.title('Игра "Крестики - Нолики"') root.resizable(False, False) App(root).grid() root.mainloop() if __name__=='__main__': main()
Офлайн
Спасибо, шикарный код. Еще долго буду его изучать. И возможно, когда-то смогу так писать. Понял, что я произвел гк). Ну тут как в анекдоте, главное чтобы его было как можно больше. А также были поводы и силы производить его в не ограниченном количестве. И тогда мне будет, чем поделиться))) А ведь очень хочется)))
Офлайн
anickoneДо шика ещё далеко… Ваш класс Square неправильный и бессмысленный, мой класс Square правильный, но такой же бессмысленный как и ваш. Какой смысл создавать сильно урезанный по функционалу класс (как вы сделали) или класс который почти полностью совпадает с классом Label (как сделал я)? Если то, что нам нужно есть в других доступных модулях, то не стоит изобретать велосипед. Можем ли мы создать класс которого нет в других модулях? Посмотрим на ваш интерфейс — в нём есть три группы объектов состоящих из одинаковых виджетов: это блок статистики, блок статуса игры и игровые ячейки, — все они состоят из одного или нескольких Label и рамки. Для них мы сделаем класс Cells (ячейки в рамке). Ещё у вас есть два блока радиокнопок, для них мы создадим класс Radiogrup (радиокнопки в рамке). Оба эти класса поместим в отдельный документ с именем mywidgets.py, в той же папке, что и файл с игрой “Крестики — Нолики”.
Спасибо, шикарный код. Еще долго буду его изучать. И возможно, когда-то смогу так писать.
#Это код для файла mywidgets.py import tkinter as tk import tkinter.ttk as ttk class Cells(ttk.LabelFrame): '''Создаёт блок ячеек по числу столбцов cols и строк rows''' def __init__(self, root, cols=1, rows=1, title='', text='', justify='left', font='Menlo 13', bd=0, *args, **kwargs): ttk.LabelFrame.__init__(self, root, text=title, *args, **kwargs) self.grup = [] for i in range(cols*rows): cell=tk.Label(self, text=text, font=font, justify=justify, **kwargs) cell.grid(column=i%cols, row=i//cols, sticky='nsew') self.grup.append(cell) def bind_(self, event, func, n): '''Назначает ячейке событие event с передачей номера ячейки n в вызываемую функцию func''' self.grup[n].bind(event, lambda x: func(n)) class Radiogrup(ttk.LabelFrame): '''Создаёт именованную группу радиокнопок для каждого элемента в items''' grup = [] def __init__(self, root, items=tuple(), *args, **kwargs): ttk.LabelFrame.__init__(self, root, *args, **kwargs) self.var = tk.IntVar() for i,text in enumerate(items): rb=ttk.Radiobutton(self, text=text, variable=self.var, value=i) rb.grid(sticky='we') self.grup.append(rb)
import tkinter as tk import tkinter.ttk as ttk import mywidgets as mw #модуль mywidgets содержит классы Cells и Radiogrup, должен находиться в той же папке, что и текущий файл from random import choice,randint,sample,shuffle OX = ['O', 'X'] VINS = ((0, 1, 2), (3, 4, 5), (6, 7, 8), (0, 3, 6), (1, 4, 7), (2, 5, 8), (0, 4, 8), (2, 4, 6)) PS = [[0, 2, 6, 8], [1, 3, 5, 7]] S='Сыграно игр: {}.\nИз них:\n побед - {};\n поражений - {};\n ничьих - {}.' class App(tk.Frame): '''Создаёт интерфейс и стратегию в игре "Крестики-Нолики"''' user_win=pc_win=drawn=0 def __init__(self, master): super(App, self).__init__(master) self.create_widgets() def create_widgets(self): '''Размещение виджетов в окне''' # Статистика игр self.info = mw.Cells(self, title='Статистика', text=S.format(0,0,0,0)) self.info.grid(column=0, row=0, rowspan=2, sticky='nsew') # Radiogrup Выбор хода self.step = mw.Radiogrup(self, text='Выбрать ход', items=('Случайным','Первым','Вторым')) self.step.grid(column=1, row=0, rowspan=2, sticky='nsew') # Radiogrup Выбор знака self.sign = mw.Radiogrup(self, text='Выбор знака', items=('Ваш знак "0"','Ваш знак "X"')) self.sign.grid(column=2, row=0, sticky='nsew') # кнопка старт self.btn = ttk.Button(self, text="Старт", command=self.start) self.btn.grid(column=2, row=1, sticky='nsew') # Статус игры self.stat = mw.Cells(self, title='Статус игры', text='Добро пожаловать в игру "Крестики - Нолики"!') self.stat.grid(column=0, row=2, columnspan=3, sticky='nsew') # Игровые ячейки self.cells = mw.Cells(self, cols=3, rows=3, bd=3, relief='raise', width=2, font='Menlo 100') self.cells.grid(column=0, row=3, columnspan=3) def start(self): '''Запуск игры''' self.continue_ = True self.board = [''] * 9 self.free_cells = list(range(9)) self.stat.grup[0].config(text='Осторожно! Игра активирована! Удачи!') for i in range(9): self.cells.bind_("<Button-1>", self.turn_user, i) #привязка ячейки к событию "<Button-1>" self.cells.grup[i].config(bg='white', text='') if self.step.var.get() == 2: self.turn_pc() elif self.step.var.get() == 0: if choice([1, 2]) == 2: self.turn_pc() self.widget_state() def widget_state(self, wst='disabled'): '''Отключение элементов управления во время игры и включение после''' [i.config(state=wst) for i in [*mw.Radiogrup.grup, self.btn]] def find_best_turn(self, sign): for iturn in self.free_cells: iboard = self.board[:] iboard[iturn] = sign for a, b, c in VINS: if iboard[a] == iboard[b] == iboard[c] == sign: return iturn def default_choice(self): choise = [4] for i in PS: shuffle(i) choise.extend(i) for iturn in choise: if iturn in self.free_cells: return iturn def turn_user(self, cell_num): '''Ход игрока''' self.turn(cell_num, self.sign.var.get()) if self.continue_: self.turn_pc() #передача хода компьютеру def turn_pc(self): '''Ход компьютера''' cell_num = self.find_best_turn(not self.sign.var.get()) if cell_num == None: cell_num = self.find_best_turn(self.sign.var.get()) if cell_num == None: cell_num = self.default_choice() self.turn(cell_num, not self.sign.var.get()) def turn(self, cell_num, sign): '''Обработка текущего хода и определение исхода игры''' self.cells.grup[cell_num].unbind("<Button-1>") #отключает ячейку self.cells.grup[cell_num].config(text=OX[sign]) #пишет символ в ячейку self.board[cell_num] = sign #пишет символ в список ходов self.free_cells.remove(cell_num) #удаляет номер из списка свободных ячеек for a, b, c in VINS: if self.board[a] == self.board[b] == self.board[c] == sign: if sign == self.sign.var.get(): self.stat.grup[0].config(text='Поздравляю с победой!') self.user_win += 1 color = 'green' else: self.stat.grup[0].config(text='Победа за компьютером! Ну как так?!!!') self.pc_win += 1 color = 'red' [self.cells.grup[i].config(bg=color) for i in (a, b, c)] #окраска выигравших или проигравших ячеек [self.cells.grup[i].unbind("<Button-1>") for i in self.free_cells] #деактивирует оставшиеся ячейки self.game_over() return #если не было выигрышной комбинации, проверяем наличие свободных ячеек, если их нет — ничья if not self.free_cells: self.stat.grup[0].config(text='Ничья. Вы точно можете лучше!') self.drawn += 1 [cell.config(background='yellow') for cell in self.cells.grup] self.game_over() def game_over(self): '''Окончание игры''' self.continue_ = False self.info.grup[0].config(text=S.format(self.user_win + self.pc_win + self.drawn, self.user_win, self.pc_win, self.drawn)) self.widget_state('normal') def main(): root = tk.Tk() root.title('Игра "Крестики - Нолики"') root.resizable(False, False) App(root).grid() root.mainloop() if __name__=='__main__': main()
Офлайн