From e153109f2c7a99309e286bc1bedd5fe4313f4fef Mon Sep 17 00:00:00 2001 From: Julien Veyssier Date: Sun, 30 Nov 2014 15:36:25 +0100 Subject: [PATCH] [qcookboob] tab navigation implemented (closes #1290) --- weboob/applications/qcookboob/main_window.py | 252 +++++++++++------- weboob/applications/qcookboob/minirecipe.py | 32 ++- .../applications/qcookboob/ui/main_window.ui | 145 ++-------- .../applications/qcookboob/ui/minirecipe.ui | 107 ++++++-- weboob/applications/qcookboob/ui/result.ui | 177 ++++++++++++ 5 files changed, 458 insertions(+), 255 deletions(-) create mode 100644 weboob/applications/qcookboob/ui/result.ui diff --git a/weboob/applications/qcookboob/main_window.py b/weboob/applications/qcookboob/main_window.py index 3b7dd8d5..25dbe434 100644 --- a/weboob/applications/qcookboob/main_window.py +++ b/weboob/applications/qcookboob/main_window.py @@ -21,18 +21,122 @@ import os import codecs from PyQt4.QtCore import SIGNAL, Qt, QStringList -from PyQt4.QtGui import QApplication, QCompleter +from PyQt4.QtGui import QApplication, QCompleter, QFrame, QShortcut, QKeySequence from weboob.capabilities.recipe import CapRecipe from weboob.tools.application.qt import QtMainWindow, QtDo from weboob.tools.application.qt.backendcfg import BackendCfg from weboob.applications.qcookboob.ui.main_window_ui import Ui_MainWindow +from weboob.applications.qcookboob.ui.result_ui import Ui_Result from .minirecipe import MiniRecipe from .recipe import Recipe +class Result(QFrame): + def __init__(self, weboob, app, parent=None): + QFrame.__init__(self, parent) + self.ui = Ui_Result() + self.ui.setupUi(self) + + self.parent = parent + self.weboob = weboob + self.app = app + self.minis = [] + self.current_info_widget = None + + # action history is composed by the last action and the action list + # An action is a function, a list of arguments and a description string + self.action_history = {'last_action': None, 'action_list': []} + self.connect(self.ui.backButton, SIGNAL("clicked()"), self.doBack) + self.ui.backButton.hide() + + def doAction(self, description, fun, args): + ''' Call fun with args as arguments + and save it in the action history + ''' + self.ui.currentActionLabel.setText(description) + if self.action_history['last_action'] is not None: + self.action_history['action_list'].append(self.action_history['last_action']) + self.ui.backButton.setToolTip(self.action_history['last_action']['description']) + self.ui.backButton.show() + self.action_history['last_action'] = {'function': fun, 'args': args, 'description': description} + return fun(*args) + + def doBack(self): + ''' Go back in action history + Basically call previous function and update history + ''' + if len(self.action_history['action_list']) > 0: + todo = self.action_history['action_list'].pop() + self.ui.currentActionLabel.setText(todo['description']) + self.action_history['last_action'] = todo + if len(self.action_history['action_list']) == 0: + self.ui.backButton.hide() + else: + self.ui.backButton.setToolTip(self.action_history['action_list'][-1]['description']) + return todo['function'](*todo['args']) + + def processFinished(self): + self.parent.ui.searchEdit.setEnabled(True) + QApplication.restoreOverrideCursor() + self.process = None + self.parent.ui.stopButton.hide() + + def searchRecipe(self,pattern): + if not pattern: + return + self.doAction(u'Search recipe "%s"' % pattern, self.searchRecipeAction, [pattern]) + + def searchRecipeAction(self, pattern): + self.ui.stackedWidget.setCurrentWidget(self.ui.list_page) + for mini in self.minis: + self.ui.list_content.layout().removeWidget(mini) + mini.hide() + mini.deleteLater() + + self.minis = [] + self.parent.ui.searchEdit.setEnabled(False) + QApplication.setOverrideCursor(Qt.WaitCursor) + + backend_name = str(self.parent.ui.backendEdit.itemData(self.parent.ui.backendEdit.currentIndex()).toString()) + + self.process = QtDo(self.weboob, self.addRecipe, fb=self.processFinished) + self.process.do(self.app._do_complete, self.parent.getCount(), ('title'), 'iter_recipes', pattern, backends=backend_name, caps=CapRecipe) + self.parent.ui.stopButton.show() + + def addRecipe(self, recipe): + minirecipe = MiniRecipe(self.weboob, self.weboob[recipe.backend], recipe, self) + self.ui.list_content.layout().insertWidget(self.ui.list_content.layout().count()-1,minirecipe) + self.minis.append(minirecipe) + + def displayRecipe(self, recipe, backend): + self.ui.stackedWidget.setCurrentWidget(self.ui.info_page) + if self.current_info_widget is not None: + self.ui.info_content.layout().removeWidget(self.current_info_widget) + self.current_info_widget.hide() + self.current_info_widget.deleteLater() + wrecipe = Recipe(recipe, backend, self) + self.ui.info_content.layout().addWidget(wrecipe) + self.current_info_widget = wrecipe + QApplication.restoreOverrideCursor() + + def searchId(self, id): + QApplication.setOverrideCursor(Qt.WaitCursor) + if '@' in id: + backend_name = id.split('@')[1] + id = id.split('@')[0] + else: + backend_name = None + for backend in self.weboob.iter_backends(): + if (backend_name and backend.name == backend_name) or not backend_name: + recipe = backend.get_recipe(id) + if recipe: + self.doAction('Details of recipe "%s"' % recipe.title, self.displayRecipe, [recipe, backend]) + QApplication.restoreOverrideCursor() + + class MainWindow(QtMainWindow): def __init__(self, config, weboob, app, parent=None): QtMainWindow.__init__(self, parent) @@ -49,22 +153,32 @@ class MainWindow(QtMainWindow): self.search_history = self.loadSearchHistory() self.updateCompletion() - # action history is composed by the last action and the action list - # An action is a function, a list of arguments and a description string - self.action_history = {'last_action': None, 'action_list': []} - self.connect(self.ui.backButton, SIGNAL("clicked()"), self.doBack) - self.ui.backButton.hide() - self.connect(self.ui.stopButton, SIGNAL("clicked()"), self.stopProcess) - self.ui.stopButton.hide() - self.connect(self.ui.searchEdit, SIGNAL("returnPressed()"), self.search) self.connect(self.ui.idEdit, SIGNAL("returnPressed()"), self.searchId) count = self.config.get('settings', 'maxresultsnumber') self.ui.countSpin.setValue(int(count)) + showT = self.config.get('settings', 'showthumbnails') + self.ui.showTCheck.setChecked(showT == '1') + + self.connect(self.ui.stopButton, SIGNAL("clicked()"), self.stopProcess) + self.ui.stopButton.hide() self.connect(self.ui.actionBackends, SIGNAL("triggered()"), self.backendsConfig) - self.connect(self.ui.actionQuit, SIGNAL("triggered()"), self.close) + q = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Q), self) + self.connect(q, SIGNAL("activated()"), self.close) + n = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_PageDown), self) + self.connect(n, SIGNAL("activated()"), self.nextTab) + p = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_PageUp), self) + self.connect(p, SIGNAL("activated()"), self.prevTab) + w = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_W), self) + self.connect(w, SIGNAL("activated()"), self.closeCurrentTab) + + l = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_L), self) + self.connect(l, SIGNAL("activated()"), self.ui.searchEdit.setFocus) + self.connect(l, SIGNAL("activated()"), self.ui.searchEdit.selectAll) + + self.connect(self.ui.resultsTab, SIGNAL("tabCloseRequested(int)"), self.closeTab) self.loadBackendsList() @@ -127,31 +241,34 @@ class MainWindow(QtMainWindow): def stopProcess(self): self.process.process.finish_event.set() - def doAction(self, description, fun, args): - ''' Call fun with args as arguments - and save it in the action history - ''' - self.ui.currentActionLabel.setText(description) - if self.action_history['last_action'] is not None: - self.action_history['action_list'].append(self.action_history['last_action']) - self.ui.backButton.setToolTip(self.action_history['last_action']['description']) - self.ui.backButton.show() - self.action_history['last_action'] = {'function': fun, 'args': args, 'description': description} - return fun(*args) + def closeTab(self, index): + if self.ui.resultsTab.widget(index) != 0: + tabToClose = self.ui.resultsTab.widget(index) + self.ui.resultsTab.removeTab(index) + del(tabToClose) - def doBack(self): - ''' Go back in action history - Basically call previous function and update history - ''' - if len(self.action_history['action_list']) > 0: - todo = self.action_history['action_list'].pop() - self.ui.currentActionLabel.setText(todo['description']) - self.action_history['last_action'] = todo - if len(self.action_history['action_list']) == 0: - self.ui.backButton.hide() - else: - self.ui.backButton.setToolTip(self.action_history['action_list'][-1]['description']) - return todo['function'](*todo['args']) + def closeCurrentTab(self): + self.closeTab(self.ui.resultsTab.currentIndex()) + + def prevTab(self): + index = self.ui.resultsTab.currentIndex() - 1 + size = self.ui.resultsTab.count() + if size != 0: + self.ui.resultsTab.setCurrentIndex(index % size) + + def nextTab(self): + index = self.ui.resultsTab.currentIndex() + 1 + size = self.ui.resultsTab.count() + if size != 0: + self.ui.resultsTab.setCurrentIndex(index % size) + + def newTab(self, txt, backend, recipe=None): + id = '' + if recipe is not None: + id = recipe.id + new_res = Result(self.weboob, self.app, self) + self.ui.resultsTab.addTab(new_res, txt) + new_res.searchId(id) def search(self): pattern = unicode(self.ui.searchEdit.text()) @@ -162,67 +279,18 @@ class MainWindow(QtMainWindow): self.search_history.append(pattern) self.updateCompletion() - self.searchRecipe() - - def searchRecipe(self): - pattern = unicode(self.ui.searchEdit.text()) - if not pattern: - return - self.doAction(u'Search recipe "%s"' % pattern, self.searchRecipeAction, [pattern]) - - def searchRecipeAction(self, pattern): - self.ui.stackedWidget.setCurrentWidget(self.ui.list_page) - for mini in self.minis: - self.ui.list_content.layout().removeWidget(mini) - mini.hide() - mini.deleteLater() - - self.minis = [] - self.ui.searchEdit.setEnabled(False) - QApplication.setOverrideCursor(Qt.WaitCursor) - - backend_name = str(self.ui.backendEdit.itemData(self.ui.backendEdit.currentIndex()).toString()) - - self.process = QtDo(self.weboob, self.addRecipe, fb=self.addRecipeEnd) - self.process.do(self.app._do_complete, self.getCount(), ('title'), 'iter_recipes', pattern, backends=backend_name, caps=CapRecipe) - self.ui.stopButton.show() - - def addRecipeEnd(self): - self.ui.searchEdit.setEnabled(True) - QApplication.restoreOverrideCursor() - self.process = None - self.ui.stopButton.hide() - - def addRecipe(self, recipe): - minirecipe = MiniRecipe(self.weboob, self.weboob[recipe.backend], recipe, self) - self.ui.list_content.layout().addWidget(minirecipe) - self.minis.append(minirecipe) - - def displayRecipe(self, recipe, backend): - self.ui.stackedWidget.setCurrentWidget(self.ui.info_page) - if self.current_info_widget is not None: - self.ui.info_content.layout().removeWidget(self.current_info_widget) - self.current_info_widget.hide() - self.current_info_widget.deleteLater() - wrecipe = Recipe(recipe, backend, self) - self.ui.info_content.layout().addWidget(wrecipe) - self.current_info_widget = wrecipe - QApplication.restoreOverrideCursor() + new_res = Result(self.weboob, self.app, self) + self.ui.resultsTab.addTab(new_res, pattern) + self.ui.resultsTab.setCurrentWidget(new_res) + new_res.searchRecipe(pattern) def searchId(self): - QApplication.setOverrideCursor(Qt.WaitCursor) id = unicode(self.ui.idEdit.text()) - if '@' in id: - backend_name = id.split('@')[1] - id = id.split('@')[0] - else: - backend_name = None - for backend in self.weboob.iter_backends(): - if (backend_name and backend.name == backend_name) or not backend_name: - recipe = backend.get_recipe(id) - if recipe: - self.doAction('Details of recipe "%s"' % recipe.title, self.displayRecipe, [recipe, backend]) - QApplication.restoreOverrideCursor() + new_res = Result(self.weboob, self.app, self) + self.ui.resultsTab.addTab(new_res, id) + self.ui.resultsTab.setCurrentWidget(new_res) + new_res.searchId(id) + def closeEvent(self, ev): self.config.set('settings', 'backend', str(self.ui.backendEdit.itemData( diff --git a/weboob/applications/qcookboob/minirecipe.py b/weboob/applications/qcookboob/minirecipe.py index 5d343b05..caf36cab 100644 --- a/weboob/applications/qcookboob/minirecipe.py +++ b/weboob/applications/qcookboob/minirecipe.py @@ -20,7 +20,7 @@ import urllib from PyQt4.QtGui import QFrame, QImage, QPixmap, QApplication -from PyQt4.QtCore import Qt +from PyQt4.QtCore import Qt, SIGNAL from weboob.applications.qcookboob.ui.minirecipe_ui import Ui_MiniRecipe from weboob.capabilities.base import empty @@ -46,7 +46,12 @@ class MiniRecipe(QFrame): self.ui.shortDescLabel.setText('') self.ui.backendLabel.setText(backend.name) - self.gotThumbnail() + self.connect(self.ui.newTabButton, SIGNAL("clicked()"), self.newTabPressed) + self.connect(self.ui.viewButton, SIGNAL("clicked()"), self.viewPressed) + self.connect(self.ui.viewThumbnailButton, SIGNAL("clicked()"), self.gotThumbnail) + + if self.parent.parent.ui.showTCheck.isChecked(): + self.gotThumbnail() def gotThumbnail(self): if not empty(self.recipe.thumbnail_url): @@ -54,6 +59,18 @@ class MiniRecipe(QFrame): img = QImage.fromData(data) self.ui.imageLabel.setPixmap(QPixmap.fromImage(img).scaledToHeight(100,Qt.SmoothTransformation)) + def viewPressed(self): + QApplication.setOverrideCursor(Qt.WaitCursor) + recipe = self.backend.get_recipe(self.recipe.id) + if recipe: + self.parent.doAction('Details of recipe "%s"' % + recipe.title, self.parent.displayRecipe, [recipe, self.backend]) + + def newTabPressed(self): + recipe = self.backend.get_recipe(self.recipe.id) + self.parent.parent.newTab(u'Details of recipe "%s"' % + recipe.title, self.backend, recipe=recipe) + def enterEvent(self, event): self.setFrameShadow(self.Sunken) QFrame.enterEvent(self, event) @@ -65,8 +82,9 @@ class MiniRecipe(QFrame): def mousePressEvent(self, event): QFrame.mousePressEvent(self, event) - QApplication.setOverrideCursor(Qt.WaitCursor) - recipe = self.backend.get_recipe(self.recipe.id) - if recipe: - self.parent.doAction('Details of recipe "%s"' % - recipe.title, self.parent.displayRecipe, [recipe, self.backend]) + if event.button() == 2: + self.gotThumbnail() + elif event.button() == 4: + self.newTabPressed() + else: + self.viewPressed() diff --git a/weboob/applications/qcookboob/ui/main_window.ui b/weboob/applications/qcookboob/ui/main_window.ui index 0bb74df5..a1069cdf 100644 --- a/weboob/applications/qcookboob/ui/main_window.ui +++ b/weboob/applications/qcookboob/ui/main_window.ui @@ -83,131 +83,27 @@ - - - QFrame::NoFrame + + + -1 - - QFrame::Raised + + true + + + true - - - 0 - - - 0 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 60 - 20 - - - - <<back - - - - - - - - 75 - true - - - - - - - Qt::AlignCenter - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - true - - - - - 0 - 0 - 708 - 261 - - - - - - - - - - - - - - true - - - - - 0 - 0 - 96 - 26 - - - - - - - - + + + + Show thumbnails + + + @@ -228,7 +124,7 @@ 0 0 748 - 23 + 27 @@ -244,21 +140,12 @@ false - Backends - - - Quit - - - Ctrl+Q - - diff --git a/weboob/applications/qcookboob/ui/minirecipe.ui b/weboob/applications/qcookboob/ui/minirecipe.ui index b952ceae..f213a4e6 100644 --- a/weboob/applications/qcookboob/ui/minirecipe.ui +++ b/weboob/applications/qcookboob/ui/minirecipe.ui @@ -6,7 +6,7 @@ 0 0 - 464 + 578 136 @@ -43,20 +43,21 @@ - - - QFormLayout::ExpandingFieldsGrow - - - - - - 75 - true - - + + + - Title + TextLabel + + + + + + + TextLabel + + + true @@ -96,16 +97,6 @@ - - - - TextLabel - - - true - - - @@ -119,13 +110,75 @@ - - + + + + + 75 + true + + - TextLabel + Title + + + + + + + 120 + 20 + + + + View + + + + + + + + 120 + 20 + + + + View in new tab + + + + + + + + 120 + 20 + + + + View thumbnail + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + diff --git a/weboob/applications/qcookboob/ui/result.ui b/weboob/applications/qcookboob/ui/result.ui new file mode 100644 index 00000000..b51eca2e --- /dev/null +++ b/weboob/applications/qcookboob/ui/result.ui @@ -0,0 +1,177 @@ + + + Result + + + + 0 + 0 + 645 + 733 + + + + + 0 + 0 + + + + + 10000 + 10000 + + + + Frame + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 60 + 20 + + + + <<back + + + + + + + + 75 + true + + + + + + + Qt::AlignCenter + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + true + + + + + 0 + 0 + 605 + 667 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + true + + + + + 0 + 0 + 605 + 667 + + + + + + + + + + + + + + +