Vastly improve startup time by reducing notifications
[ugit.git] / ugit / qobserver.py
blob9e1ca548df4d842f443be9eb21680e3758d7c066
1 #!/usr/bin/env python
2 from PyQt4.QtCore import QObject
3 from PyQt4.QtCore import SIGNAL
4 from PyQt4.QtGui import QSpinBox
5 from PyQt4.QtGui import QPixmap
6 from PyQt4.QtGui import QTextEdit
7 from PyQt4.QtGui import QLineEdit
8 from PyQt4.QtGui import QListWidget
9 from PyQt4.QtGui import QCheckBox
10 from PyQt4.QtGui import QFontComboBox
11 from PyQt4.QtGui import QAbstractButton
12 from PyQt4.QtGui import QSplitter
13 from PyQt4.QtGui import QAction
15 from observer import Observer
17 class QObserver(Observer, QObject):
19 def __init__(self, model, view):
20 Observer.__init__(self, model)
21 QObject.__init__(self)
23 self.view = view
25 self.__actions = {}
26 self.__callbacks = {}
27 self.__model_to_view = {}
28 self.__view_to_model = {}
29 self.__connected = []
31 def SLOT(self, *args):
32 '''Default slot to handle all Qt callbacks.
33 This method delegates to callbacks from add_signals.'''
35 widget = self.sender()
36 sender = str(widget.objectName())
38 if sender in self.__callbacks:
39 self.__callbacks[sender](*args)
41 elif sender in self.__view_to_model:
42 model = self.model
43 model_param = self.__view_to_model[sender]
44 if isinstance(widget, QTextEdit):
45 value = unicode(widget.toPlainText())
46 model.set_param(model_param, value,
47 notify=False)
48 elif isinstance(widget, QLineEdit):
49 value = unicode(widget.text())
50 model.set_param(model_param, value)
51 elif isinstance(widget, QCheckBox):
52 model.set_param(model_param, widget.isChecked())
53 elif isinstance(widget, QSpinBox):
54 model.set_param(model_param, widget.value())
55 elif isinstance(widget, QFontComboBox):
56 value = unicode(widget.currentFont().toString())
57 model.set_param(model_param, value)
58 else:
59 print("SLOT(): Unknown widget:", sender, widget)
61 def connect(self, obj, signal_str, *args):
62 '''Convenience function so that subclasses do not have
63 to import QtCore.SIGNAL.'''
64 signal = signal_str
65 if type(signal) is str:
66 signal = SIGNAL(signal)
67 return QObject.connect(obj, signal, *args)
69 def add_signals(self, signal_str, *objects):
70 '''Connects object's signal to the QObserver.'''
71 for obj in objects:
72 self.connect(obj, signal_str, self.SLOT)
74 def add_callbacks(self, **callbacks):
75 '''Registers callbacks that are called in response to GUI events.'''
76 for sender, callback in callbacks.iteritems():
77 self.__callbacks[sender] = callback
78 self.autoconnect(getattr(self.view, sender))
80 def add_observables(self, *params):
81 '''This method assumes that widgets and model params
82 share the same name.'''
83 for param in params:
84 self.model_to_view(param, getattr(self.view, param))
86 def model_to_view(self, model_param, *widgets):
87 '''Binds model params to qt widgets(model->view)'''
88 self.__model_to_view[model_param] = widgets
89 for w in widgets:
90 view = str(w.objectName())
91 self.__view_to_model[view] = model_param
92 self.__autoconnect(w)
94 def autoconnect(self, *widgets):
95 for w in widgets:
96 self.__autoconnect(w)
98 def __autoconnect(self, widget):
99 '''Automagically connects Qt widgets to QObserver.SLOT'''
101 if widget in self.__connected: return
102 self.__connected.append(widget)
104 if isinstance(widget, QTextEdit):
105 self.add_signals(
106 'textChanged()', widget)
107 elif isinstance(widget, QLineEdit):
108 self.add_signals(
109 'textChanged(const QString&)', widget)
110 elif isinstance(widget, QListWidget):
111 self.add_signals(
112 'itemSelectionChanged()', widget)
113 self.add_signals(
114 'itemClicked(QListWidgetItem *)', widget)
115 self.add_signals(
116 'itemDoubleClicked(QListWidgetItem*)', widget)
117 elif isinstance(widget, QAbstractButton):
118 self.add_signals(
119 'released()', widget)
120 elif isinstance(widget, QAction):
121 self.add_signals(
122 'triggered()', widget)
123 elif isinstance(widget, QCheckBox):
124 self.add_signals(
125 'stateChanged(int)', widget)
126 elif isinstance(widget, QSpinBox):
127 self.add_signals(
128 'valueChanged(int)', widget)
129 elif isinstance(widget, QFontComboBox):
130 self.add_signals(
131 'currentFontChanged(const QFont&)', widget)
132 elif isinstance(widget, QSplitter):
133 self.add_signals(
134 'splitterMoved(int,int)', widget)
135 else:
136 raise Exception(
137 "Asked to connect unknown widget:\n\t"
138 "%s => %s" %( type(widget),
139 str(widget.objectName()) ))
141 def add_actions(self, model_param, callback):
142 '''Register view actions that are called in response to
143 view changes.(view->model)'''
144 self.__actions[model_param] = callback
146 def subject_changed(self, param, value):
147 '''Sends a model param to the view(model->view)'''
149 if param in self.__model_to_view:
150 notify = self.model.get_notify()
151 self.model.set_notify(False)
152 for widget in self.__model_to_view[param]:
153 if isinstance(widget, QSpinBox):
154 widget.setValue(value)
155 elif isinstance(widget, QPixmap):
156 widget.load(value)
157 elif isinstance(widget, QTextEdit):
158 widget.setText(value)
159 elif isinstance(widget, QLineEdit):
160 widget.setText(value)
161 elif isinstance(widget, QListWidget):
162 widget.clear()
163 for i in value: widget.addItem(i)
164 elif isinstance(widget, QCheckBox):
165 widget.setChecked(value)
166 elif isinstance(widget, QFontComboBox):
167 font = widget.currentFont()
168 font.fromString(value)
169 else:
170 print('subject_changed(): '
171 + 'Unknown widget:',
172 widget_name, widget, value)
173 self.model.set_notify(notify)
175 if param not in self.__actions: return
176 widgets = []
177 if param in self.__model_to_view:
178 for widget in self.__model_to_view[param]:
179 widgets.append(widget)
180 # Call the model callback w/ the view's widgets as the args
181 self.__actions[param](*widgets)
183 def refresh_view(self, *params):
184 if not params:
185 params= tuple(self.__model_to_view.keys()
186 + self.__actions.keys())
187 notified = []
188 for param in params:
189 if param not in notified:
190 notified.append(param)
191 self.model.notify_observers(*notified)