Как мне поменять выводы print() на вывод в окно приложения PyQt5?

Мы делаем простенькую игру и я не знаю, как сделать вывод в отдельном окне,
т.к. не силен в PyQt5. Помогите, пожалуйста.

    import random
    from time import *
    from random import choices as cho
    from random import randrange as RR
    from Enemies import enemylist

    class Hero:
 
        def __init__(self, name, health, armor, fights=0):
            self.name = name
            self.health = health
            self.fights = fights
            self.armor = armor
            self.dmgup = 0
            self.crtchup = 0
            self.sboy1 = False
            self.sboy2 = False
            self.seek_points = False
            self.weaponlist = [[10, 25, 'булава'], [15, 25, "полуторный меч"], [20, 15, "кувалда"],
                               [30, 10, "тяжёлая секира"], [10, 50, "кинжал"], [15, 30, "арбалет"]]
            self.startweapon = random.choice(self.weaponlist)
            self.weaponlist.pop(self.weaponlist.index(self.startweapon))
            self.crit_chan = self.startweapon[1]
            self.damage = self.startweapon[0]
            self.weapon = self.startweapon[2]
            self.health_default = self.health
            self.armor_default = self.armor
    
        def print_info(self):
            print('Поприветстввуйте героя ->', self.name)
            print('Уровень здоровья:', self.health_default)
            print('Класс брони:', self.armor_default)
            print('Сила удара:', self.damage + self.dmgup)
            print('Оружие:', self.weapon)
            print('Шанс критического удара:', str(self.crit_chan + self.crtchup) + '%')
            print("\n")
            sleep(5)
    
        def strike(self, enemy):
            if self.sboy1 == True:
                self.crit_chan = (self.crit_chan + self.crtchup) // 2
                self.sboy1 = False
            crit_str = False
            crit = cho([1, 2], weights=(self.crit_chan + self.crtchup, 100 - (self.crit_chan + self.crtchup)))[0]
            if crit == 1:
                crit_str = True
            if crit_str == True:
                if self.sboy2 == True:
                    self.damage = (self.damage + self.dmgup) * 4
                else:
                    self.damage = (self.damage + self.dmgup) * 2
                print('-> КРИТИЧЕСКИЙ УДАР! ' + self.name + ' атакует ' + enemy.name + ' с силой ' + str(
                    self.damage) + ', используя ' + self.weapon + '\n')
                if self.seek_points == True:
                    enemy.health -= self.damage + self.dmgup
                else:
                    enemy.armor -= self.damage + self.dmgup
                if self.sboy2 == True:
                    self.damage //= 4
                else:
                    self.damage //= 2
            else:
                print('-> УДАР! ' + self.name + ' атакует ' + enemy.name + ' с силой ' + str(
                    self.damage) + ', используя ' + self.weapon + '\n')
                if self.seek_points == True:
                    enemy.health -= self.damage
                else:
                    enemy.armor -= self.damage
            sleep(2)
            if enemy.armor < 0:
                enemy.health += enemy.armor
                enemy.armor = 0
            print(enemy.name + ' покачнулся(-ась).\nКласс его(её) брони упал до ' + str(
                enemy.armor) + ', а уровень здоровья до ' + str(enemy.health))
    
        def fight(self, enemy):
            while self.health and enemy.health > 0:
                self.strike(enemy)
                if enemy.health <= 0:
                    print(enemy.name, 'пал(а) в этом нелёгком бою!\n')
                    enemy.health = 0
                    sleep(2)
                    if self.health > 0:
                        rul = cho([1, 2, 3, 4], weights=(25, 25, 25, 25))[0]
                        self.health = self.health_default
                        self.armor = self.armor_default
                        if rul == 1:
                            self.health_default += 10
                            print('HP UP')
                        elif rul == 2:
                            self.dmgup += 7
                            print('DMG UP')
                        elif rul == 3:
                            self.armor_default += 10
                            print('DEF UP')
                        elif rul == 4:
                            self.crtchup += 5
                            print('CRIT chance UP')
                        self.fights += 1
                    if self.fights % 4 == 0:
                        bigrul = cho([1, 2], weights=(75, 25))[0]
                        if bigrul == 1 and self.sboy2 == False:
                            print(self.name,
                                  'получил пасивный навык "Сбой": его шанс критического удара уменьшился в 2 раза, но урон от критической атаки вырос в 2 раза')
                            self.sboy1 = True
                            self.sboy2 = True
                        elif bigrul == 2:
                            print(self.name, 'получил пасивный навыу "Взгляд палача": его атки игнорируют защиту врга')
                            self.seek_points = True
                    if self.fights % 3 == 0:
                        dropedweap = random.choice(self.weaponlist)
                        if dropedweap != self.startweapon:
                            print('Какая удача вы обнаружили', dropedweap[2], 'с уроном', dropedweap[0],
                                  'и шансом критического удара', dropedweap[1], ". Подобрать его?")
                            print("ДА или НЕТ")
                            while True:
                                otvet1 = input()
                                if otvet1.upper() == 'ДА':
                                    self.startweapon = dropedweap
                                    self.weaponlist.pop(self.weaponlist.index(dropedweap))
                                    self.crit_chan = self.startweapon[1]
                                    self.damage = self.startweapon[0]
                                    self.weapon = self.startweapon[2]
                                    self.print_info()
                                    break
                                elif otvet1.upper() == 'НЕТ':
                                    print('Вы оставили', dropedweap[2], "валятся на земле")
                                    break
                                else:
                                    print('Да или Нет?')
                    break
                sleep(5)
    
                enemy.strike(self)
                if self.health <= 0:
                    print(self.name, 'пал в этом нелёгком бою!\n')
                    self.health = 0
                sleep(5)
 

class Monster:
    def __init__(self):
        self.names = enemylist
        enemynow = self.names[RR(0, len(self.names))]
        self.name = enemynow[0]
        self.health = enemynow[1]
        self.armor = enemynow[2]
        self.crit_chan = enemynow[3]
        self.damage = enemynow[4]
        self.weapon = enemynow[5]
        if cho([1, 2], weights=(25, 75))[0] == 1:
            self.damage *= 2
            self.armor = round(self.armor * 1.5)
            self.health = round(self.health * 1.5)
            self.crit_chan += 10
            print("Кажется,", self.name, "мутирует: его здоровье теперь", self.health, ', броня', self.armor, ', урон',
                  self.damage, ", а шанс критического удара", self.crit_chan, "\n")
            sleep(3)

    def print_info(self):
        print('На пути появился', str(self.name))
        print('Уровень здоровья:', self.health)
        print('Класс брони:', self.armor)
        print('Сила удара:', self.damage)
        print('Шанс критического удара:', str(self.crit_chan) + '%')
        print('\n')
        sleep(5)

    def strike(self, enemy):
        crit_str = False
        crit = cho([1, 2], weights=(self.crit_chan, 100 - self.crit_chan))[0]
        if crit == 1:
            crit_str = True
        if crit_str == True:
            self.damage *= 2
            print('-> КРИТИЧЕСКИЙ УДАР! ' + self.name + ' атакует ' + enemy.name + ' с силой ' + str(
                self.damage) + ', используя ' + self.weapon + '\n')
            enemy.armor -= self.damage
        else:
            print('-> УДАР! ' + self.name + ' атакует ' + enemy.name + ' с силой ' + str(
                self.damage) + ', используя ' + self.weapon + '\n')
            enemy.armor = self.damage
        sleep(3)
        if enemy.armor < 0:
            enemy.health += enemy.armor
            enemy.armor = 0
        print(enemy.name + ' покачнулся(-ась).\nКласс его(её) брони упал до ' + str(
            enemy.armor) + ', а уровень здоровья до ' + str(enemy.health))

    def fight(self, enemy):
        while self.health and enemy.health > 0:
            self.strike(enemy)
            if enemy.health <= 0:
                print(enemy.name, 'пал(а) в этом нелёгком бою!\n')
                enemy.health = 0
                sleep(3)

                break
            sleep(5)

            enemy.strike(self)
            if self.health <= 0:
                print(self.name, 'пал в этом нелёгком бою!\n')
                self.health = 0
            sleep(5)


class location():

    def __init__(self, hardness):
        self.hardness = hardness
        self.roomcount = (self.hardness * 5) + RR(-2, 2)

    def generation(self):
        campfiersgen = self.roomcount // 1 + (self.hardness)


print("Добро пожаловать в Hero's quest".title())
print("Введите свое имя")
n = input()
knight = Hero(n, 25, 10)
knight.print_info()
print(' ')
print("Введите колличество раундов (1-5)")
co = int(input())
print("Начнется же приключение" + '\n')
for i in range(1, co + 1):
    rascal = Monster()
    sleep(3)
    rascal.print_info()
    sleep(3)
    knight.print_info()
    sleep(1)
    knight.fight(rascal)
    sleep(2)

P.S. Модуль Enemies.py содержит список и только список.


Ответы (1 шт):

Автор решения: S. Nick
  1. Всегда приводите минимально-воспроизводимый пример, который демонстрирует проблему.
    Чтобы запустить и как-то протестировать ваше приложение, я заменил модуль Enemies.py на выдуманный enemylist = [...]

  2. Не используйте timr.sleep() в основном потоке GUI - это ничего кроме заморозки интерфейса не делает.

  3. Будьте осторожны с использованием while True: в основном цикле,
    это также может заморозить ваше приложение.
    Помните, что длительные вычисления должны выполняться в дополнительном потоке.
    QApplication.processEvents() - обрабатывает некоторые ожидающие события для вызывающего потока.
    Вы можете вызывать эту функцию время от времени, когда ваша программа занята выполнением длительной операции. Обычно он используется в качестве запасного wheel при использовании длинных циклов блокировки в коде, выполняющемся в потоке графического интерфейса, чего следует избегать.

  4. В общем случае, ответ на ваш вопрос заключается в замене строки:

    print(f'Какая-то строка для печати')
    

    на

    _print = f'Какая-то строка для печати'
    self.textBrowser.append(_print)
    

    где self.textBrowser это экземпляр класса QTextBrowser, который предоставляет браузер форматированного текста с гипертекстовой навигацией.

  5. input() - не используется в GUI.
    Вместо него вам надо вызвать какое-то модальное окно и запросить в нем какие-то данные.


Я не проверял вашу логику программы, но внес некоторые изменения, которые необходимы для работы приложения.
Попробуйте, если что-то конкретное будет не понятно - спросите.

import sys
#import random
#from time import *
from random import choices as cho, choice 
from random import randrange as RR
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.Qt import *

#?from Enemies import enemylist                      #?#?#?#?#?#?#?
enemylist = [
    ['Name_1', 2, 3, 4, 5, 'weapon6'],
    ['Name_2', 22, 23, 24, 25, 'weapon26'],
    ['Name_3', 3332, 3333, 3334, 3335, 'weapon336'],
]


class Hero(QObject):
    def __init__(self, name, health, armor, fights, parent=None):
        super().__init__(parent)  
        self.parent = parent
        
        self.name = name
        self.health = health
        self.fights = fights
        self.armor = armor
        self.dmgup = 0
        self.crtchup = 0
        self.sboy1 = False
        self.sboy2 = False
        self.seek_points = False
        self.weaponlist = [
            [10, 25, 'булава'], 
            [15, 25, "полуторный меч"], 
            [20, 15, "кувалда"],
            [30, 10, "тяжёлая секира"], 
            [10, 50, "кинжал"], 
            [15, 30, "арбалет"]
        ]
        self.startweapon = choice(self.weaponlist)
        self.weaponlist.pop(self.weaponlist.index(self.startweapon))
        self.crit_chan = self.startweapon[1]
        self.damage = self.startweapon[0]
        self.weapon = self.startweapon[2]
        self.health_default = self.health
        self.armor_default = self.armor

    def print_info(self):
        self.parent.textBrowser.append(f"Поприветстввуйте героя: {self.name}")
        self.parent.textBrowser.append(f"Уровень здоровья:         {self.health_default}")
        self.parent.textBrowser.append(f"Класс брони:                {self.armor_default}")
        self.parent.textBrowser.append(f"Сила удара:                 {self.damage + self.dmgup}")
        self.parent.textBrowser.append(f"Оружие:                       {self.weapon}")
        self.parent.textBrowser.append(f"Шанс критического удара:{self.crit_chan + self.crtchup}%")

    def strike(self, enemy):
        if self.sboy1 == True:
            self.crit_chan = (self.crit_chan + self.crtchup) // 2
            self.sboy1 = False
        crit_str = False
        crit = cho(
            [1, 2], 
            weights=(self.crit_chan + self.crtchup, 
                     100 - (self.crit_chan + self.crtchup))
        )[0]
        if crit == 1:
            crit_str = True
        if crit_str == True:
            if self.sboy2 == True:
                self.damage = (self.damage + self.dmgup) * 4
            else:
                self.damage = (self.damage + self.dmgup) * 2
            _print = f'-> КРИТИЧЕСКИЙ УДАР! {self.name} атакует '\
                f'{enemy.name} с силой {self.damage} '\
                f'используя {self.weapon}\n'
            self.parent.textBrowser.append(_print)
            
            if self.seek_points == True:
                enemy.health -= self.damage + self.dmgup
            else:
                enemy.armor -= self.damage + self.dmgup
            if self.sboy2 == True:
                self.damage //= 4
            else:
                self.damage //= 2
        else:
            _print = f'-> УДАР! {self.name}, атакует {enemy.name}' \
                f' с силой {self.damage}, используя {self.weapon}\n'
            self.parent.textBrowser.append(_print)

            if self.seek_points == True:
                enemy.health -= self.damage
            else:
                enemy.armor -= self.damage
#        sleep(2)
        if enemy.armor < 0:
            enemy.health += enemy.armor
            enemy.armor = 0
            
        _print = f'{enemy.name} покачнулся(-ась).\n' \
            f'Класс его(её) брони упал до {enemy.armor}' \
            f', а уровень здоровья до {enemy.health}'
        self.parent.textBrowser.append(_print)

    def fight(self, enemy):
        while self.health and enemy.health > 0:
            self.strike(enemy)
            if enemy.health <= 0:
                self.parent.textBrowser.append(
                    f'{enemy.name}, пал(а) в этом нелёгком бою!\n')
                enemy.health = 0
                if self.health > 0:
                    rul = cho([1, 2, 3, 4], weights=(25, 25, 25, 25))[0]
                    self.health = self.health_default
                    self.armor = self.armor_default
                    if rul == 1:
                        self.health_default += 10
#                        print('HP UP')
                        self.parent.textBrowser.append('HP UP')
                    elif rul == 2:
                        self.dmgup += 7
                        self.parent.textBrowser.append('DMG UP')
                    elif rul == 3:
                        self.armor_default += 10
                        self.parent.textBrowser.append('DEF UP')
                    elif rul == 4:
                        self.crtchup += 5
                        self.parent.textBrowser.append('CRIT chance UP')
                    self.fights += 1
     
                if self.fights % 4 == 0:
                    bigrul = cho([1, 2], weights=(75, 25))[0]
                    if bigrul == 1 and self.sboy2 == False:
                        _print = f'{self.name} получил пасивный навык ' \
                            f'"Сбой": его шанс критического удара ' \
                            f'уменьшился в 2 раза, но урон от ' \
                            f'критической атаки вырос в 2 раза'
                        self.parent.textBrowser.append(_print)

                        self.sboy1 = True
                        self.sboy2 = True
                    elif bigrul == 2:
                        _print = f'{self.name} получил пасивный навыу '\
                            f'"Взгляд палача": его атки игнорируют '\
                            f'защиту врга'
                        self.parent.textBrowser.append(_print)
                        self.seek_points = True
                
                if self.fights % 3 == 0:
                    dropedweap = choice(self.weaponlist)
                    if dropedweap != self.startweapon:
                        self.parent.textBrowser.append(f'Какая удача вы обнаружили {dropedweap[2]}, с уроном, {dropedweap[0]}, '
                              f'и шансом критического удара, {dropedweap[1]}. Подобрать его?')
                        self.parent.textBrowser.append("ДА или НЕТ")
                        
                        while True:
# ---                       otvet1 = input()
                            otvet1 = self.getTextInputDialog(dropedweap)
                            
                            if otvet1.upper() == 'ДА':
                                self.startweapon = dropedweap
                                self.weaponlist.pop(self.weaponlist.index(dropedweap))
                                self.crit_chan = self.startweapon[1]
                                self.damage = self.startweapon[0]
                                self.weapon = self.startweapon[2]
# ???                           self.print_info()
                                break
                            elif otvet1.upper() == 'НЕТ':
                                _print = f'Вы оставили {dropedweap[2]} валятся на земле'
                                self.parent.textBrowser.append(_print)
                                break
#                            else:
#                                print('Да или Нет?')
#?              break
#            sleep(5)

            enemy.strike(self)                          # ?

            if self.health <= 0:
                _print = f'{self.name} пал в этом нелёгком бою!\n'
                self.parent.textBrowser.append(_print)
                self.health = 0
#            sleep(5)
            QApplication.processEvents()

# +++
    def getTextInputDialog(self, dropedweap):
        text, okPressed = QtWidgets.QInputDialog.getText(
            None, 
            "Критический удар",
         f"""Какая удача вы обнаружили `{dropedweap[2]}`, с уроном `{dropedweap[0]}`, 
       и шансом критического удара, {dropedweap[1]}. Подобрать его?'
                                   ДА или НЕТ?""", 
            QtWidgets.QLineEdit.Normal, 
            "Да")

        if not okPressed:
            text = 'НЕТ'
        return text.upper()
                                                  

class Monster(QObject):
    def __init__(self, parent=None):
        super(Monster, self).__init__(parent)  
        self.parent = parent
        
        self.names = enemylist
        enemynow = self.names[RR(0, len(self.names))]
        self.name = enemynow[0]
        self.health = enemynow[1]
        self.armor = enemynow[2]
        self.crit_chan = enemynow[3]
        self.damage = enemynow[4]
        self.weapon = enemynow[5]
        if cho([1, 2], weights=(25, 75))[0] == 1:
            self.damage *= 2
            self.armor = round(self.armor * 1.5)
            self.health = round(self.health * 1.5)
            self.crit_chan += 10
            _print = f'\nКажется, {self.name} мутирует: его здоровье '\
                f'теперь {self.health}, броня {self.armor} , урон ' \
                f'{self.damage}, а шанс критического удара {self.crit_chan}\n'
            self.parent.textBrowser.append(_print)

    def print_info(self):
        self.parent.textBrowser.append(f"""
        На пути появился -> {self.name}
        Уровень здоровья:    {self.health}
        Класс брони:           {self.armor}
        Сила удара:             {self.damage}
        Шанс критического удара: {self.crit_chan}%
        """)

    def strike(self, enemy):
        crit_str = False
        crit = cho([1, 2], weights=(self.crit_chan, 100 - self.crit_chan))[0]
        if crit == 1:
            crit_str = True
        if crit_str == True:
            self.damage *= 2
            _print = f'-> КРИТИЧЕСКИЙ УДАР! {self.name}, атакует ' \
                f'{enemy.name} с силой {self.damage}' \
                f', используя {self.weapon}\n'
            self.parent.textBrowser.append(_print)
            enemy.armor -= self.damage
        else:
            _print = f'-> УДАР! {self.name}, атакует {enemy.name}' \
                f' с силой {self.damage}, используя {self.weapon}\n'
            self.parent.textBrowser.append(_print)
            enemy.armor = self.damage
        if enemy.armor < 0:
            enemy.health += enemy.armor
            enemy.armor = 0
        _print = f'{enemy.name} покачнулся(-ась).\n'\
            f'Класс его(её) брони упал до {enemy.armor}' \
            f', а уровень здоровья до {enemy.health}'
        self.parent.textBrowser.append(_print)

    def fight(self, enemy):
        while self.health and enemy.health > 0:
            self.strike(enemy)
            if enemy.health <= 0:
                _print = f'{enemy.name} пал(а) в этом нелёгком бою!\n'
                self.parent.textBrowser.append(_print)
                enemy.health = 0
#                break
#            sleep(5)

            enemy.strike(self)
            if self.health <= 0:
                _print = f'{self.name} пал в этом нелёгком бою!\n'
                self.parent.textBrowser.append(_print)
                self.health = 0
            QApplication.processEvents()
            
       
''' ???????????????
class location():

    def __init__(self, hardness):
        self.hardness = hardness
        self.roomcount = (self.hardness * 5) + RR(-2, 2)

    def generation(self):
        campfiersgen = self.roomcount // 1 + (self.hardness)
'''

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.central_widget = QWidget(self)
        self.setCentralWidget(self.central_widget)
        
        self.label = QLabel("Добро пожаловать в Hero's quest")
        self.label.setAlignment(Qt.AlignHCenter|Qt.AlignTop)
        self.label_2 = QLabel("Введите свое имя")
        self.label_2.setAlignment(Qt.AlignRight|Qt.AlignTop)
        
        self.health = QSpinBox()
        self.health.setRange(0, 100)
        self.health.setSingleStep(5)
        self.health.setValue(25) 
        
        self.armor = QSpinBox()
        self.armor.setRange(0, 100)
        self.armor.setSingleStep(1)
        self.armor.setValue(10) 
        
        self.fights = QSpinBox(minimum=0, maximum=4)
        self.co = QSpinBox(minimum=1, maximum=5)
        
        self.button = QPushButton('Start')
        self.button.clicked.connect(self.on_clicked)
        
        self.groupBox = QGroupBox("Настройте параметры и нажмите Start") 
        
        self.gb_layout = QFormLayout(self.groupBox)
        self.gb_layout.addRow("Уровень здоровья:", self.health)
        self.gb_layout.addRow("Класс брони:", self.armor)
        self.gb_layout.addRow("Fights:", self.fights)
        self.gb_layout.addRow("Введите колличество раундов:", self.co)
        self.gb_layout.addRow("", self.button)

        self.name_lineEdit = QLineEdit(
            placeholderText='Введите ваше имя и нажмите Enter')
        self.name_lineEdit.returnPressed.connect(self._returnPressed)

        self.textBrowser = QTextBrowser()
        
        self.layout = QGridLayout(self.central_widget)
        self.layout.addWidget(self.label, 0, 0, 1, 2)
        self.layout.addWidget(self.label_2, 1, 0) 
        self.layout.addWidget(self.name_lineEdit, 1, 1) 
        
        self.layout.addWidget(self.groupBox, 3, 0, 1, 1)
        self.layout.addWidget(self.textBrowser, 2, 1, 3, 1)

        self.layout.setRowStretch(2, 1)
        self.layout.setRowStretch(4, 1)
        self.layout.setColumnStretch(1, 1)
        
        self.groupBox.setEnabled(False)
        
    def _returnPressed(self):
        if not self.name_lineEdit.text():
            return        
        self.groupBox.setEnabled(True)
        self.co.setFocus()

    def _rascal(self):
        for i in range(self.co.value()):               #(1, co + 1):
            self.textBrowser.append(f"<h3>-=- {i+1} раунд. -=-</h3>")       
        
            self.rascal = Monster(self)
            self.rascal.print_info()
# ???        self.knight.print_info()                     # ???
            self.knight.fight(self.rascal)
            
    def on_clicked(self):
        self.textBrowser.append("<h2>Начинается приключения.</h2>")
        self.groupBox.setEnabled(False)
        QApplication.processEvents()
        self.knight = Hero(
            self.name_lineEdit.text(), 
            self.health.value(), 
            self.armor.value(), 
            self.fights.value(), 
            self)
        self.knight.print_info()
        
        self._rascal()
        
        self.textBrowser.append(
            f"<b style='color: #C81912;'> {'-'*77}</b><br>")
        self.groupBox.setEnabled(True)


Stylesheet = '''
/* тут вы можете установить свои стили для виджетов */
'''


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    app.setStyleSheet(Stylesheet)
    app.setFont(QtGui.QFont("Times", 11, QtGui.QFont.Bold))
    w = MainWindow()                     
    w.setWindowTitle("Простенькая игра")
    w.resize(1200, 600)
    w.show()
    sys.exit(app.exec())

введите сюда описание изображения

введите сюда описание изображения

→ Ссылка