Самый простой способ открыть окно и нарисовать на нем закрашенный прямоугольник в Python - PullRequest
0 голосов
/ 21 декабря 2018

Я пробовал pygame и tkinter, но оба блокируют основной поток с помощью цикла while.Есть некоторые обходные пути, но я думаю, что они относительно сложны.Например, в Java я могу просто создать JFrame, добавить к нему JPanel и нарисовать на JPanel.Это не блокирует поток, в котором был создан JFrame / JPanel.Есть ли ключевое различие между Python и Java, так что Python не может делать то же самое, или я просто использовал неправильные пакеты или использовал их неправильно?

Edit1: Main Question: Самый простой способ открыть окно и нарисоватьзаполненный прямоугольник на нем в Python без блокировки основного потока.

Edit2:

Пример tkinter:

import tkinter

root = tkinter.Tk()
canvas = tkinter.Canvas(root)
canvas.create_rectangle(10, 10, 60, 60, fill='blue')
canvas.pack()
tkinter.mainloop()

print("I won't get printed until window is closed")

Пример Pygame:

import pygame

pygame.init()

width = 500
height = 500
window = pygame.display.set_mode((width, height))

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

print("I won't get printed until window is closed")

что я хочу:

class Canvas:
    def __init__(self):
        ...  # create window that does not block main thread

    def draw_rect(self, x, y, width, height, color):
        ...  # draw rect on window

    def clear(self):
        ...  # clear window

canvas = Canvas()
canvas.draw_rect(10, 10, 60, 60, 'blue')
print('I get printed even while window is active')

Edit3:

что я хочу в Java:

 import javax.swing.*;
 import java.awt.*;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.function.Consumer;

 public class Canvas {

     public static void main(String[] args) {
         // test the Canvas class
         Canvas canvas = new Canvas(500, 500); // creates canvas window
         canvas.fillRect(50, 50, 200, 200, Color.BLUE); // draws a rect
         canvas.clear(); // removes all drawings from canvas window
         canvas.fillRect(50, 100, 300, 200, Color.RED);
         canvas.fillRect(100,150,300,300, Color.CYAN);

         // everything here will be executed
     }

     private JFrame frame = new JFrame();
     private List<Consumer<Graphics>> drawTasks = new ArrayList<>();

     public Canvas(int width, int height) {
         JPanel panel = new JPanel() {
             @Override
             public void paint(Graphics g) {
                 super.paint(g);
                 for (Consumer<Graphics> drawTask : drawTasks) {
                     drawTask.accept(g);
                 }
             }
         };
         panel.setPreferredSize(new Dimension(width, height));
         frame.add(panel);
         frame.pack();
         frame.setResizable(false);
         frame.setLocationRelativeTo(null);
         frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
         frame.setVisible(true);
     }

     public void fillRect(int x, int y, int width, int height, Color color){
         drawTasks.add(graphics -> {
             graphics.setColor(color);
             graphics.fillRect(x, y, width, height);
         });
         frame.repaint();
     }

     public void clear() {
         drawTasks.clear();
         frame.repaint();
     }
}

1 Ответ

0 голосов
/ 21 декабря 2018

Когда вы запускаете свой Java-код, создается отдельный поток (поток диспетчеризации событий), который обрабатывает GUI для вас.Эквивалент в pygame будет выглядеть примерно так:

import pygame
import threading

class Canvas(threading.Thread):
    def __init__(self):
        super().__init__()
        self.screen = pygame.display.set_mode((800, 600))
        self.screen.fill((50, 50, 50))
        self.clock = pygame.time.Clock()
        self.start()

    def draw_rect(self, x, y, width, height, color):
        pygame.draw.rect(self.screen, pygame.Color(color), (x, y, width, height))

    def clear(self):
        self.screen.fill((50, 50, 50))

    def run(self):
        while True:
            for e in pygame.event.get():
                print(e)
                if e.type == pygame.QUIT:
                    return
            pygame.display.update()
            self.clock.tick(60)

canvas = Canvas()
canvas.draw_rect(10, 10, 60, 60, 'blue')
print('I get printed even while window is active')

, но, как вы видите, если вы запустите этот код, он не будет работать, потому что система событий pygame работает правильно только при вызове из основного потока.(по крайней мере, в Windows. Он работает в Linux IIRC).

Так что то, о чем вы просите, не является надежным с Pygame, таким образом.


Используя tkinter, вы можете использоватьчто-то вроде этого:

import threading

class Canvas(threading.Thread):
    def __init__(self):
        super().__init__()
        self.start()

    def run(self):
        import tkinter
        root = tkinter.Tk()
        s = tkinter.StringVar()
        s.set('Foo')
        f = tkinter.StringVar()
        f.set('Bar')
        tkinter.Label(root,textvariable=s).pack()
        tkinter.Button(root,textvariable=f).pack()
        root.mainloop()


app = Canvas()
print('I get printed even while window is active')

, который будет работать, но немного хакерский, потому что модуль tkinter нужно импортировать в тот же поток, в котором вы вызываете mainloop().

...