View Single Post
Old 03-06-2022, 01:45 AM   #27
nikosan
Connoisseur
nikosan doesn't litternikosan doesn't litter
 
Posts: 61
Karma: 198
Join Date: Feb 2022
Device: Kobo Sage (prev. Kindle PW5)
Quote:
Originally Posted by BetterRed View Post
I doubt the database is corrupt, more likely something in Safe mode is preventing the db from being opened - which is the absolute converse of normal expectations - bizarroł on steroids with flummox on the side.

If you have one of the SQLite utilities you could try poking the library database with it in Safe Mode - I use this one ==>> DB Browser for SQLite

BR
I'm just going with the theory that I screwed something up somewhere because I really don't know what I'm doing I don't know anything about how to use SQLite or similar... I feel like I'm wading into waters that are too deep for my swimming ability!


Quote:
Originally Posted by JimmXinu View Post
When doing VL changes, it looks like Action Chains is calling VM's apply_column_and_sort() instead of switch_view(), which I would have expected.

For one thing, it means that the "7.1" time can't be trusted--VM is resetting the time at the start of switch_view(). What you see there for 7.1 includes all the wall-clock time passed since the last interaction. You may be right, but you can't trust this output to tell you.

(Presumably Action Chains is also reading the VM config data needed to call apply_column_and_sort()? I don't know what it does...)
Hmmm, okay. That does explain why some of those numbers are so high... I did think that was weird. It was calculating how long it took me to click on the next library, not from my library clicks.

So this is my Action Chains script... I just copy/pasted from this post. Is there something I should change in that, to make it call switch_view() instead?
PHP Code:
from calibre_plugins.action_chains.actions.base import ChainAction

class MyAction(ChainAction):

    
# VL View
    
name 'VL View Action'

    
def run(selfguisettingschain):
        
pass
import copy
from functools import partial

from qt
.core import (QApplicationQtQWidgetQVBoxLayoutQHBoxLayoutQGridLayout,
                     
QGroupBoxQAbstractTableModelQModelIndexQSizePolicy,
                     
QToolButtonQSpacerItemQIconQBrushpyqtSignal)

from calibre import prints
from calibre
.constants import DEBUG
from calibre
.gui2 import error_dialog
from calibre
.gui2.widgets2 import Dialog

from calibre_plugins
.action_chains.actions.base import ChainAction
from calibre_plugins
.action_chains.common_utils import get_iconViewLog
from calibre_plugins
.action_chains.database import get_valid_vls
from calibre_plugins
.action_chains.gui.delegates import ComboDelegate
from calibre_plugins
.action_chains.gui.models import UPDOWN
from calibre_plugins
.action_chains.gui.views import TableView
import calibre_plugins
.action_chains.config as cfg

ALL_BOOKS 
'_ALL_BOOKS'
KEY_TABS_VIEWS_TABLE_STATE 'tabsViewsTableStates'

def get_vls(db):
    
vls get_valid_vls(db)
    
vls.insert(0ALL_BOOKS)
    return 
vls

def view_manager_views
(gui):
    try:
        
import calibre_plugins.view_manager.config as vm_cfg
        views 
vm_cfg.get_library_config(gui.current_db)[vm_cfg.KEY_VIEWS]
        return 
views
    except
:
        
import traceback
        
print(traceback.format_exc())
        return []

class 
TabsModel(QAbstractTableModel):

    
error pyqtSignal(strstr)

    
def __init__(selfplugin_actiontabs_config=[]):
        
QAbstractTableModel.__init__(self)
        
self.tabs_config tabs_config
        self
.plugin_action plugin_action
        self
.gui self.plugin_action.gui
        self
.db self.gui.current_db
        self
.col_map = ['tab_name','view_name','errors']
        
self.editable_columns = ['tab_name','view_name']
        
#self.hidden_cols = ['errors']
        
self.hidden_cols = []
        
self.col_min_width = {
            
'tab_name'300,
            
'view_name'300
        
}
        
all_headers = [_('VL'), _('View'), _('errors')]
        
self.headers all_headers

    def rowCount
(selfparent):
        if 
parent and parent.isValid():
            return 
0
        
return len(self.tabs_config)

    
def columnCount(selfparent):
        if 
parent and parent.isValid():
            return 
0
        
return len(self.headers)

    
def headerData(selfsectionorientationrole):
        if 
role == Qt.DisplayRole and orientation == Qt.Horizontal:
            return 
self.headers[section]
        
elif role == Qt.DisplayRole and orientation == Qt.Vertical:
            return 
section 1
        
return None

    def data
(selfindexrole):
        if 
not index.isValid():
            return 
None;
        
rowcol index.row(), index.column()
        if 
row or row >= len(self.tabs_config):
            return 
None
        tab_config 
self.tabs_config[row]
        
col_name self.col_map[col]
        
value tab_config.get(col_name'')
        
error tab_config.get('errors''')

        if 
role in [Qt.DisplayRoleQt.UserRoleQt.EditRole]:
            if 
col_name == 'errors':
                if 
error:
                    return 
error
            
else:
                return 
value

        elif role 
== Qt.DecorationRole:
            if 
col_name == 'errors':
                if 
error:
                    return 
QIcon(get_icon('dialog_error.png'))
                
        
elif role == Qt.ToolTipRole:
            if 
col_name == 'errors':
                if 
error:
                    return 
error

        elif role 
== Qt.ForegroundRole:
            
color None
            
if error:
                
color Qt.red
            
if color is not None:
                return 
QBrush(color)

        return 
None

    def setData
(selfindexvaluerole):
        
done False

        row
col index.row(), index.column()
        
tab_config self.tabs_config[row]
        
val str(value).strip()
        
col_name self.col_map[col]
        
        if 
role == Qt.EditRole:
            
# make sure no duplicate event entries
            
if col_name == 'tab_name':
                
old_name self.data(indexQt.DisplayRole)
                
names self.get_names()
                if 
old_name in names:
                    
names.remove(old_name)
                if 
val in names:
                    
msg _('Duplicate vls')
                    
details _('Name ({}) is used in more than one entry'.format(val))
                    
self.error.emit(msgdetails)
                else:
                    
tab_config[col_name] = val
            
else:
                
tab_config[col_name] = val
            done 
True
            
        
return done

    def flags
(selfindex):
        
flags QAbstractTableModel.flags(selfindex)
        if 
index.isValid():
            
tab_config self.tabs_config[index.row()]
            
col_name self.col_map[index.column()]
            if 
col_name in self.editable_columns:
                
flags |= Qt.ItemIsEditable
        
return flags

    def insertRows
(selfrowcountidx):
        
self.beginInsertRows(QModelIndex(), rowrow count 1)
        for 
i in range(0count):
            
tab_config = {}
            
tab_config['tab_name'] = ''
            
tab_config['view_name'] = ''
            
self.tabs_config.insert(row itab_config)
        
self.endInsertRows()
        return 
True

    def removeRows
(selfrowcountidx):
        
self.beginRemoveRows(QModelIndex(), rowrow count 1)
        for 
i in range(0count):
            
self.tabs_config.pop(row i)
        
self.endRemoveRows()
        return 
True

    def move_rows
(selfrowsdirection=DOWN):
        
srows sorted(rowsreverse=direction == DOWN)
        for 
row in srows:
            
pop self.tabs_config.pop(row)
            
self.tabs_config.insert(row+directionpop)
        
self.layoutChanged.emit()

    
def get_names(self):
        
names = []
        
col self.col_map.index('tab_name')
        for 
row in range(self.rowCount(QModelIndex())):
            
index self.index(rowcolQModelIndex())
            
name self.data(indexQt.DisplayRole)
            
# empty name belong to separators, dont include
            
if name:
                
names.append(name)
        return 
names

    def validate
(self):
        for 
tab_config in self.tabs_config:
            
errors = []
            
tab_name tab_config['tab_name']
            
view_name tab_config['view_name']
            if 
tab_name not in get_vls(self.db):
                
errors.append(_('VL is not available'))
            if 
view_name not in view_manager_views(self.gui):
                
errors.append(_('View is not available'))
            if 
errors:
                
tab_config['errors'] = ' ::: '.join(errors)

class 
TabsTable(TableView):

    
def __init__(selfparent):
        
TableView.__init__(selfparent)
        
self.plugin_action parent.plugin_action
        self
.doubleClicked.connect(self._on_double_clicked)
        
self.gui self.plugin_action.gui
        self
.db self.gui.current_db
        self
.horizontalHeader().setStretchLastSection(False)
        
#self.setShowGrid(False)

    
def set_model(self_model):
        
self.setModel(_model)
        
_model.error.connect(lambda *argserror_dialog(self, *argsshow=True))
        
self.col_map _model.col_map

        
# Hide columns
        
for col_name in _model.hidden_cols:
            
col self.col_map.index(col_name)
            
self.setColumnHidden(colTrue)

        
self.tabs_delegate ComboDelegate(selfget_vls(self.db))
        
self.setItemDelegateForColumn(self.col_map.index('tab_name'), self.tabs_delegate)

        
self.views_delegate ComboDelegate(selfview_manager_views(self.gui))
        
self.setItemDelegateForColumn(self.col_map.index('view_name'), self.views_delegate)

        
self.resizeColumnsToContents()
        
# Make sure every other column has a minimum width
        
for col_namewidth in _model.col_min_width.items():
            
col self.col_map.index(col_name)
            
self._set_minimum_column_width(colwidth)

    
def _on_double_clicked(selfindex):
        
self.model()
        
col_name m.col_map[index.column()]
        if 
col_name == 'errors':
            
tab_config m.tabs_config[index.row()]
            
details tab_config.get('errors''')
            
self._view_error_details(details)

    
def _view_error_details(selfdetails):
        
ViewLog(_('Errors details'), detailsself)

class 
ConfigWidget(QWidget):

    
def __init__(selfplugin_action):
        
QWidget.__init__(self)
        
self.plugin_action plugin_action
        self
.gui plugin_action.gui
        self
.db self.gui.current_db
        
#self.tabs_config = tabs_config
        
self._init_controls()

    
def _init_controls(self):
        
self.setWindowTitle(_('Configure views'))
        
self.QVBoxLayout()
        
self.setLayout(l)

        
settings_l QGridLayout()
        
l.addLayout(settings_l)

        
_table_gb QGroupBox(_('Views'))
        
_table_l QHBoxLayout()
        
_table_gb.setLayout(_table_l)
        
l.addWidget(_table_gb)
        
        
self._table TabsTable(self)
        
_table_l.addWidget(self._table)
        
        
_model self._model TabsModel(self.plugin_action)
        
_model.validate()
        
self._table.set_model(_model)
        
self._table.selectionModel().selectionChanged.connect(self._on_table_selection_change)
        
        
# restore table state
        
state cfg.plugin_prefs.get(KEY_TABS_VIEWS_TABLE_STATENone)
        if 
state:
            
self._table.apply_state(state)

        
# Add a vertical layout containing the the buttons to move up/down etc.
        
button_layout QVBoxLayout()
        
_table_l.addLayout(button_layout)
        
        
move_up_button self.move_up_button QToolButton(self)
        
move_up_button.setToolTip(_('Move row up'))
        
move_up_button.setIcon(QIcon(I('arrow-up.png')))
        
button_layout.addWidget(move_up_button)
        
spacerItem1 QSpacerItem(2040QSizePolicy.MinimumQSizePolicy.Expanding)
        
button_layout.addItem(spacerItem1)

        
add_button self.add_button QToolButton(self)
        
add_button.setToolTip(_('Add row'))
        
add_button.setIcon(QIcon(I('plus.png')))
        
button_layout.addWidget(add_button)
        
spacerItem2 QSpacerItem(2040QSizePolicy.MinimumQSizePolicy.Expanding)
        
button_layout.addItem(spacerItem2)

        
delete_button self.delete_button QToolButton(self)
        
delete_button.setToolTip(_('Delete row'))
        
delete_button.setIcon(QIcon(I('minus.png')))
        
button_layout.addWidget(delete_button)
        
spacerItem4 QSpacerItem(2040QSizePolicy.MinimumQSizePolicy.Expanding)
        
button_layout.addItem(spacerItem4)

        
move_down_button self.move_down_button QToolButton(self)
        
move_down_button.setToolTip(_('Move row down'))
        
move_down_button.setIcon(QIcon(I('arrow-down.png')))
        
button_layout.addWidget(move_down_button)

        
move_up_button.clicked.connect(partial(self._table.move_rows,UP))
        
move_down_button.clicked.connect(partial(self._table.move_rows,DOWN))
        
add_button.clicked.connect(self._table.add_row)
        
delete_button.clicked.connect(self._table.delete_rows)
        
        
self._on_table_selection_change()

        
self.setMinimumSize(400300)
        
l.addStretch(1)

    
def _on_table_selection_change(self):
        
sm self._table.selectionModel()
        
selection_count len(sm.selectedRows())
        
self.delete_button.setEnabled(selection_count 0)
        
self.move_up_button.setEnabled(selection_count 0)
        
self.move_down_button.setEnabled(selection_count 0)

    
def save_table_state(self):
        
# save table state
        
cfg.plugin_prefs[KEY_TABS_VIEWS_TABLE_STATE] = self._table.get_state()

    
def load_settings(selfsettings):
        
self._model.tabs_config settings['tabs_config']
        
self._model.validate()
        
self._model.layoutChanged.emit()

    
def save_settings(self):
        
self.save_table_state()
        
        
settings = {}
        
tabs_config self._table.model().tabs_config
        
# remove error keys from event_members
        
for tab_config in tabs_config:
            try:
                
del tab_config['errors']
            
except:
                
pass
        settings
['tabs_config'] = tabs_config
        
return settings


class SwitchToVLView(ChainAction):

    
name 'Switch To VL View'      

    
def run(selfguisettingschain):
        
idx gui.vl_tabs.currentIndex()
        
vl str(gui.vl_tabs.tabData(idx) or '').strip() or ALL_BOOKS
        
print('debug1: vl: {}'.format(vl))
        if 
vl:
            
view self.vl_view_lookup(vlsettings['tabs_config'])
            print(
'debug2: view: {}'.format(view))
            if 
view:
                if 
not view in view_manager_views(gui):
                    if 
DEBUG:
                        
prints('Action Chains: Switch To VL View: view ({}) is not available'.format(view))
                    return                  
                
self.switch_view(guiviewvl)      
            else:
                if 
DEBUG:
                    
prints('Action Chains: Switch To VL View: VL Tab ({}) has no configured view'.format(vl))

    
def vl_view_lookup(selfvltabs_config):
        for 
tab_config in tabs_config:
            if 
vl == tab_config['tab_name']:
                return 
tab_config['view_name']

    
def switch_view(selfguiviewvl):
        
view_manager gui.iactions.get('View Manager')
        if 
view_manager:
            
import calibre_plugins.view_manager.config as vm_cfg
            library_config 
vm_cfg.get_library_config(gui.current_db)
            
view_info copy.deepcopy(library_config[vm_cfg.KEY_VIEWS][view])
            
####
            
view_info[vm_cfg.KEY_APPLY_VIRTLIB] = False
            
####
            
selected_ids gui.library_view.get_selected_ids()
            
# Persist this as the last selected view
            
if library_config.get(vm_cfg.KEY_LAST_VIEWNone) != view:
                
library_config[vm_cfg.KEY_LAST_VIEW] = view
                vm_cfg
.set_library_config(gui.current_dblibrary_config)

#            if view_info.get(vm_cfg.KEY_APPLY_VIRTLIB,False):
#                view_manager.apply_virtlib(view_info[vm_cfg.KEY_VIRTLIB])
            
if view_info[vm_cfg.KEY_APPLY_RESTRICTION]:
                
view_manager.apply_restriction(view_info[vm_cfg.KEY_RESTRICTION])
            if 
view_info[vm_cfg.KEY_APPLY_SEARCH]:
                
view_manager.apply_search(view_info[vm_cfg.KEY_SEARCH])
            
view_manager.apply_column_and_sort(view_info)

            
gui.library_view.select_rows(selected_ids)
            
view_manager.current_view view
            view_manager
.rebuild_menus()
        else:
            if 
DEBUG:
                
prints('Action Chains: Switch To VL View: View Manager Plugin not available')

    
def validate(selfsettings):
        if 
not settings:
            return (
_('Settings Error'), _('You must configure this action before running it'))
        return 
True

    def config_widget
(self):
        return 
ConfigWidget 
Quote:
Originally Posted by JimmXinu View Post
I'm not usually involved in debugging whole libraries. Is it appropriate to ask for a zipped copy of the library to look at? If it's not too private or too copyrighted, that is.
That's really nice of you to offer but I don't think I'm comfortable sharing my whole library... Would it work to share a ZIP of a library with a smaller selection, with all eBook formats removed? "Remove all formats from selected books"? I made a dupe library that way (copied the structure from my main library), and it's giving me the same lags as my full library.
nikosan is offline   Reply With Quote