qobserver: add support for optional args in init()
[ugit.git] / ugit / qobserver.py
blob2e552d4d0d60796be898009bd0fc55b83bdf8874
1 #!/usr/bin/env python
2 from PyQt4.QtCore import Qt
3 from PyQt4.QtCore import QObject
4 from PyQt4.QtCore import SIGNAL
5 from PyQt4.QtCore import QDate
6 from PyQt4.QtGui import QDateEdit
7 from PyQt4.QtGui import QSpinBox
8 from PyQt4.QtGui import QPixmap
9 from PyQt4.QtGui import QTextEdit
10 from PyQt4.QtGui import QLineEdit
11 from PyQt4.QtGui import QListWidget
12 from PyQt4.QtGui import QCheckBox
13 from PyQt4.QtGui import QFontComboBox
14 from PyQt4.QtGui import QAbstractButton
15 from PyQt4.QtGui import QSplitter
16 from PyQt4.QtGui import QAction
18 from ugit.observer import Observer
20 class QObserver(Observer, QObject):
22 def __init__(self, model, view, *args, **kwargs):
23 Observer.__init__(self, model)
24 QObject.__init__(self)
26 self.view = view
28 self.__actions = {}
29 self.__callbacks = {}
30 self.__model_to_view = {}
31 self.__view_to_model = {}
32 self.__connected = []
33 # Call user-defined startup routine
34 self.init(model, view, *args, **kwargs)
36 def init(self, model, view, *args, **kwargs):
37 pass
39 def SLOT(self, *args):
40 '''Default slot to handle all Qt callbacks.
41 This method delegates to callbacks from add_signals.'''
43 widget = self.sender()
44 sender = str(widget.objectName())
46 if sender in self.__callbacks:
47 self.__callbacks[sender](*args)
49 elif sender in self.__view_to_model:
50 model = self.model
51 model_param = self.__view_to_model[sender]
52 if isinstance(widget, QTextEdit):
53 value = unicode(widget.toPlainText())
54 model.set_param(model_param, value,
55 notify=False)
56 elif isinstance(widget, QLineEdit):
57 value = unicode(widget.text())
58 model.set_param(model_param, value)
59 elif isinstance(widget, QCheckBox):
60 model.set_param(model_param, widget.isChecked())
61 elif isinstance(widget, QSpinBox):
62 model.set_param(model_param, widget.value())
63 elif isinstance(widget, QFontComboBox):
64 value = unicode(widget.currentFont().toString())
65 model.set_param(model_param, value)
66 elif isinstance(widget, QDateEdit):
67 fmt = Qt.ISODate
68 value = str(widget.date().toString(fmt))
69 model.set_param(model_param, value)
70 elif isinstance(widget, QListWidget):
71 pass
72 else:
73 print("SLOT(): Unknown widget:", sender, widget)
75 def connect(self, obj, signal_str, *args):
76 '''Convenience function so that subclasses do not have
77 to import QtCore.SIGNAL.'''
78 signal = signal_str
79 if type(signal) is str:
80 signal = SIGNAL(signal)
81 return QObject.connect(obj, signal, *args)
83 def add_signals(self, signal_str, *objects):
84 '''Connects object's signal to the QObserver.'''
85 for obj in objects:
86 self.connect(obj, signal_str, self.SLOT)
88 def add_callbacks(self, **callbacks):
89 '''Registers callbacks that are called in response to GUI events.'''
90 for sender, callback in callbacks.iteritems():
91 self.__callbacks[sender] = callback
92 self.autoconnect(getattr(self.view, sender))
94 def add_observables(self, *params):
95 '''This method assumes that widgets and model params
96 share the same name.'''
97 for param in params:
98 self.model_to_view(param, getattr(self.view, param))
100 def model_to_view(self, model_param, *widgets):
101 '''Binds model params to qt widgets(model->view)'''
102 self.__model_to_view[model_param] = widgets
103 for w in widgets:
104 view = str(w.objectName())
105 self.__view_to_model[view] = model_param
106 self.__autoconnect(w)
108 def autoconnect(self, *widgets):
109 for w in widgets:
110 self.__autoconnect(w)
112 def __autoconnect(self, widget):
113 '''Automagically connects Qt widgets to QObserver.SLOT'''
115 if widget in self.__connected: return
116 self.__connected.append(widget)
118 if isinstance(widget, QTextEdit):
119 self.add_signals(
120 'textChanged()', widget)
121 elif isinstance(widget, QLineEdit):
122 self.add_signals(
123 'textChanged(const QString&)', widget)
124 elif isinstance(widget, QListWidget):
125 self.add_signals(
126 'itemSelectionChanged()', widget)
127 self.add_signals(
128 'itemClicked(QListWidgetItem *)', widget)
129 self.add_signals(
130 'itemDoubleClicked(QListWidgetItem*)', widget)
131 elif isinstance(widget, QAbstractButton):
132 self.add_signals(
133 'released()', widget)
134 elif isinstance(widget, QAction):
135 self.add_signals(
136 'triggered()', widget)
137 elif isinstance(widget, QCheckBox):
138 self.add_signals(
139 'stateChanged(int)', widget)
140 elif isinstance(widget, QSpinBox):
141 self.add_signals(
142 'valueChanged(int)', widget)
143 elif isinstance(widget, QFontComboBox):
144 self.add_signals(
145 'currentFontChanged(const QFont&)', widget)
146 elif isinstance(widget, QSplitter):
147 self.add_signals(
148 'splitterMoved(int,int)', widget)
149 elif isinstance(widget, QDateEdit):
150 self.add_signals(
151 'dateChanged(const QDate&)', widget)
152 else:
153 raise Exception(
154 "Asked to connect unknown widget:\n\t"
155 "%s => %s" %( type(widget),
156 str(widget.objectName()) ))
158 def add_actions(self, **kwargs):
159 '''Register view actions that are called in response to
160 view changes.(view->model)'''
161 for model_param, callback in kwargs.iteritems():
162 if type(callback) is list:
163 self.__actions[model_param] = callback
164 else:
165 self.__actions[model_param] = [callback]
167 def subject_changed(self, param, value):
168 '''Sends a model param to the view(model->view)'''
170 if param in self.__model_to_view:
171 notify = self.model.get_notify()
172 self.model.set_notify(False)
173 for widget in self.__model_to_view[param]:
174 if isinstance(widget, QSpinBox):
175 widget.setValue(value)
176 elif isinstance(widget, QPixmap):
177 widget.load(value)
178 elif isinstance(widget, QTextEdit):
179 widget.setText(value)
180 elif isinstance(widget, QLineEdit):
181 widget.setText(value)
182 elif isinstance(widget, QListWidget):
183 widget.clear()
184 for i in value:
185 widget.addItem(i)
186 elif isinstance(widget, QCheckBox):
187 widget.setChecked(value)
188 elif isinstance(widget, QFontComboBox):
189 font = widget.currentFont()
190 font.fromString(value)
191 elif isinstance(widget, QDateEdit):
192 if not value: return
193 fmt = Qt.ISODate
194 date = QDate.fromString(value, fmt)
195 if date:
196 widget.setDate(date)
197 else:
198 print('subject_changed(): '
199 + 'Unknown widget:',
200 str(widget.objectName()),
201 widget, value)
202 self.model.set_notify(notify)
204 if param not in self.__actions:
205 return
206 widgets = []
207 if param in self.__model_to_view:
208 for widget in self.__model_to_view[param]:
209 widgets.append(widget)
210 # Call the model callback w/ the view's widgets as the args
211 for action in self.__actions[param]:
212 action(*widgets)
214 def refresh_view(self, *params):
215 if not params:
216 params= tuple(self.__model_to_view.keys()
217 + self.__actions.keys())
218 notified = []
219 for param in params:
220 if param not in notified:
221 notified.append(param)
222 self.model.notify_observers(*notified)