Форум программистов, компьютерный форум, киберфорум
Python для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.93/55: Рейтинг темы: голосов - 55, средняя оценка - 4.93
3 / 3 / 1
Регистрация: 22.10.2022
Сообщений: 7

Нарисовать домик

22.10.2022, 12:50. Показов 15280. Ответов 20
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Здравствуйте, я начал изучать Python по учебнику К.Ю.Полякова, и не могу понять что не так в коде. По идее программа должна запросить два числовых значения (координаты), и потом на их основе вывести картинку. Сам код картинки правильный, по учебнику, но непонятно куда вставлять верхнюю его часть. Помогите, подскажите

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
print('Введите координаты домика')
x = int(input('x = '))
y = int(input('y = '))
home(x,y)
 
from graph import *
 
# Основная часть домика
def frame():
    penColor('black')
    brushColor('green')
    rectangle(x,y,x+100,y+100)
 
# Крыша
def roof():
    brushColor('brown')
    polygon([(x-10,y),(x+50,y-50),(x+110,y),(x-10,y)])
 
# Окно
def window():
    penColor('white')
    penSize(3)
    brushColor('black')
    rectangle(x+20,y+20,x+50,y+70)
    line(x+20,y+40,x+50,y+40)
    line(x+35,y+40,x+35,y+70)
 
def home(x,y):
    frame(x,y)
    roof(x,y)
    window(x,y)
 
home(x,y)
 
run()
0
Лучшие ответы (1)
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
22.10.2022, 12:50
Ответы с готовыми решениями:

Модуль Graph. Нарисовать домик. Нарисовать узор из окружностей
Помогите пожалуйста)))) 1)Нарисовать домик за начала рисунка взять координаты левого угла домика и все остальные вычислить через эту...

Нарисовать домик
Вобщем нужно нарисовать домик, чтобы было использовано по крайней мере 30-40 примитивов (различные кружки, треугольники и т.п). Домик,...

Нарисовать домик
Нарисовать домик, чтобы при нажатии на «А» свет в окне загорался, а потом гаснет при нажатии «В»

20
Эксперт PythonЭксперт Java
19530 / 11067 / 2931
Регистрация: 21.10.2017
Сообщений: 23,294
22.10.2022, 12:57
Цитата Сообщение от Aleksandr_theGr Посмотреть сообщение
непонятно куда вставлять верхнюю его часть
С самого верху написать def run():


Добавлено через 35 секунд
И home(x, y) снизу убрать

Добавлено через 4 минуты
Кликните здесь для просмотра всего текста
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
from graph import *
 
 
def run():
    print('Введите координаты домика')
    x = int(input('x = '))
    y = int(input('y = '))
    home(x, y)
 
 
# Основная часть домика
def frame(x, y):
    penColor('black')
    brushColor('green')
    rectangle(x, y, x + 100, y + 100)
 
 
# Крыша
def roof(x, y):
    brushColor('brown')
    polygon([(x - 10, y), (x + 50, y - 50), (x + 110, y), (x - 10, y)])
 
 
# Окно
def window(x, y):
    penColor('white')
    penSize(3)
    brushColor('black')
    rectangle(x + 20, y + 20, x + 50, y + 70)
    line(x + 20, y + 40, x + 50, y + 40)
    line(x + 35, y + 40, x + 35, y + 70)
    
 
def home(x, y):
    frame(x, y)
    roof(x, y)
    window(x, y)
 
run()
2
3 / 3 / 1
Регистрация: 22.10.2022
Сообщений: 7
22.10.2022, 13:43  [ТС]
Добавлено через 7 минут
Цитата Сообщение от iSmokeJC Посмотреть сообщение
С самого верху написать def run():


Добавлено через 35 секунд
И home(x, y) снизу убрать

Добавлено через 4 минуты
Кликните здесь для просмотра всего текста
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
from graph import *
 
 
def run():
    print('Введите координаты домика')
    x = int(input('x = '))
    y = int(input('y = '))
    home(x, y)
 
 
# Основная часть домика
def frame(x, y):
    penColor('black')
    brushColor('green')
    rectangle(x, y, x + 100, y + 100)
 
 
# Крыша
def roof(x, y):
    brushColor('brown')
    polygon([(x - 10, y), (x + 50, y - 50), (x + 110, y), (x - 10, y)])
 
 
# Окно
def window(x, y):
    penColor('white')
    penSize(3)
    brushColor('black')
    rectangle(x + 20, y + 20, x + 50, y + 70)
    line(x + 20, y + 40, x + 50, y + 40)
    line(x + 35, y + 40, x + 35, y + 70)
    
 
def home(x, y):
    frame(x, y)
    roof(x, y)
    window(x, y)
 
run()
Хмм, всё равно не работает. Сначала попробовал внести ваши правки, потом всё заменил на ваш код и ничего. Там суть такая - вводим два числа (координаты), и после этого должен появиться рисунок, а он не появляется, и ошибки никакой не выдаёт, просто молчит.
0
Эксперт PythonЭксперт Java
19530 / 11067 / 2931
Регистрация: 21.10.2017
Сообщений: 23,294
22.10.2022, 14:41
Цитата Сообщение от Aleksandr_theGr Посмотреть сообщение
а он не появляется, и ошибки никакой не выдаёт, просто молчит
Это проблемы твоей библиотеки graph. Я такую не знаю
1
3 / 3 / 1
Регистрация: 22.10.2022
Сообщений: 7
22.10.2022, 15:14  [ТС]
Лучший ответ Сообщение было отмечено 8Observer8 как решение

Решение

Цитата Сообщение от iSmokeJC Посмотреть сообщение
Это проблемы твоей библиотеки graph. Я такую не знаю
Ураа, методом тыка получилось Спасибо что отозвались

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from graph import *
 
print('Введите координаты домика')
x = int(input('x = '))
y = int(input('y = '))
def home(x, y):
    frame(x, y)
    roof(x, y)
    window(x, y)
 
# Основная часть домика
def frame(x, y):
    penColor('black')
    brushColor('green')
    rectangle(x, y, x + 100, y + 100)
 
# Крыша
def roof(x, y):
    brushColor('brown')
    polygon([(x - 10, y), (x + 50, y - 50), (x + 110, y), (x - 10, y)])
 
# Окно
def window(x, y):
    penColor('white')
    penSize(3)
    brushColor('black')
    rectangle(x + 20, y + 20, x + 50, y + 70)
    line(x + 20, y + 40, x + 50, y + 40)
    line(x + 35, y + 40, x + 35, y + 70)
 
home(x,y)
 
run()
1
9037 / 2937 / 494
Регистрация: 05.10.2013
Сообщений: 7,962
Записей в блоге: 216
22.10.2022, 18:02
Переписал на PyQt6 по урокам:
Для запуска примера нужно установить PyQt6 командой:
pip install PyQt6


small-house-qpainter-pyqt6/main.py

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import sys
 
from PyQt6.QtCore import QSize, Qt
from PyQt6.QtGui import QColor, QFont, QPainter, QPainterPath, QPen
from PyQt6.QtWidgets import (QApplication, QHBoxLayout, QLabel, QLineEdit,
                             QPushButton, QVBoxLayout, QWidget)
 
 
class Widget(QWidget):
 
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Домик")
        self.setFixedSize(QSize(400, 400));
        
        hbox = QHBoxLayout()
        coordLabel = QLabel("Введите координаты домика:")
        hbox.addWidget(coordLabel)
        self.xLineEdit = QLineEdit("150")
        self.yLineEdit = QLineEdit("200")
        hbox.addWidget(self.xLineEdit)
        hbox.addWidget(self.yLineEdit)
        hbox.addStretch(1)
 
        vbox = QVBoxLayout(self)
        vbox.addLayout(hbox)
        applyButton = QPushButton("Применить")
        applyButton.setFixedSize(QSize(100, 30))
        applyButton.clicked.connect(self.onApplyButtonClick)
        vbox.addWidget(applyButton);
        vbox.addStretch(1)
        self.setLayout(vbox)
 
        font = QFont("serif", 11)
        coordLabel.setFont(font)
        self.xLineEdit.setFont(font)
        self.yLineEdit.setFont(font)
        applyButton.setFont(font)
 
        self.housePosX = int(self.xLineEdit.text())
        self.housePosY = int(self.yLineEdit.text())
 
    def onApplyButtonClick(self):
        self.housePosX = int(self.xLineEdit.text())
        self.housePosY = int(self.yLineEdit.text())
        self.update()
 
    def paintEvent(self, event):
        qp = QPainter()
        qp.begin(self)
        qp.setPen(Qt.GlobalColor.black)
        
        # Основная часть домика
        qp.setBrush(Qt.GlobalColor.green)
        qp.drawRect(self.housePosX, self.housePosY, 100, 100)
        
        # Крыша
        path = QPainterPath();
        path.moveTo(self.housePosX-10, self.housePosY);
        path.lineTo(self.housePosX+50, self.housePosY-50);
        path.lineTo(self.housePosX+110, self.housePosY);
        path.lineTo(self.housePosX-10, self.housePosY);
        qp.fillPath(path, QColor(92, 64, 51))
        
        # Окно
        pen = QPen(Qt.GlobalColor.white, 3)
        qp.setPen(pen)
        qp.setBrush(Qt.GlobalColor.black)
        qp.drawRect(self.housePosX+20, self.housePosY+20, 30, 50)
        qp.drawLine(
            self.housePosX+20, self.housePosY+40,
            self.housePosX+50, self.housePosY+40)
        qp.drawLine(
            self.housePosX+35, self.housePosY+40,
            self.housePosX+35, self.housePosY+70)
 
        qp.end()
 
if __name__ == "__main__":
    app = QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec())
Миниатюры
2
9037 / 2937 / 494
Регистрация: 05.10.2013
Сообщений: 7,962
Записей в блоге: 216
22.10.2022, 23:20
Преимущество PyQt6 в том, код на нём легко переправить на Qt6 C++, чтобы собрать не такой большой EXE, как с помощью PyInstaller: small-house-qpainter-qt6-cpp-exe.zip (7.25 Мб) Qt6 C++ позволяет без больших трудозатрат собирать APK для Android. Введение в Qt6 C++: https://zetcode.com/gui/qt5/

Название: 197360720-447973e0-4cd5-43d8-b238-41a4b7ebf6c5.png
Просмотров: 368

Размер: 6.4 Кб



main.cpp

C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#include <QtGui/QFont>
#include <QtGui/QPainter>
#include <QtGui/QPainterPath>
#include <QtWidgets/QApplication>
#include <QtWidgets/QHBoxLayout>
#include <QtWidgets/QLabel>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QVBoxLayout>
#include <QtWidgets/QWidget>
 
#ifdef _WIN32
#include <windows.h>
#endif
 
class Widget : public QWidget
{
public:
    Widget()
    {
        setWindowTitle("Домик. QPainter, Qt6, C++");
        setFixedSize(QSize(400, 400));
 
        QHBoxLayout *hbox = new QHBoxLayout();
        QLabel *coordLabel = new QLabel("Введите координаты домика:");
        hbox->addWidget(coordLabel);
        m_xLineEdit = new QLineEdit("150");
        m_yLineEdit = new QLineEdit("200");
        hbox->addWidget(m_xLineEdit);
        hbox->addWidget(m_yLineEdit);
        hbox->addStretch(1);
 
        QVBoxLayout *vbox = new QVBoxLayout();
        vbox->addLayout(hbox);
        QPushButton *applyButton = new QPushButton("Применить");
        applyButton->setFixedSize(QSize(100, 30));
        connect(applyButton, &QPushButton::clicked, this, &Widget::onApplyButtonClick);
        vbox->addWidget(applyButton);
        vbox->addStretch(1);
        setLayout(vbox);
 
        QFont font("serif", 11);
        coordLabel->setFont(font);
        m_xLineEdit->setFont(font);
        m_yLineEdit->setFont(font);
        applyButton->setFont(font);
 
        m_housePosX = m_xLineEdit->text().toInt();
        m_housePosY = m_yLineEdit->text().toInt();
    }
 
private:
    void paintEvent(QPaintEvent *event) override
    {
        Q_UNUSED(event);
        QPainter qp;
        qp.begin(this);
        qp.setPen(Qt::GlobalColor::black);
 
        // Основная часть домика
        qp.setBrush(Qt::GlobalColor::green);
        qp.drawRect(m_housePosX, m_housePosY, 100, 100);
 
        // Крыша
        QPainterPath path;
        path.moveTo(m_housePosX - 10, m_housePosY);
        path.lineTo(m_housePosX + 50, m_housePosY - 50);
        path.lineTo(m_housePosX + 110, m_housePosY);
        path.lineTo(m_housePosX - 10, m_housePosY);
        qp.fillPath(path, QColor(92, 64, 51));
 
        // Окно
        QPen pen = QPen(Qt::GlobalColor::white, 3);
        qp.setPen(pen);
        qp.setBrush(Qt::GlobalColor::black);
        qp.drawRect(m_housePosX + 20, m_housePosY + 20, 30, 50);
        qp.drawLine(
            m_housePosX + 20, m_housePosY + 40,
            m_housePosX + 50, m_housePosY + 40);
        qp.drawLine(
            m_housePosX + 35, m_housePosY + 40,
            m_housePosX + 35, m_housePosY + 70);
 
        qp.end();
    }
 
private:
    QLineEdit *m_xLineEdit;
    QLineEdit *m_yLineEdit;
    int m_housePosX, m_housePosY;
 
private slots:
    void onApplyButtonClick()
    {
        // qDebug() << "click";
        m_housePosX = m_xLineEdit->text().toInt();
        m_housePosY = m_yLineEdit->text().toInt();
        update();
    }
};
 
int main(int argc, char *argv[])
{
#ifdef _WIN32
    if (AttachConsole(ATTACH_PARENT_PROCESS))
    {
        freopen("CONOUT$", "w", stdout);
        freopen("CONOUT$", "w", stderr);
    }
#endif
 
    QApplication app(argc, argv);
    Widget w;
    w.show();
    return app.exec();
}
small-house-qpainter-qt6-cpp.pro

Windows Batch file
1
2
3
4
5
6
7
8
9
10
11
12
13
# Build commands for CMD:
# qmake -makefile
# mingw32-make
# "./release/app"
 
QT += core gui widgets
 
CONFIG += c++11
 
SOURCES += \
    main.cpp
 
TARGET = app
Миниатюры
Вложения
Тип файла: zip small-house-qpainter-qt6-cpp-exe.zip (7.25 Мб, 6 просмотров)
3
9037 / 2937 / 494
Регистрация: 05.10.2013
Сообщений: 7,962
Записей в блоге: 216
22.10.2022, 23:28
Скриншот выше не обновил, где включил папку platforms с qwindows.dll
Изображения
 
1
9037 / 2937 / 494
Регистрация: 05.10.2013
Сообщений: 7,962
Записей в блоге: 216
23.10.2022, 02:32
Ещё одно преимущество PyQt6 в том, что QPainter сильно похож Canvas API, поэтому несложно сделать веб-версию своего приложения, которая может быть развёрнута, например, на бесплатном хостинге GitHub Pages и будет запускаться в один клик на всех ОС, где есть браузер, в том числе на мобильных устройствах. Демка в песочнице: https://plnkr.co/edit/sLfUEO4fTHJQ2gSs

Итого, получается, что:
  • для себя можно делать PyQt6-приложения, которые занимают минимальное место. Особенно актуально для ноутбуков
  • для других можно собирать на Qt C++ в исполняемые файлы для Windows, Linux, macOS, Android (Google Play), iOS (Apple Store). Особенно, если не хочется палить исходники - лицензия LGPL позволяет это делать, если не собирать в статику (то есть все Qt-dll в один exe), а для статики можно использовать GPL, но тогда нужно давать ссылку на свои исходники, например, на GitHub
  • можно сделать веб-версию, которая запускается в один клик на всех ОС и которую можно встраивать в соц. сети в виде веб-приложений



index.html

PHP/HTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
<html>
 
<head>
    <title>Домик</title>
 
    <style>
        .myFont *
        {
            font-size: 18.667px;
            font-family: serif
        }
    </style>
</head>
 
<body>
    <div style="position: absolute; width: 400px;" class="myFont">
        <div style="margin: 10px;">
            <span>Введите координаты домика:</span>
            <input id="housePosX" type="text" style="width: 50px;" value="150" />
            <input id="housePosY" type="text" style="width: 50px;" value="200" />
            <button onclick="onApplyButtonClick()">Применить</button>
        </div>
    </div>
 
    <canvas id="renderCanvas" width="400" height="400"></canvas>
 
    <script>
        const ctx = document.getElementById("renderCanvas").getContext("2d");
 
        const housePosXElem = document.getElementById("housePosX");
        const housePosYElem = document.getElementById("housePosY");
 
        let housePosX = parseInt(housePosXElem.value);
        let housePosY = parseInt(housePosYElem.value);
 
        draw();
 
        function onApplyButtonClick()
        {
            // console.log("click");
            housePosX = parseInt(housePosXElem.value);
            housePosY = parseInt(housePosYElem.value);
            draw();
        }
 
        function draw()
        {
            ctx.lineWidth = 1;
 
            // Заливка фона
            ctx.fillStyle = "rgba(240, 240, 240, 255)";
            ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
 
            // Основная часть домика
            ctx.fillStyle = "lightseagreen";
            ctx.fillRect(housePosX, housePosY, 100, 100);
            ctx.strokeStyle = "black";
            ctx.strokeRect(housePosX, housePosY, 100, 100);
 
            // Крыша
            ctx.fillStyle = "brown";
            ctx.beginPath();
            ctx.moveTo(housePosX - 10, housePosY);
            ctx.lineTo(housePosX + 50, housePosY - 50);
            ctx.lineTo(housePosX + 110, housePosY);
            ctx.lineTo(housePosX - 10, housePosY);
            ctx.fill();
 
            // Окно
            ctx.fillStyle = "black";
            ctx.fillRect(housePosX + 20, housePosY + 20, 30, 50);
            ctx.lineWidth = 3;
            ctx.strokeStyle = "white";
            ctx.strokeRect(housePosX + 20, housePosY + 20, 30, 50);
            ctx.beginPath();
            ctx.moveTo(housePosX + 20, housePosY + 40);
            ctx.lineTo(housePosX + 50, housePosY + 40);
            ctx.moveTo(housePosX + 35, housePosY + 40);
            ctx.lineTo(housePosX + 35, housePosY + 70);
            ctx.stroke();
        }
    </script>
</body>
 
</html>
Миниатюры
2
3 / 3 / 1
Регистрация: 22.10.2022
Сообщений: 7
01.11.2022, 16:09  [ТС]
Цитата Сообщение от 8Observer8 Посмотреть сообщение
Ещё одно преимущество PyQt6 в том, что QPainter сильно похож Canvas API, поэтому несложно сделать веб-версию своего приложения, которая может быть развёрнута, например, на бесплатном хостинге GitHub Pages и будет запускаться в один клик на всех ОС, где есть браузер, в том числе на мобильных устройствах. Демка в песочнице: https://plnkr.co/edit/sLfUEO4fTHJQ2gSs

Итого, получается, что:
  • для себя можно делать PyQt6-приложения, которые занимают минимальное место. Особенно актуально для ноутбуков
  • для других можно собирать на Qt C++ в исполняемые файлы для Windows, Linux, macOS, Android (Google Play), iOS (Apple Store). Особенно, если не хочется палить исходники - лицензия LGPL позволяет это делать, если не собирать в статику (то есть все Qt-dll в один exe), а для статики можно использовать GPL, но тогда нужно давать ссылку на свои исходники, например, на GitHub
  • можно сделать веб-версию, которая запускается в один клик на всех ОС и которую можно встраивать в соц. сети в виде веб-приложений



index.html

PHP/HTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
<html>
 
<head>
    <title>Домик</title>
 
    <style>
        .myFont *
        {
            font-size: 18.667px;
            font-family: serif
        }
    </style>
</head>
 
<body>
    <div style="position: absolute; width: 400px;" class="myFont">
        <div style="margin: 10px;">
            <span>Введите координаты домика:</span>
            <input id="housePosX" type="text" style="width: 50px;" value="150" />
            <input id="housePosY" type="text" style="width: 50px;" value="200" />
            <button onclick="onApplyButtonClick()">Применить</button>
        </div>
    </div>
 
    <canvas id="renderCanvas" width="400" height="400"></canvas>
 
    <script>
        const ctx = document.getElementById("renderCanvas").getContext("2d");
 
        const housePosXElem = document.getElementById("housePosX");
        const housePosYElem = document.getElementById("housePosY");
 
        let housePosX = parseInt(housePosXElem.value);
        let housePosY = parseInt(housePosYElem.value);
 
        draw();
 
        function onApplyButtonClick()
        {
            // console.log("click");
            housePosX = parseInt(housePosXElem.value);
            housePosY = parseInt(housePosYElem.value);
            draw();
        }
 
        function draw()
        {
            ctx.lineWidth = 1;
 
            // Заливка фона
            ctx.fillStyle = "rgba(240, 240, 240, 255)";
            ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
 
            // Основная часть домика
            ctx.fillStyle = "lightseagreen";
            ctx.fillRect(housePosX, housePosY, 100, 100);
            ctx.strokeStyle = "black";
            ctx.strokeRect(housePosX, housePosY, 100, 100);
 
            // Крыша
            ctx.fillStyle = "brown";
            ctx.beginPath();
            ctx.moveTo(housePosX - 10, housePosY);
            ctx.lineTo(housePosX + 50, housePosY - 50);
            ctx.lineTo(housePosX + 110, housePosY);
            ctx.lineTo(housePosX - 10, housePosY);
            ctx.fill();
 
            // Окно
            ctx.fillStyle = "black";
            ctx.fillRect(housePosX + 20, housePosY + 20, 30, 50);
            ctx.lineWidth = 3;
            ctx.strokeStyle = "white";
            ctx.strokeRect(housePosX + 20, housePosY + 20, 30, 50);
            ctx.beginPath();
            ctx.moveTo(housePosX + 20, housePosY + 40);
            ctx.lineTo(housePosX + 50, housePosY + 40);
            ctx.moveTo(housePosX + 35, housePosY + 40);
            ctx.lineTo(housePosX + 35, housePosY + 70);
            ctx.stroke();
        }
    </script>
</body>
 
</html>
Миниатюры
Ого. Спасибо за все эти советы, но признаюсь для меня пока это всё просто набор слов )) я совсем начинающий. Ну как дойду до таких практик, просмотрю ваш код еще раз
2
9037 / 2937 / 494
Регистрация: 05.10.2013
Сообщений: 7,962
Записей в блоге: 216
01.11.2022, 19:27
Цитата Сообщение от Aleksandr_theGr Посмотреть сообщение
но признаюсь для меня пока это всё просто набор слов
Для меня также библиотека graph непонятна. Последовательно пройдите эти уроки https://zetcode.com/pyqt6/ и вам PyQt6 покажется не таким сложным, как сейчас, а даже очень дружелюбным. Заодно в английском нелишним будет попрактиковаться. Если в каком-то предложении или абзаце возникнет сложность, то используйте https://translate.google.com/ Этот переводчик переводит такие тексты на "хорошо" и даже чаще всего на "отлично". Лучше сразу взять что-то наиболее популярное, чтобы потом не переучиться и чтобы больше людей могло помочь. В конце туториала вы найдёте игру Тетрис. Как вариант, можно практиковаться в программировании, в изучении PyQt и Python на простых игрушках: Змейка, Арканойд и т.д. Таблицу рекордов сделать на SQLite, добавить мультиплеер на Qt WebSockets - огромный простор для фантазии в изучении PyQt.
4
9037 / 2937 / 494
Регистрация: 05.10.2013
Сообщений: 7,962
Записей в блоге: 216
10.11.2022, 20:53
В PyQt есть обёртка над OpenGL. В перспективе можно рисовать не только в 2D, но и в 3D. На базовом уровне ничего сложного в OpenGL нет. Объекты рисуются треугольниками. Даже линию/отрезок нужно рисовать двумя треугольниками, которые образуют прямоугольник с малой высотой. Можно один раз задать прямоугольник, а потом трансформировать его с помощью использования класса QMatrix4x4. Координаты треугольников объекта задаются в массиве. Вершинный шейдер берёт последовательно координаты вершин из массива, а фрагментный шейдер пробегается по пикселям и задаёт цвет пикселя. Цвет можно передать через переменную в фрагментный шейдер.

Пример создания пустого окна с заданным цветом холста. Для запуска примера нужно поставить пакеты:

pip install PyQt6
pip install PyOpenGL
main.py

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import sys
 
from OpenGL import GL as gl
from PyQt6.QtCore import Qt
from PyQt6.QtOpenGLWidgets import QOpenGLWidget
from PyQt6.QtWidgets import QApplication
 
 
class Widget(QOpenGLWidget):
 
    def __init__(self):
        super().__init__()
        self.setWindowTitle("PyQt6, OpenGL 3.3")
        self.resize(400, 400)
 
    def initializeGL(self):
        gl.glClearColor(0.5, 0.5, 0.5, 1)
    
    def paintGL(self):
        gl.glClear(gl.GL_COLOR_BUFFER_BIT)
 
if __name__ == "__main__":
    QApplication.setAttribute(Qt.ApplicationAttribute.AA_UseDesktopOpenGL)
    app = QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec())
Здесь класс окна Widget наследуется от класса QOpenGLWidget. В классе QOpenGLWidget определены методы initializeGL() и paintGL(), которые переопределяются в примере выше. Метод initializeGL() будет вызван один раз до показа окна. Метод paintGL() будет вызван один раз после initializeGL() и далее будет вызываться каждый раз, когда система решит перерисовать окно, например, если изменить размер окна. Функция glClearColor(0.5, 0.5, 0.5, 1) выставляет цвет заливки холста. В данном случае цвет серый, а 1 в конце - это прозрачность (1 - непрозрачно, 0.5 - наполовину прозрачно, 0 - прозрачно). Цвет задаётся в нормированном виде, то есть от 0 до 1 для каждой компоненты цвета RGB. Нормирование происходит на 255, то есть если у вас есть цвет (128, 68, 35), то в OpenGL нужно будет использовать (128/255, 68/255, 35/255). Функция glClear(gl.GL_COLOR_BUFFER_BIT) очищает холст, заливая его заданным цветом.



Миниатюры
1
9037 / 2937 / 494
Регистрация: 05.10.2013
Сообщений: 7,962
Записей в блоге: 216
10.11.2022, 20:59
Можно избавиться от написания "gl.", если импортировать из PyOpenGL так:

Python
1
from OpenGL.GL import *
А не так:

Python
1
from OpenGL import GL as gl
main.py

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import sys
 
from OpenGL.GL import *
from PyQt6.QtCore import Qt
from PyQt6.QtOpenGLWidgets import QOpenGLWidget
from PyQt6.QtWidgets import QApplication
 
 
class Widget(QOpenGLWidget):
 
    def __init__(self):
        super().__init__()
        self.setWindowTitle("PyQt6, OpenGL 3.3")
        self.resize(400, 400)
 
    def initializeGL(self):
        glClearColor(0.5, 0.5, 0.5, 1)
    
    def paintGL(self):
        glClear(GL_COLOR_BUFFER_BIT)
 
if __name__ == "__main__":
    QApplication.setAttribute(Qt.ApplicationAttribute.AA_UseDesktopOpenGL)
    app = QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec())
1
9037 / 2937 / 494
Регистрация: 05.10.2013
Сообщений: 7,962
Записей в блоге: 216
10.11.2022, 22:21
Пример рисования двух прямоугольников с заданными координатами, размерами, углом поворота и цветами на PyQt6 и OpenGL3



На OpenGL можно самому определить систему координат. Можно где угодно расположить начало системы координат - в любой точке холста и любое направление осей. В данном случае, я как и в примерах темы расположил начало системы координат в верхнем левом углу, а ось Y направил вниз:

Python
1
2
        projMatrix = QMatrix4x4()
        projMatrix.ortho(0, 400, 400, 0, -10, 10);
Описание метода ortho: https://doc.qt.io/qt-6/qmatrix4x4.html#ortho

void QMatrix4x4::ortho(float left, float right, float bottom, float top, float nearPlane, float farPlane)

Multiplies this matrix by another that applies an orthographic projection for a window with lower-left corner (left, bottom), upper-right corner (right, top), and the specified nearPlane and farPlane clipping planes.
Задал такие координаты, размеры, углы и цвета для прямоугольников:

Python
1
2
3
4
5
6
7
8
9
        self.x0, self.y0 = 150, 150
        self.w0, self.h0 = 100, 80
        self.angle0 = 0
        self.color0 = QVector3D(0.9, 0.2, 0.2);
 
        self.x1, self.y1 = 150, 270
        self.w1, self.h1 = 150, 50
        self.angle1 = 20
        self.color1 = QVector3D(0.3, 0.9, 0.3);
Для запуска программу нужно установить следующие пакеты:

pip install PyQt6
pip install PyOpenGL
pip install numpy
main.py

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import sys
 
import numpy as np
from OpenGL.GL import *
from PyQt6.QtCore import QSize, Qt
from PyQt6.QtGui import QMatrix4x4, QVector3D
from PyQt6.QtOpenGL import QOpenGLBuffer, QOpenGLShader, QOpenGLShaderProgram
from PyQt6.QtOpenGLWidgets import QOpenGLWidget
from PyQt6.QtWidgets import QApplication
 
 
class Widget(QOpenGLWidget):
 
    def __init__(self):
        super().__init__()
        self.setWindowTitle("PyQt6, OpenGL3")
        self.setFixedSize(QSize(300, 300))
 
    def initializeGL(self):
        glClearColor(0.95, 0.95, 0.95, 1.0)
 
        vertShaderSrc = """
            #version 330 core
            in vec3 aPosition;
            uniform mat4 uMvpMatrix;
            void main()
            {
                gl_Position = uMvpMatrix * vec4(aPosition, 1.0);
            }
        """
 
        fragShaderSrc = """
            #version 330 core
            uniform vec3 uColor;
            void main()
            {
                gl_FragColor = vec4(uColor, 1.0);
            }
        """
 
        self.program = QOpenGLShaderProgram(self)
        self.program.addShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Vertex, vertShaderSrc)
        self.program.addShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Fragment, fragShaderSrc)
        self.program.bindAttributeLocation("aPosition", 0)
        self.program.link()
        self.program.bind()
 
        vertPositions = np.array([
            -0.5, -0.5,
            0.5, -0.5,
            -0.5, 0.5,
            0.5, 0.5], dtype=np.float32)
        self.vertPosBuffer = QOpenGLBuffer()
        self.vertPosBuffer.create()
        self.vertPosBuffer.bind()
        self.vertPosBuffer.allocate(vertPositions, len(vertPositions) * 4)
        self.program.setAttributeBuffer(0, GL_FLOAT, 0, 2)
        self.program.enableAttributeArray(0)
 
        projMatrix = QMatrix4x4()
        projMatrix.ortho(0, 400, 400, 0, -10, 10);
 
        viewMatrix = QMatrix4x4()
        viewMatrix.lookAt(
            QVector3D(0, 0, 10),
            QVector3D(0, 0, 0),
            QVector3D(0, 1, 0))
 
        self.projViewMatrix = QMatrix4x4()
        self.projViewMatrix = projMatrix * viewMatrix
 
        self.uMvpMatrixLocation = self.program.uniformLocation("uMvpMatrix")
        self.mvpMatrix = QMatrix4x4()
 
        self.uColorLocation = self.program.uniformLocation("uColor");
 
        self.x0, self.y0 = 150, 150
        self.w0, self.h0 = 100, 80
        self.angle0 = 0
        self.color0 = QVector3D(0.9, 0.2, 0.2);
 
        self.x1, self.y1 = 150, 270
        self.w1, self.h1 = 150, 50
        self.angle1 = 20
        self.color1 = QVector3D(0.3, 0.9, 0.3);
 
        self.modelMatrix = QMatrix4x4()
 
    def paintGL(self):
        glClear(GL_COLOR_BUFFER_BIT)
 
        # First Rectangle
        self.modelMatrix.setToIdentity();
        self.modelMatrix.translate(self.x0, self.y0);
        self.modelMatrix.rotate(self.angle0, QVector3D(0, 0, 1));
        self.mvpMatrix = self.projViewMatrix * self.modelMatrix
        self.mvpMatrix.scale(self.w0, self.h0);
        self.program.bind();
        self.program.setUniformValue(self.uMvpMatrixLocation, self.mvpMatrix);
        self.program.setUniformValue(self.uColorLocation, self.color0);
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
 
        # Second Rectangle
        self.modelMatrix.setToIdentity();
        self.modelMatrix.translate(self.x1, self.y1);
        self.modelMatrix.rotate(self.angle1, QVector3D(0, 0, 1));
        self.mvpMatrix = self.projViewMatrix * self.modelMatrix
        self.mvpMatrix.scale(self.w1, self.h1);
        self.program.bind();
        self.program.setUniformValue(self.uMvpMatrixLocation, self.mvpMatrix);
        self.program.setUniformValue(self.uColorLocation, self.color1);
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
 
if __name__ == "__main__":
    QApplication.setAttribute(Qt.ApplicationAttribute.AA_UseDesktopOpenGL)
    app = QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec())
Миниатюры
2
9037 / 2937 / 494
Регистрация: 05.10.2013
Сообщений: 7,962
Записей в блоге: 216
07.12.2022, 14:54
Немного поясню пример выше с шейдерным OpenGL. Конечно, наверное, лучше сначала было на нешейдерном нарисовать. В шейдерном определяют массив вершин треугольников, который сохраняют в оперативную память видеокарты. Шейдеры компилируются и отправляются тоже в оперативную память видеокарты. Далее, при вызове метода glDrawArrays графический процессор видеокарты (GPU) запускает вершинный шейдер для каждой вершины треугольника. Вершины по очереди попадают в переменную aPosition. Матрица Projection * View * Model умножается на каждую вершину. Фрагментный шейдер вызывается для каждого пикселя, устанавливая цвет пикселя.

В примере выше создан класс Widget, который унаследован от QOpenGLWidget. Это позволило переопределить два метода:

Python
1
2
3
4
5
6
7
8
9
from PyQt6.QtOpenGLWidgets import QOpenGLWidget
 
class Widget(QOpenGLWidget):
 
    def initializeGL(self):
        # ...
 
    def paintGL(self):
        # ...
Метод initializeGL() будет вызван один раз после вызова метода w.show():

Python
1
2
3
4
5
6
if __name__ == "__main__":
    QApplication.setAttribute(Qt.ApplicationAttribute.AA_UseDesktopOpenGL)
    app = QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec())
Метод paintGL() будет вызываться каждый раз, когда операционная система решит перерисовать окно. Например, если свернуть окно и показать.

В методе initializeGL() один раз вершины двух прямоугольников копируются в память оперативной памяти видеокарты:

Python
1
2
3
4
5
6
7
8
9
        vertPositions = np.array([
            -0.5, -0.5,
            0.5, -0.5,
            -0.5, 0.5,
            0.5, 0.5], dtype=np.float32)
        self.vertPosBuffer = QOpenGLBuffer()
        self.vertPosBuffer.create()
        self.vertPosBuffer.bind()
        self.vertPosBuffer.allocate(vertPositions, len(vertPositions) * 4)
Использован способ определения треугольников методом GL_TRIANGLE_STRIP, который указан в функции glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)

Чем отличаются несколько методов определения треугольников:



Миниатюры
2
9037 / 2937 / 494
Регистрация: 05.10.2013
Сообщений: 7,962
Записей в блоге: 216
07.12.2022, 15:32
Получение доступа к переменным шейдеров

Шейдеры, при каждом запуске программы, один раз компилируются и копируются в оперативную память видеокарты, потому что существуют разные производители видеокарта. В результате компиляции получается объект "program" класса QOpenGLShaderProgram, через который можно получать доступ к шейдерам. В шейдерах есть переменные, которые нужно задать извне: aPosition, uMvpMatrix и uColor. Для доступа к этим шейдерным переменным создаются идентификаторы:

Python
1
2
self.uMvpMatrixLocation = self.program.uniformLocation("uMvpMatrix")
self.uColorLocation = self.program.uniformLocation("uColor")
Для переменной aPosition можно задать идентификатор самостоятельно до момента линковки шейдеров:

Python
1
2
3
self.program.bindAttributeLocation("aPosition", 0)
self.program.link()
self.program.bind()
Теперь aPosition ассоциируется с идентификатором 0. Теперь можно настроить доступ к массиву треугольников, указал этот идентификатор, тип данных в массиве (float) и сколько чисел в массиве приходится на одну вершину (здесь 2, потому что задано по две координаты для одной вершины):

Python
1
2
self.program.setAttributeBuffer(0, GL_FLOAT, 0, 2)
self.program.enableAttributeArray(0)
Метод enableAttributeArray означает "активацию", что мы настроили и применили. Теперь вершинный шейдер знает по сколько значений копировать в переменную aPosition. При рисовании шейдер 4 раза последовательно скопирует по два значения в переменную aPosition:

Python
1
2
3
4
5
6
7
8
9
        vertShaderSrc = """
            #version 330 core
            in vec3 aPosition;
            uniform mat4 uMvpMatrix;
            void main()
            {
                gl_Position = uMvpMatrix * vec4(aPosition, 1.0);
            }
        """
Так будут заданы координаты 4-x вершин, по которым будет нарисовано два треугольника, то есть два полигона в 2D или 3D мире.

Переменная "uniform" означает, что она общая для всех вершин данного вызова glDrawArrays(), ещё значение устанавливается до вызова glDrawArrays(), например для результирующей матрицы и цвета полигонов:

Python
1
2
3
4
self.program.bind();
self.program.setUniformValue(self.uMvpMatrixLocation, self.mvpMatrix);
self.program.setUniformValue(self.uColorLocation, self.color0);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
Происходит передача значений в переменные uMvpMatrix и uColor.
2
9037 / 2937 / 494
Регистрация: 05.10.2013
Сообщений: 7,962
Записей в блоге: 216
11.01.2023, 18:55
Как вариант, на Python можно создать окно на PySDL2, вывести GUI на ImGUI и нарисовать объекты с помощью нешейдерного OpenGL версии 1 со статическим конвейером. Правда, на ImGUI и Python не работает вывод на русском, хотя на C++ работает. Я создал issue по этому поводу: https://github.com/pyimgui/pyimgui/issues/316

pip install imgui PyOpenGL PySDL


main.py

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import ctypes
import sys
 
import imgui
from OpenGL.GL import *
import sdl2
from imgui.integrations.sdl2 import SDL2Renderer
 
g_maxFPS = 30.0
 
def main():
    if sdl2.SDL_Init(sdl2.SDL_INIT_VIDEO) != 0:
        print(sdl2.SDL_GetError())
        return -1
 
    winW, winH = (400, 400)
    window = sdl2.SDL_CreateWindow(
        b"ImGUI, OpenGL1, SDL2, Python",
        sdl2.SDL_WINDOWPOS_CENTERED, sdl2.SDL_WINDOWPOS_CENTERED,
        winW, winH, sdl2.SDL_WINDOW_OPENGL)
    if not window:
        print(sdl2.SDL_GetError())
        return -1
 
    context = sdl2.SDL_GL_CreateContext(window)
    sdl2.SDL_GL_SetAttribute(sdl2.SDL_GL_DOUBLEBUFFER, 1)
 
    # Setup Dear ImGui context
    imgui.create_context()
    impl = SDL2Renderer(window)
 
    glClearColor(0.3, 0.2, 0.2, 1)
 
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    glOrtho(0, 400, 400, 0, -1, 1)
    glViewport(0, 0, winW, winH)
 
    xCoord, yCoord = 150, 200
 
    event = sdl2.SDL_Event()
    running = True
    startTicks = 0.0
    frameTicks = 0.0
    while running:
        while sdl2.SDL_PollEvent(ctypes.byref(event)) != 0:
            if event.type == sdl2.SDL_QUIT:
                running = False
            impl.process_event(event)
        impl.process_inputs()
 
        startTicks = sdl2.SDL_GetTicks()
 
        imgui.new_frame()
 
        imgui.begin("Control Panel", True)
        imgui.text("Enter the coordinates of the house:")
        # Get X
        imgui.text("x:")
        imgui.same_line()
        changed, xCoord = imgui.input_text("##", str(xCoord), 10)
        xCoord = int(xCoord)
        # Get Y
        imgui.text("y:")
        imgui.same_line()
        changed, yCoord = imgui.input_text("###", str(yCoord), 10)
        yCoord = int(yCoord)
        # if imgui.button("Apply"):
            # print("({}, {})".format(xCoord, yCoord))
        imgui.end()
 
        glClear(GL_COLOR_BUFFER_BIT) # Clear screen
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
 
        glColor3f(0, 1, 0)
        glPushMatrix()
        glTranslate(xCoord, yCoord, 0)
        glScale(100, 100, 1)
        glBegin(GL_QUADS)
        glVertex2f(-0.5, -0.5)
        glVertex2f(-0.5, 0.5)
        glVertex2f(0.5, 0.5)
        glVertex2f(0.5, -0.5)
        glEnd()
        glPopMatrix()
 
        imgui.render()
        impl.render(imgui.get_draw_data())
        sdl2.SDL_GL_SwapWindow(window)
 
        # Limit the FPS to the max FPS
        frameTicks = sdl2.SDL_GetTicks() - startTicks
        if (1000.0 / g_maxFPS > frameTicks):
            sdl2.SDL_Delay(int(1000.0 / g_maxFPS - frameTicks))
 
    sdl2.SDL_GL_DeleteContext(context)
    sdl2.SDL_DestroyWindow(window)
    sdl2.SDL_Quit()
    return 0
 
if __name__ == "__main__":
    sys.exit(main())
Миниатюры
2
9037 / 2937 / 494
Регистрация: 05.10.2013
Сообщений: 7,962
Записей в блоге: 216
11.01.2023, 19:01
Ошибся в названии пакета. Надо было PySDL2 вместо PySDL:

pip install imgui PyOpenGL PySDL2
0
1 / 1 / 0
Регистрация: 02.02.2023
Сообщений: 3
02.02.2023, 16:05
Друг, спасибо что ты есть, так подробно разжевать вопрос это талант, сам изучаю Python последние 3 месяца и ты очень помог, спасибо!
1
9037 / 2937 / 494
Регистрация: 05.10.2013
Сообщений: 7,962
Записей в блоге: 216
05.02.2023, 16:15
Текущий итог моих умозаключений по поводу инструментов для рисования графики. Библиотека PySDL2 хороша, но есть критичные для меня недостатки, об этом ниже. Её API похож на PyGame, так как PyGame использует SDL2. PySDL2 может быть использована как база (окно, клавиатура, мышь) для OpenGL графики для рисования не только в 2D, но и в 3D. Данный домик можно было бы нарисовать в 3D и это необходимо сделать на следующей итерации развития проекта. На этапе релиза проекта PySDL2 можно переписать на SDL С++ (из PyQt значительно проще переписать на Qt C++), чтобы собрать EXE, а также SDL2 позволяет собирать для Android, iOS и для Web в WASM (WebAssembly) с помощью Emscripten SDK. То есть можно иметь у себя на компьютере Python версии приложений, которые проще развивать, чем С++, они занимают мало места, а при необходимости переписывать на C++.

Но есть альтернативный вариант и это PyQt и Qt C++. Этот вариант имеет ряд преимуществ:
  • Отличная поддержка WebSockets. Можно писать серверы на Node.js или на Python и развёртывать их на бесплатных хостингах: https://render.com/ и https://glitch.com/ Это можно использовать для тренировки написания клиент-серверных приложений, например, написания небольших игр с мультиплеером или кооперативом: Крестики-Нолики, Морской Бой, Шашки, Танчики, карточные игры типа Дурак или Покер и т.д. Можно писать неигровые 3D приложения с анимациями, где присутствуют несколько человек одновременно. Состояние с чистым C++ в плане библиотек для веб-сокетов плохое. Я пробовал socket.io-client-cpp и websocketpp, но не смог справиться с проблемами, например: Invalid use of reserved bits
  • Qt имеет намного более интуитивно понятное создание GUI, чем ImGUI. Python версия ImGUI не умеет выводить текст на русском: Cyrillic fonts. ImGUI, OpenGL, SDL2, Python
  • PyQt намного проще "переправить" на Qt C++, чем PySDL2 на SDL2 для создания релизов в виде исполняемых файлов. Qt уже имеет большинство всего необходимого из коробки, например: работа с линейной алгеброй (QMatrix4x4, QVector3D, QQuaternion), QPainter, XML, JSON, WebSockets, SQLite, GUI и т.д. На Qt намного проще собирать релизы для Android, iOS и Web. Например, на Qt Android всё само автоматически настраивается. Я собирал под Android приложения с OpenGL графикой. На SDL2 только видел какие сложные инструкции.
  • PyQt изучать намного более выгодно, чем SDL2 (и другие инструменты), потому что даже если человек не будет заниматься компьютерной графикой, то он получит знания и навыки работы с GUI и другими модулями PyQt, познакомится с концепцией сигнал/слот и т.д. Ему несложно будет параллельно осваивать Qt C++. Очевидно, что вакансий на Qt значительно больше, чем на других GUI-фремворках. Поэтому, чтобы не распыляться, лучше сразу учиться программировать на PyQt.

Для себя решил, что лучше всего брать OpenGL версии 2 вместо версии 1 и версии 3, потому что у меня почему-то приложения на версии 3 не запускаются на эмуляторе Android (и на железе тоже). OpenGL 2.1 - практически тоже самое, что и OpenGL ES 2.0. Версии 2.1 может быть более чем достаточно для всех задач. Данная задача с домиком очень показательна - надо обязательно как можно чаще делать задачи в сочетании графики с GUI, конечно, если интересна графика.



Code
1
pip install numpy PyOpenGL PyQt6
assets/shaders/default.vert
glSlang
1
2
3
4
5
6
7
attribute vec2 aPosition;
uniform mat4 uMvpMatrix;
 
void main()
{
    gl_Position = uMvpMatrix * vec4(aPosition, 0.0, 1.0);
}
assets/shaders/default.frag
glSlang
1
2
3
4
5
6
7
8
precision mediump float;
 
uniform vec3 uColor;
 
void main()
{
    gl_FragColor = vec4(uColor, 1.0);
}
main.py
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
import sys
 
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QApplication
 
from main_window import MainWindow
 
if __name__ == "__main__":
    QApplication.setAttribute(Qt.ApplicationAttribute.AA_UseDesktopOpenGL)
    app = QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec())
main_window.py.py
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
from types import SimpleNamespace
 
from PyQt6.QtCore import QSize, Qt, pyqtSignal
from PyQt6.QtGui import QFont
from PyQt6.QtWidgets import (QHBoxLayout, QLabel, QLineEdit, QPushButton,
                             QSizePolicy, QVBoxLayout, QWidget)
 
from opengl_widget import OpenGLWidget
 
 
class MainWindow(QWidget):
 
    setParams = pyqtSignal(object)
 
    def __init__(self):
        super().__init__()
        self.setWindowTitle("OpenGL21 PyQt6 Python")
        self.setFixedSize(QSize(312, 400))
 
        coordLabel = QLabel("Введите координаты домика:")
        coordLabel.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
        self.xLineEdit = QLineEdit("75")
        self.yLineEdit = QLineEdit("100")
 
        xLabel = QLabel("x:")
        yLabel = QLabel("y:")
        hbox = QHBoxLayout()
        hbox.addWidget(xLabel)
        hbox.addWidget(self.xLineEdit)
        hbox.addWidget(yLabel)
        hbox.addWidget(self.yLineEdit)
        # hbox.addStretch(1)
 
        applyButton = QPushButton("Применить")
        applyButton.setFixedSize(QSize(100, 30))
        applyButton.clicked.connect(self.onApplyButtonClick)
        applyButtonLayout = QHBoxLayout()
        applyButtonLayout.setAlignment(Qt.AlignmentFlag.AlignHCenter)
        applyButtonLayout.addWidget(applyButton)
 
        openGLWidget = OpenGLWidget()
 
        vbox = QVBoxLayout()
        vbox.addWidget(coordLabel)
        vbox.addLayout(hbox)
        vbox.addLayout(applyButtonLayout)
        vbox.addWidget(openGLWidget)
        self.setLayout(vbox)
 
        font = QFont("serif", 11)
        coordLabel.setFont(font)
        self.xLineEdit.setFont(font)
        self.yLineEdit.setFont(font)
        xLabel.setFont(font)
        yLabel.setFont(font)
        applyButton.setFont(font)
 
        self.setParams.connect(openGLWidget.setParamsSlot)
        obj = SimpleNamespace()
        obj.x = int(self.xLineEdit.text())
        obj.y = int(self.yLineEdit.text())
        self.setParams.emit(obj)
 
    def onApplyButtonClick(self):
        obj = SimpleNamespace()
        obj.x = int(self.xLineEdit.text())
        obj.y = int(self.yLineEdit.text())
        self.setParams.emit(obj)
opengl_widget.py.py
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import numpy as np
from OpenGL.GL import *
from PyQt6.QtGui import QMatrix4x4, QSurfaceFormat, QVector3D
from PyQt6.QtOpenGL import QOpenGLBuffer, QOpenGLShader, QOpenGLShaderProgram
from PyQt6.QtOpenGLWidgets import QOpenGLWidget
 
from renderable import Renderable
 
 
class OpenGLWidget(QOpenGLWidget):
 
    def __init__(self):
        super().__init__()
        self.housePosX = 0
        self.housePosY = 0
        # Set format
        format = QSurfaceFormat()
        format.setSamples(8)
        self.setFormat(format)
 
    def initializeGL(self):
        glClearColor(0.77, 0.93, 0.95, 1)
 
        self.program = QOpenGLShaderProgram()
        self.program.addShaderFromSourceFile(QOpenGLShader.ShaderTypeBit.Vertex,
            "assets/shaders/default.vert")
        self.program.addShaderFromSourceFile(QOpenGLShader.ShaderTypeBit.Fragment,
            "assets/shaders/default.frag")
        self.program.bindAttributeLocation("aPosition", 0)
        self.program.link()
        self.program.bind()
 
        vertPositions = np.array([
            0, 0, # Quad
            1, 0,
            0, 1,
            1, 1,
            0, 0, # Triangle
            1.2, -1,
            2.4, 0], dtype=np.float32)
        self.vertPosBuffer = QOpenGLBuffer()
        self.vertPosBuffer.create()
        self.vertPosBuffer.bind()
        self.vertPosBuffer.allocate(vertPositions, len(vertPositions) * 4)
        self.program.setAttributeBuffer(0, GL_FLOAT, 0, 2)
        self.program.enableAttributeArray(0)
 
        self.modelMatrix = QMatrix4x4()
        self.mvpMatrix = QMatrix4x4()
 
        projMatrix = QMatrix4x4()
        projMatrix.ortho(0, 200, 200, 0, 1, -1)
 
        viewMatrix = QMatrix4x4()
        viewMatrix.lookAt(
            QVector3D(0, 0, 1),
            QVector3D(0, 0, 0),
            QVector3D(0, 1, 0))
 
        self.projViewMatrix = QMatrix4x4()
        self.projViewMatrix = projMatrix * viewMatrix
 
        self.uMvpMatrixLocation = self.program.uniformLocation("uMvpMatrix")
        self.uColorLocation = self.program.uniformLocation("uColor")
 
        houseBody = Renderable(0, 0, 100, 100, QVector3D(0, 0.7, 0), 0, 4)
        roof = Renderable(-10, 0, 50, 50, QVector3D(0.35, 0.25, 0.2), 4, 4)
        window = Renderable(20, 20, 30, 50, QVector3D(0, 0, 0), 0, 4)
        windowLine0 = Renderable(20, 38, 30, 4, QVector3D(1, 1, 1), 0, 4)
        windowLine1 = Renderable(33, 40, 4, 30, QVector3D(1, 1, 1), 0, 4)
 
        self.objects = []
        self.objects.append(houseBody)
        self.objects.append(roof)
        self.objects.append(window)
        self.objects.append(windowLine0)
        self.objects.append(windowLine1)
 
    def paintGL(self):
        glClear(GL_COLOR_BUFFER_BIT)
 
        for obj in self.objects:
            self.modelMatrix.setToIdentity()
            self.modelMatrix.translate(self.housePosX + obj.x,
                self.housePosY + obj.y)
            self.modelMatrix.rotate(obj.angle, QVector3D(0, 0, 1))
            self.mvpMatrix = self.projViewMatrix * self.modelMatrix
            self.mvpMatrix.scale(obj.w, obj.h)
            self.program.bind()
            self.program.setUniformValue(self.uMvpMatrixLocation, self.mvpMatrix)
            self.program.setUniformValue(self.uColorLocation, obj.color)
            glDrawArrays(GL_TRIANGLE_STRIP, obj.startIndex, obj.amountOfVertices)
 
    def setParamsSlot(self, params):
        self.housePosX = params.x
        self.housePosY = params.y
        self.update()
renderable.py.py
Python
1
2
3
4
5
6
7
8
class Renderable():
    def __init__(self, x, y, w, h, color, startIndex, amountOfVertices, angle = 0):
        self.x, self.y = x, y
        self.w, self.h = w, h
        self.angle = angle
        self.color = color
        self.startIndex = startIndex
        self.amountOfVertices = amountOfVertices
Миниатюры
Вложения
Тип файла: zip house-2d-opengl21-qt6-cpp-win10-64bit-exe.zip (7.48 Мб, 2 просмотров)
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
05.02.2023, 16:15
Помогаю со студенческими работами здесь

Нарисовать домик
помогите написать программу домика.. Домик состоит из двух квадратов (дом и окно), прямоугольник (дверь), треугольника (крыша) и ломаной...

Нарисовать домик
надо сделать домик как на рисунке в турбопаскаль.спасибо тому кто поможет

Нарисовать домик
ребят,помогите пожалуйста 1)создать модуль и откомпелировать его. 2)сделать графику(домик обязательно)

Нарисовать домик
Ребят нарисуйте домик простой какойнить

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


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
Thinkpad X220 Tablet — это лучший бюджетный ноутбук для учёбы, точка.
Programma_Boinc 23.12.2025
Рецензия / Мнение/ Перевод Ниже машинный перевод статьи The Thinkpad X220 Tablet is the best budget school laptop period . Thinkpad X220 Tablet — это лучший бюджетный ноутбук для учёбы,. . .
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Как объединить две одинаковые БД Access с разными данными
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru