Найти - Пользователи
Полная версия: Насколько правильно
Начало » GUI » Насколько правильно
1 2
mironich
Незнал как тему назвать, получаеться есть приложение, использующее PyQt, и в нем много QLabel-ов, в которые выводится кол-во обработанных данных, для кадой заводиться счетчик , увеличиваеться, и устанавливаеться значение лэйблу.
Насколько правильно будет, опрделить наследника у QLabel добавить int аттрибут в котором будет храниться кол-во обработанных данных, и метод который будет увеличивать аттрибут с кол-вом даных и устанавливать значение в Label.
С точки зрения правильности кода насколько это правильно?
Выглядеть будет это так:
class NumLabel(QLabel)
	def __init__(self, parent = None):
		super(QLabel, self).__init__(parent)
		self.current_value = 0
	def inc_value(self, count = 1):
		self.current_value += count
		self.setText(str(self.current_value))
reclosedev
Не зная что это за счетчики и QLabel (и почему их много), без общей картины тут не ответишь. Возможно в конкретном приложении это нормально.

А на первый взгляд получается, что объединяется логика и представление.
mironich
reclosedev, есть приложение для взаимодействия VK API, есть там класс
Main
Наследованный от QApplication, и там куча таких строк .
	def inc_upload_count(self, count):
		self.upload_count += count
		self.main_form.upload_page.load_count.setText(str(self.upload_count))
	def inc_upload_errors_count(self):
		self.upload_error_count += 1 #Счетчик ошибок загрузки 
		self.main_form.upload_page.errors_ct_label.setText(str(self.upload_error_count)) #Ставим лэйблу кол-во ошибок загрузки
Соответсвенно сигналы от обьектов которые грузят фото подкл. к этим слотам, в общем в приложении во многих местах надо выводить кол-во произведенных действий\ошибок..
Вот я и подумал что надобы код причесать когда приложение чуть-чуть разрослось.
Была мысль слелать что-то вроде:

def inc_and_set(self, counter, label):
	counter += 1
	label.setText(counter)
И вызывать так.
signal.connect(lambda: inc_and_set(self.err_count, self.mainform.errLabel))
Но.. Т.к int Immutable не прокатит, да ине красиво..
reclosedev
В данном случае код из первого поста может и подойдет.

Проблема в том, что сразу идет привязка к лэйблам. Код, который ничего о них не знает, и возможно работает в другом потоке будет вызывать методы которые меняют GUI.

Можно рассмотреть такое решение:
# -*- coding: utf-8 -*-
import functools
 
from PyQt4 import QtGui, QtCore
 
 
class ObservableVariable(QtCore.QObject):
 
    changed = QtCore.pyqtSignal(object)
 
    def __init__(self, initial_value=0):
        super(ObservableVariable, self).__init__()
        self._value = initial_value
         
    def get_value(self):
        return self._value
     
    def set_value(self, new_val):
        self._value = new_val
        self.changed.emit(new_val)
  
    value = property(get_value, set_value)
  
    def __iadd__(self, other):
        self.value += other
        return self
  
    def __isub__(self, other):
        self.value -= other
        return self
  
    def __str__(self):
        return str(self.value)
 
  
class GuiExample(QtGui.QWidget):
    def __init__(self):
        super(GuiExample, self).__init__()
        self.counter1 = ObservableVariable(10)
        self.counter2 = ObservableVariable()
        self.counter3 = ObservableVariable()
        self.create_gui_associated_with_counters()
 
    def create_gui_associated_with_counters(self):
        layout = QtGui.QVBoxLayout(self)
        # связать можно с Label
        self.label1 = QtGui.QLabel(str(self.counter1))
        layout.addWidget(self.label1)
        self.label2 = QtGui.QLabel(str(self.counter2))
        layout.addWidget(self.label2)
         
        def on_value_changed(label, value):
            label.setText(str(value))
        self.counter1.changed.connect(functools.partial(on_value_changed, 
                                                        self.label1))
        self.counter2.changed.connect(functools.partial(on_value_changed, 
                                                        self.label2))
 
        # Или с другими виджетами
        spinbox = QtGui.QSpinBox()
        layout.addWidget(spinbox)
        self.counter3.changed.connect(spinbox.setValue)
        # изменение значения пользователем
        spinbox.valueChanged.connect(self.counter3.set_value)
        
        button = QtGui.QPushButton('test', clicked=self.some_operation)
        layout.addWidget(button)
 
    def some_operation(self):
        self.counter1 -= 1
        self.counter2 += 2
        self.counter3 += 1
        if self.counter1.value == 0:
            self.counter1.value = 10
 
 
app = QtGui.QApplication([])
gui = GuiExample()
gui.show()
 
app.exec_()
Оно может и выглядит немного “оверкильным”, но позволяет обращаться со счетчиками (да и с переменными) в коде почти как с обычными значениями.
- Нет привязки к конкретному гуи элементу (лэйбл).
- На одну ObservableVariable() можно повесить много наблюдателей (лэйбл, столбец диаграммы, спинбокс, прогресс бар и т.п.).
- Можно добавить возможность изменения значения пользователем (см. пример со спинбоксом).
- Изменять значение можно в негуи потоке, т.к. обновление гуи произойдет по сигналу.
mironich
Я вот еще подумал, м.б Event использовать, слать сообщения обьекту который наследует QApplication…
Кода поубавиться, но насколько это правильно?
reclosedev, изучаю ваше решение, выглядит хорошо.
reclosedev, ваше решение прекрасно!
Жаль сам к нему не дошел…:( Подобное вообще в голову не приходило…
Спасибо!

mironich
А если засунуть счетчик ошибок обычное int поле в базовый класс форм для приложения, в обработчике события его увеличивать и устанавливать в label, не будет ли это “бъединяется логика и представление.”, это просто уменьшит кол-во кода существенно.
Выглядеть примерно так будет:
class BasePage(QWidget):
        QWidget.__init__(self, parent):
             self.error_counter = 0
             self.errors_counter_label = BadLabel('0')
   	def customEvent(self, event): //Обрабатываем свои события
		if event.type() == events.ErrorEvent():
			self.error_counter += 1
                        self.errors_counter_label.setText(self.error_counter )
mironich
А если засунуть счетчик ошибок обычное int поле в базовый класс форм для приложения, в обработчике события его увеличивать и устанавливать в label, не будет ли это “бъединяется логика и представление.”, это просто уменьшит кол-во кода существенно.
Выглядеть примерно так будет:
class BasePage(QWidget):
        QWidget.__init__(self, parent):
             self.error_counter = 0
             self.errors_counter_label = BadLabel('0')
   	def customEvent(self, event): //Обрабатываем свои события
		if event.type() == events.ErrorEvent():
			self.error_counter += 1
                        self.errors_counter_label.setText(self.error_counter )
И где должна производиться проверка пользовательских данных?
В классе формы, или приложения?
reclosedev
Сильно задумываться по этому поводу, наверное, не стоит. Не фреймворк же пишите.

mironich
Выглядеть примерно так будет:
На первый взгляд, получается много типов event'ов, if'ов, и опять жестко на каждый if привязан лэйбл. А если “и в нем много QLabel” может потом это станет таблицей, а может и не изменится.

mironich
И где должна производиться проверка пользовательских данных?
В классе формы, или приложения?
Смотря какая форма, какие данные. По мне, тут нет однозначного ответа. Зачастую, достаточно QValidator'ов в инпутах, а может логика проверки сложная и форме просто не хватит данных.
mironich
На первый взгляд, получается много типов event'ов,
Ну да у меня моих пользовательских уже 7 штук, но все кроме этого делают другие вещи.
Вообще евенты использую т.к
Есть базовый класс формы, есть на ней набор виджетов, с которыми выполняються манипуляции в зависимости от евента, и получаеться когда потомки базового класса формы, используються обьекту который с ними взаимодействует не надо кучу слотов подкл. и сигналов создавать.
Обьекты которые взаимодействуют с gui тоже все от одного базового класса в котором определены вызовы event-ов.
Благодаря вышенаписанному кол-ва однотипного кода сокращаеться в разы.


if'ов
Большое кол-во ведет к чему-то не хорошему?
Плохая читабельность кода, и.т.д… Или это нормально 6-7 условий elif?
Зачастую, достаточно QValidator'ов в инпутах,
Да они стоят, но!
Пользователь может не до конца ввести данные, для этого у валидатора вызывал в коде формы validate, но потом как перенес методы для управления данными сделал валидацию в классе для работы с данными но уже через re.match, получаеться лишний раз создаеться код для проверки..
Не вызываю из класса формы валидатор, дабы класс формы был независим от остальных обьектов.

Про event - ы, вот пример.
При выполнении запросов к VK API может вылезти каптча, диалог для ее ввода определен в базовом классе форм, и в базовом классе классов которые выполняют работу по взаимодействию с VK API есть метод для отсылки event-а форме, в event-е передаеться картинка для каптчи.
Есть еще несколько таких действий.
В итоге при создании обьекта для работы с VK_API в конструктор надо всеголишь передать форму которая будет получать event-ы и все…
Не каких сигналлов коннектов..
Можно было конечно через сигналы, и подключять из нутри формы сигнал обьекта к нужному слоту, но тогда бы класс формы перестал бы быть независимым.
reclosedev
На словах сложно все это воспринимается.

Честно говоря, не вижу преимуществ евентов над сигналами.
mironich
if'ов
Большое кол-во ведет к чему-то не хорошему?
Плохая читабельность кода, и.т.д… Или это нормально 6-7 условий elif?
Ну если if'ы выглядят как:
if event.type() == events.XXXEvent():
    self.XXX_counter += 1
    self.XXX_counter_label.setText(self.XXX_counter )
elif event.type() == events.YYYEvent():
    self.YYY_counter += 1
    self.YYY_counter_label.setText(self.YYY_counter )
elif ...
то явно что-то не так.

mironich
В итоге при создании обьекта для работы с VK_API в конструктор надо всеголишь передать форму которая будет получать event-ы и все…
Не каких сигналлов коннектов..
А должен ли класс для работы с ВК знать о форме?
mironich
но тогда бы класс формы перестал бы быть независимым.
А зачем? Скорее он будет использован один раз, а вот VK_API может и в другом приложении.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB