From 3130210f09e5351c2fc65288d53ad3d3006f6dc8 Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Thu, 3 Jan 2008 03:15:50 -0800 Subject: [PATCH] Log window improvements Added onthefly search to the logwindow Better maingui ui for the splitters Another unittest Signed-off by: David Aguilar --- t/modeltest.py | 18 +- t/testmodel.py | 7 +- ugitlibs/controllers.py | 6 +- ugitlibs/model.py | 6 +- ugitlibs/models.py | 18 +- ugitlibs/qobserver.py | 33 ++-- ugitlibs/utilcontroller.py | 50 ++++-- ui/maingui.ui | 414 ++++++++++++++++++++++----------------------- 8 files changed, 278 insertions(+), 274 deletions(-) diff --git a/t/modeltest.py b/t/modeltest.py index 2aec534..dd1fd31 100755 --- a/t/modeltest.py +++ b/t/modeltest.py @@ -3,32 +3,30 @@ import unittest from testmodel import TestModel class ModelTest(unittest.TestCase): - def setUp(self): self.model = TestModel() - def tearDown(self): del self.model + + def testCreate(self): + self.model.create(foo='bar') + self.failUnless(self.model.get_foo()=='bar') def testRegularAttributes(self): '''Test accessing attribute via model.attribute.''' self.failUnless( self.model.attribute == 'value' ) - def testUppercaseAttributes(self): - '''Test accessing attribute via model.ATTRIBUTE.''' - self.failUnless( self.model.ATTRIBUTE == 'value' ) - def testMixedcaseAttributes(self): '''Test accessing attribute via model.Attribute.''' - self.failUnless( self.model.Attribute == 'value' ) + self.failUnless( self.model.AtTrIbUte == 'value' ) def testExplicitAttr(self): - '''Test accessing an explicit attribute.''' + '''Test accessing an explicit attribute. Just in case we f** up getattr''' self.failUnless( self.model.hello == 'world' ) def testRealMethod(self): '''Test calling a concrete model method.''' - self.failUnless( self.model.realMethod() == 'real' ) + self.failUnless( self.model.testMethod() == 'test' ) def testGetter(self): '''Test calling using the get* method.''' @@ -36,7 +34,7 @@ class ModelTest(unittest.TestCase): def testSetter(self): '''Test using the set* method.''' - self.model.set('newAttribute','baz') + self.model.set_param('newAttribute','baz') self.failUnless( self.model.newattribute == 'baz' ) self.failUnless( self.model.getNewAttribute() == 'baz' ) diff --git a/t/testmodel.py b/t/testmodel.py index 787c309..8dbec55 100755 --- a/t/testmodel.py +++ b/t/testmodel.py @@ -3,9 +3,8 @@ from ugitlibs.model import Model class TestModel(Model): def __init__(self): - duck = Model().create(sound='quack',name='ducky') - goose = Model().create(sound='cluck',name='goosey') + goose = Model().create(sound='cluck',name='goose') Model.__init__(self, attribute = 'value', mylist=[duck,duck,goose]) @@ -13,6 +12,4 @@ class TestModel(Model): self.set_list_params(mylist=Model) self.set_mylist([duck,duck,goose, 'meow', 'caboose',42]) - print self - - def realMethod(self): return 'real' + def testMethod(self): return 'test' diff --git a/ugitlibs/controllers.py b/ugitlibs/controllers.py index 411f1c1..9585f3c 100644 --- a/ugitlibs/controllers.py +++ b/ugitlibs/controllers.py @@ -27,6 +27,9 @@ class Controller(QObserver): def __init__(self, model, view): QObserver.__init__(self, model, view) + # parent-less log window + qtutils.LOGGER = log_window(model, QtGui.qApp.activeWindow()) + self.__last_inotify_event = time.time() # The diff-display context menu @@ -189,9 +192,6 @@ class Controller(QObserver): self.start_inotify_thread() self.refresh_view() - # parent-less log window - qtutils.LOGGER = log_window(model, None) - self.init_log() self.rescan() diff --git a/ugitlibs/model.py b/ugitlibs/model.py index 11f2eb6..f1ad075 100644 --- a/ugitlibs/model.py +++ b/ugitlibs/model.py @@ -192,8 +192,7 @@ class Model(Observable): if param in self.__list_params: # A list of Model-derived objects listparam = [] - objspec = self.__list_params[param] - cls = self.__get_class(objspec) + cls = self.__list_params[param] for item in val: listparam.append(cls().from_dict(item)) return listparam @@ -202,8 +201,7 @@ class Model(Observable): elif is_dict(val): if param in self.__object_params: # "module.submodule:ClassName" - objectspec = self.__object_params[param] - cls = self.__get_class(objectspec) + cls = self.__object_params[param] return cls().from_dict(val) # Atoms and uninteresting hashes/dictionaries diff --git a/ugitlibs/models.py b/ugitlibs/models.py index 3c7ec9b..58f9a9a 100644 --- a/ugitlibs/models.py +++ b/ugitlibs/models.py @@ -8,9 +8,14 @@ import model class Model(model.Model): def __init__(self): model.Model.__init__(self) + self.init_config_data() + # chdir to the root of the git tree. + # This keeps paths relative. + cdup = git.show_cdup() + if cdup: os.chdir(cdup) # These methods are best left implemented in git.py - git_attrs=( + for cmd in ( 'add', 'add_or_remove', 'cat_file', @@ -27,16 +32,9 @@ class Model(model.Model): 'rebase', 'remote_url', 'rev_list_range', - ) - - for attr in git_attrs: - setattr(self, attr, getattr(git,attr)) + ): + setattr(self, cmd, getattr(git,cmd)) - # chdir to the root of the git tree. This is critical - # to being able to properly use the git porcelain. - cdup = git.show_cdup() - if cdup: os.chdir(cdup) - self.init_config_data() self.create( ##################################################### # Used in various places diff --git a/ugitlibs/qobserver.py b/ugitlibs/qobserver.py index c2d0c0e..ac33218 100644 --- a/ugitlibs/qobserver.py +++ b/ugitlibs/qobserver.py @@ -24,14 +24,6 @@ class QObserver(Observer, QObject): self.__callbacks = {} self.__model_to_view = {} self.__view_to_model = {} - - def connect(self, obj, signal_str, *args): - '''Convenience function so that subclasses do not have - to import QtCore.SIGNAL.''' - signal = signal_str - if type(signal) is str: - signal = SIGNAL(signal) - return QObject.connect(obj, signal, *args) def SLOT(self, *args): '''Default slot to handle all Qt callbacks. @@ -48,20 +40,33 @@ class QObserver(Observer, QObject): model_param = self.__view_to_model[sender] if isinstance(widget, QTextEdit): value = str(widget.toPlainText()) - model.set_param(model_param, value, notify=False) + model.set_param(model_param, value, + notify=False) elif isinstance(widget, QLineEdit): value = str(widget.text()) - model.set_param(model_param, value, notify=False) + model.set_param(model_param, value, + notify=False) elif isinstance(widget, QCheckBox): - model.set_param(model_param, widget.isChecked()) + model.set_param(model_param, widget.isChecked(), + notify=False) elif isinstance(widget, QSpinBox): - model.set_param(model_param, widget.value()) + model.set_param(model_param, widget.value(), + notify=False) elif isinstance(widget, QFontComboBox): - model.set_param(model_param, - str(widget.currentFont().toString())) + value = unicode(widget.currentFont().toString()) + model.set_param(model_param, value, + notify=False) else: print("SLOT(): Unknown widget:", sender, widget) + def connect(self, obj, signal_str, *args): + '''Convenience function so that subclasses do not have + to import QtCore.SIGNAL.''' + signal = signal_str + if type(signal) is str: + signal = SIGNAL(signal) + return QObject.connect(obj, signal, *args) + def add_signals(self, signal_str, *objects): '''Connects object's signal to the QObserver.''' for obj in objects: diff --git a/ugitlibs/utilcontroller.py b/ugitlibs/utilcontroller.py index 12a7399..bb4cfe3 100644 --- a/ugitlibs/utilcontroller.py +++ b/ugitlibs/utilcontroller.py @@ -177,7 +177,7 @@ class OptionsController(QObserver): QObserver.refresh_view(self) - + # save button def save_settings(self): params_to_save = [] params = self.model.get_config_params() @@ -194,6 +194,7 @@ class OptionsController(QObserver): self.view.done(QDialog.Accepted) + # cancel button, undo changes def restore_settings(self): params = self.backup_model.get_config_params() self.model.copy_params(self.backup_model, params) @@ -227,6 +228,7 @@ class OptionsController(QObserver): def log_window(model, parent): model = model.clone() + model.create(search_text='') view = OutputGUI(parent) ctl = LogWindowController(model,view) return view @@ -236,48 +238,60 @@ class LogWindowController(QObserver): QObserver.__init__(self, model, view) self.model_to_view('search_text', 'search_line') - self.add_signals('textChanged(const QString&)', - self.view.search_line) + self.add_signals('textChanged(const QString&)', self.view.search_line) + + self.connect(self.view.search_line, + 'textChanged(const QString&)', self.insta_search) self.connect(self.view.clear_button, 'released()', self.clear) self.connect(self.view.next_button, 'released()', self.next) self.connect(self.view.prev_button, 'released()', self.prev) - self.connect(self.view.output_text, 'cursorPositionChanged()', - self.cursor_position_changed) - + self.connect(self.view.output_text, + 'cursorPositionChanged()', self.cursor_position_changed) self.reset() def clear(self): self.view.output_text.clear() self.reset() + def insta_search(self,*rest): + self.search_offset = 0 + txt = self.model.get_search_text().lower() + if len(txt.strip())>2: self.next() + def reset(self): self.search_offset = 0 def next(self): - text = self.model.get_search_text() + text = self.model.get_search_text().lower().strip() if not text: return output = str(self.view.output_text.toPlainText()) if self.search_offset + len(text) > len(output): if qtutils.question( - self.view, - unicode(self.tr("%s not found")) % text, - unicode(self.tr("Could not find '%s'.\n" + self.view, + unicode(self.tr("%s not found")) % text, + unicode(self.tr( + "Could not find '%s'.\n" "Search from the beginning?" - )) % text): + )) % text, + default=False): self.search_offset = 0 + else: + return - find_in = output[self.search_offset:] + find_in = output[self.search_offset:].lower() try: index = find_in.index(text) except: self.search_offset = 0 if qtutils.question( - self.view, - unicode(self.tr("%s not found")) % text, - unicode(self.tr("Could not find '%s'.\n" + self.view, + unicode(self.tr("%s not found")) % text, + unicode(self.tr( + "Could not find '%s'.\n" "Search from the beginning?" - )) % text): + )) % text, + default=False): self.next() return cursor = self.view.output_text.textCursor() @@ -291,13 +305,13 @@ class LogWindowController(QObserver): self.search_offset = new_offset def prev(self): - text = self.model.get_search_text() + text = self.model.get_search_text().lower().strip() if not text: return output = str(self.view.output_text.toPlainText()) if self.search_offset == 0: self.search_offset = len(output) - find_in = output[:self.search_offset] + find_in = output[:self.search_offset].lower() try: offset = find_in.rindex(text) except: diff --git a/ui/maingui.ui b/ui/maingui.ui index e38a518..a6fd64e 100644 --- a/ui/maingui.ui +++ b/ui/maingui.ui @@ -17,175 +17,169 @@ Qt::Vertical - - 2 - - + - Qt::Vertical + Qt::Horizontal 2 - - - Qt::Horizontal - - - 2 - - - - - 0 - - - - - 0 - - - - - Unstaged Changes - - - - - - - Includes untracked files in the unstaged changes list. + + + + 0 + + + + + 0 + + + + + Unstaged Changes + + + + + + + Includes untracked files in the unstaged changes list. Disabling this causes ugit to only display files known to git in the unstaged changes list. - - - Show Untracked Files - - - true - - - false - - - - - - - Qt::Vertical - - - QSizePolicy::MinimumExpanding - - - - 20 - 22 - - - - - - - - - - - Courier - 11 - - - - A list of all the unstaged changes. + + + Show Untracked Files + + + true + + + false + + + + + + + Qt::Vertical + + + QSizePolicy::MinimumExpanding + + + + 20 + 22 + + + + + + + + + + + Courier + 11 + + + + A list of all the unstaged changes. This list includes those files known to git and can optionally include untracked files if "Show Untracked Files" is checked. - - - true - - - QAbstractItemView::ExtendedSelection - - - QListView::ListMode - - - true - - - - - - - - - 0 - - - - - 0 - - - 0 - - - - - - 0 - 0 - - - - Staged Changes (Will Commit) - - - - - - - Qt::Vertical - - - QSizePolicy::MinimumExpanding - - - - 20 - 22 - - - - - - - - - - - Courier - 11 - - - - A list of all staged changes for the next commit. - - - true - - - QAbstractItemView::ExtendedSelection - - - QListView::ListMode - - - - - + + + true + + + QAbstractItemView::ExtendedSelection + + + QListView::ListMode + + + true + + + + + + + 0 + + + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Staged Changes (Will Commit) + + + + + + + Qt::Vertical + + + QSizePolicy::MinimumExpanding + + + + 20 + 22 + + + + + + + + + + + Courier + 11 + + + + A list of all staged changes for the next commit. + + + true + + + QAbstractItemView::ExtendedSelection + + + QListView::ListMode + + + + + + + + + Qt::Vertical + + 0 @@ -350,57 +344,57 @@ retrieve the latest commit message prior to committing. - - - - - 0 - - - - - QFrame::StyledPanel - - - QFrame::Plain - - - - - - Qt::AlignCenter - - - - - - - - Courier - 12 - - - - true - - - QTextEdit::NoWrap - - - true - - - false - - - 2 - - - Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::NoTextInteraction|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - + + + + 0 + + + + + QFrame::StyledPanel + + + QFrame::Plain + + + + + + Qt::AlignCenter + + + + + + + + Courier + 12 + + + + true + + + QTextEdit::NoWrap + + + true + + + false + + + 2 + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::NoTextInteraction|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + @@ -412,7 +406,7 @@ retrieve the latest commit message prior to committing. 0 0 780 - 26 + 28 -- 2.11.4.GIT