rvadmin
Май 9, 2008 11:32:28
Привет всем! Необходимо создать функцию, которая перед SQL запросом вызывает QDialog c Busy QProgressBar, а после скрывает его.
Это необходимо, потому что SQL сервер удаленный и может долго процессить запросы
Все что у меня получилось, это показывать диалог, скрывать его, но! Во время выполнения SQL запроса progressbar не анимируется вообще, а только после sql запроса (если не скрывать форму).
Вот то, что получилось:
class sql:
# skipped #
def executeX(self,q):
self.app.myDialog.show()
self.app.myProgress.setValue(random.randint(1,100))
QtGui.QApplication.processEvents(QtCore.QEventLoop.AllEvents)
self.dbc.execute(q)
self.app.myDialog.hide()
Как заставить progressBar анимироваться? Или может в отдельном потоке приплюсовывать значения? (я пробовал, но не получается обращаться с одного потока к объектам другого потока)
Заранее всем спасибо
P.S. Всех с Великим праздником!
rvadmin
Или может в отдельном потоке приплюсовывать значения?
Я у себя реализовывал это именно так. Т.е. создавал диалог и новой нитью говорил ему “exec_()”, внутри которого крутился “processEvents”. Всё работало. У тебя, ИМХО, проблема а том, что “processEvents” вызывается только при изменении ПрогрессБара. Попробуй воткнуть его в бесконечный цикл со “sleep(0.13)”, а изменять значения ПрогрессБара внешним сигналом (SIGNAL).
rvadmin
Май 10, 2008 00:33:43
ZZZ
rvadmin
Или может в отдельном потоке приплюсовывать значения?
Я у себя реализовывал это именно так. Т.е. создавал диалог и новой нитью говорил ему “exec_()”, внутри которого крутился “processEvents”. Всё работало. У тебя, ИМХО, проблема а том, что “processEvents” вызывается только при изменении ПрогрессБара. Попробуй воткнуть его в бесконечный цикл со “sleep(0.13)”, а изменять значения ПрогрессБара внешним сигналом (SIGNAL).
Дело все в том, что нужно изменять значения ПрогрессБара в отдельном потоке, и значения на самом деле никак не будут связаны с прогрессом, потому что мы используем mysqldb execute.
Я пробовал создавать новый поток, и менять значения прогрессбара, но python ругался, что второй поток не может обращаться к объектам первого потока.
Также пробовал засунуть все диалоговое окно во второй поток - другая ошибка - виджеты можно создавать только в GUI потоке.
P.S. во время выполнения mysqldb execute - даже QT-шная анимация прогрессбара останавливается. Из-за этого и не работает Busy ProgressBar
ZZZ
Май 10, 2008 03:11:05
rvadmin
Также пробовал засунуть все диалоговое окно во второй поток - другая ошибка - виджеты можно создавать только в GUI потоке.
Хм… Никогда не напарывался. Странно… У меня всегда очень многопоточные программы. Блин, не уверен, создавал ли я когда-нить виджет не в основном потоке… Интересно… Надо попробовать.
Но в твоей задаче это не нужно.
rvadmin
P.S. во время выполнения mysqldb execute - даже QT-шная анимация прогрессбара останавливается. Из-за этого и не работает Busy ProgressBar
Это нормально. Попробуй мой способ, а для коммутации – "
emit". У меня это стопроцентно работало. Сейчас у меня эта часть кода на рефакторинге. Как закончу – выложу. Если интересно, конечно…
rvadmin
Май 10, 2008 08:10:01
ZZZ
rvadmin
Также пробовал засунуть все диалоговое окно во второй поток - другая ошибка - виджеты можно создавать только в GUI потоке.
Хм… Никогда не напарывался. Странно… У меня всегда очень многопоточные программы. Блин, не уверен, создавал ли я когда-нить виджет не в основном потоке… Интересно… Надо попробовать.
Но в твоей задаче это не нужно.
rvadmin
P.S. во время выполнения mysqldb execute - даже QT-шная анимация прогрессбара останавливается. Из-за этого и не работает Busy ProgressBar
Это нормально. Попробуй мой способ, а для коммутации – "emit". У меня это стопроцентно работало. Сейчас у меня эта часть кода на рефакторинге. Как закончу – выложу. Если интересно, конечно…
Да, интересно! Выложи, пожалуйста!
ZZZ
Май 11, 2008 01:30:41
rvadmin
Да, интересно! Выложи, пожалуйста!
Сегодня весь день проиграли в Heroes III с отчемом и братцем… Двенадцать часов!!! И всё-таки нет ничего лучше старого добного Хереса и никакие новомодные “пятёрочки” даже рядом не валялись!
Если завтра это не повторится, то сделаю…
ZZZ
Май 11, 2008 05:25:39
Эх и не спится же мне! Полцарства за глубокий сон! Только нету у меня полцарства…
Решил для себя систематезировать эту тему, прежде чем продолжать рефакторинг.
Буду рад здравой критике.
# -*- coding: utf-8 -*-
import sys
import time
from PyQt4.Qt import *
def func_1(wait):
‘'’Стандартная функция, которая ничего не знает о выполняемой программе\n.'''\
‘'’Не имеет никакой возможности доложить о прогрессе выполнения.'''
time.sleep(wait)
return True
def func_2(wait):
‘'’А вот теперь, собственно вывод.\n'''\
‘'’Эту проблему можно решить многими способами.\n'''\
‘'’Здесь функция возвращает итератор прогрессa в процентах.\n'''\
‘'’Последнее значение есть возврат собственно вызываемой функции.\n'''\
‘'’Само собой все функции, использующие этод метод, должны подчинятся этому api.\n'''\
‘'’Удобно использовать при чётком разбиении частей выполнения.'''
for p in xrange(0, 100, 100/int(wait/0.2)):
time.sleep(0.2)
yield p
yield True
class CallObject_3(QObject):
‘'’Серьёзный вариант.\n'''\
‘'’Для каждого вызова создаётся объект, обеспецивающий определённый api.\n'''\
‘'’Может как сам отрисовывать что-нить, так и выдавать сигналы объекту, занимающимся этим.\n'''\
‘'’Мне оказалось удобно создать глобальный объект (Progress), который передаётся каждому объекту-обработчику выполнения (CallObject), ‘'’\
‘'’который регистрируется регистрируется в Progress и говорит ему о своём выполнении/зевершении. ‘'’\
‘'’Progress сам решает что, куда и когда выводить, на основе способа регистрации CallObject и пришедшей информации.\n'''
‘'’Например некоторые вещи выводятся модальным QDialog, а некоторые в фоне на статусБар.\n'''\
‘'’Вариант удобен при большом количестве разнородных “ожиданий”. Довольно быстро реализуется определённый набор этих классов.'''
def __init__(self, Progress):
QObject.__init__(self)
self.Progress = Progress
def start(self, *Args, **Kw):
class T(QThread):
def run(self):
‘'’Собственно тело функции.'''
for p in xrange(0, 100, 100/int(self.Wait/0.2)):
time.sleep(0.2)
self.emit(SIGNAL(“progress(int)”), p)
self.Result = True
def start(self, Wait):
self.Wait = Wait
QThread.start(self)
self.Progress.ProgressBar.setMaximum(100)
self.Progress.show()
t = T()
self.connect(t, SIGNAL(“progress(int)”), self.Progress.ProgressBar.setValue)
t.start(*Args, **Kw)
while not t.isFinished():
time.sleep(0.013) ## Чтобы не сильно нагружать просессор.
qApp.processEvents()
self.Result = t.Result
self.Progress.hide()
self.Progress.ProgressBar.setMaximum(0)
return self.Result
'''
Возможен ещё один вариант, похожий на CallObject_3.
Объекту, занимающемуся отрисовкой, передаётся _класс_ CallObject, реализующий строго определённый api
и параметры. Progress, используя тот самый api создаёт из этого класса объект, передаёт параметры,
коннектится к определённым сигналам… работает, короче…
Тоже иногда может оказаться довольно удобным.
Уже нет сил для примера. Думаю, что всё ясно.
'''
class ProgressDialog(QDialog):
def __init__(self, Parent):
QDialog.__init__(self, Parent)
self.Layout = QVBoxLayout(self)
self.ProgressBar = QProgressBar(self)
self.Layout.addWidget(self.ProgressBar)
self.ProgressBar.setRange(0, 0)
def exec_1(self, Func, *Args, **Kw):
class T(QThread):
def run(self):
self.Result = Func(*self.Args, **self.Kw)
def start(self, Func, *Args, **Kw):
self.Func = Func
self.Args = Args
self.Kw = Kw
QThread.start(self)
self.show()
t = T()
t.start(Func, *Args, **Kw)
while not t.isFinished():
time.sleep(0.013) ## Чтобы не сильно нагружать просессор.
qApp.processEvents()
self.hide()
return t.Result
def exec_2(self, Func, *Args, **Kw):
class T(QThread):
def run(self):
last = 0
for p in Func(*self.Args, **self.Kw):
if type(p) is int and last < p <= 100:
self.emit(SIGNAL(“progress(int)”), p)
last = p
else:
last = p
self.Result = last
def start(self, Func, *Args, **Kw):
self.Func = Func
self.Args = Args
self.Kw = Kw
QThread.start(self)
self.ProgressBar.setMaximum(100)
self.show()
t = T()
self.connect(t, SIGNAL(“progress(int)”), self.ProgressBar.setValue)
t.start(Func, *Args, **Kw)
while not t.isFinished():
time.sleep(0.013) ## Чтобы не сильно нагружать просессор.
qApp.processEvents()
self.hide()
self.ProgressBar.setMaximum(0)
return t.Result
class MainWindow(QDialog):
def __init__(self, Parent = None):
QDialog.__init__(self, Parent)
self.Layout = QVBoxLayout(self)
self.Button_1 = QPushButton(“func_1”, self)
self.Layout.addWidget(self.Button_1)
self.connect(self.Button_1, SIGNAL(“clicked()”), self.start_1)
self.Button_2 = QPushButton(“func_2”, self)
self.Layout.addWidget(self.Button_2)
self.connect(self.Button_2, SIGNAL(“clicked()”), self.start_2)
self.Button_3 = QPushButton(“CallObject_3”, self)
self.Layout.addWidget(self.Button_3)
self.connect(self.Button_3, SIGNAL(“clicked()”), self.start_3)
def start_1(self):
‘'’Без прогресса, но без тормоза GUI.'''\
‘'’Думаю, что тут всё ясно и без лишних комментариев.'''
print ‘Start.’
Progress = ProgressDialog(self)
print Progress.exec_1(func_1, 5.0)
print ‘Stop.’
def start_2(self):
‘'’См. func_2.'''
print ‘Start.’
Progress = ProgressDialog(self)
print Progress.exec_2(func_2, 5.0)
print ‘Stop.’
def start_3(self):
print ‘Start.’
Progress = ProgressDialog(self)
O = CallObject_3(Progress)
print O.start(5.0)
print ‘Stop.’
App = QApplication(sys.argv)
MW = MainWindow()
MW.show()
App.setQuitOnLastWindowClosed(True)
sys.exit(App.exec_())
Будут вопросы – спрашивай.
rvadmin
Май 11, 2008 16:00:12
ZZZ, Огромное спасибо! У меня получилось! Все работает так, как я хотел!!! :)
P.S.
'''Серьёзный вариант.\n'''\
‘'’Для каждого вызова создаётся объект, обеспецивающий определённый api.\n'''\
Зачем использовать комментарии так, если ‘'’ ‘'’ - мультистрочный вариант? :)
Можно просто
'''Серьёзный вариант.
Для каждого вызова создаётся объект, обеспецивающий определённый api.'''
Еще раз спасибо за помощь!
ZZZ
Май 12, 2008 02:56:04
rvadmin
Зачем использовать комментарии так, если ‘'’ ‘'’ - мультистрочный вариант?
Можно просто
'''Серьёзный вариант.
Для каждого вызова создаётся объект, обеспецивающий определённый api.''!'
Смотри:
def func_2(wait):
‘'’А вот теперь, собственно вывод.
Эту проблему можно решить многими способами.
Здесь функция возвращает итератор прогрессa в процентах.
Последнее значение есть возврат собственно вызываемой функции.
Само собой все функции, использующие этод метод, должны подчинятся этому api.
Удобно использовать при чётком разбиении частей выполнения.'''
for p in xrange(0, 100, 100/int(wait/0.2)):
time.sleep(0.2)
yield p
yield True
Разве так лучше? Мне не нравится сдвиг к началу строки.
Да и довольно быстро научился не замечать того, как я форматирую докстрины.
poltergeist
Май 12, 2008 08:08:55
def func_2(wait):
‘'’А вот теперь, собственно вывод.\n'''\
‘'’Эту проблему можно решить многими способами.\n'''\
‘'’Здесь функция возвращает итератор прогрессa в процентах.\n'''\
‘'’Последнее значение есть возврат собственно вызываемой функции.\n'''\
‘'’Само собой все функции, использующие этод метод, должны подчинятся этому api.\n'''\
‘'’Удобно использовать при чётком разбиении частей выполнения.'''
for p in xrange(0, 100, 100/int(wait/0.2)):
time.sleep(0.2)
yield p
yield True
def _func_2(wait):
‘'’А вот теперь, собственно вывод.
Эту проблему можно решить многими способами.
Здесь функция возвращает итератор прогрессa в процентах.
Последнее значение есть возврат собственно вызываемой функции.
Само собой все функции, использующие этод метод, должны подчинятся этому api.
Удобно использовать при чётком разбиении частей выполнения.
‘'’
for p in xrange(0, 100, 100/int(wait/0.2)):
time.sleep(0.2)
yield p
yield True
print help(func_2)
print help(_func_2)
Вывод:
Help on function func_2 in module __main__:
func_2(wait)
А вот теперь, собственно вывод.
Эту проблему можно решить многими способами.
Здесь функция возвращает итератор прогрессa в процентах.
Последнее значение есть возврат собственно вызываемой функции.
Само собой все функции, использующие этод метод, должны подчинятся этому api.
Удобно использовать при чётком разбиении частей выполнения.
None
Help on function _func_2 in module __main__:
_func_2(wait)
А вот теперь, собственно вывод.
Эту проблему можно решить многими способами.
Здесь функция возвращает итератор прогрессa в процентах.
Последнее значение есть возврат собственно вызываемой функции.
Само собой все функции, использующие этод метод, должны подчинятся этому api.
Удобно использовать при чётком разбиении частей выполнения.
None
А разница ведь есть:) Обязательно к прочтению:
http://commandline.org.uk/python/twelve-commandments-of-python-style-2008-04-25-19-00.html