- ВКонтакте
- РћРТвЂВВВВВВВВнокласснРСвЂВВВВВВВВРєРСвЂВВВВВВВВ
- РњРѕР№ Р В Р’В Р РЋРЎв„ўР В Р’В Р РЋРІР‚ВВВВВВВВРЎР‚
- Viber
- Skype
- Telegram
Предотвращение высчитывания слишком длинных чисел внутри модуля на PyQt5
Я пишу встроенный калькулятор на PyQt5 и хочу предотвратить зависание от больших чисел. Сам калькулятор импортируется как Calculator из файла calculator.py в main.py. Я пробовал следущее в calculator.py:
import multiprocessing as mp
from sympy import sympify
from PyQt5.QtWidgets import QWidget, QApplication, QLabel, QGridLayout, QPushButton
import sys
# ...
def timeout_risk(expr):
return sympify(expr, rational=True)
class Calculator(QWidget):
def __init__(self):
super().__init__()
self.output = QLabel('')
btns = ['2','+', '=']
l = QGridLayout(self)
l.addWidget(self.output, 0, 1)
for i in range(len(btns)):
l.addWidget(QPushButton(btns[i], clicked=self.on_click), 1, i)
def on_click(self):
sender = self.sender()
if sender.text() in ('2','+'):
self.output.setText(self.output.text()+sender.text())
elif sender.text() == '=':
self.calculate(self.output.text())
def calculate(self, txt):
if mp.current_process().name == 'MainProcess':
pool = mp.Pool(1)
async_result = pool.apply_async(timeout_risk, (txt,))
res = async_result.get(timeout=2)
self.output.setText(res)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = Calculator()
win.show()
app.exec_()
Но такое решение замедляет программу если pool не в 'main'. Существуют ли другие доступные способы оптимизировать скорость и решить эту проблему на Windows?
P.S. Обработчики TimeoutError в коде есть
Upd: Добавил демо замедления, при импорте я естественно не могу использовать часть 'main'
Ответы (2 шт):
Я если честно не очень понял суть вопроса, если вам нужно ограничить дланну вывода, то можно просто же полученый результат измерить, точнее получить длинну вычисленной строки и если она больше предположим 10 символов, то вывести оповещение о том что число слишком большое. Если вам нужно ограничить время расчёта, то расчёт можно запустить в потоке и если время работы потока больше чем требуется, принудительно завершить поток и в результат вывести ошибку. Я правильно понял ваш вопрос или нет?
Такая архитектура: отдельный процесс-вычислитель, который выполняет sympify
. Qt UI общается с вычислителем через две очереди: expressions
на вход, results
на выход. В нормальном случае цикл обработки очень короткий: запускать новый процесс на каждый пример не нужно. Но если UI в течении двух секунд не получил ответ, он убивает повисший вычислитель и запускает новый. Тут появляются временные затраты, но они не велики по сравнению с двухсекундным ожиданием.
Ниже крохотное демо, которое читает выражения со стандартного входа и вычисляет его. Если вычисление не уложилось в две секунды, пользователю сообщают об ошибке, убивают старый вычислитель, запускают новый. Всё повторяется незаметно для пользователя:
import multiprocessing
import queue
import sympy
import sys
def computer(expressions, results):
sys.set_int_max_str_digits(0)
while True:
expr = expressions.get()
result = str(sympy.sympify(expr, rational=True))
results.put(result)
if __name__ == '__main__':
while True:
expressions = multiprocessing.Queue()
results = multiprocessing.Queue()
p = multiprocessing.Process(target=computer, args=(expressions, results))
p.start()
while True:
expressions.put(input('> '))
try:
result = results.get(block=True, timeout=2)
except queue.Empty:
p.terminate()
print('ERROR: timeout')
break
print('=', result)
$ python calc.py > 2 + 2 = 4 > 9 ** 9 ** 9 ERROR: timeout > 2 + 2 = 4 >