Я встраиваю окно pygame в Tkinter, как мне манипулировать окном pygame? - PullRequest
1 голос
/ 19 апреля 2019

Итак, я делаю игру в Pygame, и я тоже хочу использовать tkinter. Я встроил окно pygame в окно tkinter, но, похоже, ничего не могу с ним поделать.

Для контекста, вот полный код:

import Tkinter as tk
import os
import platform
import pygame

class window(object):
    def __init__(self):
        self.root = tk.Tk()  # Main window
        self.root.title("SquareScape")
        self.root.iconbitmap(r'C:\Users\17_es\PycharmProjects\square_puzzle\images\icon.ico')
        self.root.configure(background='#9b9b9b')

        # Large Frame
        self.win_frame = tk.Frame(self.root, width=670, height=520, highlightbackground='#595959', highlightthickness=2)

        # menu (left side)
        self.menu = tk.Frame(self.win_frame, width=150, height=516, highlightbackground='#595959', highlightthickness=2)
        self.menu_label = tk.Label(self.menu, text="Settings", bg='#8a8a8a', font=("Courier", "16", "bold roman"))
        self.mute = tk.Button(self.menu, text="XXXX", font="Courier", bg='#bcbcbc', activebackground='#cdcdcd')

        # pygame
        self.pygame_frame = tk.Frame(self.win_frame, width=514, height=514, highlightbackground='#595959', highlightthickness=2)
        self.embed = tk.Frame(self.pygame_frame, width=512, height=512,)

        # Packing
        self.win_frame.pack(expand=True)
        self.win_frame.pack_propagate(0)

        self.menu.pack(side="left")
        self.menu.pack_propagate(0)
        self.menu_label.pack(ipadx=60, ipady=2)
        self.mute.pack(ipadx=40, ipady=2, pady=5)

        self.pygame_frame.pack(side="left")
        self.embed.pack()

        #This embeds the pygame window
        os.environ['SDL_WINDOWID'] = str(self.embed.winfo_id())
        if platform.system == "Windows":
            os.environ['SDL_VIDEODRIVER'] = 'windib'

        #Start pygame
        pygame.init()
        self.win = pygame.display.set_mode((512, 512))
        self.win.fill(pygame.Color(255, 255, 255))
        pygame.display.init()

        self.root.mainloop()

screen = window()

#Here is sample code that I want to run
pygame.draw.rect(screen.win, (0, 0, 255), (200, 200, 100, 100))

Когда я использую pygame.draw.rect(screen.win, (0, 0, 255), (200, 200, 100, 100)), ничего не происходит. Использование pygame внутри класса работало, но в моей более сложной игре использование self.variable для всех моих переменных кажется ненужным.

Как я могу запустить свой код в окне pygame за пределами класса окна?

1 Ответ

0 голосов
/ 19 апреля 2019

Итак, помимо очевидного пропущенного вызова pygame.display.flip, который, как я полагаю, подразумевается при вызове pygame.display.init (pygame.init уже вызывает этот вызов), я обнаружил, что tkinter необходимо инициализировать его. окна и виджеты до того, как упакованный фрейм станет полностью доступным для использования Pygame.

Я сделал это, добавив вызов к self.root.update_idletasks() перед вызовом pygame.init - и, явно настроив видеодрайвер для моей платформы (что вы уже делаете для Windows), заставил все работать.

В любом случае, в вашем коде вы также не указали, где хотели выполнять вызовы функций Pygamedrawing - вполне возможно, что все правильно, но код после screen.window() просто никогда не запускается ( или, скорее, просто запустите при выходе из программы) - потому что вы вызываете tkinter.mainloop внутри метода __init__ вашего класса приложения.

Перенос вызова на mainloop за пределы __init__ является хорошей практикой, поэтому вы также можете инициализировать другие объекты и ресурсы - и у вас действительно есть объект screen для управления объектами. Выполнение этого вызова внутри __init__ похоже на выполнение всей вашей программы «внутри инициализации».

Короче говоря:

  • вызовите tkinter.update_iddletasks() перед инициализацией pygame
  • не забудьте позвонить pygame.display.flip после того, как вы что-нибудь нарисовали с помощью Pygame
  • расположите ваш код так, чтобы ваши вызовы рисования действительно выполнялись и не блокировались после вызова, чтобы войти в цикл tkinter
  • Вы должны серьезно рассмотреть возможность использования Python 3.7 или более поздней версии - (единственный код "python 2" - это import Tkinter, который становится import tkinter в Python 3). Python 2 - это действительно в конце строки, и в нем нет обновлений для таких проектов, как pygame. .

Тем не менее, вот ваш код, модифицированный для работы в Linux + Python 3 (все еще должен работать в Windows) и для выполнения некоторых действий с использованием встроенного фрейма pygame.

import tkinter as tk
import os
import platform
import pygame
import time

class window(object):
    def __init__(self):
        self.root = tk.Tk()  # Main window
        self.root.title("SquareScape")
        # self.root.iconbitmap(r'C:\Users\17_es\PycharmProjects\square_puzzle\images\icon.ico')
        self.root.configure(background='#9b9b9b')

        # Large Frame
        self.win_frame = tk.Frame(self.root, width=670, height=520, highlightbackground='#595959', highlightthickness=2)

        # menu (left side)
        self.menu = tk.Frame(self.win_frame, width=150, height=516, highlightbackground='#595959', highlightthickness=2)
        self.menu_label = tk.Label(self.menu, text="Settings", bg='#8a8a8a', font=("Courier", "16", "bold roman"))
        self.mute = tk.Button(self.menu, text="XXXX", font="Courier", bg='#bcbcbc', activebackground='#cdcdcd')

        tk.Button(self.menu, text="<->", command=lambda: setattr(self, "direction", (-self.direction[0], self.direction[1]))).pack()
        tk.Button(self.menu, text="^", command=lambda: setattr(self, "direction", (self.direction[0], -self.direction[1]))).pack()

        # pygame
        self.pygame_frame = tk.Frame(self.win_frame, width=514, height=514, highlightbackground='#595959', highlightthickness=2)
        self.embed = tk.Frame(self.pygame_frame, width=512, height=512,)

        # Packing
        self.win_frame.pack(expand=True)
        self.win_frame.pack_propagate(0)

        self.menu.pack(side="left")
        self.menu.pack_propagate(0)
        self.menu_label.pack(ipadx=60, ipady=2)
        self.mute.pack(ipadx=40, ipady=2, pady=5)

        self.pygame_frame.pack(side="left")
        self.embed.pack()
        #This embeds the pygame window
        os.environ['SDL_WINDOWID'] = str(self.embed.winfo_id())
        system = platform.system()
        if system == "Windows":
            os.environ['SDL_VIDEODRIVER'] = 'windib'
        elif system == "Linux":
            os.environ['SDL_VIDEODRIVER'] = 'x11'

        self.root.update_idletasks()
        #Start pygame
        pygame.init()
        self.win = pygame.display.set_mode((512, 512))

        self.bg_color = (255, 255, 255)
        self.win.fill(self.bg_color)
        self.pos = 0, 0
        self.direction = 10, 10
        self.size = 40
        self.color = (0, 255, 0)
        self.root.after(30, self.update)

        self.root.mainloop()


    def update(self):

        first_move = True
        pygame.draw.rect(self.win, self.bg_color, self.pos + (self.size, self.size))


        self.pos = self.pos[0] + self.direction[0], self.pos[1] + self.direction[1]


        if self.pos[0] < 0 or self.pos[0] > 512 - self.size:
            self.direction = -self.direction[0], self.direction[1]
            self.pos = self.pos[0] + 2 * self.direction[0], self.pos[1] + self.direction[1]
        if self.pos[1] < 0 or self.pos[1] > 512 - self.size:
            self.direction = self.direction[0], -self.direction[1]
            self.pos = self.pos[0] + self.direction[0], self.pos[1] + 2 * self.direction[1]

        pygame.draw.rect(self.win, self.color, self.pos + (self.size, self.size))
        pygame.display.flip()
        self.root.after(30, self.update)


screen = window()
tk.mainloop()
...